BASCODE
По-моему уже недели две форсится история про переписывание PostgreSQL на Rust. Наконец готов про это написать. Что это. Человек взял восемь платных аккаунтов Codex по $200 в месяц и гоняет через Conductor по 10-20 сессий разом. Наращивал, пока не уперся в…
Вчера в офисе обсуждали тему автоматического переезда сервиса с одного стека на другой.
Мой поинт был в том, что достаточно хорошо покрыть тестами текущий сервис, а затем переписать его логику на другой язык, придерживаясь парадигм нового стека. Затем доводим новый сервис до того чтобы все тесты были зеленые, и готово!
Чужой опыт показал, что не все так просто. Признаю.
Мой поинт был в том, что достаточно хорошо покрыть тестами текущий сервис, а затем переписать его логику на другой язык, придерживаясь парадигм нового стека. Затем доводим новый сервис до того чтобы все тесты были зеленые, и готово!
Чужой опыт показал, что не все так просто. Признаю.
🔥4
BASCODE
Главное вовремя уйти :-)
Написал себе читалку markdown, которая выключается через секунду после запуска.
Пишу серию из трех статей на Хабр про историю горутин. Это чтиво по 40 минут на трех файлах, и вычитывать их в IDE нечем: моношрифт во всю ширину монитора, ни полей, ни абзацного ритма. Глазами читателя так не посмотришь.
Соседние варианты не подошли. Отдельный вьюер надо ставить и обновлять, а grip поднимает сервер и висит в фоне все то время, пока открыта вкладка.
Три файла складываются в одну страницу, справа сайдбар с документами и оглавлением текущего. Браузер открывается, процесс выходит.
И вкладка после этого продолжает работать. Шрифты, стили, mermaid, katex, картинки в base64 вшиты в тот же HTML, внешних URL там нет ни одного. Закрыл ноут, ушел в офлайн, открыл вкладку завтра: темы переключаются, диаграммы рисуются, переходы между файлами живые. Цена вопроса бинарь на 16 мегабайт (внутри лежит все, что странице может понадобиться). Терпимо.
Темы светлая, темная и бумага. Размеры текста 13, 16 и 20 пикселей: на двадцати сразу видно, где абзац разъехался.
Флаг
Флаг
Честная часть.
https://github.com/dsbasko/rmd
Пишу серию из трех статей на Хабр про историю горутин. Это чтиво по 40 минут на трех файлах, и вычитывать их в IDE нечем: моношрифт во всю ширину монитора, ни полей, ни абзацного ритма. Глазами читателя так не посмотришь.
Соседние варианты не подошли. Отдельный вьюер надо ставить и обновлять, а grip поднимает сервер и висит в фоне все то время, пока открыта вкладка.
rmd {file_0}.md {file_1}.md {file_n}.mdТри файла складываются в одну страницу, справа сайдбар с документами и оглавлением текущего. Браузер открывается, процесс выходит.
И вкладка после этого продолжает работать. Шрифты, стили, mermaid, katex, картинки в base64 вшиты в тот же HTML, внешних URL там нет ни одного. Закрыл ноут, ушел в офлайн, открыл вкладку завтра: темы переключаются, диаграммы рисуются, переходы между файлами живые. Цена вопроса бинарь на 16 мегабайт (внутри лежит все, что странице может понадобиться). Терпимо.
Темы светлая, темная и бумага. Размеры текста 13, 16 и 20 пикселей: на двадцати сразу видно, где абзац разъехался.
Флаг
--edit делает страницу редактируемой. Кликаешь в абзац, он превращается в textarea с markdown-исходником именно этого блока, Cmd+S пишет файлы на диск. Опечатки правлю по ходу чтения, не возвращаясь в IDE.Флаг
--tunnel поднимает рядом ngrok и печатает публичную ссылку, чтобы отдать черновик на вычитку.Честная часть.
--edit вместе с --tunnel это не ссылка "почитать": у кого она есть, тот перепишет файлы у тебя на диске. И картинки инлайнятся без вопросов, так что  в чужом markdown приедет в страницу как base64 и уедет по ссылке вместе с ней. Чужие файлы перед туннелем стоит открыть глазами.brew install dsbasko/tap/rmd
https://github.com/dsbasko/rmd
BASCODE
Написал себе читалку markdown, которая выключается через секунду после запуска. Пишу серию из трех статей на Хабр про историю горутин. Это чтиво по 40 минут на трех файлах, и вычитывать их в IDE нечем: моношрифт во всю ширину монитора, ни полей, ни абзацного…
This media is not supported in your browser
VIEW IN TELEGRAM
Вчера выложил свою читалку markdown, а демку к ней снимать было так впадлу, что я отложил это в долгий ящик.
@Rostislaved в комментариях подметил, что не хватает preview. Справедливо... Просто записывать экран, вырезать дубли и потом пережимать все в гифку не хотелось, от слова совсем.
Сделал по-другому: демо теперь собирается командой, скрипты лежат рядом с кодом.
Терминальную часть снимает VHS. Он печатает команду в настоящий bash и записывает то, что реально ответило. Никакого мокапа, на экране живой прогон с публичной ссылкой.
Страницу пишу через Chrome DevTools Protocol. На каждый кадр скриншот, курсор нарисован поверх, скролл и клики идут по таймлайну. Node дает глобальный WebSocket, так что ни Playwright, ни node_modules не нужны. Кадры собирает в гифку gifski, он же давит цвета и масштабирует.
Снимать демо руками было влом, а потратить пол дня на написание генератора почему-то норм... Автоматизация головного мозга...
P.S. телега режет качество гифки, вот оригинал.
@Rostislaved в комментариях подметил, что не хватает preview. Справедливо... Просто записывать экран, вырезать дубли и потом пережимать все в гифку не хотелось, от слова совсем.
Сделал по-другому: демо теперь собирается командой, скрипты лежат рядом с кодом.
make demo
Терминальную часть снимает VHS. Он печатает команду в настоящий bash и записывает то, что реально ответило. Никакого мокапа, на экране живой прогон с публичной ссылкой.
Страницу пишу через Chrome DevTools Protocol. На каждый кадр скриншот, курсор нарисован поверх, скролл и клики идут по таймлайну. Node дает глобальный WebSocket, так что ни Playwright, ни node_modules не нужны. Кадры собирает в гифку gifski, он же давит цвета и масштабирует.
Снимать демо руками было влом, а потратить пол дня на написание генератора почему-то норм... Автоматизация головного мозга...
P.S. телега режет качество гифки, вот оригинал.
🔥2
select не опрашивает каналы по кругу.
Пишу для Хабра серию из трех статей про горутины и по дороге выкидываю из нее целые куски. Механизм интересный, а главу уводит в сторону, вот и режу саму статью. Складывать их некуда, так что буду выкладывать сюда. Первые два, оба проверяются за пять минут.
Первое. Когда готовы сразу несколько каналов,
Ровно по трети. Без перестановки первый case побеждал бы всегда, а остальные голодали.
Рядом живет второй порядок обхода, совсем про другое:
Второе.
Восемь горутин дерутся за один замок, критическая секция 30 мкс, я меряю собственное ожидание:
Максимум в двух прогонах подряд встал на 1.076 и 1.079 мс. Это не случайность, это тот самый порог: как только ожидание переваливает за миллисекунду, обгонять больше не дают.
Код обоих замеров лежит в репозитории, каталоги select_fair и mutex_starve.
Деталь, которая удивила меня сильнее всего. Очередь ждущих живет не в мьютексе. В нем 8 байт, очередь туда не влезет физически. Все ждущие лежат в глобальной таблице семафоров рантайма: 251 корзина, в каждой сбалансированное дерево sema.go:49. То есть твой мьютекс не хранит своих ждущих, он хранит только число, а очередь ему подбирают по хешу адреса...
Пишу для Хабра серию из трех статей про горутины и по дороге выкидываю из нее целые куски. Механизм интересный, а главу уводит в сторону, вот и режу саму статью. Складывать их некуда, так что буду выкладывать сюда. Первые два, оба проверяются за пять минут.
Первое. Когда готовы сразу несколько каналов,
select берет не первый по порядку. Он строит pollorder, случайную перестановку кейсов select.go:191, и идет уже по ней. Три всегда готовых канала, 300 тысяч итераций:case 1 (в исходнике 1-й): 100216 33.4%
case 2 (в исходнике 2-й): 99812 33.3%
case 3 (в исходнике 3-й): 99972 33.3%
Ровно по трети. Без перестановки первый case побеждал бы всегда, а остальные голодали.
Рядом живет второй порядок обхода, совсем про другое:
lockorder select.go:206 сортирует каналы по адресу hchan. Это защита от дедлока, когда два select работают на одном наборе каналов. Классическое "бери локи в одном порядке", только внутри рантайма.Второе.
sync.Mutex умеет голодать. Если горутина прождала замок дольше миллисекунды (starvationThresholdNs = 1e6, internal/sync/mutex.go:55, мьютекс переходит в режим голодания и дальше вручает замок первому в очереди напрямую, без розыгрыша.Восемь горутин дерутся за один замок, критическая секция 30 мкс, я меряю собственное ожидание:
среднее ожидание: 125.855µs
максимум ожидания: 1.076358ms
макс. обгонов: 34
Максимум в двух прогонах подряд встал на 1.076 и 1.079 мс. Это не случайность, это тот самый порог: как только ожидание переваливает за миллисекунду, обгонять больше не дают.
Код обоих замеров лежит в репозитории, каталоги select_fair и mutex_starve.
Деталь, которая удивила меня сильнее всего. Очередь ждущих живет не в мьютексе. В нем 8 байт, очередь туда не влезет физически. Все ждущие лежат в глобальной таблице семафоров рантайма: 251 корзина, в каждой сбалансированное дерево sema.go:49. То есть твой мьютекс не хранит своих ждущих, он хранит только число, а очередь ему подбирают по хешу адреса...
🔥1🤯1
Про процессы... Часто думаю о их важности, и неочевидности.
Онбординг расписан по дням: доступы, бадди, первый PR за неделю. А оффбординг это обходной лист.
Наткнулся на Launch HN стартапа Hypercubic, они мигрируют мейнфреймы. Объясняют, почему такие проекты валятся: COBOL распарсит кто угодно, даже агент агент. А вот зачем в 1995 в биллинг добавили конкретный кейс и что сломается, если его убрать, ответить уже некому. Средний возраст инженеров, которые помнят, 55 лет.
Код читается за вечер. Причина не восстанавливается, ее нет в репозитории.
Мне кажется что об этом нужно думать не в процессе ухода, а в повседневной работе. Что я думаю поможет? Наверное ADR рядом с кодом: проблема, варианты, решение, последствия. И практика Контура: когда эксперт уходит, документацию пишут остающиеся, а он отвечает на вопросы.
Прикинул на себе. Если я завтра выпаду на месяц или два, задачи разберут за день. А вопросы "почему тут так" уйдут в никуда...
Онбординг расписан по дням: доступы, бадди, первый PR за неделю. А оффбординг это обходной лист.
Наткнулся на Launch HN стартапа Hypercubic, они мигрируют мейнфреймы. Объясняют, почему такие проекты валятся: COBOL распарсит кто угодно, даже агент агент. А вот зачем в 1995 в биллинг добавили конкретный кейс и что сломается, если его убрать, ответить уже некому. Средний возраст инженеров, которые помнят, 55 лет.
Код читается за вечер. Причина не восстанавливается, ее нет в репозитории.
Мне кажется что об этом нужно думать не в процессе ухода, а в повседневной работе. Что я думаю поможет? Наверное ADR рядом с кодом: проблема, варианты, решение, последствия. И практика Контура: когда эксперт уходит, документацию пишут остающиеся, а он отвечает на вопросы.
Прикинул на себе. Если я завтра выпаду на месяц или два, задачи разберут за день. А вопросы "почему тут так" уйдут в никуда...
🔥1
Ядро готово отдать 501 995 потоков. Мой тест умер на 75 275.
Меряю потолки потоков для серии, которую пишу. Думал, упрусь в константу ядра, а уперся в systemd. В статью эта развилка не влезет, утянет главу в тюнинг ядра, а здесь ей самое место.
Тест примитивный:
Обычный терминал.
В
А теперь то, ради чего я вообще это писал. Потолок не константа, он едет за лимитами:
Снимаю потолок systemd и поднимаю
Каждый раз тот же
Есть и еще один потолок, до которого я не добрался.
Оба теста лежат в репозитории, каталоги pthread_limit и pthread_cost: собираются одним gcc, порт под macOS рядом.
Вывод у меня скорее эксплуатационный. Если сервис уперся в потолок потоков, смотреть надо не в характеристики машины, а в
Меряю потолки потоков для серии, которую пишу. Думал, упрусь в константу ядра, а уперся в systemd. В статью эта развилка не влезет, утянет главу в тюнинг ядра, а здесь ей самое место.
Тест примитивный:
pthread_create в бесконечном цикле, каждый поток сразу засыпает навсегда. Смотрю, на каком номере ядро скажет хватит.Обычный терминал.
поток #75275 не создался: Resource temporarily unavailable (errno 11)
В
/proc/sys/kernel/threads-max при этом лежит 501995, в ulimit -u 250997, но до них дело не дошло. Обрубил не ядро, а systemd: DefaultTasksMax выдает моему scope 75299 задач, ровно 15% от threads-max.А теперь то, ради чего я вообще это писал. Потолок не константа, он едет за лимитами:
ulimit -u 30000 # обрыв на потоке #29276
ulimit -u 60000 # обрыв на потоке #59276
Снимаю потолок systemd и поднимаю
ulimit:systemd-run --user --scope -q -p TasksMax=infinity bash -c 'ulimit -u 150000; /tmp/pthread_limit'
поток #149273 не создался: Resource temporarily unavailable (errno 11)
Каждый раз тот же
errno, и каждый раз обрыв в другом месте. До круглого числа ulimit не дотягивает, потому что RLIMIT_NPROC считает все задачи пользователя, а не только мои. И ни разу это не ядро: threads-max так и остался нетронутым на полумиллионе. На маке, где я мерил до этого, лестницы нет вовсе: потолок прибит константой sysctl kern.num_taskthreads, прогон обрывался на потоке #4096.Есть и еще один потолок, до которого я не добрался.
ulimit -s равен 8192 КБ, то есть каждый поток резервирует под стек 8 МБ. Отдельным замером: десять тысяч потоков забрали 80 ГБ виртуального адресного пространства и 81 МБ физической. Системе все равно, страницы никто не трогает.Оба теста лежат в репозитории, каталоги pthread_limit и pthread_cost: собираются одним gcc, порт под macOS рядом.
Вывод у меня скорее эксплуатационный. Если сервис уперся в потолок потоков, смотреть надо не в характеристики машины, а в
pids.max того cgroup, где он живет. Ядро готово дать полмиллиона, а обычный терминал получил 75 тысяч. В кубере поверх этого ляжет еще и podPidsLimit :-)🔥1
Указатель на локальную переменную пережил переезд стека на 2 мегабайта.
Стек горутины стартует с 2 КБ и растет удвоением. Растет он не на месте: рантайм берет новый кусок памяти, копирует туда старый целиком и чинит все указатели, которые смотрели внутрь старого.
Проверяю. Беру адрес локальной переменной, загоняю горутину в рекурсию на 20 тысяч кадров, беру адрес той же переменной снова:
Переменная уехала на 2 МБ и осталась собой. Ни одна строчка моего кода про это не узнала.
Go про все это знает сам, компилятор оставляет карты указателей на каждый кадр. Отсюда и цена горутины в 2 КБ. Можно стартовать с крохотного стека, потому что если не хватит, он переедет.
Код замера лежит в репозитории, каталог stack_move:
Механизм целиком, вместе с починкой указателей по картам компилятора, пойдет в серию статей, которую пишу на выходных. Возможно на этой неделе уже выложу 1 часть из 3.
Стек горутины стартует с 2 КБ и растет удвоением. Растет он не на месте: рантайм берет новый кусок памяти, копирует туда старый целиком и чинит все указатели, которые смотрели внутрь старого.
Проверяю. Беру адрес локальной переменной, загоняю горутину в рекурсию на 20 тысяч кадров, беру адрес той же переменной снова:
адрес до переезда: 0x107b57a64f50
адрес после: 0x107b57c5ff50
сдвиг: 2076672 байт
значение x: 42
Переменная уехала на 2 МБ и осталась собой. Ни одна строчка моего кода про это не узнала.
Go про все это знает сам, компилятор оставляет карты указателей на каждый кадр. Отсюда и цена горутины в 2 КБ. Можно стартовать с крохотного стека, потому что если не хватит, он переедет.
Код замера лежит в репозитории, каталог stack_move:
go run ./stack_move, адреса на твоей машине будут свои.Механизм целиком, вместе с починкой указателей по картам компилятора, пойдет в серию статей, которую пишу на выходных. Возможно на этой неделе уже выложу 1 часть из 3.
🔥5
Карьера это сумма решений, которые в отношении нас принимают другие люди.
Фраза Михаила Завилейского, многолетнего топ-менеджера DataArt. Вспомнилась она на 69 выпуске подкаста
Оффер, грейд, зарплата, сложный проект, место в команде, сокращение и т.д. Ни одно из этих решений ты не принимаешь сам. Влиять можешь, готовиться можешь, но кнопку всеровно нажимает кто-то другой.
И вот вопрос, о котором задумывается мало инженеров: кто эти люди? По каким принципам они решают? Что для них важно? Какую часть твоей работы и нанесенной ценности они вообще видят?
Мы неделями спорим про архитектуру, вылизываем пайплайны, зубрим новые технологии и инструменты. Вкладываемся в то, что видим сами и можем пощупать. А что из этого доходит до людей, чьи решения потом складываются в твою карьеру, не знаем. Часто не знаем даже их списка.
Чем дольше кручу эту мысль, тем сильнее подозреваю, что реальный список этих людей отличается от того, который рисуется в голове...
Фраза Михаила Завилейского, многолетнего топ-менеджера DataArt. Вспомнилась она на 69 выпуске подкаста
Тысячи фичей про психологию инженера, где гостем был Александр Орлов из Стратоплана.Оффер, грейд, зарплата, сложный проект, место в команде, сокращение и т.д. Ни одно из этих решений ты не принимаешь сам. Влиять можешь, готовиться можешь, но кнопку всеровно нажимает кто-то другой.
И вот вопрос, о котором задумывается мало инженеров: кто эти люди? По каким принципам они решают? Что для них важно? Какую часть твоей работы и нанесенной ценности они вообще видят?
Мы неделями спорим про архитектуру, вылизываем пайплайны, зубрим новые технологии и инструменты. Вкладываемся в то, что видим сами и можем пощупать. А что из этого доходит до людей, чьи решения потом складываются в твою карьеру, не знаем. Часто не знаем даже их списка.
Чем дольше кручу эту мысль, тем сильнее подозреваю, что реальный список этих людей отличается от того, который рисуется в голове...
🔥3💯2
Миллион горутин уместился в 2.6 ГБ.
Еще вчера дописал статью, где это меряется.
Первая часть серии про то, из чего физически сделан оператор
Зашел с вопроса, который на собеседованиях задают все, а понимают единицы: почему поток дорогой, а горутина нет. Все замеры свои, на одной машине (i9-14900KF, Linux), рядом тридцать распакованных релизов Go для сверки фактов.
Пара цифр, которых в канале еще не было.
- Миллион горутин: 2.6 ГБ, по 2049 байт стека и 601 байту кучи на штуку.
- Переключение: 106 нс у горутины против 1360 нс у потока, причем мерил при
Потолок, на котором недавно умер мой тест, в статье дожат до конца: ядро было готово отдать полмиллиона, обрубил
Вторая часть через неделю: переезжающий стек и вытеснение сигналом ОС. Статья в целом готова, жду релиза 1.27, финальных замеров и вычитки.
Статья: https://habr.com/ru/articles/1070050/
Код всех замеров: https://github.com/dsbasko/sandbox-gorutines
Еще вчера дописал статью, где это меряется.
Первая часть серии про то, из чего физически сделан оператор
go f(). Та самая, из которой сюда уезжали выкинутые куски: select с мьютексом, потолки потоков, переезд стека.Зашел с вопроса, который на собеседованиях задают все, а понимают единицы: почему поток дорогой, а горутина нет. Все замеры свои, на одной машине (i9-14900KF, Linux), рядом тридцать распакованных релизов Go для сверки фактов.
Пара цифр, которых в канале еще не было.
- Миллион горутин: 2.6 ГБ, по 2049 байт стека и 601 байту кучи на штуку.
- Переключение: 106 нс у горутины против 1360 нс у потока, причем мерил при
GOMAXPROCS=1. Дешевизна не из параллельности, ядро в переключении горутины не участвует вообще.Потолок, на котором недавно умер мой тест, в статье дожат до конца: ядро было готово отдать полмиллиона, обрубил
systemd.Вторая часть через неделю: переезжающий стек и вытеснение сигналом ОС. Статья в целом готова, жду релиза 1.27, финальных замеров и вычитки.
Статья: https://habr.com/ru/articles/1070050/
Код всех замеров: https://github.com/dsbasko/sandbox-gorutines
🔥3❤1👏1
Есть долг, которого нет ни в одном бэклоге.
Например я отвечаю за куски системы, которые кроме меня не понимает никто.
Технический долг мы считать научились: линтеры, покрытие, тикет "отрефакторить потом". А вот для когнитивного долга нет даже метрики. Раньше этим болели лиды и архитекторы, подписывающие то, чего сами не пересчитывали. Теперь так у каждого, у кого открыт чат с агентом.
На прошлой неделе про это наткнулся сразу в двух местах. Джеффри Литт на HN, Understanding is the new bottleneck. И почти день в день разбор на Хабре, где термин назвали прямо: когнитивный долг это разрыв между пониманием, которое у тебя есть, и ответственностью, которую ты уже взял.
Ловлю это у себя на ревью. Прилетает MR, код рабочий, тесты зеленые, глазами читается нормально. Спрашиваю автора, почему ретрай на три попытки, а не на пять. Пауза... Внятного ответа нет.
Поэтому взял за правило: перед аппрувом один вопрос по любому месту в коде. Есть ответ, мержим. Нет ответа, разбираемся сейчас, пока автор еще помнит, что там вообще происходило.
Через месяц спрашивать будет уже не у кого. А отвечать за этот ретрай будет и тот кто написал код, и тот кто поставил под ним аппрув.
Например я отвечаю за куски системы, которые кроме меня не понимает никто.
Технический долг мы считать научились: линтеры, покрытие, тикет "отрефакторить потом". А вот для когнитивного долга нет даже метрики. Раньше этим болели лиды и архитекторы, подписывающие то, чего сами не пересчитывали. Теперь так у каждого, у кого открыт чат с агентом.
На прошлой неделе про это наткнулся сразу в двух местах. Джеффри Литт на HN, Understanding is the new bottleneck. И почти день в день разбор на Хабре, где термин назвали прямо: когнитивный долг это разрыв между пониманием, которое у тебя есть, и ответственностью, которую ты уже взял.
Ловлю это у себя на ревью. Прилетает MR, код рабочий, тесты зеленые, глазами читается нормально. Спрашиваю автора, почему ретрай на три попытки, а не на пять. Пауза... Внятного ответа нет.
Поэтому взял за правило: перед аппрувом один вопрос по любому месту в коде. Есть ответ, мержим. Нет ответа, разбираемся сейчас, пока автор еще помнит, что там вообще происходило.
Через месяц спрашивать будет уже не у кого. А отвечать за этот ретрай будет и тот кто написал код, и тот кто поставил под ним аппрув.
👍2❤1🔥1
В рантайме Go есть два места, подписанных "деликатный танец". Оба про то, как не повесить программу насмерть.
Нашел их, пока копал рантайм для серии статьей, которую пишу для хабра. В статью влезет только один, и то одним абзацем, так что вот оба целиком.
Первый 💃
Живет в
Комментарий в коде диктует порядок действий:
Сначала снять с себя признак крутящегося, потом барьер памяти
Второй 💃🏻
Живет в
Семафор зовет
Читать рантайм ради таких комментариев отдельное удовольствие. Редкий жанр, где инженер объясняет не что делает код, а от какого именно невоспроизводимого ужаса он защищает...
Нашел их, пока копал рантайм для серии статьей, которую пишу для хабра. В статью влезет только один, и то одним абзацем, так что вот оба целиком.
Первый 💃
Живет в
findRunnable, функции, которой каждый поток ищет себе работу proc.go:3622. Ситуация такая. Поток не нашел горутин, покрутился вхолостую и решил засыпать. Ровно в этот момент другой поток кладет новую работу в очередь, проверяет, есть ли кто-то бодрствующий, видит нашего (тот еще числится крутящимся) и потому никого не будит. Через миг наш засыпает. Работа лежит, все спят.Комментарий в коде диктует порядок действий:
We must drop nmspinning first and then check all sources again (with #StoreLoad memory barrier in between). If we do it the other way around, another thread can submit work after we've checked all sources but before we drop nmspinning; as a result nobody will unpark a thread to run the work.
Сначала снять с себя признак крутящегося, потом барьер памяти
StoreLoad, потом заново перепроверить все источники работы: локальные очереди, глобальную, таймеры, работу сборщика мусора. Обратный порядок ломает все.Второй 💃🏻
Живет в
acquireSudog proc.go:488, и он смешнее:Delicate dance: the semaphore implementation calls acquireSudog, acquireSudog calls new(sudog), new calls malloc, malloc can call the garbage collector, and the garbage collector calls the semaphore implementation in stopTheWorld.
Семафор зовет
acquireSudog, тот зовет new, new зовет malloc, malloc может запустить сборщик мусора, а сборщик в stopTheWorld зовет семафор. Кольцо замкнулось. Рвут его грубо: на время new(sudog) поднимают счетчик m.locks, и сборщик просто не может стартовать.Читать рантайм ради таких комментариев отдельное удовольствие. Редкий жанр, где инженер объясняет не что делает код, а от какого именно невоспроизводимого ужаса он защищает...
❤1👍1
Интересная беседа с гофером из Gopher Club Николая.
Сегодня познакомился с интересным и глубоким человеком. Новый знакомый уже несколько лет вкладывается в столярное дело. На старости лет хочет строить деревянные яхты. Из софта при этом не уходит. Мечтает и остается.
От него же фраза, которую я утащил на подумать. "Людям придется компенсировать внешние изменения внутренними."
Сначала я прочитал ее как "готовь запасной аэродром". Мечта об уходе сейчас у каждого второго. Столярка, кофейня, ферма, что угодно, где предметы вместо тикетов.
Только уход это внешнее изменение, а не внутреннее. Из реки в реку гарантировано переезжает только гребец, со всеми привычками. И в новой реке тоже течет. Тот же чел сам и добил. Нет сферы, где ничего бы не менялось. Просто чужое течение с берега выглядит спокойнее.
Проверял на себе. С конца 2025 кранчил каждый день, дошло до проблем со здоровьем. Смена работы ничего бы не решила. Привычку кранчить я увез бы с собой вместе с ноутбуком. Кранчить ведь можно и яхты. Починило внутреннее, границы и режим. Снаружи не поменялось ничего, та же команда, та же река.
А мечта работает не исполнением, а наличием. Яхт еще нет и, возможно, не будет. Но глагол уже поменялся. Не "застрял в софте", а "остаюсь в софте".
P.S. Сегодня мне исполнилось 36 лет. И за это время я успел понять, что должен меняться гребец, а не река.
Сегодня познакомился с интересным и глубоким человеком. Новый знакомый уже несколько лет вкладывается в столярное дело. На старости лет хочет строить деревянные яхты. Из софта при этом не уходит. Мечтает и остается.
От него же фраза, которую я утащил на подумать. "Людям придется компенсировать внешние изменения внутренними."
Сначала я прочитал ее как "готовь запасной аэродром". Мечта об уходе сейчас у каждого второго. Столярка, кофейня, ферма, что угодно, где предметы вместо тикетов.
Только уход это внешнее изменение, а не внутреннее. Из реки в реку гарантировано переезжает только гребец, со всеми привычками. И в новой реке тоже течет. Тот же чел сам и добил. Нет сферы, где ничего бы не менялось. Просто чужое течение с берега выглядит спокойнее.
Проверял на себе. С конца 2025 кранчил каждый день, дошло до проблем со здоровьем. Смена работы ничего бы не решила. Привычку кранчить я увез бы с собой вместе с ноутбуком. Кранчить ведь можно и яхты. Починило внутреннее, границы и режим. Снаружи не поменялось ничего, та же команда, та же река.
А мечта работает не исполнением, а наличием. Яхт еще нет и, возможно, не будет. Но глагол уже поменялся. Не "застрял в софте", а "остаюсь в софте".
P.S. Сегодня мне исполнилось 36 лет. И за это время я успел понять, что должен меняться гребец, а не река.
❤3🔥3🎉2
Одна переменная окружения возвращает Go в 2013 год.
Вышла вторая часть серии про рантайм. Первая была про то, почему поток дорогой, а горутина нет. Эта про механизмы, которых из кода не видно: стек, который переезжает по памяти целиком, вытеснение сигналом ОС и полный жизненный цикл горутины.
Шесть лет в Go существовал цикл, который невозможно было прервать. Крутится без вызовов функций, вытеснять нечем, а сборщику мусора нужна остановка мира. Вот все и вставало насмерть. Приехало это в 1.2, починили сигналом только в 1.14.
Потрогать можно прямо сейчас, современным компилятором:
Программа виснет до первой строчки вывода. Без переменной отрабатывает за миллисекунды.
Что еще в статье:
- Вытеснение прилетает сигналом SIGURG, а обработчик подделывает вызов функции, которого в коде не было. Увидишь под strace ливень SIGURG, это не сломанная сеть и не атака, это планировщик работает.
- Дамп горутин разобран построчно: номер, причина ожидания, минуты зависания, created by с точным местом рождения. Самый практичный навык из всей серии, окупается на первом же инциденте.
- Детектор дедлоков в проде не сработает никогда. Он считает живые потоки, а не разбирает граф ожиданий, поэтому сотня зависших горутин при работающем HTTP-сервере для него невидима.
Третья часть через неделю... Я надеюсь :-)
https://habr.com/ru/articles/1073044/
Вышла вторая часть серии про рантайм. Первая была про то, почему поток дорогой, а горутина нет. Эта про механизмы, которых из кода не видно: стек, который переезжает по памяти целиком, вытеснение сигналом ОС и полный жизненный цикл горутины.
Шесть лет в Go существовал цикл, который невозможно было прервать. Крутится без вызовов функций, вытеснять нечем, а сборщику мусора нужна остановка мира. Вот все и вставало насмерть. Приехало это в 1.2, починили сигналом только в 1.14.
Потрогать можно прямо сейчас, современным компилятором:
GODEBUG=asyncpreemptoff=1 go run ./preempt
Программа виснет до первой строчки вывода. Без переменной отрабатывает за миллисекунды.
Что еще в статье:
- Вытеснение прилетает сигналом SIGURG, а обработчик подделывает вызов функции, которого в коде не было. Увидишь под strace ливень SIGURG, это не сломанная сеть и не атака, это планировщик работает.
- Дамп горутин разобран построчно: номер, причина ожидания, минуты зависания, created by с точным местом рождения. Самый практичный навык из всей серии, окупается на первом же инциденте.
- Детектор дедлоков в проде не сработает никогда. Он считает живые потоки, а не разбирает граф ожиданий, поэтому сотня зависших горутин при работающем HTTP-сервере для него невидима.
Третья часть через неделю... Я надеюсь :-)
https://habr.com/ru/articles/1073044/
👍2⚡1🔥1
Дженерик методы в Go 1.27 приехали, а главную боль не закрыли.
Дали метод конкретного типа, теперь он объявляет свои параметры типа:
Собрал на go1.27.0, работает. Раньше дженерик выносили в функцию уровня пакета и корежили читаемость ради компилятора.
А вот возможности положить такой метод в интерфейс по прежнему нельзя, а просили по большей части именно это. Проверил оба захода. В лоб компилятор отвечает сразу:
Второй заход хитрее, объявляем интерфейс без дженерика и пробуем удовлетворить его дженерик-методом. Тоже мимо:
Причина не в том, что кто-то не дописал код. Интерфейс диспетчеризуется в рантайме через итаб, таблицу с одним указателем на метод. У дженерик-метода инстанциаций столько, сколько типов подставят вызывающие, и часть из них появится в чужом пакете, который соберут через год. В момент сборки типа этого списка еще нет.
Формулировка в самом proposal мягче моей: "не умеем реализовать вызовы таких методов, по крайней мере эффективно".
Так что типобезопасные плагины через интерфейсы стоят в очереди не за релизом, а за идеей, как это исполнять...
Дали метод конкретного типа, теперь он объявляет свои параметры типа:
func (n *Node[T]) Map[U any](f func(T) U) Node[U]
Собрал на go1.27.0, работает. Раньше дженерик выносили в функцию уровня пакета и корежили читаемость ради компилятора.
А вот возможности положить такой метод в интерфейс по прежнему нельзя, а просили по большей части именно это. Проверил оба захода. В лоб компилятор отвечает сразу:
interface method must have no type parameters
Второй заход хитрее, объявляем интерфейс без дженерика и пробуем удовлетворить его дженерик-методом. Тоже мимо:
*Node[int] does not implement Stringer (wrong type for method Map)
have Map[U any](func(int) U) Node[U]
want Map(func(int) string) Node[string]
Причина не в том, что кто-то не дописал код. Интерфейс диспетчеризуется в рантайме через итаб, таблицу с одним указателем на метод. У дженерик-метода инстанциаций столько, сколько типов подставят вызывающие, и часть из них появится в чужом пакете, который соберут через год. В момент сборки типа этого списка еще нет.
Формулировка в самом proposal мягче моей: "не умеем реализовать вызовы таких методов, по крайней мере эффективно".
Так что типобезопасные плагины через интерфейсы стоят в очереди не за релизом, а за идеей, как это исполнять...
This media is not supported in your browser
VIEW IN TELEGRAM
dndmode с 1.3.0 уже дорос до 3.4.1.
Если пропустил: это CLI, который накрывает все экраны мака, блокирует клавиатуру и трекпад на уровне HID и держит систему бодрой. Фоновая задача пашет дальше, прохожий ничего не потыкает. Теперь коротко о том что изменилось...
Хоткея больше нет. Вместо одной комбинации теперь
Набираешь вслепую... Ни подсказок, ни обратной связи. dndmode сверяет хвост набранного и снимает блок когда совпало. Наружу не проходит ни одно нажатие, даже правильное.
Пароль открытым текстом в конфиге мне не нравился. Поэтому, можем его спрятать:
CLI попросит дважды набрать код настоящими нажатиями, и положит в конфиг соль с хешем.
Еще одна удобная штука, фоновый режим. Раньше приходилось запускать блок экрана через dndmode в терминале, сейчас для этого можно запустить фоновый процесс:
Уходит в фон и ждет комбинацию клавиш
😩 А теперь самое важное изменение за всю историю проекта 💪
Экран заливается йоптаскриптом. Кириллица, отборный мат, бесконечный скролл. Кто подойдет к маку без меня, читает не мой код, а вот эту хуйню. Нрайса!
Ну и сайт появился... Остальное можно было и не делать :-)
https://dsbasko.github.io/dndmode/
https://github.com/dsbasko/dndmode
Если пропустил: это CLI, который накрывает все экраны мака, блокирует клавиатуру и трекпад на уровне HID и держит систему бодрой. Фоновая задача пашет дальше, прохожий ничего не потыкает. Теперь коротко о том что изменилось...
Хоткея больше нет. Вместо одной комбинации теперь
unlock_code, по сути это пароль на стероидах:unlock_code: s w o r d f i s h
Набираешь вслепую... Ни подсказок, ни обратной связи. dndmode сверяет хвост набранного и снимает блок когда совпало. Наружу не проходит ни одно нажатие, даже правильное.
Пароль открытым текстом в конфиге мне не нравился. Поэтому, можем его спрятать:
dndmode --set-password
CLI попросит дважды набрать код настоящими нажатиями, и положит в конфиг соль с хешем.
cat ~/.config/dndmode/config.yml больше ничего не выдает. Честная часть: это защита от взгляда через плечо и от улетевшего в бэкап конфига, а не от офлайн-перебора. Внутри обычный SHA-256 с солью, не KDF.Еще одна удобная штука, фоновый режим. Раньше приходилось запускать блок экрана через dndmode в терминале, сейчас для этого можно запустить фоновый процесс:
dndmode --watch
Уходит в фон и ждет комбинацию клавиш
Ctrl+Option+Cmd+D, которая заблокирует экран. dndmode --status покажет PID, аптайм и поднят ли щит прямо сейчас, dndmode --kill его прибьет. Комбинация меняется в конфиге ключом activate_hotkey, подхватится после рестарта (kill, потом watch).dndmode --style terminal:ys
Экран заливается йоптаскриптом. Кириллица, отборный мат, бесконечный скролл. Кто подойдет к маку без меня, читает не мой код, а вот эту хуйню. Нрайса!
Ну и сайт появился... Остальное можно было и не делать :-)
https://dsbasko.github.io/dndmode/
https://github.com/dsbasko/dndmode
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Распаковал 30 релизов Go подряд, 2.9 ГБ исходников. Ради одного вопроса.
Вопрос такой: почему рантайм устроен именно так, а не иначе. Релизноты-то можно вычитать, там все написано. А на "почему" в них ответа нет вообще.
Единственный способ, который у меня сработал: поставить рядом proc.c времен Go 1.0 и proc.go 2026-го и читать их как две редакции одной книги.
Что видно, когда листаешь подряд:
2009, одна очередь под одним замком и
1.1, схема GMP, локальные очереди, воровство работы
1.2, горутину стало можно прервать, хаком поверх проверки стека
1.3 и 1.4, стеки научились переезжать по памяти и ужались до 2 КБ
1.14, вытеснение сигналом SIGURG
1.25,
1.26, рантайм научился замечать утекшие горутины
А теперь то, ради чего я это все и затевал. Семантика двух символов
Обычно "обратная совместимость" звучит как оправдание для легаси. Здесь наоборот: интерфейс зафиксировали так удачно, что под ним можно было переделать вообще все.
Хоть и с запозданием в три дня, но финальная третья часть статьи готова!
https://habr.com/ru/articles/1077498/
Вопрос такой: почему рантайм устроен именно так, а не иначе. Релизноты-то можно вычитать, там все написано. А на "почему" в них ответа нет вообще.
Единственный способ, который у меня сработал: поставить рядом proc.c времен Go 1.0 и proc.go 2026-го и читать их как две редакции одной книги.
Что видно, когда листаешь подряд:
2009, одна очередь под одним замком и
GOMAXPROCS=1 по умолчанию, с честным комментарием авторов, что цель сейчас просто чтобы хоть что-то работало1.1, схема GMP, локальные очереди, воровство работы
1.2, горутину стало можно прервать, хаком поверх проверки стека
1.3 и 1.4, стеки научились переезжать по памяти и ужались до 2 КБ
1.14, вытеснение сигналом SIGURG
1.25,
GOMAXPROCS сам читает лимит cgroup1.26, рантайм научился замечать утекшие горутины
А теперь то, ради чего я это все и затевал. Семантика двух символов
go f() не менялась с 2009 года ни разу. Внутренности под ней переписали трижды, включая полный переезд рантайма с C на Go, и ни одна программа от этого не сломалась.Обычно "обратная совместимость" звучит как оправдание для легаси. Здесь наоборот: интерфейс зафиксировали так удачно, что под ним можно было переделать вообще все.
Хоть и с запозданием в три дня, но финальная третья часть статьи готова!
https://habr.com/ru/articles/1077498/
🔥6
Рубрика "лысая мудрость"
Как я больше года храбро готовился, но так ничего и не сделал.
Второго сентября на HN мне на глаза попалось эссе Exit the Cave. Там была мысль, после которой я перестал себе нравиться: "есть вид трусости, который очень похож на дисциплину". Автор занимается борьбой и делит все на входы и результат. Часы на мате и аккуратный дневник тренировок меряются легко, но не стоят ничего. По настоящему стоит только схватка, где тебя при свидетелях кладут на лопатки.
Ну и смотрю я на себя...
Youtube серия про тамагочи на Go готовится с августа 2025 года, когда я отдыхая в Киргизии решил что буду это делать... Сценарий переписан четыре раза, репозиторий готов и разложен на 17 шагов, design.md вылизан так, что хоть в учебник вставляй. Красиво...
Только вот роликов на youtube0️⃣
И ведь не скажешь, что бездельничал... Много вечеров садился и что-то улучшал, если конечно не писал статьи или не изучал какуето другую тему. Дисциплина же!
Обратный опыт у меня, кстати тоже есть... dndmode вывалил наружу сырым, замечания прилетели в тот же день, сейчас там 3.4.1 и свой сайт. Никакой доблести: просто выложил раньше, чем стало не стыдно.
Суть саморефлексии в том, что мы можем бесконечно долго дисциплинированно готовиться, но так ничего не сделать. Будь то поиск новой работы, попытки познакомиться с девушкой или как в моем случае начать делиться знаниями на🎞
Интересно, хватит ли духу выложить первый ролик как есть? Кривой, с моей дикцией и лысиной в 4K :-)
Как я больше года храбро готовился, но так ничего и не сделал.
Второго сентября на HN мне на глаза попалось эссе Exit the Cave. Там была мысль, после которой я перестал себе нравиться: "есть вид трусости, который очень похож на дисциплину". Автор занимается борьбой и делит все на входы и результат. Часы на мате и аккуратный дневник тренировок меряются легко, но не стоят ничего. По настоящему стоит только схватка, где тебя при свидетелях кладут на лопатки.
Ну и смотрю я на себя...
Youtube серия про тамагочи на Go готовится с августа 2025 года, когда я отдыхая в Киргизии решил что буду это делать... Сценарий переписан четыре раза, репозиторий готов и разложен на 17 шагов, design.md вылизан так, что хоть в учебник вставляй. Красиво...
Только вот роликов на youtube
И ведь не скажешь, что бездельничал... Много вечеров садился и что-то улучшал, если конечно не писал статьи или не изучал какуето другую тему. Дисциплина же!
Обратный опыт у меня, кстати тоже есть... dndmode вывалил наружу сырым, замечания прилетели в тот же день, сейчас там 3.4.1 и свой сайт. Никакой доблести: просто выложил раньше, чем стало не стыдно.
Суть саморефлексии в том, что мы можем бесконечно долго дисциплинированно готовиться, но так ничего не сделать. Будь то поиск новой работы, попытки познакомиться с девушкой или как в моем случае начать делиться знаниями на
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7⚡1🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Выложил свой statusline для claude code в публичный репозиторий.
Лимитов у подписки три: пять часов, неделя общая и неделя отдельно на Fable. В JSON приходят первые два. А сессию на Fable обрывает как раз третий, я в него упирался при зеленых остальных. Смотреть /usage надоело.
Полез в бинарник. Тот же эндпоинт
Про цвета...
Метры лимитов красятся по прогнозу, а не по текущему проценту: процент делится на долю окна, которая уже прошла. Прогноз до 50% серый, до 70 желтый, до 90 оранжевый, до 100 красный, выше 100 мигает. Так 30% в середине недели желтые, а те же 30% за день до сброса серые. У all и fab прогноз свой, отсчет до сброса берет цвет худшего.
Контекст проще, он красится по факту: зеленый до 50%, желтый до 65, оранжевый до 80, дальше мигает красным. Fable у меня по этой шкале горит первым, 19 против 8 у общего на одном экране.
Отдельная история с ultracode. Пока он включен, Claude Code отдает в статус обычный xhigh. Зато в транскрипт сессии пишется запись ultra_effort_enter, а при выключении ultra_effort_exit. Строка читает хвост транскрипта, последняя запись побеждает, и рисует ult фиолетовым. К сожалению эндпоинт не документированный. Anthropic поменяет формат, и fab из строки пропадет, остальное останется. Windows тоже не проверял, там ps и Keychain.
Один файл на Node без зависимостей,
https://github.com/dsbasko/cc-statusline
Лимитов у подписки три: пять часов, неделя общая и неделя отдельно на Fable. В JSON приходят первые два. А сессию на Fable обрывает как раз третий, я в него упирался при зеленых остальных. Смотреть /usage надоело.
Полез в бинарник. Тот же эндпоинт
/api/oauth/usage, который читает /usage, отдает в limits[] запись weekly_scoped со scope Fable. Токен лежит в Keychain, скрипт его берет, ходит раз в пять минут отдельным процессом и кладет ответ в кеш. Сама строка сеть не ждет, теплый запуск 30 мс.Про цвета...
Метры лимитов красятся по прогнозу, а не по текущему проценту: процент делится на долю окна, которая уже прошла. Прогноз до 50% серый, до 70 желтый, до 90 оранжевый, до 100 красный, выше 100 мигает. Так 30% в середине недели желтые, а те же 30% за день до сброса серые. У all и fab прогноз свой, отсчет до сброса берет цвет худшего.
Контекст проще, он красится по факту: зеленый до 50%, желтый до 65, оранжевый до 80, дальше мигает красным. Fable у меня по этой шкале горит первым, 19 против 8 у общего на одном экране.
Отдельная история с ultracode. Пока он включен, Claude Code отдает в статус обычный xhigh. Зато в транскрипт сессии пишется запись ultra_effort_enter, а при выключении ultra_effort_exit. Строка читает хвост транскрипта, последняя запись побеждает, и рисует ult фиолетовым. К сожалению эндпоинт не документированный. Anthropic поменяет формат, и fab из строки пропадет, остальное останется. Windows тоже не проверял, там ps и Keychain.
Один файл на Node без зависимостей,
make install линкует его в ~/.claude и правит settings.json:git clone https://github.com/dsbasko/cc-statusline \
&& cd cc-statusline \
&& make install
https://github.com/dsbasko/cc-statusline
🔥4👌1
Кодить без AI сейчас как заниматься айкидо. Красиво и глубоко и десять лет до черного пояса. А потом в подворотне тебя бьет парень после полугода бокса.
Синтаксис наизусть и тысяча строк в голове были боевыми навыками и за них платили. Теперь это даже не спорт. Уважаемый и с медалями. Но в драке с дедлайном побеждает тот у кого агент пишет бойлерплейт пока он сам думает.
Что будет с теми кто принципиально без агентов. Их не заменят. Они станут IT-амишами. Лошадь, масляная лампа и чистый код написанный руками. Крепко, честно и никому не мешает. Раз в год к ним будут возить экскурсии и показывать человека который сам пишет структуры под JSON :-)
Я к амишам с уважением. Они свою черту выбрали сознательно. Но большинство трад-программистов на ней стоят зря и ждут что мода пройдет...
Синтаксис наизусть и тысяча строк в голове были боевыми навыками и за них платили. Теперь это даже не спорт. Уважаемый и с медалями. Но в драке с дедлайном побеждает тот у кого агент пишет бойлерплейт пока он сам думает.
Что будет с теми кто принципиально без агентов. Их не заменят. Они станут IT-амишами. Лошадь, масляная лампа и чистый код написанный руками. Крепко, честно и никому не мешает. Раз в год к ним будут возить экскурсии и показывать человека который сам пишет структуры под JSON :-)
Я к амишам с уважением. Они свою черту выбрали сознательно. Но большинство трад-программистов на ней стоят зря и ждут что мода пройдет...
😁5🔥2🍌1