Go Update – Telegram
Go Update
3.1K subscribers
8 photos
70 links
Канал про новости связанные с языком программирования Go. Эволюция языка, стандартной библиотеки и просто интересные вещи над которыми работает Go Core Team и не только.

Админ: @lepage_d
Download Telegram
net/http: delete inappropriate headers in func Error

Из недавних мелких изменений: начиная с Go 1.23 (и выставленной директиве go 1.23.0 в go.mod) функции ServeContent, ServeFile, and ServeFileFS в случае ошибки удаляют заголовки Cache-Control Content-Encoding, Etag, и Last-Modified. Это стоит учитывать, если у вас есть матрешка вокруг интерфейса ResponseWriter которая, например, сжимает ответ и добавляет заголовок Content-Encoding: gzip.

Старое поведение можно восстановить с помощью директивы GODEBUG=httpservecontentkeepheaders=1.

Изначально планировалось сделать тоже самое для функции http.Error. В последствии от этого отказались, так как это изменение ломало слишком много пользовательского кода.
👍73
🔥 weak: new package providing weak pointers 🔥

Соскучились по интересным изменениям? Их есть у меня!

Начиная с Go 1.24 у нас появится пакет который даст там т.н. "слабые указатели". Суть этих указателей заключается в том, что сборщик мусора вправе в любой момент времени собрать данные на которые они указывают. Т.е. сделать их указатели невалидными.

Простой пример кода:

weakPtr := weak.Make(&SomeStruct{someData})
runtime.GC()
println(weakPtr.Pointer() == nil) // может быть true, а может и false


На самом деле эти указатели появились еще с приходом пакета unique о котором я писал ранее. Но затащить два пакета в рамках одного proposal'а нельзя, поэтому пришлось ждать Go 1.24. Go Core Team (и в особенности Расс Кокс) очень долго сопротивлялись добавлению этого вида указателей в язык, т.н. они ломают абстракцию со сборщиком мусора и имеют очень неочевидные механики взаимодействия с finalizers. Однако после добавление пакета unique отрицать необходимость (хоть и редкую) такой вещи стало невозможно, поэтому было принято решение дать ее использовать всем.

Основной плюс этого пакета в том, что разработчикам кешей стало намного легче собирать мусор. Даже человек, который слабо помнит алгоритмы, теперь может реализовать LRU кеши элементарно на мапах. Как именно? А вот это я покажу в сообщении о следующем за этим изменении.
🔥33👍321
🧹 runtime: add AddCleanup - типобезопасная альтернатива SetFinalizer

Сейчас для отслеживания настоящего момента удаления объекта из памяти у нас есть одна функция: runtime.SetFinalizer. Она говорит рантайму, что нас (через коллбэк) нужно уведомить о том, что объект был убран из памяти сборщиком мусора. Чаще всего это нужно, чтобы мы не забыли закрыть внешние дескрипторы (файлы/сеть/прочее), ассоциированные с объектом.

Однако у этой функции есть и ряд минусов:
Она не является типобезопасной. Вот вам простой пример кода, который развалится только во время выполнения.
Она допускает "воскрешение мёртвых объектов" (ситуации, когда через финализатор можно вернуть данные по удаляемому указателю обратно к жизни - пример кода). Эту штуку кстати ребята из TailScale использовали в своей либе go4.org/intern. У них еще крутая статья есть.
Нельзя поставить несколько финализаторов на объект. Это здорово мешает, когда есть дополнительные вещи которые мы хотим выполнить при «смерти» объекта, а автор вызываемого кода уже повесил свой финализатор. Обходные пути есть, но у всех них есть свои тонкости.

В качестве решения предложена функция AddCleanup вот с такой сигнатурой:


func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup

type Cleanup struct { ... }

// Stop cancels the cleanup call.
func (c Cleanup) Stop() { ... }


Суть в том, что мы вешаем коллбэк на указатель (ptr), но в сам коллбэк приходит объект (arg) который мы ассоциировали с указателем.

Что это нам дает?
Больше нельзя указать неправильную сигнатуру для коллбэка.
Идет разделение между тем, что мы отслеживаем и тем, что мы получаем в коллбэк при «смерти» отслеживаемого. Более того, сама функция пытается уберечь нас от ситуаций типо arg == ptr (правда, только в рантайме).
Сначала выполняется финализатор (если он есть), а затем выполняется один или более коллбэк который мы повесили с помощью нового механизма.

А ждет нас это счастье в Go 1.24, ведь само изменение уже в мастере.

LRU Cache Example

