Backend VK Hub
前往频道在 Telegram
Комьюнити VK для бэкендеров. Cамые хардовые кейсы, дискуссии в кругу своих и прямой доступ к нашим экспертам 😎
显示更多1 119
订阅者
-124 小时
-17 天
-630 天
正在加载数据
吸引订阅者
十月 '2610月 '26
十月 '26
+4
在0个频道中
九月 '26
+9
在1个频道中
Get PRO
八月 '26
+12
在1个频道中
Get PRO
七月 '26
+12
在0个频道中
Get PRO
六月 '26
+7
在1个频道中
Get PRO
五月 '26
+17
在1个频道中
Get PRO
四月 '26
+21
在1个频道中
Get PRO
三月 '26
+8
在0个频道中
Get PRO
二月 '26
+25
在1个频道中
Get PRO
一月 '26
+40
在1个频道中
Get PRO
十二月 '25
+427
在21个频道中
Get PRO
十一月 '25
+154
在0个频道中
Get PRO
十月 '25
+770
在20个频道中
| 日期 | 订阅者增长 | 提及 | 频道 | |
| 17 十月 | +1 | |||
| 16 十月 | 0 | |||
| 15 十月 | +1 | |||
| 14 十月 | 0 | |||
| 13 十月 | 0 | |||
| 12 十月 | 0 | |||
| 11 十月 | +1 | |||
| 10 十月 | 0 | |||
| 09 十月 | 0 | |||
| 08 十月 | 0 | |||
| 07 十月 | 0 | |||
| 06 十月 | 0 | |||
| 05 十月 | 0 | |||
| 04 十月 | 0 | |||
| 03 十月 | 0 | |||
| 02 十月 | 0 | |||
| 01 十月 | +1 |
频道帖子
| 2 | Python 3.15 принёс ленивые импорты, UTF-8 по умолчанию и профилировщик Tachyon
Свежий релиз Python 3.15.0 вышел 9 октября. Ленивые импорты поменяли запуск приложений, текстовые файлы на Windows теперь читаются в кодировке UTF-8, а новый встроенный семплирующий профилировщик Tachyon подключается к живому процессу.
Разобрали главное, а подробности доступны в документации.
Ленивые импорты
Ключевое слово lazy откладывает загрузку модуля до первого обращения к имени (PEP 810).
lazy import json
print("Старт") # json ещё не загружен
data = json.loads("{}") # json загружается здесь
Вместо модуля интерпретатор держит прокси, а ошибка загрузки возникает в точке первого использования. В traceback при этом видны и место обращения, и исходная строка lazy import.
Разрешён такой импорт только на уровне модуля, а режим для всего процесса задают -X lazy_imports и PYTHON_LAZY_IMPORTS. Библиотекам с поддержкой нескольких версий подойдёт переменная lazy_modules, которую старые Python игнорируют.
Авторы PEP обещают сократить запуск CLI на 50–70%, но можно нарваться и на побочные эффекты, поскольку регистрация плагинов при импорте уедет на момент первого использования.
UTF-8 по умолчанию
Теперь open("file.txt") без encoding читает UTF-8 независимо от локали (PEP 686). В локали C и POSIX так было и раньше, поэтому основной удар приходится на Windows. CSV в cp1251, которые раньше читались без указания кодировки, теперь прочитаются как UTF-8.
Авторы PEP предупреждают, что возможна даже тихая порча данных. Найти такие места поможет команда для CI.
python -X warn_default_encoding -W error::EncodingWarning -m pytest
Старое поведение возвращается через PYTHONUTF8=0.
Профилировщик Tachyon
Пакет profiling.sampling читает стеки процесса снаружи, без хуков. Частота доходит до 1 МГц, а нагрузку на процесс авторы называют практически нулевой.
python -m profiling.sampling attach --flamegraph -d 30 -o profile.html <PID>
Команда подключается к работающему процессу и за 30 секунд собирает флеймграф. Режимы wall и cpu помогают отличить вычисления от ожидания, а gil показывает код, который монополизирует интерпретатор.
Среди форматов вывода есть pstats, дифференциальный флеймграф и JSON для Firefox Profiler. Время в отчётах оценивается по семплам, а профилировщик и цель должны работать на одной минорной версии Python.
JIT и free-threading
JIT остаётся экспериментальным и включается переменной PYTHON_JIT=1. Прирост по geomean pyperformance составляет 7–8% на x86-64 Linux и 11–12% на AArch64 macOS. На отдельных бенчмарках разброс велик, а для I/O-bound кода выигрыш незначителен.
На Windows официальные 64-разрядные сборки теперь используют интерпретатор на хвостовых вызовах. Он даёт 15–20% относительно switch-case интерпретатора.
В установщике для macOS пакет с free-threaded сборкой теперь отмечен по умолчанию и ставится рядом с обычной, но команда python3 по-прежнему запускает сборку с GIL.
Free-threaded сборка медленнее на однопоточном коде на 1–8%, но новый abi3t позволяет одному и тому же бинарнику работать и на обычных сборках и на сборках без GIL.
Новый синтаксис
Появились встроенные frozendict и sentinel, а распаковка аргументов заработала в comprehensions. Файлы .start заменяют исполняемые строки import в .pth.
flat = [*li for li in lists] # вместо вложенного цикла
MISSING = sentinel("MISSING") # отличает «не передан» от None
def get(key, default=MISSING):
...
Заключение
Тесты на 3.15 можно гонять в CI уже сейчас, а для продакшена лучше дождаться колёс зависимостей и версии 3.15.1. Начинать миграцию разумнее с EncodingWarning, а lazy и JIT включать только после собственных замеров.
#backendvkhub #python | 216 |
| 3 | ON CONFLICT DO SELECT в PostgreSQL 19: get-or-create одним запросом
Get-or-create в PostgreSQL до сих пор пишется в обход. Задача знакомая: нужен id тега, пользователя или ключа идемпотентности, и если строка уже есть, следует вернуть её, а если нет — создать. Очевидный вариант с DO NOTHING не подходит, потому что RETURNING отдаёт только вставленные строки, и на существующую запрос ответит пустым результатом.
INSERT INTO tags (name) VALUES ('postgres')
ON CONFLICT (name) DO NOTHING
RETURNING id; -- тег уже есть: 0 строк
Дальше обычно выбирают из двух неудобных вариантов. Второй SELECT означает лишний поход в базу и окно, за которое строку могут удалить. Холостой DO UPDATE SET name = EXCLUDED.name заставляет RETURNING работать, но PostgreSQL выполняет физическое обновление, даже если данные не изменились. Каждый вызов оставляет новую версию строки, мёртвый кортеж для VACUUM и запись в WAL, а если HOT не сработал, ещё и правки во всех индексах. Вдобавок срабатывают триггеры на UPDATE, и аудит фиксирует изменения, которых не было.
В PostgreSQL 19 появилось третье действие при конфликте — DO SELECT. Существующая строка возвращается как есть, без записи:
INSERT INTO tags (name) VALUES ('postgres')
ON CONFLICT (name) DO SELECT
RETURNING id, old.id IS NULL AS created;
Вторая колонка опирается на ссылку old в RETURNING, её добавили в версии 18. У вставленной строки старые значения равны NULL, у найденной заполнены, так что приложение сразу понимает, создало оно запись или получило существующую.
От DO NOTHING конструкция отличается двумя требованиями: RETURNING обязателен, а цель конфликта нужно назвать явно. Арбитром может быть только уникальный индекс или ограничение NOT DEFERRABLE, а exclusion constraints не подходят.
Если найденную строку дальше меняют в той же транзакции, её можно заблокировать в этом же запросе:
INSERT INTO balances (account_id, amount) VALUES (42, 0)
ON CONFLICT (account_id) DO SELECT FOR UPDATE
RETURNING *;
Поддерживаются все четыре режима блокировки, от FOR UPDATE до FOR KEY SHARE, и условие WHERE, по которому отбираются возвращаемые строки. Блокировку получат все конфликтующие строки, в том числе те, что под условие не попали.
PostgreSQL 19 сейчас в четвёртой бете, вышедшей 24 сентября, а релиз-кандидат обещают в начале октября. В этой бете из релиза убрали SQL/PGQ, FOR PORTION OF и операции над партициями, но DO SELECT в документации версии 19 остался. Холостые DO UPDATE в коде стоит пометить уже сейчас: после обновления каждый из них заменяется одной правкой, и таблицы с частым get-or-create перестанут копить мёртвые кортежи.
#backendvkhub #postgresql | 329 |
| 4 | 🔊 Джависты, собираем рюкзаки!
Не в школу, конечно, а на Joker 2026 с 14 по 15 октября.
➡️ Егор Леванков, старший разработчик One-cloud, поделится на конференции опытом непрерывного профилирования прода на десятках тысяч хостов. Расскажет, чем отличаются подходы для Java и натива, как устроены форматы профилей и что стоит за готовыми решениями.
В этот раз мы подготовили самый большой стенд: заглядывайте в Java-класс, чтобы проверить инженерную интуицию в «Умниках и ставках», собрать рюкзак Java-разработчика, решать задачи на инциденты с экспертами VK и поиграть в настолки и ретроигры.
📌 14–15 октября
📍 Санкт-Петербург, площадь Победы, 1, Cosmos Saint-Petersburg Pulkovskaya Hotel
💻 Можно участвовать онлайн
➡️ Купить билет
Увидимся на Joker — будем знакомиться, обсуждать технологии и обмениваться опытом.
#backendvkhub #конференция #java | 349 |
| 5 | 没有文字... | 343 |
| 6 | Сентябрь в бэкенде вышел плотным. JDK 27 поменял сборщик мусора для маленьких контейнеров, PostgreSQL 19 незадолго до релиза лишился нескольких громких фич, а PgBouncer, PHP и NGINX выпустили исправления уязвимостей, с которыми лучше не тянуть.
🔵JDK 27: G1 везде и компактные заголовки
JDK 27 вышел 15 сентября. Раньше при скромных ресурсах, вроде одного CPU и хипа меньше 1,8 ГБ, JVM по умолчанию брала Serial GC, а теперь по JEP 523 всегда выбирает G1, и сервисы в маленьких подах это заметят.
Компактные заголовки из JEP 534 включены по умолчанию: заголовок объекта сократился с 12 до 8 байт. TLS 1.3 теперь первым предлагает гибридный постквантовый обмен X25519MLKEM768, а JFR сам маскирует похожие на секреты значения в аргументах JVM и переменных окружения.
🔵PostgreSQL 19 Beta 4: откаты перед релизом
Четвёртая бета от 24 сентября убрала из 19 версии SQL/PGQ, включение контрольных сумм на лету, FOR PORTION OF для UPDATE и DELETE, а также MERGE и SPLIT PARTITIONS.
Большую часть этого списка мы упоминали в августе. На месте остались REPACK CONCURRENTLY, параллельный автовакуум и ON CONFLICT DO SELECT, а релиз-кандидат ждут в начале октября.
🔵PgBouncer 1.26: три DoS-уязвимости
Версия от 23 сентября закрывает CVE-2026-19888, CVE-2026-6668 и CVE-2026-6669, причём две первые эксплуатируются без аутентификации. Онлайн-рестарт через -R удалён, так что скрипты деплоя с этим флагом стоит проверить до обновления.
🔵PHP 8.6 RC2 и патчи четырёх веток
24 сентября вышел второй релиз-кандидат PHP 8.6, первый пропустили из-за ошибки упаковки. Финальная версия запланирована на 19 ноября и меняет умолчания сессий: use_strict_mode и cookie_httponly включены, а cookie_samesite получает значение Lax. В тот же день вышли security-релизы 8.5.11, 8.4.26, 8.3.35 и 8.2.34.
🔵NGINX: переполнение буфера в HTTP/3
Уязвимость CVE-2026-90439 затрагивает ngx_http_v3_module в версиях с 1.29.2 по 1.31.5. Исправление вышло 15 сентября в версиях 1.30.5 и 1.31.6.
🔵Go: мелкие аллокации быстрее
Go 1.27.1 вышел 1 сентября с исправлениями в компиляторе, рантайме и net/http. В блоге Go разобрали изменение из 1.27: аллокации до 80 байт идут через специализированные варианты mallocgc и работают быстрее вплоть до 20–30%, а в масштабе всей программы выигрыш доходит до 1%.
🔵Valkey 9.2 RC1: снапшоты без fork
В первом кандидате 9.2 от 16 сентября появились RDB-снапшоты без fork, они включаются параметром forkless-infrastructure-enabled. EXEC получил условия IFEQ, IFNE, NX и XX для оптимистичной блокировки без WATCH, а большие sorted set теперь хранятся в B+ дереве вместо skiplist.
🔵Rust: секреты в кеше CI
21 сентября команда Rust предупредила, что cargo miri сохранял в target/ все переменные окружения. Если target/ кешировался в GitHub Actions, секреты мог прочитать автор любого pull request. Исправление вошло в nightly от 22 сентября, но кеши стоит очистить, а секреты перевыпустить.
#дайджест #backendvkhub | 444 |
| 7 | HTTP QUERY: почему поиск больше не должен притворяться методом POST
Поисковый фильтр в API давно перестал быть парой простых параметров — это дерево условий, массивы идентификаторов, диапазоны дат и состояние пагинации. В GET всё это пришлось бы кодировать в URI.
Стандарт HTTP советует поддерживать адреса длиной хотя бы 8 000 байт, но лимит у каждого звена свой. Поэтому длинный URL может обрезать браузер, балансировщик или WAF ещё до сервиса. Даже если адрес прошёл всю цепочку, он раздувается от percent-encoding и целиком оседает в логах и истории браузера.
Почти каждый поиск в API живёт как POST /search. Для протокола это обычный POST: он небезопасный и неидемпотентный, поэтому прокси не имеет права повторить оборванный запрос, кеш — переиспользовать такой ответ, а клиент с retry рискует задублировать операцию.
Что предложил RFC 10008
В июне 2026 года IETF опубликовал RFC 10008 с методом QUERY. Метод объединяет безопасность и идемпотентность GET с телом запроса, как у POST.
QUERY /feed HTTP/1.1
Host: api.example.test
Content-Type: application/json
{
"filter": {
"status": ["published", "scheduled"], # допустимые статусы
"publishedAt": { "gte": "2026-01-01" } # нижняя граница даты
},
"sort": [{ "field": "publishedAt", "direction": "desc" }],
"limit": 50
}
URI здесь задаёт ресурс и область выборки, а filter и sort в теле описывают условия поиска. Называть QUERY «GET с телом» неточно, ведь у тела GET нет определённой семантики, а у QUERY оно определяет всю операцию. Content-Type при этом обязателен: без него сервер отклоняет запрос, а угадывать формат по содержимому запрещено.
Кеширование
Ключ кеша обязан включать URI, тело запроса и Content-Type, поэтому запросы с "page": 1 и "page": 2 не могут делить одну запись. Чтобы построить ключ, кешу сначала придётся прочитать всё тело, и кеширование QUERY выходит дороже, чем GET.
Если запрос тяжёлый, сервер может вернуть Location с адресом сохранённого запроса. Тогда последующие обращения пойдут обычным GET, без повторной передачи тела.
Что с поддержкой?
OpenAPI 3.2 уже описывает query как first-class операцию. Серверные фреймворки подтягиваются с разной скоростью. Spring Framework 7.1 содержит HttpMethod.QUERY, заголовок Accept-Query и привязку тела запроса. В Gin и Fiber поддержка появилась в upstream, а Fastify и NestJS пока остановились на issues.
Инфраструктура отстаёт от фреймворков. NGINX уже проксирует QUERY, но не кеширует его, потому что proxy_cache_methods по умолчанию ограничен GET, HEAD и POST. В браузерах метод не входит в CORS-safelist, поэтому междоменный вызов требует preflight, а HTML-формы method="query" не стандартизированы.
QUERY закрывает старый разрыв между GET и POST, но сам стандарт не заставляет промежуточные звенья его понимать. Перед продом прогоняйте всю цепочку на реальном трафике и держите POST /search как фолбэк.
#backendvkhub #api #поиск | 351 |
| 8 | compact object headers по умолчанию: JDK 27 отдаёт обратно пятую часть кучи
Сервис на Spring держит в памяти десятки миллионов мелких объектов: элементы кеша, DTO из очереди, узлы деревьев. Куча упирается в четыре гигабайта, хотя полезных данных там заметно меньше. Разница уходит в заголовки: каждый объект на 64-битной JVM несёт 96 бит служебной информации, а без сжатых указателей на класс все 128.
Заголовок собран из двух слов. В mark word лежат сведения о блокировке, возраст для сборщика и вычисленный identity hash, в class word лежит указатель на метаданные класса. Для объекта с парой полей сопоставимы с размером самих данных.
JEP 534 в JDK 27, вышедшей 15 сентября 2026, делает компактную раскладку значением по умолчанию: оба слова сливаются в одно 64-битное, а указатель на класс сжимается до 22 бит. Путь у фичи долгий: эксперимент в JDK 24, продуктовая настройка в JDK 25, теперь поведение по умолчанию. Заявленная экономия составляет 10–20% живых данных. То, что раскладка включилась, видно прямо на старте:
$ java -XX:+PrintFlagsFinal -version | grep -i UseCompactObjectHeaders
bool UseCompactObjectHeaders = true {product lp64_product} {default}
➡️ Размер конкретного класса можно глянуть через JOL, он печатает раскладку полей вместе с выравниванием:
System.out.println(ClassLayout.parseClass(CacheEntry.class).toPrintable());
На классе с двумя ссылочными полями заголовок падает с 12 байт до 8, и экземпляр укладывается в 16 байт вместо 24: выравнивание по 8 байт добавляет к экономии ещё четыре.
Ограничение вытекает из тех самых 22 бит: в них помещается около четырёх миллионов классов.
Приложение, которое плодит классы на лету (динамические прокси, скриптовые движки, тяжёлая кодогенерация), способно упереться в предел, и тогда загрузка класса завершится OutOfMemoryError без автоматического отката на старую раскладку.
Сюрпризы приходят и со стороны агентов и нативного кода, если те читают заголовок по фиксированному смещению.
Отключается всё флагом -XX:-UseCompactObjectHeaders, но рассчитывать на него вдолгую не стоит: старую раскладку планируют объявить устаревшей и со временем убрать. В том же релизе поменялся и сборщик по умолчанию: JEP 523 назначает G1 стандартом во всех окружениях, включая маленькие контейнеры, где раньше выбирался Serial.
Сервисам на 25-й ту же экономию даёт -XX:+UseCompactObjectHeaders без смены мажорной версии. Перед включением на проде стоит посмотреть счётчик загруженных классов (jcmd <pid> VM.class_hierarchy или метрика jvm_classes_loaded) и прогнать нагрузочный тест с теми агентами, которые реально работают в бою. Если счётчик держится в десятках тысяч, потолок в четыре миллиона вас не касается.
А вы уже мерили, сколько кучи у вас занимают заголовки объектов?
#backendvkhub #jdk #java | 392 |
| 9 | 没有文字... | 382 |
| 10 | Pattern Matching в Java 21+: switch, который заменяет цепочки instanceof
С JDK 21 switch Java наконец умеет то, что программисты пятнадцать лет искали в Scala и Kotlin — паттерн-матчинг по типам, деструктуризацию записей и exhaustive-проверки на sealed-иерархиях.
В карточках — что именно поменялось, как выкинуть цепочки instanceof, зачем нужны record patterns и где лежат грабли.
#backendvkhub #java | 570 |
| 11 | 没有文字... | 461 |
| 12 | AOT-кеш в JVM: минус половина времени старта без GraalVM
Пока сервис деплоили раз в неделю, никого не волновало, что JVM стартует пять секунд. В контейнерах эти секунды всплывают постоянно: при каждом рестарте пода, при каждой новой реплике под нагрузкой, при каждом rolling update. Инстанс уже съедает память и процессор, но запросы ещё не принимает, а под всплеск трафика он доезжает до готовности как раз к тому моменту, когда пик прошёл и p99 просел.
Обиднее всего, что работа каждый раз повторяется впустую. JVM читает байт-код, парсит, верифицирует, загружает и связывает все классы, до которых дотягивается код. Для типичного Spring Boot это тысячи классов, и разбирает она их совершенно одинаково — просто нигде не запоминает.
Классическое решение — GraalVM Native Image. Проблему он вроде как закрывает, но просит взамен closed-world анализ, конфиги под каждую рефлексию, отдельный профиль сборки и отказ от JIT ради статического бинарника. На проекте с историей такой переезд легко растягивается на квартал — и не факт, что заканчивается успехом.
Project Leyden берёт дешевле: JVM остаётся обычной, а разбор классов уезжает в разогревочный прогон, например, на этап сборки образа. Результат ложится в файл, и в проде JVM просто читает готовое. Старт сокращается примерно вдвое, в коде не меняется ни строки.
В JDK 25 всё делается двумя командами. Раньше, когда механизм только появился в JDK 24, шагов было три — сначала записать конфигурацию, потом собрать из неё кеш, но эти два действия объединили:
# разогревочный прогон, на выходе -- app.aot
java -XX:AOTCacheOutput=app.aot -jar app.jar
# продакшен
java -XX:AOTCache=app.aot -jar app.jar
Со Spring Boot есть нюанс: обучающий прогон должен чем-то закончиться, иначе приложение просто останется работать и кеш никогда не соберётся. Поэтому его поднимают до готовности контекста и сразу гасят флагом -Dspring.context.exit=onRefresh.
Классами дело не ограничивается. В кеш попадают ещё и профили методов: на обучении JVM запоминает, что вызывалось чаще всего, а в проде JIT читает эту статистику сразу и берётся за горячие места, вместо того чтобы собирать её самому в первые минуты работы. Сервис быстрее выходит на полную скорость.
Цифры получаются такие: на живом приложении Spring Boot старт сократился с 4,9 до 2,4 секунды, на PetClinic ранние сборки давали 40–41%, разогрев ускоряется на 15–25%.
Взамен придётся следить за одной вещью: обучение и продакшен должны совпадать — тот же classpath, те же флаги, та же версия приложения. Разойдутся — JVM остановится с ошибкой, а не станет работать на устаревшем кеше. Поэтому сборку кеша обычно вешают на тот же шаг пайплайна, где собирается образ.
У тех, кто сидит на ZGC, всё это до недавнего времени не работало вовсе: сборщик прячет служебные данные прямо внутри ссылок на объекты, и кеш такого не переносил. Починили только в JDK 26.
Главное же отличие от Native Image в том, что Leyden ничего не забирает взамен. Рефлексия, динамическая загрузка классов и JIT остаются на месте. Если в проде что-то пойдёт не так, как на обучении, JVM спокойно доработает обычным способом — без падений и без необходимости заранее описывать в конфигах каждый возможный случай.
#backendvkhub #graalvm #jvm | 452 |
| 13 | In-process базы данных: libSQL, DuckDB, LanceDB
Чтение из локального файла занимает микросекунды. Запрос к удалённой PostgreSQL — 30–80 мс. In-process БД убирают сетевой вызов из пути чтения: libSQL для OLTP, DuckDB для аналитики, LanceDB для векторного поиска.
В карточках разбираем архитектуру каждой из трёх баз, показатели их производительности по бенчмаркам и границы применимости — где эти решения работают, а где нет.
#backendvkhub #архитектура_бд | 499 |
| 14 | 🔵Go 1.27: дженерик-методы и профиль утёкших горутин
Релиз вышел 19 августа, и главное в нём — методы наконец получили параметры типов. Дженерики в Go живут с 1.18, но методы всё это время оставались за бортом: параметризовать можно было функцию или тип целиком, а метод нет. Обходились через свободные функции с приёмником-параметром, что ломало цепочки вызовов и читалось плохо.
Второе по важности — профиль утёкших горутин, доехавший до стабильного состояния после эксперимента в 1.26. Он показывает горутины, которые навсегда заблокировались, потому что канал, мьютекс или sync.Cond, где они ждут, стал недостижим.
Диагностика тут всегда была мучительной: горутина висит, а место, где ошиблись с синхронизацией, отработало полчаса назад и следов не оставило. Теперь runtime/pprof отдаёт это отдельным профилем, и GoLand 2026.2 умеет его читать с первого дня.
Из стандартной библиотеки приехал encoding/json/v2 вместе с низкоуровневым encoding/json/jsontext. Старый encoding/json никуда не делся, но внутри теперь работает на движке v2 — ускорение разбора получают все, ничего не переписывая. Плюс в стандартную библиотеку добавили uuid: генерация идентификаторов перестала требовать внешней зависимости.
🔵Java 27: финальный RC и G1 везде
JDK 27 прошёл финальный релиз-кандидат 20 августа, релиз ожидается в середине сентября. Список фич заморожен ещё в июне, так что сюрпризов не будет.
Из того, что затронет всех: G1 становится сборщиком по умолчанию во всех окружениях. Раньше в стеснённых условиях — один процессор и меньше 1792 МБ памяти — JVM молча выбирала Serial GC. Теперь такого переключения нет, хотя сам Serial никуда не убрали и явный выбор через флаг работает как прежде.
Задевает это тех, кто крутит контейнеры с маленькими лимитами и не указывает сборщик руками: стоит прогнать бенчмарк на обоих вариантах, для совсем мелких heap Serial иногда выигрывает.
🔵PostgreSQL: 28 уязвимостей за один заход
13 августа обновились все поддерживаемые ветки — 18.6, 17.11, 16.15, 15.19 и 14.24. Закрыли 28 проблем с безопасностью и больше 110 багов.
Среди них есть тяжёлые: переполнение буфера в обработке регулярных выражений с возможностью выполнить произвольный код и путаница типов через аргументы internal, обе с оценкой 8,8 по CVSS.
Отдельно стоит посмотреть CVE-2026-14666 — кеширование row-level security игнорирует изменения ролей. Если мультитенантность построена на политиках, это ровно тот класс проблем, ради которого их и вводили.
В релизе отдельно оговорены индексы: GIN, btree_gist и ltree требуют проверки после апдейта. Что именно проверять, написано в release notes, и заглянуть туда лучше до окна обслуживания.
🔵Beta 3 и операции над партициями
Третья бета девятнадцатой вышла тем же числом. К уже известным SQL/PGQ и встроенному REPACK добавились операции над секциями прямо в ALTER TABLE: MERGE PARTITIONS склеивает несколько в одну, SPLIT PARTITIONS режет одну на несколько. Раньше такая перестройка означала новые таблицы, перенос данных и переключение через ATTACH/DETACH.
GA ждут в сентябре-октябре. Планируете переезжать в этом цикле — стенд стоит поднимать сейчас: API уже не изменится, а собственные несовместимости лучше найти заранее.
#backendvkhub #дайджест | 452 |
| 15 | 📢 Как мы увеличили время в ленте ВКонтакте на 11%
Рекомендательная система ВКонтакте — зрелый пайплайн, но и в нём есть точки роста. В этом году команда AI VK улучшила несколько этапов: заменила классический мультитаргет ранкера на композитный, стохастику в блендере — на детерминированный алгоритм Брезенхема, эвристики в ALS — на вероятностную оценку.
🔥 Результат: +11% времени в ленте, CTR лайка вырос на 30%, CTR скрытия поста снизился на 10%.
➡️ Подробности в статье на Хабре | 383 |
| 16 | Тесты конкурентного кода, которые не флакают
В каждом проекте с горутинами есть тест, который проходит локально, падает в CI и снова проходит после перезапуска. Обычно его помечают как известную проблему и гоняют пайплайн до зелёного.
Причина почти всегда во времени. Код опирается на time.Sleep , context.WithTimeout и time.AfterFunc, а реальные часы идут вперёд независимо от того, успела горутина или нет. Тест проверяет таймаут в пять секунд — значит, честно висит пять секунд и всё равно иногда не угадывает момент.
Go 1.25 вывел testing/synctest из экспериментального статуса и закрывает эту проблему на уровне рантайма. В пакете всего две функции.
synctest.Test запускает код в изолированном пузыре, где time работает на фальшивых часах: время стоит, пока хоть одна горутина может выполняться, и прыгает вперёд, когда все заблокированы.
func TestReadTimeout(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
ch := make(chan int)
_, err := ReadWithTimeout(ch, 60*time.Second)
if err == nil {
t.Fatal("expected timeout, got nil")
}
})
}
Таймаут в минуту, а тест выполняется мгновенно: горутина заблокировалась на чтении из канала, часы в пузыре прыгнули на минуту вперёд, таймер сработал. Ни параметризации таймаута ради тестируемости, ни подмены часов через интерфейс.
Вторая функция нужна, когда в пузыре работают фоновые горутины. synctest.Wait блокируется, пока все они не окажутся заблокированы на канале, таймере или подобном:
func TestWorkerProcessesJobs(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
w := NewWorker()
go w.Run(t.Context())
w.Submit("a")
w.Submit("b")
synctest.Wait() // обе задачи разобраны
if got := w.Processed(); got != 2 {
t.Fatalf("processed = %d, want 2", got)
}
})
}
Без Wait пришлось бы ставить time.Sleep наугад и надеяться, что воркер успел, — ровно то, из-за чего тест и флакал. Детектор гонок про Wait знает, так что -race работает корректно.
Правок в коде обычно не требуется, но одна встречается регулярно. Если компонент внутри себя вызывает context.Background(), контекст создаётся вне пузыря и под фальшивые часы не попадает. Такие места переделывают так, чтобы контекст приходил снаружи и в тесте передавался t.Context().
Границы у пузыря жёсткие. Горутины, запущенные до synctest.Test или в init, живут по настоящим часам. Сторонние абстракции над временем не перехватываются — подменяется только стандартный time, так что самописный Clock придётся инжектить как раньше.
И ещё: synctest.Run из Go 1.24 объявлен устаревшим, а в 1.26 удалён — если пробовали пакет на экспериментальной стадии, вызовы нужно заменить на Test.
Начинать разумнее с того теста, который все обходят стороной: обернуть в synctest.Test, убрать time.Sleep в пользу Wait и прогнать тысячу раз.
#backendvkhub #go | 422 |
| 17 | Redis — это не только строки. Шесть структур данных, которые закрывают целые классы задач
Большинство используют Redis как key-value кеш: SET, GET, EXPIRE. При этом внутри есть Sorted Sets для рейтингов, HyperLogLog для приблизительного подсчёта уникальных значений, Bitmaps для аналитики, GEO для геопоиска, Hash для объектов и Sets для тегов.
Каждая структура закрывает свой класс задач, где обычные строки либо не тянут по производительности, либо требуют кучи хаков. Пройдёмся по одной штуке — с реальным use case и командами.
#backendvkhub #redis | 497 |
| 18 | 视频消息 | 428 |
| 19 | 视频消息 | 391 |
| 20 | 视频消息 | 404 |
