🐧 Go 1.24 будет требовать ядро Linux 3.2 и выше.
Небольшая, но важная, заметка. В 2021 году (Go 1.18) минимальным ядром на Linux стала версия 2.6.32. Спустя почти четыре года, нас снова ждет изменение требования: теперь в Go 1.24 минимальную версию ядра поднимут до 3.2. Учтите это при обновлении, если у вас очень старые дистрибутивы на серверах (или у вас зоопарк), а разрабатываете вы на самом новом и крутом.
П.С. Да, я в курсе, что актуальная версия ядра вообще 6.x.y 🙂️️️️️️
Небольшая, но важная, заметка. В 2021 году (Go 1.18) минимальным ядром на Linux стала версия 2.6.32. Спустя почти четыре года, нас снова ждет изменение требования: теперь в Go 1.24 минимальную версию ядра поднимут до 3.2. Учтите это при обновлении, если у вас очень старые дистрибутивы на серверах (или у вас зоопарк), а разрабатываете вы на самом новом и крутом.
П.С. Да, я в курсе, что актуальная версия ядра вообще 6.x.y 🙂️️️️️️
👍25❤2
Про нововведения в Go 1.24
Долго собирался написать серию (хотя бы один!) постов про вещи которые к нам подъедут в Go 1.24, но так никак и не смог собраться. А потом обнаружил, что один наш коллега уже все это сделал в подробном и, главное, интерактивным виде. Правда есть нюанс — сам пост на английском.
Однако тем кто не знает английского не стоить грустить — методом гуглежки было обнаружено, что от него же есть серия статей которая почти полностью дублирует записи в блоге. И оно в телеграмме! Поэтому смело идем и начинаем читать, что нас ждет в предстоящей версии.
П.С. Ну а я постараюсь в ближайшее время запилить обзор предложение от Яна Ланса Тейлора о новом синтаксисе обработки ошибок и как оно разделило сообщество почти поровну.
Долго собирался написать серию (хотя бы один!) постов про вещи которые к нам подъедут в Go 1.24, но так никак и не смог собраться. А потом обнаружил, что один наш коллега уже все это сделал в подробном и, главное, интерактивным виде. Правда есть нюанс — сам пост на английском.
Однако тем кто не знает английского не стоить грустить — методом гуглежки было обнаружено, что от него же есть серия статей которая почти полностью дублирует записи в блоге. И оно в телеграмме! Поэтому смело идем и начинаем читать, что нас ждет в предстоящей версии.
П.С. Ну а я постараюсь в ближайшее время запилить обзор предложение от Яна Ланса Тейлора о новом синтаксисе обработки ошибок и как оно разделило сообщество почти поровну.
👍12❤1
0️⃣ Omitzero или убираем пустые значения при серилизации в json
Я уверен, что большинство в курсе о том, что пакет
Оба пункта довольно долго портили кровь разработчикам сложных вложенных API и заставляли их пользоваться указателями, там, где можно было обойтись без них. И вот Go Team приняла волевое решение и реализовала новый тег. Встречаем
КМК именно это можно было привезти еще в версии Go 1.10, т.к. фиче не нужны новые языковые фишки, а необходимость ее была видна еще с Go 1.1. Чтож, лучше поздно чем никогда.
Я уверен, что большинство в курсе о том, что пакет
json поддерживает структурный тег omitempty. На данный момент это единственный прямолинейный способ указать, что поле не следует выводить в json если оно содержит нулевое значение. Однако у этого тега есть два минуса:1. Он плохо работает со структурами.2. Он неумеет в переопределение принципа по которому определяется сам факт нулевого значения.Оба пункта довольно долго портили кровь разработчикам сложных вложенных API и заставляли их пользоваться указателями, там, где можно было обойтись без них. И вот Go Team приняла волевое решение и реализовала новый тег. Встречаем
omitzero. Принцип работы прост до невозможности:1. Если поле это структура и она пустая, то маршалить ее он не будет.2. Если у типа поля определен метод IsZero и он вернул true то и его маршалить не будут.КМК именно это можно было привезти еще в версии Go 1.10, т.к. фиче не нужны новые языковые фишки, а необходимость ее была видна еще с Go 1.1. Чтож, лучше поздно чем никогда.
GitHub
encoding/json: add omitzero option · Issue #45669 · golang/go
The omitempty json tag is kind of confusing to use when working with nested structs. The following example illustrates the most basic case using an empty struct for argument's sake. type Foo st...
👍35❤8
🎉 Вышел Go 1.24! 🎉
Этот момент настал!
Ключевые нововведения:
— Дженерики теперь умеют в псевдонимы (aliases) т.е. теперь можно писать так
Поздравляю тех, кому приходилось работать с указателями в дженериках, теперь можно хоть нормальные сигнатуры наружу выставлять.
— Теперь можно отслеживать версии и использовать утилиты прямо с помощью команды
—
— Бинари запущенные с помощью
—
— С помощью новой переменной окружения
—
— Новая реализация map на основе Swiss Tables. Бигтехи ваши вопросы на собесах больше неактуальны 🤡.
— Инлайнинг стал мощнее. Теперь, с большей вероятностью, тело функции которую вы передаете как аргумент будет вставлено в место ее вызова. Делали в основном для итераторов которые уступали по скорости циклам. Вместе с прошлым изменением и прочими изменениями в рантайме дало совокупный буст в 3-5% по производительности.
— Различные улучшения CGO с помощью новых директив.
— Новый способ гонять бенчи
— С помощью переменной окружения
— Пакет со слабыми указателями стал доступен всем.
— Подъехала замена финалайзерам. С типобезопастностью и сигнатурой которая намекает как делать правильно (и длинной докой объясняющей все сценарии). А еще их можно повесить сколько угодно в отличии от финалайзеров. Почему это важно я писал ранее.
— Пакет testing/synctest который в будущем позволит нам полностью дропнуть все моки времени. Пока обещают изменения API поэтому скрыто за флагом
— Появился OpenRoot. TLDR: открыв каталог таким образом из него нельзя убежать используя симлинки и прочая. Штука интересная для тех у кого пути до файлов генерируются извне.
— Куча прочих улучшений, включая новый функции для итераторов в пакеты
— У тестов и бенчей появился свой контекст! Можно будет дропнуть кучу кода отвечающего за создание контекстов в тестах.
Полное описание (жирного) релиза вот тут.
Этот момент настал!
Ключевые нововведения:
— Дженерики теперь умеют в псевдонимы (aliases) т.е. теперь можно писать так
type MyType[T any] = myType[T,*T]
Поздравляю тех, кому приходилось работать с указателями в дженериках, теперь можно хоть нормальные сигнатуры наружу выставлять.
— Теперь можно отслеживать версии и использовать утилиты прямо с помощью команды
go tool. Краткий смысл сего: оно помещает все зависимости добавляемой тулзы в require блок, а саму утилиту в блок tool. Учтите, что если вы не указываете отдельный go.mod файл с помощью флага -modfile (его принимают большинство команд) то зависимости внешней тулзы перемешаются с вашими. Однако есть и хорошая новость — из-за умного механизма «вычистки» MVS если кто то импортирует ваш модуль, то зависимости которые нужны для утилит к нему не попадут.—
go test теперь гоняет go vet с небольшим числом анализаторов перед запуском тестов для поиска распространенных ошибок (в том числе ошибок в оформлении тестов). go vet тоже подтянули, теперь, например, ругается на printf функции где аргумент только строка.— Бинари запущенные с помощью
go run и go tool теперь кешируются внутри внутри билд кэша. Никаких больше пересборок на каждом go run. —
go build и go install научили в json вывод.— С помощью новой переменной окружения
GOAUTH можно определить принцип для авторизации на приватных серверах модулей. Подробности с помощью go help goauth —
go build теперь вставляет в бинарь информацию о версии (наконец-то можно перестать делать это вручную!) с использованием тега и/или хеша коммита вашего гита (или mercurial и прочая).— Новая реализация map на основе Swiss Tables. Бигтехи ваши вопросы на собесах больше неактуальны 🤡.
— Инлайнинг стал мощнее. Теперь, с большей вероятностью, тело функции которую вы передаете как аргумент будет вставлено в место ее вызова. Делали в основном для итераторов которые уступали по скорости циклам. Вместе с прошлым изменением и прочими изменениями в рантайме дало совокупный буст в 3-5% по производительности.
— Различные улучшения CGO с помощью новых директив.
— Новый способ гонять бенчи
for b.Loop() { ... }. Основной плюс (кроме того, что меньше писать) в том, что гонялке бенчмарков больше не нужно вызывать вашу функцию несколько раз, ибо сама найдет нужные параметры во время цикла. А значит дорогие блоки инициализации и удаления по итогу бенча стали дешевле. — С помощью переменной окружения
GOCACHEPROG можно настроить свое утилиту которое будет отвечать за кеширование, вместо стандартного «все на диск в папочку». Пригодиться тем, у кого распределенные билды или много чего собирается в докере. Документация.— Пакет со слабыми указателями стал доступен всем.
— Подъехала замена финалайзерам. С типобезопастностью и сигнатурой которая намекает как делать правильно (и длинной докой объясняющей все сценарии). А еще их можно повесить сколько угодно в отличии от финалайзеров. Почему это важно я писал ранее.
— Пакет testing/synctest который в будущем позволит нам полностью дропнуть все моки времени. Пока обещают изменения API поэтому скрыто за флагом
GOEXPERIMENT=synctest— Появился OpenRoot. TLDR: открыв каталог таким образом из него нельзя убежать используя симлинки и прочая. Штука интересная для тех у кого пути до файлов генерируются извне.
— Куча прочих улучшений, включая новый функции для итераторов в пакеты
bytes и strings.— У тестов и бенчей появился свой контекст! Можно будет дропнуть кучу кода отвечающего за создание контекстов в тестах.
Полное описание (жирного) релиза вот тут.
🔥41👍18❤6
😢 Ян Ланс Тейлор покидает Google 😢
После 19 (!!!) лет работы и более 16 лет работы над нашим языком Go, Ян Ланс Тейлор объявил о том, что покидает Google.
В числе его заслуг:
— Работа компилятора Go.
— GCCGO фронтенд который позволяет собирать программы на Go с использованием компилятора GCC.
— Дженерики, которые появились в версии 1.18.
— Поддержка Go во внутренней системе сборки SWIG Google.
— Куча предложений и работ над улучшением языка.
Сам Ян говорит, что очень доволен своей работой. Go развивается и его принятие рынком растет гораздо быстрее чем оригинальная команда могла даже вообразить когда начинала работу над языком. Однако вместе с этим ему видно, что сам Google изменился, язык Go изменился и вообще ландшафт языков программирования изменился с тех пор как он начал эту работу. В течении последнего года он понял, что больше не подходит для работой над Go внутри Google. И пришла пора двигаться дальше.
На некоторое время возьмет перерыв от работы, но надеется, что ему еще удастся поработать над Go в будущем.
П.С. Мужик, конечно, легенда — работает на OSS и GNU проектами с 1990 года. Желаю ему хорошо отдохнуть и продолжать делать крутые вещи.
П.П.С. После ухода Яна и ухода Расса с должности руководителя Go (но не ухода из Google!) из старой гвардии остался только Роберт Гризмер. С одной стороны это хорошо — значит язык не зависит от конкретных людей. С другой есть волнение за будущее языка и то как новые лица справятся с таким наследием. Поживем — увидим!
После 19 (!!!) лет работы и более 16 лет работы над нашим языком Go, Ян Ланс Тейлор объявил о том, что покидает Google.
В числе его заслуг:
— Работа компилятора Go.
— GCCGO фронтенд который позволяет собирать программы на Go с использованием компилятора GCC.
— Дженерики, которые появились в версии 1.18.
— Поддержка Go во внутренней системе сборки SWIG Google.
— Куча предложений и работ над улучшением языка.
Сам Ян говорит, что очень доволен своей работой. Go развивается и его принятие рынком растет гораздо быстрее чем оригинальная команда могла даже вообразить когда начинала работу над языком. Однако вместе с этим ему видно, что сам Google изменился, язык Go изменился и вообще ландшафт языков программирования изменился с тех пор как он начал эту работу. В течении последнего года он понял, что больше не подходит для работой над Go внутри Google. И пришла пора двигаться дальше.
На некоторое время возьмет перерыв от работы, но надеется, что ему еще удастся поработать над Go в будущем.
П.С. Мужик, конечно, легенда — работает на OSS и GNU проектами с 1990 года. Желаю ему хорошо отдохнуть и продолжать делать крутые вещи.
П.П.С. После ухода Яна и ухода Расса с должности руководителя Go (но не ухода из Google!) из старой гвардии остался только Роберт Гризмер. С одной стороны это хорошо — значит язык не зависит от конкретных людей. С другой есть волнение за будущее языка и то как новые лица справятся с таким наследием. Поживем — увидим!
😐24😨15👍9❤6🤯2😁1😡1
🔀 Динамический GOMAXPROCS 🔀
До релиза Go 1.25 осталось около недели, а значит, самое время восполнить пробелы и написать всё то, о чем я (по-хорошему) должен был рассказать в течение последнего полугодия разработки новой версии языка. Начнем с небольшого, но значимого изменения: динамического
Для тех, кто не в курсе: Go позволяет определить число горутин, которые могут одновременно выполняться в пределах рантайма. Ключевое слово — одновременно. Это значит, что горутин у вас может быть сколько угодно, но выполняться из них (лопатить код, а не находится в ожидании сети, файлов или ответа из сишной либы), в момент времени, будет только число, указанное в
Смысл вот в чем: в контейнеризированном мире на приложение может быть выделено даже не ядро, а фракция или квота, при этом значения сии могут быть динамическими и изменяться в процессе работы кластера. Процесс может ошибочно исходить из того, что у него есть 64 ядра всего блейда (и как следствие — возможность 64-х активных потоков), но по факту доступных именно нашему процессу ядер намного меньше. И это не вдаваясь в подробности таких вещей, как "привязка к ядрам CPU конкретного процесса" которые актуальны в NUMA окружениях.
До версии Go 1.25 эту проблему частично решала библиотека automaxprocs от Uber, которая выставляла значения наиболее приближенные к ожидаемым оркестратором. Но делала она это только один раз и только на старте. Кроме того, много людей банально не знали об этой тонкости работы рантайма Go и, как следствие, неправильно использовали доступные ресурсы CPU.
Начиная с версии Go 1.25,
На изменение
• Изменение числа ядер на машине.
• Изменение привязки приложения к ядрам CPU.
• И, специально для Linux, средний лимит пропускной способности, основанный на квотах CPU
Стоит заметить, что это изменение, при всех его очевидных плюсах, имеет один, но явный неочевидный минус — если вы шардировали кеши по числу
• Вы можете не выставлять в
• Вы можете самостоятельно выставить
• Также можно оставить прошлое поведение с помощью
P.S. Для полноценного мониторинга Go теперь держит в памяти дескриптор доступа к файлам cgroups на время жизни всего процесса.
До релиза Go 1.25 осталось около недели, а значит, самое время восполнить пробелы и написать всё то, о чем я (по-хорошему) должен был рассказать в течение последнего полугодия разработки новой версии языка. Начнем с небольшого, но значимого изменения: динамического
GOMAXPROCS.Для тех, кто не в курсе: Go позволяет определить число горутин, которые могут одновременно выполняться в пределах рантайма. Ключевое слово — одновременно. Это значит, что горутин у вас может быть сколько угодно, но выполняться из них (лопатить код, а не находится в ожидании сети, файлов или ответа из сишной либы), в момент времени, будет только число, указанное в
GOMAXPROCS. По-хорошему, это число обычно равно числу физических ядер на вашем CPU. Но в серверных окружениях начинаются тонкости, главная из которых, cgroups, является столпом для Docker и K8S.Смысл вот в чем: в контейнеризированном мире на приложение может быть выделено даже не ядро, а фракция или квота, при этом значения сии могут быть динамическими и изменяться в процессе работы кластера. Процесс может ошибочно исходить из того, что у него есть 64 ядра всего блейда (и как следствие — возможность 64-х активных потоков), но по факту доступных именно нашему процессу ядер намного меньше. И это не вдаваясь в подробности таких вещей, как "привязка к ядрам CPU конкретного процесса" которые актуальны в NUMA окружениях.
До версии Go 1.25 эту проблему частично решала библиотека automaxprocs от Uber, которая выставляла значения наиболее приближенные к ожидаемым оркестратором. Но делала она это только один раз и только на старте. Кроме того, много людей банально не знали об этой тонкости работы рантайма Go и, как следствие, неправильно использовали доступные ресурсы CPU.
Начиная с версии Go 1.25,
GOMAXPROCS будет не только выставляться автоматически, но и периодически обновляться в течение жизни приложения, в зависимости от того, как меняется внешнее окружение.На изменение
GOMAXPROCS будут влиять в совокупности три вещи:• Изменение числа ядер на машине.
• Изменение привязки приложения к ядрам CPU.
• И, специально для Linux, средний лимит пропускной способности, основанный на квотах CPU
cgroup.Стоит заметить, что это изменение, при всех его очевидных плюсах, имеет один, но явный неочевидный минус — если вы шардировали кеши по числу
GOMAXPROCS то вас ожидают очень неприятные паники или скачки нагрузки. Поэтому, если-же вас по какой-то причине не устраивает новое поведение, то у вас есть целых три варианта:• Вы можете не выставлять в
go.mod версию go 1.25.x — обновление придёт к вам только когда вы захотите перейти на поведение языка версии 1.25.• Вы можете самостоятельно выставить
GOMAXPROCS с помощью переменных окружения или с помощью функции GOMAXPROCS. В таком случае автообновление будет выключено, и рантайм доверится вашему суждению.• Также можно оставить прошлое поведение с помощью
GODEBUG переменных containermaxprocs=0 и updatemaxprocs=0.P.S. Для полноценного мониторинга Go теперь держит в памяти дескриптор доступа к файлам cgroups на время жизни всего процесса.
GitHub
Go compiler and runtime meeting notes · Issue #43930 · golang/go
Google's Go compiler and runtime team meets periodically (roughly weekly) to discuss ongoing development of the compiler and runtime. While not open to the public, there's been desire by th...
🔥41👍10👏3
🏎️ Об оптимизациях в Go 1.25 🏎️
В новом релизе, как и всегда, к нам приедут новые оптимизации для компилятора. Две из них меня заинтересовали больше всего:
• Цепочка из четырёх 1, 2, 3, 4 PR, суть которых можно описать с помощью одного примера:
Если в Go 1.24 и ранее такой код приводил к аллокации в хипе, то начиная с Go 1.25 — нет. А всё просто:
• Нулевые значения и "константные" переменные больше не аллоцируют память в хипе при присвоении значения интерфейсу. Продемонстрировать проще всего вот так:
Если ранее подобный код приводил к аллокации, то теперь компилятор достаточно умён, чтобы на этапе компиляции выделить специальное read-only место в памяти и использовать именно его во время исполнения. Особенно приятно, что
В новом релизе, как и всегда, к нам приедут новые оптимизации для компилятора. Две из них меня заинтересовали больше всего:
• Цепочка из четырёх 1, 2, 3, 4 PR, суть которых можно описать с помощью одного примера:
var x []int
for i := 0; i < 4; i++ {
x = append(x, i)
}
Если в Go 1.24 и ранее такой код приводил к аллокации в хипе, то начиная с Go 1.25 — нет. А всё просто:
make и append теперь, в большинстве случаев, не аллоцируют память в хип до 32 байтов включительно. Вместо этого они используют память на стеке и лишь при превышении объёма начинают идти в хип. Такая вот консервативная оптимизация для слайсов всех типов.• Нулевые значения и "константные" переменные больше не аллоцируют память в хипе при присвоении значения интерфейсу. Продемонстрировать проще всего вот так:
type doubleInt struct{ value1, value2 int }
localVariable := doubleInt{value1: 3, value2: 2}
globalAny = any(localVariable)
localVariable2 := doubleInt{}
globalAny2 = any(localVariable2)
Если ранее подобный код приводил к аллокации, то теперь компилятор достаточно умён, чтобы на этапе компиляции выделить специальное read-only место в памяти и использовать именно его во время исполнения. Особенно приятно, что
reflect.Value.IsZero теперь использует сравнение по указателю для нулевых значений структур и массивов, что существенно удешевляет проверку.GitHub
cmd/compile: storing zero value into interface should not allocate · Issue #71323 · golang/go
Go version go1.23.5 Output of go env in your module/workspace: GOARCH=amd64 GOOS=linux What did you do? I ran this benchmark: var sinkAny any func BenchmarkInterfaceAny(b *testing.B) { b.ReportAllo...
🔥21❤4👍3👏1
Меня реально бесит, что на десктопе ссылки выглядит как и
моношрифт с кодом. Вся читаемость падает в ноль.Telegram
Go Update
🏎️ Об оптимизациях в Go 1.25 🏎️
В новом релизе, как и всегда, к нам приедут новые оптимизации для компилятора. Две из них меня заинтересовали больше всего:
• Цепочка из четырёх 1, 2, 3, 4 PR, суть которых можно описать с помощью одного примера:
var x…
В новом релизе, как и всегда, к нам приедут новые оптимизации для компилятора. Две из них меня заинтересовали больше всего:
• Цепочка из четырёх 1, 2, 3, 4 PR, суть которых можно описать с помощью одного примера:
var x…
❤4👍1👎1👀1
Короче я нашёл проблему — ребята которые делают Swift телеграмм так «удачно» подобрали цвета, что именно в моей «стоковой» теме моношрифт и ссылки выглядят одинаково. Удобство 11/10!
😁6❤3
Об обсуждениях.
Вот уже более тринадцати лет нормальный синтаксис неподвижно обсуждается на GitHub.
По воле отцов-основателей выступает Повелитель Простоты и правит миллионами микросервисов благодаря мощи своего неисчислимого бойлерплейта.
Он — гниющий реликт минимализма, в ком незримые муки разработчиков продлеваются загадочными отказами «это не вписывается в философию».
Он — незыблемый столп экосистемы, которому каждый день приносят в жертву тысячи бестолковых предложений о try-catch, идиотские идеи которых были озвучены уже десятки раз, а все самопротиворечащие доводы высказаны и рассмотрены.
На боли и страданиях гоферов стоит тот самый "Idiomatic Go"
Вы знаете, что такое безумие?
Кто-то скажет, что факт того, что язык получивший свой релиз в 2012-ом году, но не имевший дженериков аж до 2022го — это безумие. На что можно резонно возразить: дескать, нормальный синтаксис — это полдела, основная проблема в экосистеме! Другие могут сказать, что почти все реализации (включая и ту, которую в итоге получили мы) обладают рядом минусов и требуется их хорошо продумать, перед тем как вносить этот инструмент в язык. Третьи скажут, что внесение дженериков в язык привлекает вниманиеC++ любителей всё усложнять, а сам Go был ценен в первую очередь простотой и понятливостью даже свежему после универа программисту. Короче: оправданий можно придумать массу. На самом деле доподлинно никто не знает, почему внедрение дженериков заняло так много времени, поэтому любые попытки найти рациональное объяснение — не более чем «игра для ума» (по-простому — ещё один способ вентиляции лёгких), которая не несёт в себе никакой практической пользы.
Нет, скажу я вам. Безумие это хождение по кругу годами в поисках идеального синтаксиса который устроит всех. Например, обсуждение синтаксиса лямбд, которое ведётся уже более 8 лет и которое, по итогу, так и не сдвинулось с места. Синтаксиса! Не семантики и не деталей реализации. Ибо главный выбор, который остался перед нами, — это то, «будет ли новый синтаксис использовать
Честно — у меня нет цензурных слов. Если вам когда-нибудь хотелось узнать, что такое "Bikeshedding" (в русском языке нет прямого перевода, но термин расшифровывается как «бессмысленное обсуждение тривиальных вопросов»), то это обсуждение — самый наглядный пример, который у меня есть. Где-то полгода назад, когда, как мне казалось, картинка начала выкристаллизовываться, я хотел написать об этом запись в блоге. И попросить людей поучаствовать, если им есть что высказать или подсказать, ведь это шанс поучаствовать в развитии языка, а создатели действительно слушают мнение людей по этому вопросу! Теперь я понимаю — первый раз за долгое время меня спасло то, что я что-то не сделал. Ибо с тех пор, несмотря на как минимум два резюме по итогам «ещё одной итерации обсуждения», продвижения по-прежнему нет.
Я не знаю, какой здесь сделать вывод. Но я уверен, что нас ждёт ещё минимум столько же комментариев в будущем.
In the grim darkness of the far future there is only "
Вот уже более тринадцати лет нормальный синтаксис неподвижно обсуждается на GitHub.
По воле отцов-основателей выступает Повелитель Простоты и правит миллионами микросервисов благодаря мощи своего неисчислимого бойлерплейта.
Он — гниющий реликт минимализма, в ком незримые муки разработчиков продлеваются загадочными отказами «это не вписывается в философию».
Он — незыблемый столп экосистемы, которому каждый день приносят в жертву тысячи бестолковых предложений о try-catch, идиотские идеи которых были озвучены уже десятки раз, а все самопротиворечащие доводы высказаны и рассмотрены.
На боли и страданиях гоферов стоит тот самый "Idiomatic Go"
Вы знаете, что такое безумие?
Кто-то скажет, что факт того, что язык получивший свой релиз в 2012-ом году, но не имевший дженериков аж до 2022го — это безумие. На что можно резонно возразить: дескать, нормальный синтаксис — это полдела, основная проблема в экосистеме! Другие могут сказать, что почти все реализации (включая и ту, которую в итоге получили мы) обладают рядом минусов и требуется их хорошо продумать, перед тем как вносить этот инструмент в язык. Третьи скажут, что внесение дженериков в язык привлекает внимание
Нет, скажу я вам. Безумие это хождение по кругу годами в поисках идеального синтаксиса который устроит всех. Например, обсуждение синтаксиса лямбд, которое ведётся уже более 8 лет и которое, по итогу, так и не сдвинулось с места. Синтаксиса! Не семантики и не деталей реализации. Ибо главный выбор, который остался перед нами, — это то, «будет ли новый синтаксис использовать
=>, или ->, а может, \-> для разделения объявления и тела лямбды?». И если кто-то думает, что я шучу, то ему достаточно глянуть issue, в котором уже больше 900 скрытых комментариев, по которым уже несколько раз делали сводку (вручную, с помощью Gabby, с помощью других LLM, комбинацией из трёх прошлых способов) и которое раз за разом возвращается назад, к своему началу. К выбору идеального синтаксиса. К безумию.Честно — у меня нет цензурных слов. Если вам когда-нибудь хотелось узнать, что такое "Bikeshedding" (в русском языке нет прямого перевода, но термин расшифровывается как «бессмысленное обсуждение тривиальных вопросов»), то это обсуждение — самый наглядный пример, который у меня есть. Где-то полгода назад, когда, как мне казалось, картинка начала выкристаллизовываться, я хотел написать об этом запись в блоге. И попросить людей поучаствовать, если им есть что высказать или подсказать, ведь это шанс поучаствовать в развитии языка, а создатели действительно слушают мнение людей по этому вопросу! Теперь я понимаю — первый раз за долгое время меня спасло то, что я что-то не сделал. Ибо с тех пор, несмотря на как минимум два резюме по итогам «ещё одной итерации обсуждения», продвижения по-прежнему нет.
Я не знаю, какой здесь сделать вывод. Но я уверен, что нас ждёт ещё минимум столько же комментариев в будущем.
In the grim darkness of the far future there is only "
if err != nil".GitHub
proposal: spec: short function literals · Issue #21498 · golang/go
[edit: As of today, end of May 2025, this proposal has received ~1000 comments. Please read the summary before adding more comments. Your ideas may have been discussed already. Thanks.] [edit: As o...
❤12👍4🔥2👀2🗿1
⏲️ testing/synctest — останавливаем время ⏲️
Одними из самых сложных сценариев для тестирования являются сценарии где замешано время. Если использовать реальное время то тесты либо а) начинают занимать слишком много времени либо б) теряют в надежности из-за гонки "кода со временем". Особенно хорошо это ощущается в тестах которые используют
Приведу очень простой пример:
Для того, что-бы убедиться, что наш код выведет "do work..." 4 раза нам нужно подождать примерно 2 секунды, в течении которых наша программа фактически ничего не делает. Более того, нет никаких гарантий, что наш текст будет выведен именно 4 раза, а не 3 или 2 с учетом нагрузки на планировщик ОС.
Когда такой тест у нас один это нормально. Когда у нас их десятки это проблема. А когда у нас их за сотню то это минуты простаивающего CI, который мог бы делать что-то полезное. И тут у нас есть два решения:
• В пределах тестов кратно уменьшить время "ожидания". Допустим не 500мс а 50. Или вообще 5мс. Проблема такого подхода в том, что исполнение (и работа планировщика) тоже занимает время и тест становится менее надежным. При этом проблема "впустую потраченного времени" никуда не уходит.
• Используем одну из сторонних библиотек для "подделки" времени и замыкаемся на ее семантику (и баги) по всей программе.
Кроме того, оба способа еще приводят к необходимости пробрасывать параметры (время, или сам мок работы со временем) через всю программу, что усложняет чтение кода.
Хорошая новость, что разработчики языка озаботились это проблемой, и в Go 1.25 пакет
Первая это
• Блокирующая попытка посылки или получения данных из канала созданного в том же "пузыре".
•
• Вызов
•
Однако следующие операции не приводят к "прочной блокировке" горутин:
• Лок мьютексов.
• Блокировка на операциях ввода-вывода (файлы, сеть и прочая).
• Сисколлы.
Вторая функция это
Таком образом как только все горутины "прочно заблокированы" происходит одно из следующих:
• Если
• В противном случае время внутри "пузыря" сдвигается на достаточную величину для запуска в работу хотя-бы одной горутины если функция создававшая "пузырь" не закончила исполнение.
• В противном случае, происходит дедлок и наш вызов
Проще всего понять на живом примере (код не влез в этот пост). Обратите внимание, что время выставлено в часы однако время теста и порядок вывода в лог сохраняется раз за разом. Так мы обеспечиваем не только быстрое исполнение, но и ожидаемый детерминизм. Другой плюс нового пакета, заключается в том, что если горутина создавшая "пузырь" выйдет до того, как закончат работу все её дочерние горутины, то вы получите панику (пример). Таким образом на этапе тестирования еще и обеспечивается контроль очистки ресурсов.
Для более подробной документации рекомендую глянуть документацию. Там-же приведены и другие примеры, сценарии и особенности работы.
Одними из самых сложных сценариев для тестирования являются сценарии где замешано время. Если использовать реальное время то тесты либо а) начинают занимать слишком много времени либо б) теряют в надежности из-за гонки "кода со временем". Особенно хорошо это ощущается в тестах которые используют
time.Ticker, time.Timer и time.AfterFunc для управление потоком исполнения.Приведу очень простой пример:
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
for {
select {
case <-time.After(500 * time.Millisecond):
println("do work...")
case <-ctx.Done():
println("Context done:", ctx.Err())
}
}
Для того, что-бы убедиться, что наш код выведет "do work..." 4 раза нам нужно подождать примерно 2 секунды, в течении которых наша программа фактически ничего не делает. Более того, нет никаких гарантий, что наш текст будет выведен именно 4 раза, а не 3 или 2 с учетом нагрузки на планировщик ОС.
Когда такой тест у нас один это нормально. Когда у нас их десятки это проблема. А когда у нас их за сотню то это минуты простаивающего CI, который мог бы делать что-то полезное. И тут у нас есть два решения:
• В пределах тестов кратно уменьшить время "ожидания". Допустим не 500мс а 50. Или вообще 5мс. Проблема такого подхода в том, что исполнение (и работа планировщика) тоже занимает время и тест становится менее надежным. При этом проблема "впустую потраченного времени" никуда не уходит.
• Используем одну из сторонних библиотек для "подделки" времени и замыкаемся на ее семантику (и баги) по всей программе.
Кроме того, оба способа еще приводят к необходимости пробрасывать параметры (время, или сам мок работы со временем) через всю программу, что усложняет чтение кода.
Хорошая новость, что разработчики языка озаботились это проблемой, и в Go 1.25 пакет
synctest стал наконец доступен любому желающему. Публичное API содержит всего две функции:Первая это
synctest.Test. Она запускает замыкание в изолированном пространстве-пузыре где время течёт "иначе". Все горутины созданные внутри этого замыкания разделяют этот "пузырь". Время в них стоит на месте, до тех пор пока все горутины не станут "прочно заблокированы". Термин "прочно заблокированы" может прозвучать странно, но по сути это лишь определенный набор блокировок которые приводят к сдвигу времени в "пузыре». Вот их полный список:• Блокирующая попытка посылки или получения данных из канала созданного в том же "пузыре".
•
select с блокирующими операциями над каналами созданным в том же "пузыре".• Вызов
sync.Cond.Wait или time.Sleep.•
sync.WaitGroup.Wait, если sync.WaitGroup.Add был вызван из того же "пузыря" (не горутины, это важно).Однако следующие операции не приводят к "прочной блокировке" горутин:
• Лок мьютексов.
• Блокировка на операциях ввода-вывода (файлы, сеть и прочая).
• Сисколлы.
Вторая функция это
synctest.Wait(). Ее единственная роль — дождаться момента когда все горутины в "пузыре" достигли "прочной блокировки" и затем вернуться.Таком образом как только все горутины "прочно заблокированы" происходит одно из следующих:
• Если
Wait был вызван, то он возвращается. Время не сдвигается.• В противном случае время внутри "пузыря" сдвигается на достаточную величину для запуска в работу хотя-бы одной горутины если функция создававшая "пузырь" не закончила исполнение.
• В противном случае, происходит дедлок и наш вызов
Test паникует.Проще всего понять на живом примере (код не влез в этот пост). Обратите внимание, что время выставлено в часы однако время теста и порядок вывода в лог сохраняется раз за разом. Так мы обеспечиваем не только быстрое исполнение, но и ожидаемый детерминизм. Другой плюс нового пакета, заключается в том, что если горутина создавшая "пузырь" выйдет до того, как закончат работу все её дочерние горутины, то вы получите панику (пример). Таким образом на этапе тестирования еще и обеспечивается контроль очистки ресурсов.
Для более подробной документации рекомендую глянуть документацию. Там-же приведены и другие примеры, сценарии и особенности работы.
go.dev
Go Playground - The Go Programming Language
👍11🔥3
Go Update
⏲️ testing/synctest — останавливаем время ⏲️ Одними из самых сложных сценариев для тестирования являются сценарии где замешано время. Если использовать реальное время то тесты либо а) начинают занимать слишком много времени либо б) теряют в надежности из…
А теперь по простому:
Этот код не закончит исполнение в ближайший год.
А этот код закончит исполнение менее чем за микросекунду и всегда выведет сначала "inside goroutine" а потом "outside goroutine".
func TestTime(t *testing.T) {
time.Sleep(365 * 24 * time.Hour)
go func() {
println("inside goroutine")
time.Sleep(time.Second)
}()
println("outside goroutine")
time.Sleep(time.Second)
}Этот код не закончит исполнение в ближайший год.
func TestTime(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
time.Sleep(365 * 24 * time.Hour)
go func() {
println("inside goroutine")
time.Sleep(time.Second)
}()
synctest.Wait()
println("outside goroutine")
time.Sleep(time.Second)
})
}А этот код закончит исполнение менее чем за микросекунду и всегда выведет сначала "inside goroutine" а потом "outside goroutine".
❤17👍4
🛡️ Go 1.25: встроенная защита от CSRF в net/http 🛡️
Тем временем разработчики стандартной библиотеки продолжают упрощать нам жизнь. Практически всё кто связаны с вебразработкой слышали про атаку CSRF. Суть в том, что используя форму (и не только) злонамеренный сайт может заставить ваш браузер выполнить, например, перевод денег из банка на другой счёт, если у банка нет защиты от CSRF, а у вас в этот момент действующая кука от ЛК банка.
Я не буду касаться описания принципов работы "same site" и "same origin", так как про это написано уже масса статей, а коснусь лишь основных механизмов защиты от CSRF:
• Вместе с кукой присылать скрытый
• Проверять через заголовок
• SameSite куки и "Non-simple requests" которые защищают от вариантов атаки HTTP -> HTTPS но в общем случае бесполезны.
На данный момент для защиты от CSRF в нашей экосистеме есть две либы: csrf и nosurf. Обе работают через дубль токена (т.е. требуют донастройки фронта) и обе почти не поддерживаются разработчиками.
Поэтому Go 1.25 в пакет
Заголовок с сайта банка при запросе через fetch в таком случае будет выглядеть так:
В то время как заголовок от зловредного сайта при «прямой» подгрузке будет выглядеть вот так
Сам proposal добавляет лишь один тип
Как это работает:
• Браузер добавляет
• В случае если дополнительно установлен список ориджинов через метод
• Запросы без
• «Безопасные» методы
Для более сложных случаев предусмотрены методы
Тем временем разработчики стандартной библиотеки продолжают упрощать нам жизнь. Практически всё кто связаны с вебразработкой слышали про атаку CSRF. Суть в том, что используя форму (и не только) злонамеренный сайт может заставить ваш браузер выполнить, например, перевод денег из банка на другой счёт, если у банка нет защиты от CSRF, а у вас в этот момент действующая кука от ЛК банка.
Я не буду касаться описания принципов работы "same site" и "same origin", так как про это написано уже масса статей, а коснусь лишь основных механизмов защиты от CSRF:
• Вместе с кукой присылать скрытый
<input> в котором присылать дубль токена, который идет в самой куке. Если при запросе то, что в форме и то, что в куке не совпало — запрос отклоняется. Недостаток в том, что токен надо привязывать к пользователю и добавлять в каждую форму.• Проверять через заголовок
Origin что инициатор запроса был сам исходный сайт, а не сторонний. Тоже обладает рядом минусов (может быть null при кривых плагинах), и требует довольно сложной настройки для правильной поддержки cross-origin запросов.• SameSite куки и "Non-simple requests" которые защищают от вариантов атаки HTTP -> HTTPS но в общем случае бесполезны.
На данный момент для защиты от CSRF в нашей экосистеме есть две либы: csrf и nosurf. Обе работают через дубль токена (т.е. требуют донастройки фронта) и обе почти не поддерживаются разработчиками.
Поэтому Go 1.25 в пакет
net/http добавили готовую «прослойку» для защиты от CSRF, построенную на механизме Fetch Metadata который присутствует во всех современных браузерах с 2023го года. Заголовок с сайта банка при запросе через fetch в таком случае будет выглядеть так:
GET /money/foo.json
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
В то время как заголовок от зловредного сайта при «прямой» подгрузке будет выглядеть вот так
GET /money/cool.jpg
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Сам proposal добавляет лишь один тип
CrossOriginProtection, который предназначен для оборачивания ваших обработчиков.Как это работает:
• Браузер добавляет
Sec-Fetch-Site (same-origin / same-site / cross-site / none). Хендлер смотрит это заголовок, и если это не same-origin или none а метод это POST/PUT/PATCH/DELETE то запрос режется. Тоже самое если нет Sec-Fetch-Site: тогда смотрим Origin и сравниваем с Host и опять же, в случае несовпадения, режем POST/PUT/PATCH/DELETE запросы.• В случае если дополнительно установлен список ориджинов через метод
AddTrustedOrigin то полученный запрос сравнивается по заголовку Origin пока не будет найдет нужный, или запрос не будет отклонен.• Запросы без
Sec-Fetch-Site и без Origin пропускаются тк их явно не мог сделать браузер, защищать нечего.• «Безопасные» методы
GET/HEAD/OPTIONS всегда пропускаются (уповают на то, что вы не меняете состояние в этих методах и следуете разумным практикам)Для более сложных случаев предусмотрены методы
AddInsecureBypassPattern и функция SetDenyHandler которая явно разрешает запрос.MDN Web Docs
Cross-site request forgery (CSRF) - Security | MDN
In a cross-site request forgery (CSRF) attack, an attacker tricks the user or the browser into making an HTTP request to the target site from a malicious site. The request includes the user's credentials and causes the server to carry out some harmful action…
👍16❤5
Осталось написать (и разобраться) про новый экспериментальный сборщик мусора и программа минимум будет закрыта 😄
👍17❤6😁2
Go Update
🎂 Тем временем Земля сделала полный оборот вокруг Солнца, и мне исполнилось 32. В этот раз буду предельно краток: я жив, у меня всё хорошо, и этот блог не заброшен. Единственное, чего мне пока очень сильно не хватает, — это времени. Заметок скопилось за три…
🎂 Настал тот день когда мне исполнилось 33!
Да, да тот самый год, в который вас поздравляют используя аналогии к одной из самых ярких личностей в истории человечества 😁️️️️️️.
В (не)далеком детстве, когда мне было лет 7-8, я смотрел на взрослых, которым было за 30 (что казалось мне очень-очень далеко), и она казались кем-то, кто такой «опытный, мудрый, знающий». Думал, что вот: вырасту я, и сам буду такой взрослый-серьезный-мудрый. Машину водить, детей воспитывать. В очках ходить.
И вот теперь мне за 30. Я вожу машину (иногда даже две). Я воспитываю сына. Я хожу в очках (что кстати минус). И вот, что я вам скажу: хрень это все про «вырастишь, сам поймешь!». Фраза пустышка, которую так любили повторять родители и старшие близкие в процессе воспитания, но которая ничего под собой не имеет. Я не чувствую себя опытнее. Или мудрее. Или более знающим. Я ничего так и не «понял». Из года в год я надеюсь, что настанет тот самый магический момент когда я стану наконец «взрослым» (и хотя-бы перестану влезать в бессмысленные споры в чатике которые могут съесть час в лучшем случае). Но этот момент всё никак не настаёт.
Тем более последние годы здорово перевернули жизнь, а рождение сына, два года назад, вообще заставило взглянуть на себя по новому. Но вот сказать, что я теперь я стал весь из себя такой «мудрый-разумный» я не могу. В душе мне по прежнему кажется, что я ментально застрял где-то в возрасте 17-19 лет. Я по прежнему смотрю на своих друзей и вижу в них школьников а не серьезных дядь, коими я видел взрослых будучи мелким. При фразе «пропусти дядю!» я по прежнему оглядываюсь, пытаясь найти того самого «дядю». А вот единственное чего с годами становится объективно меньше это время, и как следствие сил. И то просто из-за количества рутинных дел которые приходится делать день за днём. Та вещь фундаментально непонятна когда тебе 8 и времени хватает на всё и даже больше.
В общем тут уже идет пятый параграф и надо бы начать подводить итоги. И вот мой итог на следующий год: надо нормально спать. Наверное это самая важная мудрость, которую я никак не могу постигнуть. Если вам между 18 и 28-30, то знайте, что «ремонт сбитого режима, за два дня, без регистрации и смс» это не навсегда. Я пишу эти строки сонный, ибо лёг поздно (пункт про время), а в 5 утра меня поднял сын «кики мама не, кики папа мими да!», что в переводе означало что «ему хочется кефир, а мама просыпаться отказалась». Пока просыпался-вставал-наливал-ждал-укладывал сон слетел и пришлось укладывать еще и себя (что гораздо дольше). В общем на сегодня сделал вывод, что на следующий ночной сон пойду вместе с ним. Но вот вечером я вероятно опять найду оправдание в голове, что-бы этого не делать…
Какой уж тут «опытный, мудрый, знающий» если банальный сон привести в порядок не можешь.
П.С. Статья про новый сборщик мусора делается, надеюсь успеть до релиза Go 1.25.
Да, да тот самый год, в который вас поздравляют используя аналогии к одной из самых ярких личностей в истории человечества 😁️️️️️️.
В (не)далеком детстве, когда мне было лет 7-8, я смотрел на взрослых, которым было за 30 (что казалось мне очень-очень далеко), и она казались кем-то, кто такой «опытный, мудрый, знающий». Думал, что вот: вырасту я, и сам буду такой взрослый-серьезный-мудрый. Машину водить, детей воспитывать. В очках ходить.
И вот теперь мне за 30. Я вожу машину (иногда даже две). Я воспитываю сына. Я хожу в очках (что кстати минус). И вот, что я вам скажу: хрень это все про «вырастишь, сам поймешь!». Фраза пустышка, которую так любили повторять родители и старшие близкие в процессе воспитания, но которая ничего под собой не имеет. Я не чувствую себя опытнее. Или мудрее. Или более знающим. Я ничего так и не «понял». Из года в год я надеюсь, что настанет тот самый магический момент когда я стану наконец «взрослым» (и хотя-бы перестану влезать в бессмысленные споры в чатике которые могут съесть час в лучшем случае). Но этот момент всё никак не настаёт.
Тем более последние годы здорово перевернули жизнь, а рождение сына, два года назад, вообще заставило взглянуть на себя по новому. Но вот сказать, что я теперь я стал весь из себя такой «мудрый-разумный» я не могу. В душе мне по прежнему кажется, что я ментально застрял где-то в возрасте 17-19 лет. Я по прежнему смотрю на своих друзей и вижу в них школьников а не серьезных дядь, коими я видел взрослых будучи мелким. При фразе «пропусти дядю!» я по прежнему оглядываюсь, пытаясь найти того самого «дядю». А вот единственное чего с годами становится объективно меньше это время, и как следствие сил. И то просто из-за количества рутинных дел которые приходится делать день за днём. Та вещь фундаментально непонятна когда тебе 8 и времени хватает на всё и даже больше.
В общем тут уже идет пятый параграф и надо бы начать подводить итоги. И вот мой итог на следующий год: надо нормально спать. Наверное это самая важная мудрость, которую я никак не могу постигнуть. Если вам между 18 и 28-30, то знайте, что «ремонт сбитого режима, за два дня, без регистрации и смс» это не навсегда. Я пишу эти строки сонный, ибо лёг поздно (пункт про время), а в 5 утра меня поднял сын «кики мама не, кики папа мими да!», что в переводе означало что «ему хочется кефир, а мама просыпаться отказалась». Пока просыпался-вставал-наливал-ждал-укладывал сон слетел и пришлось укладывать еще и себя (что гораздо дольше). В общем на сегодня сделал вывод, что на следующий ночной сон пойду вместе с ним. Но вот вечером я вероятно опять найду оправдание в голове, что-бы этого не делать…
Какой уж тут «опытный, мудрый, знающий» если банальный сон привести в порядок не можешь.
П.С. Статья про новый сборщик мусора делается, надеюсь успеть до релиза Go 1.25.
🔥45❤24👏3