До Go 1.25 рантайм ориентировался на CPU всей машины. Поэтому pod с лимитом в 2 CPU на 64-ядерной ноде мог получить GOMAXPROCS=64.
Linux при превышении CPU limit начинал throttling — процесс мог полностью приостанавливаться на оставшуюся часть периода. Это особенно неприятно для tail latency.
runtime.GOMAXPROCS(0)
Например, pod с лимитом в 2 CPU → GOMAXPROCS будет 2. Значение также периодически пересчитывается, если cgroup limit меняется.
⚠️ Но есть ловушка: если вы вручную задаёте GOMAXPROCS через env или:
runtime.GOMAXPROCS(8)
автоматическая настройка отключается.
📍 Навигация: Вакансии • Задачи • Собесы
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👏4👾3😢1
19 августа вышел Go 1.27 — и по политике Go поддерживаемыми теперь остаются только 1.27 и 1.26.
Если проект всё ещё собирается на 1.25, это хороший момент для обновления. Для тех, кто пока не готов переходить на 1.27, есть актуальная ветка 1.26 с патч-релизом 1.26.7.
Проверить зависимости на известные уязвимости можно через:
govulncheck ./…
А заодно стоит посмотреть на изменения в encoding/json: в Go 1.27 появился новый encoding/json/v2, а старый пакет теперь работает поверх его реализации.
📍 Навигация: Вакансии • Задачи • Собесы
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8😢3💯2
This media is not supported in your browser
VIEW IN TELEGRAM
html/template работает со строковыми шаблонами, а templ позволяет писать HTML-компоненты прямо в .templ и превращать их в обычный Go-код.
templ Hello(name string) {
<div class="greeting">Привет, { name }</div>
}
После templ generate компонент можно использовать как обычную функцию:
view.Hello("гофер").Render(ctx, w)
Особенно удобно для серверного рендеринга и связки Go + htmx, когда полноценный frontend-фреймворк не нужен.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍6
Подняли тестовую БД, создали временный файл или запустили сервер? t.Cleanup позволяет привязать очистку прямо к жизненному циклу теста.
func newTestDB(t *testing.T) *sql.DB {
t.Helper()
db := openTempDB(t)
t.Cleanup(func() {
db.Close()
})
return db
}
func TestUsers(t *testing.T) {
db := newTestDB(t)
// ...
}
Cleanup выполнится после завершения теста, даже если тот упал через t.Fatal. Несколько cleanup-функций выполняются в обратном порядке регистрации.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15⚡2🙏2👾1
Обычная история: есть функция и десять сценариев. Если писать по тесту на каждый сценарий, код быстро превращается в копипасту. Table driven подход делает один тест, а сценарии складывает в таблицу.
Вы описываете кейсы в таблице, обычно это слайс структур. Потом в цикле запускаете каждый кейс как сабтест через t.Run, чтобы у каждого сценария было имя и его можно было запустить отдельно.
func Split(s, sep string) []string {
return strings.Split(s, sep)
}
func TestSplit(t *testing.T) {
tests := []struct {
name string
input string
sep string
want []string
}{
{name: "simple", input: "a/b/c", sep: "/", want: []string{"a", "b", "c"}},
{name: "no sep", input: "abc", sep: "/", want: []string{"abc"}},
{name: "trailing", input: "a/b/c/", sep: "/", want: []string{"a", "b", "c", ""}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := Split(tc.input, tc.sep)
if !reflect.DeepEqual(got, tc.want) {
t.Fatalf("want %v got %v", tc.want, got)
}
})
}
}
Еще мелочь, но полезная. Внутри сабтеста t.Fatal завершает только текущий сабтест, а не весь набор, поэтому для table driven тестов это часто удобнее чем тянуть continue и пачку if.
📍 Навигация: Вакансии • Задачи • Собесы
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2❤1🥰1🥱1
API старого encoding/json остался прежним, но под капотом теперь работает новый движок encoding/json/v2. Для существующего кода это должно быть прозрачно — Go сохраняет старую семантику через набор compatibility-опций.
Но производительность изменилась.
В независимом бенчмарке на одинаковых данных:
🔘 Unmarshal в структуру — примерно в 1,5 раза быстрее;🔘 Unmarshal в any — примерно в 1,5 раза медленнее;🔘 Marshal — немного медленнее, зато заметно меньше аллокаций;🔘 потоковая работа через новый API MarshalWrite требует значительно меньше памяти.
Самая интересная ловушка — any. Старый API разрешает дубликаты ключей JSON, поэтому новый движок не может использовать свой быстрый путь для any. В результате динамический JSON может обрабатываться медленнее.
После перехода на Go 1.27 недостаточно проверить только компиляцию. Если encoding/json находится на горячем пути — стоит прогнать свои бенчмарки. Особенно если приложение активно работает с any или map[string]any.
А encoding/json/v2 уже можно внедрять постепенно, сохраняя нужные v1-семантики через DefaultOptionsV1().
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍3⚡2🥱1👾1
С Go часто делают так: выбрали версию, прописали её в Dockerfile — и вспоминаем о ней только при следующем большом обновлении. Но security-патчи выходят внутри той же ветки. И если ваша версия старая, опубликованная уязвимость превращает её в известную цель.
Что стоит сделать:
— закрепить конкретный toolchain в go.mod;
— регулярно пересобирать Docker-образ;
— следить за security-анонсами Go;
— проверять зависимости через govulncheck.
Например:
toolchain go1.26.6
А CI может брать версию прямо из go.mod:
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤🔥2🔥2😢1
В Go тысячи горутин не привязаны напрямую к потокам ОС. Runtime распределяет их через GMP-модель:
У каждого P есть своя локальная очередь горутин. Если работы не хватает, планировщик может взять её из глобальной очереди или украсть часть очереди другого P — это work-stealing.
А вся схема — на картинке
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍6🥰2⚡1
Что произойдёт, если одна горутина надолго зациклится на CPU и не будет делать ни sleep, ни syscall, ни операций с каналами?
❤️ — вторая горутина не выполнится, потому что P всего один
🔥 — планировщик вытеснит первую горутину и запустит вторую
👍 — вторая выполнится только после runtime.Gosched()
GOMAXPROCS(1) означает, что одновременно выполняется только одна горутина, но планировщик может переключать между несколькими G.
До Go 1.14 такой бесконечный цикл действительно мог надолго заблокировать выполнение других горутин.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥61❤3👍3🤔1
Да. Пока сообщение не удалено по retention, consumer может вернуться к старому offset и обработать его снова.
исправили код → сдвинули offset назад → переиграли сообщения
Например, для consumer group можно сбросить offset через kafka-consumer-groups.
Kafka в этом плане отличается от классической очереди: прочитанное сообщение не исчезает сразу — его можно переиграть.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥2
Если идёте на Middle Go, одного синтаксиса SQL мало. На собеседовании легко уйти в индексы, транзакции и конкурентный доступ.
Сохраняйте чек-лист:
B-tree — когда используется и какие запросы ускоряет?
EXPLAIN ANALYZE — что показывает и чем отличается от EXPLAIN?
Почему PostgreSQL выбирает Seq Scan, хотя есть индекс?
Составной индекс — что это и зачем нужен?
Левый префикс — почему порядок колонок в индексе важен?
Как выбрать порядок колонок в составном индексе?
MVCC — как PostgreSQL позволяет транзакциям работать параллельно?
Уровни изоляции — какие бывают и чем отличаются?
Deadlock — как возникает и как его предотвращать?
SELECT ... FOR UPDATE — когда нужен?
N+1 — откуда появляется и как его найти?
ACID — что означает каждая буква на практике?
Connection pool — зачем он нужен и какие проблемы возникают при неправильных настройках?
Replication lag — почему реплика отстаёт и чем это опасно?
Partitioning vs Sharding — в чём разница и когда применять каждый подход?
С вас 🔥, если полезно!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥23❤5
Что реально хочется разобрать? Где чаще всего застреваете? Что пробовали, но так и не получилось нормально внедрить?
Задавайте всё, что давно хотелось спросить
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Тестирование функций, которые читают данные с консоли через fmt.Scan и stdin в Go, часто становится настоящей задачей. В таких случаях нельзя просто передать параметры функции — код напрямую взаимодействует со стандартным вводом.
Пример проблемы:
func ReadName() string {
var name string
fmt.Print("Enter your name: ")
fmt.Scan(&name)
return name
}
Для этой функции сложно написать unit-тест, потому что она читает из os.Stdin, а в тестах обычно хочется подставлять свои данные.
Решение: подменяем os.Stdin
В тестах можно временно переназначить os.Stdin на чтение из заранее подготовленного буфера:
func TestReadName(t *testing.T) {
input := "Alice\n"
r, w, _ := os.Pipe()
w.WriteString(input)
w.Close()
oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }()
os.Stdin = r
got := ReadName()
want := "Alice"
if got != want {
t.Errorf("got %q, want %q", got, want)
}
}
Функция, вызываемая в тесте, читает данные не из консоли пользователя, а из потока, в который записывают необходимые строки.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6