🔀 Динамический 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
Go Update
🎉 Вышел Go 1.24! 🎉 Этот момент настал! Ключевые нововведения: — Дженерики теперь умеют в псевдонимы (aliases) т.е. теперь можно писать так type MyType[T any] = myType[T,*T] Поздравляю тех, кому приходилось работать с указателями в дженериках, теперь…
🎉 Вышел Go 1.25! 🎉
Пока я не успел статью про новый сборщик мусора написать, поэтому быстро пробегусь по основным нововведениям которых не касался в прошлых записях.
— ♻️ Бинарей в комплекте теперь меньше. Разработчики компилятора активно использовать команду
— Новая директива
— Когда go команда обновляет
—
— 🍵 Новый экспериментальный сборщик мусора. О нем статья будет позже.
— 🔀️️️️️️ Новый тип
— 🛠 Компилятор теперь генерирует дебаг-инфу в формате DWARF5. Практический итог: бинари едят меньше места и быстрее комбинируются.
— 🏎️️️️️️ Новый экспериментальный пакет
— Тип
— 🏎️️️️️️ Функция
— Директива
—
— Новый метод
— 🔥
Читать про релиз вот тут.
Пока я не успел статью про новый сборщик мусора написать, поэтому быстро пробегусь по основным нововведениям которых не касался в прошлых записях.
— ♻️ Бинарей в комплекте теперь меньше. Разработчики компилятора активно использовать команду
go tool у себя внутри, что убирает необходимость поставлять заготовки в комплекте. Но как минимум go build и go test бинари все еще на месте.— Новая директива
ignore в go.mod. Подробное описание тут. Если в вкратце: для этого вы раньше использовали каталог testdata и каталоги которые начинаются с точки.— Когда go команда обновляет
go 1.xx.y директиву в go.mod она больше не добавляет toolchain директиву.—
go vet теперь ругается на неправильное использование sync.WaitGroup.Add и fmt.Sprintf("%s:%d", host, port) ибо есть net.JoinHostPort.— 🍵 Новый экспериментальный сборщик мусора. О нем статья будет позже.
— 🔀️️️️️️ Новый тип
runtime/trace.FlightRecorder позволяет записывать только значимые события, а не всё подряд для tracing’га (под капотом используется циклический буфер, который помнит N последних секунд).— 🛠 Компилятор теперь генерирует дебаг-инфу в формате DWARF5. Практический итог: бинари едят меньше места и быстрее комбинируются.
— 🏎️️️️️️ Новый экспериментальный пакет
encoding/json/v2. По хорошему про него тоже надо писать отдельную статью, но если в кратце — он намного быстрее того что было внутри encoding/json. А другая хорошая новость заключается в том, что если вы включили GOEXPERIMENT=jsonv2 то больше ничего менять не надо, так как encoding/json сам подключит новую внутряку.— Тип
os.Root приобрел несколько новых методов.— 🏎️️️️️️ Функция
reflect.TypeAssert позволяет приводить типы из reflect.Value в конкретный тип, минуя потенциально аллоцирующий вызов reflect.Value.Interface.— Директива
GODEBUG=checkfinalizers=1 позволяет понять, как дела в вашей очереди cleanup’ов и finalizer’ов во время каждого GC.—
SetDefaultGOMAXPROCS позволяет сбросить настройки GOMAXPROCS если вдруг переменная прилетела через Env или через прошлый вызов GOMAXPROCS.— Новый метод
sync.WaitGroup.Go - больше нет необходимости тащить errgroup если вам не нужен был возврат ошибок и отмена контекста.— 🔥
testing.T/B/F теперь имеют метод Output() io.Writer который позволяет правильно редиректить вывод внутри вызова теста.Читать про релиз вот тут.
go.dev
Go Modules Reference - The Go Programming Language
🔥26✍3❤3👍2
А ещё планируем с Колей записать подкаст по поводу релиза 1.25 где у вас будет возможность задать все интересующие вас вопросы. Вероятно объяснение нового сборщика мусора будет в первую очередь там (с помощью пальцев и активной жестикуляции).
Так что следите за анонсами 😁.
Статья здесь все равно будет, если вы не переносите видео.
Так что следите за анонсами 😁.
Статья здесь все равно будет, если вы не переносите видео.
Telegram
Николай Тузов
Go Developer, автор YouTube канала по Go: https://www.youtube.com/@nikolay_tuzov
Live канал: @ntuzov_live
Интересные подборки по Go: @golang_digest
Обратная связь: @justskiv
Поддержать:
https://boosty.to/nikolay.tuzov/
https://t.iss.one/ntuzov/126
Live канал: @ntuzov_live
Интересные подборки по Go: @golang_digest
Обратная связь: @justskiv
Поддержать:
https://boosty.to/nikolay.tuzov/
https://t.iss.one/ntuzov/126
❤17🔥10😐1
Forwarded from Николай Тузов
https://youtube.com/live/VHjXHzs742c?feature=share
Когда: в ближайшую субботу, 23 августа, 11:00 по Мск
Обсуждаем тем же составом, которым обсуждали когда-то Go v1.21:
- Николай Тузов
- Глеб Яльчик
- Дмтрий Матрёничев
А пока ждёте, советую почитать посты Димы на эту тему, у него очень подробные и крутые разборы
#gogetpodcast #go_1_25
Please open Telegram to view this post
VIEW IN TELEGRAM
Youtube
- YouTube
Enjoy the videos and music you love, upload original content, and share it all with friends, family, and the world on YouTube.
🔥7
Я только сейчас понял, что у нас снова, за долгое время, совпали цифры релиза и года. Причем впервые совпали обе цифры. В последний раз это был Go 1.6 и совпала только последняя.
Таймлайн:
Таймлайн:
go1.0 2012-03-28
go1.1 2013-05-13
go1.2 2013-12-01
go1.3 2014-06-18
!go1.4 2014-12-10
!go1.5 2015-08-19
!go1.6 2016-02-17
go1.7 2016-08-15
go1.8 2017-02-16
go1.9 2017-08-24
go1.10 2018-02-16
go1.11 2018-08-24
go1.12 2019-02-25
go1.13 2019-09-03
go1.14 2020-02-25
go1.15 2020-08-11
go1.16 2021-02-16
go1.17 2021-08-16
go1.18 2022-03-15
go1.19 2022-08-02
go1.20 2023-02-01
go1.21 2023-08-08
go1.22 2024-02-06
go1.23 2024-08-13
go1.24 2025-02-11
!go1.25 2025-08-12🔥21👍6🤯5💩2
👁Визуализатор нашего трехцветного сборщика мусора.
Сегодня на подкасте я упомянул, что у нас трехцветный сборщик мусора. Для меня, как человека, который привык разбирать вещи «на практике или в действии» самым лучшим объяснением выступил визуализатор от ребят из Pusher который они сделали в рамках своей своей статьи про его особенности. И хотя статье уже почти 8 лет, ничего из основ, которые в нее положены, с тех пор не поменялось. В общем: очень и очень рекомендую.
Так-же есть хорошая статья про сборщик мусора от Авито (хотя секция про Write Barrier довольно тяжела для восприятия) и цикл статей от Ardan Labs о основах памяти и о том как правильно читать выводы рантайма про сборку мусора.
Сегодня на подкасте я упомянул, что у нас трехцветный сборщик мусора. Для меня, как человека, который привык разбирать вещи «на практике или в действии» самым лучшим объяснением выступил визуализатор от ребят из Pusher который они сделали в рамках своей своей статьи про его особенности. И хотя статье уже почти 8 лет, ничего из основ, которые в нее положены, с тех пор не поменялось. В общем: очень и очень рекомендую.
Так-же есть хорошая статья про сборщик мусора от Авито (хотя секция про Write Barrier довольно тяжела для восприятия) и цикл статей от Ardan Labs о основах памяти и о том как правильно читать выводы рантайма про сборку мусора.
🔥29
🚀 Расширение функции
Отличные новости! Предложение, обсуждение которого которого длится уже больше четырех лет и которое выдвинул сам Роб Пайк, наконец-топодходит к принятию приняли!
В чем суть: есть у нас встроенная функция
Однако у оператора "
И вот теперь, спустя 13 лет после релиза языка, нас ждет камбэк функции
А сие значит, чтовероятно точно уже в 1.26 можно будет избавится от хелперов, таких как:
Маленькое, но давно назревшее изменение, принятию которого (пускай и в слегка измененной версии) рад даже сам Пайк.
new для создания указателя на значения 🚀Отличные новости! Предложение, обсуждение которого которого длится уже больше четырех лет и которое выдвинул сам Роб Пайк, наконец-то
В чем суть: есть у нас встроенная функция
new, которая принимает тип и возвращает указатель на значение этого типа. Хорошая функция, однако исторически так сложилось, что она была в тени оператора "&" который использовался и для создания указателей на комплексные типы и для взятия адреса существующих переменных. С учетом того, что для создания словарей, каналов и срезов используется функция make, прикладного постоянного использования у new было немного.Однако у оператора "
&" тоже есть недостатки. Самый явный — он не умеет работать со значениями примитивов (т.е. нельзя сделать a := &1, такой код просто не скомпилируется). Другая проблема в том, что для продолжения работы с указателем, в месте его взятия, нам нужно помещать выражение в скобочки. Т.е. нельзя написать &myComplexType{}.CallStuff() но можно написать (&myComplexType{}).CallStuff().И вот теперь, спустя 13 лет после релиза языка, нас ждет камбэк функции
new так как ее новый синтаксис будет принимать как типы, так и значения. Текущее предложение делает корректным вот такий синтаксис:…
a := new(123)
b := new(myConstant)
c := new(int64(-123))
…
А сие значит, что
func ptrTo[V any](v V) *V { return &v }Маленькое, но давно назревшее изменение, принятию которого (пускай и в слегка измененной версии) рад даже сам Пайк.
GitHub
spec: expression to create pointer to simple types · Issue #45624 · golang/go
(Latest proposal at #45624 (comment); --adonovan) This notion was addressed in #9097, which was shut down rather summarily. Rather than reopen it, let me take another approach. When &S{} was ad...
❤46🔥28👍18
Тут появилась запись нашего подкаста где мы обсуждали 1.25. Получилось довольно неплохо, как и говорил — поводил руками при объяснении нового сборщика мусора. Ну и про другие вещи не забыли.
❤2👍2🔥1