Вопросы на собеседовании: Go-разработчик
100 реальных вопросов с образцовыми ответами и пояснениями для уровня Senior Go-разработчик.
Смотреть пример резюме: Go-разработчик →Тренировка флешкарточками
Интервальное повторение · Hunter Pass
Вопросы
Я бы передал весь fan-out одной errgroup и ограничил её лимитом downstream-сервиса в 60 вызовов.
- Я бы создал группу через errgroup.WithContext(requestCtx), вызвал SetLimit(60) и запустил вызов для каждого клиента через g.Go с отдельным значением клиента для каждой итерации.
- Каждый вызов получил бы контекст группы, поэтому первая возвращённая ошибка отменит соседние задачи, а после g.Wait не останется живых горутин.
- Если измерения показывают задержку вызова 120 мс, то для 240 клиентов при конкурентности 60 нужны четыре волны и около 480 мс без измеренного хвостового запаса; считать 120 мс без данных нельзя.
- Это fail-fast семантика; если частичные данные клиентов допустимы, я бы собирал ошибки по клиентам, а не возвращал их из g.Go.
Зачем это спрашивают: Интервьюер проверяет, умеет ли кандидат сочетать владение жизненным циклом через errgroup, ограничение параллелизма, расчёт ёмкости и явную семантику ошибок.
Я бы зарезервировал 100 мс на формирование ответа и создал оба дочерних контекста из оставшегося deadline вызывающей стороны, а не выдавал каждому вызову по 900 мс.
- Примерно после 40 мс валидации я бы получил остаток через time.Until(parentDeadline), ограничил профиль 300 мс, цены 500 мс, а оба вызова всё равно наследовали бы общий downstream-бюджет около 760 мс.
- Для каждого лимита я бы использовал context.WithTimeoutCause, а для прикладного решения, например отмены обоих вызовов после ошибки обязательного профиля, context.WithCancelCause.
- Каждый клиентский вызов должен принимать дочерний контекст, а повтор запускается только когда остатка хватает на backoff и измеренный p95 вызова.
- Для метрик и логов я бы читал context.Cause, а errors.Is(err, context.DeadlineExceeded) и context.Canceled сопоставлял бы с транспортным контрактом.
Зачем это спрашивают: Сильный ответ рассматривает deadline как сокращающийся сквозной бюджет и различает timeout, отмену вызывающей стороны и прикладную отмену.
Я бы начал со 100 workers, потому что по закону Литтла нужно около 96 параллельных вызовов, а лимит зависимости оставляет лишь небольшой запас.
- Я бы запустил 100 фиксированных worker-горутин над ограниченным каналом jobs, передавал контекст задачи в каждый вызов и через defer освобождал ресурс задачи.
- Бюджет очереди 200 мс при 1 200 задачах в секунду допускает около 240 задач; при 16 КиБ на задачу такой буфер удерживает примерно 3,75 МиБ.
- При заполненном канале приём блокировался бы только до ctx.Done либо отклонял задачу, вместо запуска новой горутины и превышения 110 вызовов in-flight.
- Я бы настраивал пул по задержке очереди, загрузке workers, downstream p95 и доле отказов, потому что средние 80 мс могут скрывать хвост, для которого 100 workers мало.
Зачем это спрашивают: Интервьюер проверяет, выводит ли кандидат размер пула и очереди из измеренных throughput, latency, лимита зависимости и стоимости памяти.
Я бы прочитал runtime.GOMAXPROCS(0), начал максимум с восьми workers сжатия и оставил один или два процессора свободными, если тот же процесс обслуживает чувствительные к задержке RPC.
- Восемь полностью занятых workers дают теоретический предел около 160 задач в секунду при 50 мс на задачу, а 80 runnable-горутин добавят задержку планировщика, но не CPU.
- Для смешанного RPC-процесса я бы выбрал шесть workers, ограниченный входной канал и немедленный отказ в приёме, когда ожидание в очереди уже нарушит deadline задачи.
- Я бы снял CPU-профиль через runtime/pprof и trace планировщика через runtime/trace, чтобы отличить насыщение CPU от затрат на аллокации, GC, блокировки и планирование.
- Если постоянный спрос превышает ёмкость шести workers, я бы масштабировал pod или вынес сжатие отдельно, а не поднимал GOMAXPROCS выше квоты 8 CPU.
Зачем это спрашивают: Вопрос проверяет, связывает ли кандидат CPU-параллелизм с GOMAXPROCS и измеренной ёмкостью, а не с количеством горутин.
Я бы ограничил канал примерно 80 элементами, потому что 80, делённые на 400 элементов в секунду, полностью расходуют бюджет очереди 200 мс.
- Дефицит 100 элементов в секунду заполнит этот буфер примерно за 0,8 секунды, поэтому больший канал лишь отложит перегрузку и создаст видимость приёма уже просроченной работы.
- Producers использовали бы select между jobs <- item и ctx.Done, а по истечении admission deadline возвращался бы явный ответ о перегрузке.
- Workers забирали бы элементы по порядку канала, проверяли контекст каждого и пропускали обработку после его истечения; канал не умеет удалить произвольный просроченный элемент из середины.
- Настоящее решение по ёмкости состоит в добавлении consumers или снижении входного потока; буфер сглаживает всплески, но не превращает 400 элементов в секунду в 500.
Зачем это спрашивают: Сильный ответ связывает ёмкость канала с расчётом задержки очереди и определяет, как перегрузка возвращается к producers.
Я бы выполнял не более восьми вызовов к shards одновременно и записывал каждый успешный результат в заранее выделенный slice из 40 элементов по индексу shard.
- Fan-out по восемь вызовов даёт пять волн; если из deadline 1 секунда оставить 250 мс на слияние и ответ, сумма измеренных максимальных задержек вызовов в каждой волне должна быть не больше 750 мс.
- Ошибка shard сохранялась бы рядом с его индексом и не отменяла здоровые соседние вызовы, но deadline родительского контекста всё равно останавливал бы все незавершённые вызовы.
- sync.WaitGroup или errgroup с функциями, возвращающими nil, дождалась бы каждого worker, после чего один coordinator подсчитал бы успехи и выдал результаты в порядке индексов.
- Я бы возвращал частичный результат только при 36 и более успехах вместе с ID неудачных shards; ниже порога весь запрос завершался бы ошибкой.
Зачем это спрашивают: Интервьюер проверяет ограниченный fan-out, безопасный от гонок fan-in, упорядоченный вывод, работу с deadline и точный контракт частичного успеха.
Я бы использовал блокирующий select по входному каналу, ctx.Done и таймеру без default в основном цикле.
- time.NewTicker с периодом 200 мс запускал бы обычный flush, а обработка пакета должна поддерживать отмену или иметь измеренную верхнюю границу меньше 50 мс, чтобы не скрывать ctx.Done дольше целевого времени реакции.
- При ctx.Done я бы остановил ticker и приём работы, а обязательный финальный flush выполнил бы с отдельным коротким shutdown-контекстом, ограниченным остатком 50-миллисекундного бюджета.
- default я бы применил только для намеренной неблокирующей попытки отправки, например для telemetry в канал на 100 элементов с увеличением счётчика потерь при заполнении.
- Поскольку select не даёт отмене приоритет, я бы проверял ctx.Err перед ограниченной обработкой и передавал контекст в каждый блокирующий I/O-вызов.
Зачем это спрашивают: Сильный ответ учитывает блокирующее поведение select, остановку таймера, узкую роль default и отсутствие приоритета у отмены.
Я бы отдал право закрытия канала одному coordinator после завершения всех шести producers, но не отдельному producer и не receiver.
- sync.WaitGroup начиналась бы со значения шесть, каждый producer вызывал бы Done через defer, а coordinator после Wait ровно один раз выполнил бы close(records).
- Каждая отправка producer использовала бы select между records <- record и ctx.Done, чтобы отмена освободила producer, заблокированный медленным consumer.
- При штатном завершении consumer читает через range до close, а при ctx.Done прекращает дорогую обработку и следует согласованному протоколу drain или cancel.
- Буфер может поглотить измеренный всплеск, но не меняет владельца close и не гарантирует отсутствие заблокированных отправок.
Зачем это спрашивают: Интервьюер проверяет, умеет ли кандидат назначить единственного владельца close и сделать каждую блокирующую границу безопасной при отмене и нескольких producers.
Я бы использовал golang.org/x/sync/semaphore.Weighted с 1 536 МиБ разрешений, оставив около 512 МиБ для Go heap, стеков и расходов runtime.
- Каждая задача вызывает Acquire(ctx, weight) с весом 64, 256 или 768 единиц и освобождает Release(weight) через defer, поэтому одновременно работают две задачи по 768 МиБ или шесть по 256 МиБ.
- Приём учитывает deadline запроса, а задача тяжелее лимита 1 536 единиц отклоняется до Acquire, поскольку никогда не сможет получить разрешение.
- Веса я бы выводил из пикового объёма удерживаемой памяти под нагрузкой, а не из размера payload, и настроил alert при приближении RSS к cgroup-лимиту 2 ГиБ.
- Крупная ожидающая задача может задержать мелкие, поэтому для задач по 64 МиБ с более строгим SLA я бы сделал отдельные классы или резерв разрешений, а не считал одну weighted-очередь справедливой.
Зачем это спрашивают: Вопрос проверяет, умеет ли кандидат моделировать разную стоимость ресурсов с учётом запаса памяти, отмены и справедливости очереди.
Я бы сделал uploader владельцем одного dispatcher и четырёх workers, открыл методы Submit и Close и разделил ограниченный по времени вызов Close и окончательное ожидание жизненного цикла.
- NewUploader создаёт внутренний отменяемый контекст, запускает все пять горутин в одной sync.WaitGroup, а workers выбирают через select между jobs и этим контекстом.
- Submit(ctx, item) отправляет запрос dispatcher и ждёт подтверждения; конкурентные и последующие вызовы после начала остановки получают ErrClosed.
- Close(ctx) через sync.Once инициирует остановку и отменяет внутренний контекст, затем выбирает между каналом done, закрытым после Wait, и ctx.Done; deadline может вернуть управление из Close до завершения всех горутин.
- Владелец жизненного цикла должен в итоге дождаться done и только затем освободить общие клиенты или файлы, а операции workers должны поддерживать отмену или иметь ограничение по времени.
Зачем это спрашивают: Сильный ответ назначает явного владельца каждой горутине, каналу, переходу остановки и ресурсу при штатном закрытии и timeout.
Я представлю медленную операцию как резервирование с идентификатором и буду выполнять под mutex только короткие переходы состояния.
- Под блокировкой проверьте доступный лимит, создайте уникальное ожидающее резервирование, включите его в reserved, чтобы сохранить reserved <= limit, запишите версию состояния, затем снимите блокировку перед вызовом на 40 мс.
- После вызова снова захватите mutex и при успехе завершите именно это ожидающее резервирование, а при однозначной ошибке откатите его; при неоднозначном результате оставьте его ожидающим до сверки, и оба перехода должны быть идемпотентными, потому что отмена и повторы могут конкурировать с завершением.
- Отмена context не доказывает, что удалённая операция не выполнилась. Используйте ключ идемпотентности и сверяйте неоднозначный результат, иначе локальный откат может разойтись с удалённым успехом.
- Альтернатива со снимком и версией сначала выполняет вызов, а затем фиксирует результат только после повторной проверки версии и инварианта, но при конкуренции тратит вызовы впустую, а отклонённая фиксация может потребовать компенсации уже выполненного удалённого эффекта. Я выберу её, только если нельзя заранее зарезервировать лимит.
Зачем это спрашивают: Вопрос проверяет двухфазную критическую секцию, которая сохраняет числовой инвариант и учитывает устаревшее состояние и неоднозначные внешние эффекты.
Я выберу по бенчмаркам с конкуренцией, потому что RWMutex полезен только тогда, когда параллельное чтение окупает дополнительный учёт и координацию писателей.
- При 99:1 RWMutex может позволить множеству чтений по 80 нс идти одновременно, но выигрыш останется малым, если стоимость блокировки доминирует над защищённой работой.
- При 70:30 ожидающие и частые писатели уменьшают параллелизм читателей, поэтому обычный Mutex часто даёт больший throughput и более предсказуемое хвостовое ожидание.
- Go блокирует новых читателей за ожидающим писателем, что предотвращает бесконечное голодание писателя, но может создавать скопление читателей вокруг каждой записи.
- Я прогоню оба соотношения с реальным распределением ключей и сравню число операций в секунду, p50 и p99 захвата, а не стану делать вывод только по соотношению.
Зачем это спрашивают: Сильный ответ учитывает семантику RWMutex и требует измерений длительности критической секции, конкуренции и хвостовой задержки.
Я опубликую каждый неизменяемый снимок одним атомарным указателем, но не стану представлять инвариант used не больше limit независимыми атомарными счётчиками.
- Полностью постройте значение размером 2 КБ приватно, затем выполните Store его указателя; читатель, чей Load вернул этот указатель, видит записи, выполненные до публикации.
- Никогда не меняйте опубликованный снимок, потому что атомарная публикация указателя не делает последующие записи полей безопасными при 250 000 конкурентных чтений.
- Раздельные атомарные чтения и обновления used и limit могут показать или создать состояние, где used превышает limit, хотя каждая отдельная операция атомарна.
- Защитите составной переход квоты mutex либо закодируйте всё состояние в одном значении для CAS и повторяйте весь переход после неудачного CompareAndSwap.
Зачем это спрашивают: Вопрос отделяет безопасную публикацию неизменяемого состояния от более сильной атомарности, необходимой составному инварианту.
Передавайте каждый полностью инициализированный указатель через канал, потому что отправка в канал синхронизирована до завершения соответствующего получения.
- Воркер должен записать все поля результата до отправки указателя и не должен менять объект после передачи владения.
- После получения указателя агрегатор может читать эти ранее записанные поля без дополнительного mutex для данного объекта.
- Запуск горутины агрегатора или наблюдение за прошедшим временем не создаёт гарантии видимости, а общий индекс среза без синхронизации всё равно вызывает гонку.
- Для сигнала завершения через close закрытие канала синхронизировано до получения нулевого значения из-за закрытия канала.
Зачем это спрашивают: Интервьюер проверяет точное понимание модели памяти Go для публикации, передачи владения и операций, которые не создают синхронизацию.
Я буду хранить очередь, счётчик и флаг закрытия под одним mutex и создам отдельные условия notEmpty и notFull для двух предикатов.
- Производители ждут в цикле, пока count равен 1 024 и очередь открыта, затем перед добавлением снова проверяют closed под mutex; потребители ждут, пока очередь пуста и открыта, и возвращаются, когда она пуста и закрыта.
- Добавление сигнализирует notEmpty, извлечение сигнализирует notFull, а завершение устанавливает closed под mutex и вызывает Broadcast для обоих условий, чтобы все ожидающие производители и потребители заново проверили свои предикаты.
- Цикл необходим, потому что другая разбуженная горутина может изменить предикат до повторного захвата блокировки текущей горутиной, хотя Go Cond не просыпается без Signal или Broadcast.
- Буферизованный канал уже даёт границу в 1 024 элемента, блокировку отправки и получения, select и семантику close; Cond полезен, когда допуск зависит от более сложных общих предикатов или согласованного оповещения всех.
Зачем это спрашивают: Сильный ответ точно описывает протокол предикатов Cond и объясняет, какую часть собственной реализации устраняет семантика каналов.
Я оберну успешную неизменяемую инициализацию в OnceValue, чтобы все 200 вызывающих разделяли один завершённый вызов и получили один результат.
- Конкурентные вызовы блокируются до возврата единственной функции, а последующие возвращают сохранённое значение без повторной инициализации.
- Если функция вызывает panic, каждый вызов возвращённой функции вызывает panic с тем же значением; OnceValue не повторяет попытку после panic.
- Если инициализация возвращает ошибку, OnceValues может сохранить пару из значения и ошибки, поэтому временная ошибка первого вызова также сохраняется без повторной попытки.
- Если нужны повторы, используйте явный автомат состояний под mutex с backoff, а не прячьте политику повторов внутри функции Once.
Зачем это спрашивают: Интервьюер оценивает точную семантику однократного выполнения, panic и сохранённой ошибки, а не общее знакомство с singleton.
Я начну с типизированной map под блокировкой и перейду к специализированному варианту только тогда, когда это оправдают профили конкуренции и бенчмарки нагрузки.
- Map с Mutex или RWMutex даёт понятные составные операции, типизированные значения и снимки и часто достаточна, когда 30 000 записей распределены по ключам.
- sync.Map оптимизирована для записанных один раз и многократно читаемых элементов либо для горутин с непересекающимися наборами ключей; Range не является согласованным снимком, а многоключевые инварианты всё равно требуют координации.
- Шардирование по стабильному хешу может распределить 96 горутин, например, по 64 блокировкам, но изменение размера, глобальное вытеснение и точный подсчёт элементов усложняются.
- Я воспроизведу измеренный перекос ключей и сравню throughput, p99 ожидания, аллокации и mutex-профили, потому что горячий ключ может нивелировать пользу и RWMutex, и шардирования.
Зачем это спрашивают: Вопрос проверяет, сопоставляет ли кандидат семантику конкурентных map с профилем доступа и подтверждает ли дополнительную сложность измеренной конкуренцией.
Я заподозрю передачу владения строкой кэша, потому что независимые счётчики всё равно конфликтуют, если записи попадают в одну аппаратную строку кэша.
- Сравните соседнее и дополненное размещение в параллельном бенчмарке, CPU-профилях и аппаратных счётчиках, если они доступны; отдельные аллокации сами по себе не гарантируют разные строки кэша.
- Дайте каждому воркеру приватный локальный счётчик и сложите 96 результатов после цикла, убрав атомарные операции и общие записи строк кэша из горячего пути на 20 000 000 итераций.
- Если нужны чтения в реальном времени, используйте дополненные шардированные счётчики и проверяйте фактические адреса, смещения полей и шаг в измеряемом бинарном файле, а не предполагайте, что аллокатор или размещение в исходном коде обеспечивают изоляцию.
- Шаг в 64 или 128 байт зависит от архитектуры и увеличивает расход памяти, поэтому измеряйте производительность и проверяйте размещение для каждой целевой платформы.
Зачем это спрашивают: Senior-ответ определяет трафик когерентности, предпочитает локальную агрегацию и требует измеренной или проверенной по адресам изоляции вместо предположений об аллокациях или padding.
Я создам стресс-тест с seed, который позволяет повторить сгенерированную нагрузку, перекрывает Get, Set, Delete и вытеснение, а затем многократно запущу его с детектором гонок.
- Запустите все 40 горутин через один барьер, создавайте поток операций каждой горутины из записанного seed для фиксированных наборов горячих и холодных ключей, выполните суммарно 100 000 операций и проверьте инварианты кэша после завершения каждой группы.
- Запустите go test -race -count=100 ./path/to/cache и меняйте GOMAXPROCS и записанные seed в отдельных заданиях, чтобы исследовать больше расписаний; seed делает нагрузку воспроизводимой, но не делает планирование горутин детерминированным.
- Если накладные расходы детектора делают 100 повторов непрактичными, уменьшите нагрузку под -race, но сохраните отдельный большой тест без детектора для поиска логических повреждений и зависаний.
- Чистый прогон означает только то, что детектор не сообщил о гонке в наблюдавшихся выполнениях; он не доказывает линеаризуемость, отсутствие deadlock, свободу от гонок при других расписаниях или безопасность невыполненного и неинструментированного кода.
Зачем это спрашивают: Интервьюер ожидает воспроизводимую конкурентную нагрузку и точное понимание того, что могут подтвердить только наблюдавшиеся чистые прогоны детектора гонок.
Я создам один Timer на соединение, буду сбрасывать его для дедлайна в 100 мс и остановлю при выходе из цикла соединения.
- time.After создаёт новый таймер на каждой итерации, поэтому 50 000 сообщений в секунду добавляют лишние аллокации и работу кучи таймеров, хотя современный Go умеет собирать недоступные таймеры.
- В Go 1.23 и новее Reset и Stop дают контракт небуферизованного канала таймера без устаревших значений после возврата; код с поддержкой старых версий Go должен применять документированный шаблон остановки, опустошения канала и сброса.
- Обрабатывайте таймаут в том же select, что сообщения и отмену, и назначьте одной горутине владение операциями сброса таймера и чтения из него.
- Для периодической работы используйте NewTicker и defer Stop; Stop не закрывает ticker.C, поэтому получатель должен также выбирать отмену context, а не ждать закрытия канала через range.
Зачем это спрашивают: Сильный ответ сочетает контроль аллокаций горячего пути с учётом версии семантики Timer и явным владением завершением ticker.
Закрытые вопросы
- 21
Go-сервис ограничен процессором в репрезентативном 60-секундном тесте на 8 ядрах, а его CPU-профиль содержит около 480 CPU-секунд сэмплов. Как выбрать в pprof цель для оптимизации?
optimizationprofiling - 22
Go-процесс показывает 1,4 ГиБ в heap-профиле inuse_space, но 96 ГиБ в alloc_space с момента запуска. Что означают эти представления и какое выбрать первым для сокращения памяти?
concurrencydata-structuresmemory - 23
Как настроить goroutine-, block- и mutex-профили для 10-минутного нагрузочного теста с 40 000 горутин, не оставляя сэмплирование с максимальными накладными расходами?
concurrencyconfigload-testing - 24
Горячая функция parseRecord вызывается 2 миллиона раз в секунду и создает 18 МБ/с аллокаций. Как с помощью escape analysis решить, оправдано ли изменение API?
memoryapi - 25
В пакете обычно 10 000 записей; benchmark показывает 4 230 000 B/op и 19 allocs/op при построении среза и map. Как оценить предварительное выделение памяти?
decision-makingbatchslices-maps - 26
Кеш хранит подсрез размером 1 КиБ из каждого разобранного payload размером 64 МиБ, и 20 записей удерживают около 1,25 ГиБ. Что нужно изменить?
caching - 27
sync.Pool обычно обслуживает буферы по 32 КиБ, но редкие ответы по 16 МиБ заставляют процесс удерживать сотни мегабайт между GC. Как изменить правила пула?
concurrencymemory - 28
Как настроить GOGC и GOMEMLIMIT для Go-сервиса в контейнере на 2 ГиБ, если аллокации cgo, внешние отображения и стеки потоков вне учета Go runtime могут занимать до 400 МиБ?
gccontainersconcurrency - 29
При стабильной нагрузке gctrace показывает около 12 сборок в секунду с переходами heap примерно 600->1100->700 МБ, а метрики runtime относят к GC 2,5 из 8 ядер CPU. Как настраивать и проверять сервис?
validationmonitoringdata-structures - 30
Реализация B оказалась на 7% быстрее A в одном запуске Go benchmark. Какой дизайн benchmark и набор сэмплов нужны перед внедрением B в путь запросов с нагрузкой 50 000 RPS?
decision-makingdesign - 31
Go-парсер преобразует 2 миллиона записей []byte по 256 байт в string каждую минуту, то есть около 512 МБ/мин или 8,5 МБ/с. Стали бы вы использовать unsafe и какой контракт потребовали бы?
immutability - 32
Сервис хочет приводить каждый 24-байтный сетевой заголовок к структуре через unsafe.Pointer, экономя 40 нс при 3 миллионах пакетов в секунду. Как проверить дизайн и чего не доказывает checkptr?
validationstructs - 33
Шлюз делает 5 миллионов cgo-вызовов в секунду для одной 16-байтной записи, а C может сохранять пакеты после вызова. Как переработать границу по правилам cgo?
gatewaybatch - 34
Go-сервис обработки изображений использует C.malloc для 600 параллельных рабочих областей по 12 МБ, поэтому около 7,2 ГБ находятся вне Go-кучи и не видны в обычных профилях кучи. Как вы организуете владение, измерение и освобождение этой памяти?
concurrencydata-structuresmemory - 35
Сервис должен просканировать файл 20 ГБ при лимите контейнера 6 ГБ и рассматривает unix.Mmap вместо 64 параллельных pread. Как выбрать и безопасно организовать mmap на Unix?
containers - 36
Клиент должен отправить 10 000 protobuf-сообщений по 1 КБ за 2 секунды и может нуждаться в промежуточных ACK и возобновлении. Выбрать unary, batch, клиентский или двунаправленный поток?
batchmicroservices - 37
Внешний запрос имеет дедлайн 800 мс и последовательно проходит через четыре сервиса grpc-go до возврата ответа. Как вы распределите и передадите бюджет, чтобы один медленный переход не израсходовал все 800 мс?
estimationgrpcmicroservices - 38
API на grpc-go получает 20 000 unary-вызовов в секунду, 3% не аутентифицированы, а лимит тенанта равен 500 вызовам в секунду. В каком точном порядке вы свяжете интерсепторы восстановления, трассировки, аутентификации и ограничения частоты?
grpcautherror-handling - 39
Pod Kubernetes обслуживает HTTP и grpc-go и читает брокер; terminationGracePeriodSeconds равен 30, в работе 2000 сообщений. Какова точная последовательность graceful shutdown?
httpgrpckubernetes - 40
Сервис grpc-go работает в 4 репликах, требует прогрева, обслуживает 90-секундные потоки и должен выводиться из балансировщика. Как спроектировать health и readiness Kubernetes через gRPC health service?
designload-balancingmicroservices - 41
Запрос проходит через 4 слоя Go-кода, прежде чем PostgreSQL возвращает ErrNoRows, а HTTP-граница должна по-прежнему распознать доменную sentinel-ошибку через errors.Is. Как оборачивать ошибку, не теряя полезный контекст?
concurrencyerror-handlinghttp - 42
Сервис регистрации проверяет 12 полей и должен отдавать ошибки конкретных полей клиентам HTTP и gRPC, не импортируя ни один транспорт в доменный пакет. Как использовать типизированные ошибки и errors.As?
httpgrpcvalidation - 43
Четыре независимых валидатора параллельно обрабатывают импорт из 50 000 строк; два могут завершиться ошибкой, но валидные строки всё равно нужно вернуть. Как применить errors.Join, не сделав поведение частичного результата двусмысленным?
joinsvalidation - 44
У запроса дедлайн 250 мс, он вызывает 3 нижележащих сервиса и возвращается через 2 внутренних слоя. Как сохранить статус отмены или дедлайна, при этом добавив к ошибке контекст Go?
estimationresilienceconcurrency - 45
gRPC API обслуживает 20 клиентских команд и имеет 5 категорий доменных ошибок, часть из которых допускает повтор, а часть является окончательной. Как сохранить стабильность маппинга статусов и семантики повторов между релизами?
resiliencemicroservicesapi - 46
Реализуйте generic-функцию дедупликации с сохранением порядка для 10 миллионов ID, где вызывающие стороны передают строковые ID или определённые целочисленные типы ID. Какое ограничение и какие компромиссы памяти вы выберете?
queriesgenericsmemory - 47
Числовой helper добавляет смещения к 2 миллионам счётчиков, включая определённый тип shardCount с базовым типом int. Как ограничение ~int сохраняет этот тип и какие ограничения вы документируете?
sharding - 48
Вам нужен переиспользуемый worker-helper с map и filter для 5 миллионов записей в минуту. Бенчмарк показывает 38 нс/операцию и 0 аллокаций для generics, 41 нс/операцию и 0 аллокаций для интерфейса, 190 нс/операцию и 2 аллокации для рефлексии. Какой дизайн вы выпустите?
genericstypesinterfaces - 49
Сервис сериализует 50 известных типов сообщений со скоростью 200 000 сообщений в секунду; generic-реализация всё равно использует рефлексию для обхода полей структур и потребляет 22% CPU. Когда кодогенерация будет лучшим выбором?
genericsstructsserialization - 50
Публичный Go-модуль имеет 30 зависимых модулей, использующих InferMap без явных аргументов типов. В версии модуля v2 вы хотите поменять местами 2 параметра типа и сузить одно ограничение. Как оценить совместимость и провести миграцию?
hypothesis-testingmigrationsmodules - 51
После деплоя Go-сервис сохраняет нагрузку около 8 000 RPS, но за 90 минут растёт с 3 200 до 47 000 горутин; три goroutine dump из pprof, снятые с интервалом 10 минут, содержат 6 400, 9 100 и 11 800 копий одного стека в состоянии [chan send]. Как бы вы расследовали инцидент?
deploymentprofilingconcurrency - 52
Запрос запускает 20 горутин и возвращается после первых 12 результатов; при 120 RPS процесс за час растёт с 6 000 до 64 000 горутин, а 57 000 стеков останавливаются на resultCh <- result в состоянии [chan send]. Что бы вы изменили?
concurrency - 53
HTTP-прокси обрабатывает 600 RPS и за 45 минут растёт с 2 400 до 31 000 горутин; upstream возвращает заголовки за 80 мс, но зависает на теле одного ответа из 50, а 14 000 стеков заблокированы в io.ReadAll(resp.Body) без timeout клиента. Как локализовать и исправить утечку?
proxyresilienceconcurrency - 54
В сервисе есть 2 000 циклов обновления арендаторов, каждый с ticker на 30 секунд; арендаторы переподключаются в среднем каждые 5 минут, и за час число горутин растёт с 2 100 до 26 000, а старые циклы ждут в [chan receive] на range ticker.C. Где утечка и как безопасно построить цикл?
concurrencydesign - 55
Pod Kubernetes получает SIGTERM при 18 000 горутин, через 25 секунд всё ещё имеет 6 700 и завершается принудительно на лимите 30 секунд; последний dump показывает 5 900 broker workers в [select] и 700 горутин под sync.WaitGroup.Wait. Как бы вы исправили утечку при shutdown?
concurrencykubernetes - 56
Вы исправили компонент, чей Start создаёт 8 workers и 2 ticker, но прежний Stop терял workers, когда consumer прекращал чтение после 3 результатов; какой regression test вы потребуете для 100 циклов Start и Stop?
componentsregression - 57
Контрольная группа за 12 часов растёт с 9 000 до 84 000 горутин, а исправленный canary при том же RPS на pod остаётся между 9 100 и 10 400; dump на часах 0, 2, 6 и 12 показывают падение подозрительного стека [chan send] с 1 800 до 35 и дальнейший диапазон 30-40, но 5 000 горутин остаются в [IO wait]. Каких доказательств достаточно для rollout?
concurrencydeployment-strategies - 58
После деплоя при 18 000 RPS число горутин за 4 минуты растёт с 900 до 31 000: dispatcher отправляет следующую задачу в persistCh, а persister отправляет результат предыдущей задачи в небуферизованный ackCh. Как диагностировать и устранить этот цикл каналов?
concurrencydeployment - 59
После релиза p99 сервиса переводов при 9 000 RPS растёт с 85 мс до таймаута 30 секунд: путь запроса захватывает accountMu, затем ledgerMu, а сверка захватывает ledgerMu, затем accountMu. Как провести инцидент и предотвратить повторение?
incidentslatencyreact - 60
У сервиса конфигурации 64 горутины чтения и обновление каждые 500 мс; после изменения refresher сохраняет RLock при вызове Lock для обновления снимка, и p99 чтения растёт с 12 мс до 20 секунд. Что происходит и как это перепроектировать?
concurrencysnapshotconfig - 61
Coordinator запускает 10 000 задач импорта, но 0,8% пакетов возвращаются без 20-80 результатов, а часть зависает до watchdog на 60 секунд; workers вызывают WaitGroup.Add внутри новой горутины, и один путь ошибки пропускает Done. Как это исправить?
concurrencybatch - 62
Go API работает при 1 200 RPS с database/sql MaxOpenConns=80; когда downstream-вызов внутри каждой транзакции замедляется с 200 мс до 4 секунд, 900 горутин выглядят зависшими, а p99 достигает 15 секунд. Как доказать истощение пула, а не deadlock, и восстановить сервис?
sqldatabasetransactions - 63
Один деплой снижает throughput с 22 000 до 8 000 RPS и повышает p99 со 140 мс до 2,8 секунды; стеки горутин показывают и отправки в каналы, и ожидание Mutex.Lock. Как через block- и mutex-профили Go найти главное liveness-узкое место?
concurrencydeploymenthealth-checks - 64
Во время rollout 4 из 12 Go-pod перестают выполнять работу, 28% запросов достигают таймаута 5 секунд, а backlog брокера растёт на 30 000 сообщений в минуту. Как безопасно локализовать инцидент до исправления причины deadlock?
backloglockingresilience - 65
После выкладки при 30 000 RPS live heap за 20 минут растёт с 750 МиБ до 1,55 ГиБ, RSS с 1,1 до 2,3 ГиБ, diff inuse_space относит 720 МиБ к вставке в кеш, а alloc_space показывает 140 ГиБ в JSON-декодировании; что вы проверите первым?
cachingdata-structures - 66
После шторма отмен остаются 14 000 горутин, заблокированных на одной отправке в канал, стек каждой удерживает декодированный пакет примерно на 192 КиБ, а live heap растёт с 820 МиБ до 3,4 ГиБ уже после восстановления трафика; как устранить инцидент?
concurrencydata-structureserror-handling - 67
Собственная кольцевая очередь повторов сокращается со 100 000 до 2 000 элементов, но live heap остаётся на 2,6 ГиБ вместо возврата к 620 МиБ; heap-профиль показывает payload по 20 КиБ, удерживаемые слотами до head, потому что dequeue сдвигает индекс без очистки слота. Как это исправить?
indexesresiliencedata-structures - 68
Промокампания заполняет in-process map-кеш 8 миллионами ключей; после истечения записей остаётся только 400 000, но live heap держится на 1,4 ГиБ выше baseline, а heap-профиль указывает на buckets map. Что вы сделаете?
concurrencydata-structurescaching - 69
Go-сервис в pod на 4 ГиБ имеет live heap 1,2 ГиБ, около 700 МиБ измеренного RSS вне Go, GOGC=200, не имеет GOMEMLIMIT и достигает 4 ГиБ во время 60-секундного всплеска; какие начальные настройки вы проверите на canary?
deployment-strategiesdata-structuresgc - 70
После выкладки скорость аллокаций растёт с 1,1 до 2,8 ГиБ/с, CPU сборщика с 9% до 31%, p99 пауз GC с 0,7 до 4,5 мс, p99 запросов с 85 до 170 мс, а live heap остаётся около 900 МиБ; как вы диагностируете и ослабите проблему?
deploymentdata-structures - 71
У import-pod cgroup-лимит 6 ГиБ и RSS без работы 2,2 ГиБ; каждая задача на пике использует 32 МиБ native-памяти и 24 МиБ Go-памяти, одновременно работают 120 задач, а memory.current за 90 секунд достигает 5,7 ГиБ; как сдержать OOM без повреждения импортов?
memoryconcurrency - 72
При 18 000 RPS p99 Go-сервиса checkout вырос с 85 до 310 мс после включения новой валидации запросов, CPU поднялся с 4,2 до 7,8 из 8 ядер, а CPU-профиль pprof относит 34% сэмплов к regexp.Compile; что вы сделаете?
validationregexprofiling - 73
При 11 000 RPS p99 сервиса авторизации вырос с 65 до 510 мс, CPU остался на 52%, а число горутин увеличилось с 4 000 до 38 000; goroutine-профиль показывает 29 000 ожидающих один Mutex, а mutex- и block-профили указывают на блокировку кеша, удерживаемую во время 35-миллисекундного вызова Redis. Как вы устраните инцидент?
concurrencyredisauth - 74
На 10 pod при 14 000 RPS p99 вырос с 95 до 370 мс, а скорость аллокаций удвоилась с 1,2 до 2,4 ГБ/с, поэтому команда подозревает GC; паузы GC остаются ниже 0,8 мс при 9% CPU на GC, а runtime trace показывает 230 мс ожидания очереди workers после роста средней downstream-задержки с 55 до 145 мс при лимите 120 вызовов in-flight на pod. Какое решение вы примете?
latencydata-structures - 75
При 2 400 RPS релиз расширил поисковый запрос с 6 до 18 параллельных вызовов зависимости, и p99 вырос с 210 до 980 мс; у каждого вызова p50 равен 32 мс, p99 равен 620 мс, а вероятность превысить 500 мс составляет 1%. Как вы остановите инцидент с fan-out и переработаете путь?
incidentsfan-outdependencies - 76
При 12 000 RPS на 8 pod Go API p99 вырос с 75 до 640 мс, хотя p99 запросов PostgreSQL равен 16 мс; у каждого sql.DB MaxOpenConns равен 40, InUse постоянно равен 40, ожидание пула добавляет 510 мс, а новый audit-путь в 5% транзакций удерживает соединение 180 мс во время удалённого вызова. База допускает 600 соединений приложения. Что вы измените?
sqldatabasetransactions - 77
При 20 000 RPS на 20 pod с восемью ядрами p99 обычных вызовов API вырос с 80 до 290 мс после запуска синхронного экспорта PDF; экспорт составляет лишь 4% запросов, но CPU-профиль pprof с метками endpoints относит к нему 55% CPU, а каждый экспорт требует 120 мс CPU. Как вы изолируете этот noisy endpoint?
endpointsprofiling - 78
Во время canary на 10% при общих 16 000 RPS старая версия Go держит p99 на 92 мс и ошибки на 0,2%, а canary получает 1 600 RPS с p99 480 мс, ошибками 1,7% и CPU на 30% выше в течение 6 минут; его CPU-профиль относит 41% к синхронному кодированию успешных логов. Вы откатите релиз и что сделаете дальше?
deployment-strategiesrollback - 79
При 70 000 запросов в секунду три из 24 Go-pod завершаются с fatal error: concurrent map writes во время обновления таблицы маршрутизации на 2 миллиона записей; как собрать доказательства, воспроизвести гонку и исправить её?
concurrency - 80
У одного ответа из 50 000 повреждён JSON-суффикс после того, как goroutine записи ставит в очередь буфер на 32 КиБ и возвращает его в sync.Pool до завершения записи в сокет; как доказать и исправить гонку повторного использования?
concurrencydata-structuresmemory - 81
Восемь goroutine разбирают по 25 000 строк в один общий slice, но production-пакеты иногда содержат 197 000 результатов вместо 200 000; как поймать и исправить гонку slice?
concurrencyslices-mapsbatch - 82
Примерно один ответ о цене из 2 миллионов содержит сумму из запроса A и валюту из запроса B; путь timeout возвращает ответ, пока fallback-горутина продолжает записывать оба поля общей структуры результата. Как поймать и исправить гонку?
concurrencystructsresilience - 83
Гонка проявляется только около 80 000 запросов в секунду, а бинарный файл с race detector по вашим измерениям использует в 3,8 раза больше CPU и в 7,2 раза больше памяти; как применить staging и canary с детектором, не нарушив production?
deployment-strategiesmemory - 84
Один из 12 pod повреждает изменяемую запись cache на 4 КиБ примерно раз в 6 часов, а обычные нагрузочные тесты не воспроизводят сбой; как превратить логи, дампы и подозреваемое чередование reader-writer в детерминированный regression-тест и проверенное исправление?
load-testingregressioncaching - 85
Через 12 минут после деплоя 18 из 1 200 запросов checkout вызывают panic на cfg.Retry.Policy.MaxAttempts; как вы проведёте инцидент с nil pointer и предотвратите повтор?
incidentsdeploymentresilience - 86
HTTP API обслуживает 40 000 запросов в секунду и сообщает о 25 panic в минуту, причём 10 происходят после записи заголовков ответа; как вы спроектируете recovery middleware?
designmiddlewarehttp - 87
Сервер grpc-go обрабатывает 12 000 unary-вызовов в секунду и 4 000 активных streams; одна версия вызвала 9 panic в unary handlers и 3 в stream handlers, какие recovery-границы вы установите?
error-handlinggrpcmicroservices - 88
Consumer запускает 64 worker-горутины для 300 000 задач в день, а один некорректный payload уже 7 раз вызвал panic и перезапуск всего pod; где вы разместите recovery и что произойдёт с задачей?
concurrencyerror-handling - 89
Go-сервис ledger с 3 репликами вызывает panic после обновления первого из 2 индексов в памяти во время пакета на 800 счетов, а checksum позже находит 27 расхождений; должна ли recovery оставить реплику в работе или завершить процесс?
indexesreplicationerror-handling - 90
После отключения клиентов Go API всё ещё запускает 2 000 горутин обогащения в минуту, которые работают до 30 секунд, потому что обработчик вызывает context.Background; как исправить инцидент?
concurrencyapiincidents - 91
Poller создаёт context.WithTimeout(parent, 3*time.Minute) для 25 000 вызовов в минуту, но никогда не вызывает cancel; вызовы обычно завершаются за 20 мс, а heap-профиль относит к этой строке около 80 000 живых объектов timer и context. Что вы измените?
data-structuresconcurrency - 92
Ingress завершает checkout-запросы по timeout через 1,2 секунды, Go-сервер допускает 5 секунд, PostgreSQL допускает 4 секунды, а клиенты повторяют запрос на 1,2 секунде; трассировки показывают, что 31% просроченной работы продолжается более 3 секунд. Как согласовать timeout?
resiliencepostgreskubernetes - 93
Зависимость возвращает timeout на 480-й мс запроса с deadline 500 мс, но Go-клиент выполняет ещё две попытки с backoff 100 мс и утраивает нагрузку при сбое; как остановить заведомо бесполезные повторы?
resiliencedependenciesestimation - 94
HTTP-обработчик возвращает 202 за 80 мс и запускает создание счёта в горутине, но 4% счетов пропадают при отмене контекста запроса; как сделать работу надёжной?
concurrencyhttp - 95
HTTP-запросы завершаются по timeout через 800 мс, но PostgreSQL-запросы, запущенные через db.Query, работают 45 секунд и исчерпывают пул из 100 соединений; как локализовать инцидент?
queriespostgrespooling - 96
Цикл повторных попыток junior-инженера привёл к утечке 18 000 горутин за 15 минут, потому что отправки в канал игнорировали отмену запроса; как бы вы обучили инженера и выпустили исправление за 24 часа?
concurrencyresiliencementoring - 97
PR по конкурентности должен к завтрашнему дню обрабатывать 50 000 задач в минуту, но запускает горутину на каждую задачу, закрывает канал результатов со стороны получателя и не содержит тестов отмены; как бы вы провели ревью и обучили автора?
concurrencytestingresilience - 98
Go-инженер пропустил проблемы с отменой context и владением каналами в 4 из последних 6 ревью; какой шестинедельный план обучения вы составите и как измерите результат?
concurrencyownershipresilience - 99
В 14:05 деплой Go-сервиса вызвал panic из-за отправки в закрытый канал на 12 pods и привёл к 38% ошибок на 18 минут; автор изменения junior, а к инциденту подключились семь инженеров, так как бы вы провели его без обвинений?
concurrencyerror-handlingjoins - 100
Два Go-инженера спорят, использовать ли Mutex или канал с горутиной-владельцем для таблицы состояния на 200 000 операций в секунду; как бы вы направили спор и разрешили его бенчмарком за 48 часов?
concurrencyconflictmentoring