На самом деле я немного покривил душой, когда говорил про LRU кеш: получаемый кеш имеет часть его свойств, но нельзя точно указать число объектов которые будут живы в памяти и время их жизни. Те из вас кто работал с другими языками скорее всего узнают WeakMap в самом примере.

Главное:
Создаем словарь/мапу ключей к слабым указателям.
Удаляем ключ как сработает AddCleanup.

На этом всё - у вас есть минимальный пример самоочищающегося кеша.
🔥164🤯3
🐧 Go 1.24 будет требовать ядро Linux 3.2 и выше.

Небольшая, но важная, заметка. В 2021 году (Go 1.18) минимальным ядром на Linux стала версия 2.6.32. Спустя почти четыре года, нас снова ждет изменение требования: теперь в Go 1.24 минимальную версию ядра поднимут до 3.2. Учтите это при обновлении, если у вас очень старые дистрибутивы на серверах (или у вас зоопарк), а разрабатываете вы на самом новом и крутом.

П.С. Да, я в курсе, что актуальная версия ядра вообще 6.x.y 🙂️️️️️️
👍252
Минутка юмора.

Можете отправить это человеку который продолжает вас убежать в том, что ChatGPT скоро заменит всех разработчиков инженеров людей.
😁44🗿7🤡2😐1
Про нововведения в Go 1.24

Долго собирался написать серию (хотя бы один!) постов про вещи которые к нам подъедут в Go 1.24, но так никак и не смог собраться. А потом обнаружил, что один наш коллега уже все это сделал в подробном и, главное, интерактивным виде. Правда есть нюанс — сам пост на английском.

Однако тем кто не знает английского не стоить грустить — методом гуглежки было обнаружено, что от него же есть серия статей которая почти полностью дублирует записи в блоге. И оно в телеграмме! Поэтому смело идем и начинаем читать, что нас ждет в предстоящей версии.

П.С. Ну а я постараюсь в ближайшее время запилить обзор предложение от Яна Ланса Тейлора о новом синтаксисе обработки ошибок и как оно разделило сообщество почти поровну.
👍121
0️⃣ Omitzero или убираем пустые значения при серилизации в json

Я уверен, что большинство в курсе о том, что пакет json поддерживает структурный тег omitempty. На данный момент это единственный прямолинейный способ указать, что поле не следует выводить в json если оно содержит нулевое значение. Однако у этого тега есть два минуса:

1. Он плохо работает со структурами.
2. Он неумеет в переопределение принципа по которому определяется сам факт нулевого значения.

Оба пункта довольно долго портили кровь разработчикам сложных вложенных API и заставляли их пользоваться указателями, там, где можно было обойтись без них. И вот Go Team приняла волевое решение и реализовала новый тег. Встречаем omitzero. Принцип работы прост до невозможности:

1. Если поле это структура и она пустая, то маршалить ее он не будет.
2. Если у типа поля определен метод IsZero и он вернул true то и его маршалить не будут.

КМК именно это можно было привезти еще в версии Go 1.10, т.к. фиче не нужны новые языковые фишки, а необходимость ее была видна еще с Go 1.1. Чтож, лучше поздно чем никогда.
👍358
🎉 Вышел Go 1.24! 🎉

Этот момент настал!

Ключевые нововведения:
— Дженерики теперь умеют в псевдонимы (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👍186
Немного юмора.
😁64🤯6😨3
😢 Ян Ланс Тейлор покидает 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👍96🤯2😁1😡1
🔀 Динамический GOMAXPROCS 🔀

До релиза 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 на время жизни всего процесса.
🔥41👍10👏3
🏎️ Об оптимизациях в Go 1.25 🏎️

В новом релизе, как и всегда, к нам приедут новые оптимизации для компилятора. Две из них меня заинтересовали больше всего:

• Цепочка из четырёх 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 теперь использует сравнение по указателю для нулевых значений структур и массивов, что существенно удешевляет проверку.
🔥214👍3👏1
Короче я нашёл проблему — ребята которые делают Swift телеграмм так «удачно» подобрали цвета, что именно в моей «стоковой» теме моношрифт и ссылки выглядят одинаково. Удобство 11/10!
😁63
Немного примеров из комментариев. Мне кажется где-то заплакал UX дизайнер.
😁7
Об обсуждениях.

Вот уже более тринадцати лет нормальный синтаксис неподвижно обсуждается на GitHub.
По воле отцов-основателей выступает Повелитель Простоты и правит миллионами микросервисов благодаря мощи своего неисчислимого бойлерплейта.
Он — гниющий реликт минимализма, в ком незримые муки разработчиков продлеваются загадочными отказами «это не вписывается в философию».
Он — незыблемый столп экосистемы, которому каждый день приносят в жертву тысячи бестолковых предложений о try-catch, идиотские идеи
которых были озвучены уже десятки раз, а все самопротиворечащие доводы высказаны и рассмотрены.
На боли и страданиях гоферов стоит тот самый "Idiomatic Go"


Вы знаете, что такое безумие?

Кто-то скажет, что факт того, что язык получивший свой релиз в 2012-ом году, но не имевший дженериков аж до 2022го — это безумие. На что можно резонно возразить: дескать, нормальный синтаксис — это полдела, основная проблема в экосистеме! Другие могут сказать, что почти все реализации (включая и ту, которую в итоге получили мы) обладают рядом минусов и требуется их хорошо продумать, перед тем как вносить этот инструмент в язык. Третьи скажут, что внесение дженериков в язык привлекает внимание C++ любителей всё усложнять, а сам Go был ценен в первую очередь простотой и понятливостью даже свежему после универа программисту. Короче: оправданий можно придумать массу. На самом деле доподлинно никто не знает, почему внедрение дженериков заняло так много времени, поэтому любые попытки найти рациональное объяснение — не более чем «игра для ума» (по-простому — ещё один способ вентиляции лёгких), которая не несёт в себе никакой практической пользы.

Нет, скажу я вам. Безумие это хождение по кругу годами в поисках идеального синтаксиса который устроит всех. Например, обсуждение синтаксиса лямбд, которое ведётся уже более 8 лет и которое, по итогу, так и не сдвинулось с места. Синтаксиса! Не семантики и не деталей реализации. Ибо главный выбор, который остался перед нами, — это то, «будет ли новый синтаксис использовать =>, или ->, а может, \-> для разделения объявления и тела лямбды?». И если кто-то думает, что я шучу, то ему достаточно глянуть issue, в котором уже больше 900 скрытых комментариев, по которым уже несколько раз делали сводку (вручную, с помощью Gabby, с помощью других LLM, комбинацией из трёх прошлых способов) и которое раз за разом возвращается назад, к своему началу. К выбору идеального синтаксиса. К безумию.

Честно — у меня нет цензурных слов. Если вам когда-нибудь хотелось узнать, что такое "Bikeshedding" (в русском языке нет прямого перевода, но термин расшифровывается как «бессмысленное обсуждение тривиальных вопросов»), то это обсуждение — самый наглядный пример, который у меня есть. Где-то полгода назад, когда, как мне казалось, картинка начала выкристаллизовываться, я хотел написать об этом запись в блоге. И попросить людей поучаствовать, если им есть что высказать или подсказать, ведь это шанс поучаствовать в развитии языка, а создатели действительно слушают мнение людей по этому вопросу! Теперь я понимаю — первый раз за долгое время меня спасло то, что я что-то не сделал. Ибо с тех пор, несмотря на как минимум два резюме по итогам «ещё одной итерации обсуждения», продвижения по-прежнему нет.

Я не знаю, какой здесь сделать вывод. Но я уверен, что нас ждёт ещё минимум столько же комментариев в будущем.

In the grim darkness of the far future there is only "if err != nil".
12👍4🔥2👀2🗿1
⏲️ testing/synctest — останавливаем время ⏲️

Одними из самых сложных сценариев для тестирования являются сценарии где замешано время. Если использовать реальное время то тесты либо а) начинают занимать слишком много времени либо б) теряют в надежности из-за гонки "кода со временем". Особенно хорошо это ощущается в тестах которые используют 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 паникует.

Проще всего понять на живом примере (код не влез в этот пост). Обратите внимание, что время выставлено в часы однако время теста и порядок вывода в лог сохраняется раз за разом. Так мы обеспечиваем не только быстрое исполнение, но и ожидаемый детерминизм. Другой плюс нового пакета, заключается в том, что если горутина создавшая "пузырь" выйдет до того, как закончат работу все её дочерние горутины, то вы получите панику (пример). Таким образом на этапе тестирования еще и обеспечивается контроль очистки ресурсов.

Для более подробной документации рекомендую глянуть документацию. Там-же приведены и другие примеры, сценарии и особенности работы.
👍11🔥3
Go Update
⏲️ testing/synctest — останавливаем время ⏲️ Одними из самых сложных сценариев для тестирования являются сценарии где замешано время. Если использовать реальное время то тесты либо а) начинают занимать слишком много времени либо б) теряют в надежности из…
А теперь по простому:

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👍3