en
Feedback
Backend VK Hub

Backend VK Hub

Open in Telegram

Комьюнити VK для бэкендеров. Cамые хардовые кейсы, дискуссии в кругу своих и прямой доступ к нашим экспертам 😎

Show more
1 125
Subscribers
-124 hours
-67 days
-730 days

Data loading in progress...

Attracting Subscribers
September '26
September '26
+1
in 0 channels
August '26
+12
in 1 channels
Get PRO
July '26
+12
in 0 channels
Get PRO
June '26
+7
in 1 channels
Get PRO
May '26
+17
in 1 channels
Get PRO
April '26
+21
in 1 channels
Get PRO
March '26
+8
in 0 channels
Get PRO
February '26
+25
in 1 channels
Get PRO
January '26
+40
in 1 channels
Get PRO
December '25
+427
in 21 channels
Get PRO
November '25
+154
in 0 channels
Get PRO
October '25
+770
in 20 channels
Date
Subscriber Growth
Mentions
Channels
04 September0
03 September+1
02 September0
01 September0
Channel Posts
🔵Go 1.27: дженерик-методы и профиль утёкших горутин Релиз вышел 19 августа, и главное в нём — методы наконец получили параме
🔵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 #дайджест

2
📢 Как мы увеличили время в ленте ВКонтакте на 11% Рекомендательная система ВКонтакте — зрелый пайплайн, но и в нём есть точк
📢 Как мы увеличили время в ленте ВКонтакте на 11% Рекомендательная система ВКонтакте — зрелый пайплайн, но и в нём есть точки роста. В этом году команда AI VK улучшила несколько этапов: заменила классический мультитаргет ранкера на композитный, стохастику в блендере — на детерминированный алгоритм Брезенхема, эвристики в ALS — на вероятностную оценку. 🔥 Результат: +11% времени в ленте, CTR лайка вырос на 30%, CTR скрытия поста снизился на 10%. ➡️ Подробности в статье на Хабре
207
3
Тесты конкурентного кода, которые не флакают В каждом проекте с горутинами есть тест, который проходит локально, падает в CI
Тесты конкурентного кода, которые не флакают В каждом проекте с горутинами есть тест, который проходит локально, падает в 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
236
4
Redis — это не только строки. Шесть структур данных, которые закрывают целые классы задач Большинство используют Redis как ke+5
Redis — это не только строки. Шесть структур данных, которые закрывают целые классы задач Большинство используют Redis как key-value кеш: SET, GET, EXPIRE. При этом внутри есть Sorted Sets для рейтингов, HyperLogLog для приблизительного подсчёта уникальных значений, Bitmaps для аналитики, GEO для геопоиска, Hash для объектов и Sets для тегов. Каждая структура закрывает свой класс задач, где обычные строки либо не тянут по производительности, либо требуют кучи хаков. Пройдёмся по одной штуке — с реальным use case и командами. #backendvkhub #redis
366
5
Video message
371
6
Video message
350
7
Video message
333
8
Мы начинаем 🚀
313
9
Video message
341
10
Считанные минуты остаются до старта двух основных треков выступления. А пока делимся закулисьем атмосферы на нашем митапе ⬇️
344
11
Как правильно делать параллельные задачи с отменой в Go Знакомая картина в хендлере Go-сервиса: пришёл запрос с userID, надо
Как правильно делать параллельные задачи с отменой в Go Знакомая картина в хендлере Go-сервиса: пришёл запрос с userID, надо параллельно сходить в БД за пользователем, в Redis за сессией и во внешний API за биллингом. Первый рефлекс — три горутины и sync.WaitGroup.  Работает, пока одна не падает: тогда WaitGroup ждёт остальные, они молотят вхолостую и возвращают никому не нужный результат. У sync.WaitGroup нет знания ни про ошибки, ни про отмену. Правильный инструмент — golang.org/x/sync/errgroup. Обёртка над sync.WaitGroup, работающая с контекстом: первая ошибка отменяет остальные горутины через общий ctx, Wait() возвращает её. func fetchUserData(ctx context.Context, userID int64) (*UserData, error) { g, ctx := errgroup.WithContext(ctx) var user *User var session *Session var billing *Billing g.Go(func() (err error) { user, err = db.QueryUser(ctx, userID); return }) g.Go(func() (err error) { session, err = redis.GetSession(ctx, userID); return }) g.Go(func() (err error) { billing, err = billingAPI.GetBilling(ctx, userID); return }) if err := g.Wait(); err != nil { return nil, err } return &UserData{user, session, billing}, nil } errgroup.WithContext возвращает Group и производный ctx. Первая g.Go, вернувшая ошибку, отменяет этот ctx. Остальные горутины видят отмену через свой ctx и завершаются — правильные библиотеки типа  database/sql, net/http, redis уважают контекст, ошибки быстро всплывают. g.Wait() возвращает первую пришедшую ошибку. Второй сценарий — batch processing с ограничением параллелизма. Загрузить тысячу файлов не по одному, но и не все сразу — по десять в параллель: func processFiles(ctx context.Context, paths []string) error { g, ctx := errgroup.WithContext(ctx) g.SetLimit(10) for _, path := range paths { g.Go(func() error { return processOne(ctx, path) }) } return g.Wait() } SetLimit(10) разрешает не больше десяти одновременных горутин (в x/sync v0.1 с конца 2022 года). g.Go при заполненном пуле блокируется до освобождения слота,  g.TryGo возвращает false без блокировки. С Go 1.22 переменная цикла path живёт per-iteration автоматически. Пара нюансов Отмена контекста не убивает горутины — они должны сами проверять ctx.Done() или использовать библиотеки, уважающие ctx. Бесконечный цикл без проверки не остановится. Панику errgroup не ловит — пробрасывает наверх и валит процесс. Если функция может паниковать (парсинг чужого JSON, недоверенный ввод), оборачивайте тело самой горутины в recover(). Результаты пишутся в переменные без синхронизации — это норм, пока каждая горутина пишет в свою, а вот если несколько пишут в один слайс или map, нужен sync.Mutex. Если в проекте есть sync.WaitGroup, плюс переменная-ошибка под sync.Mutex, плюс ручная отмена через context.CancelFunc — почти наверняка это errgroup.WithContext, лишний код уходит. #backendvkhub #go
361
12
Как запретить пересечения периодов без триггеров в PostgreSQL Стандартная задача из SaaS: календарь бронирования созвонов. Од
Как запретить пересечения периодов без триггеров в PostgreSQL Стандартная задача из SaaS: календарь бронирования созвонов. Одна комната не должна быть занята двумя бронированиями сразу. Пользователь A ставит на 14:00–15:00, пользователь B — на 14:30–15:30, оба жмут «Сохранить» одновременно, в базе появляются оба. Классические подходы — SERIALIZABLE-транзакция с ловлей отказов, advisory lock на комнату или BEFORE-триггер. Все три работают, при этом легко ломаются при UPDATE, каскадах или под нагрузкой. В PostgreSQL для этого есть EXCLUDE constraint — родной механизм, делающий то же самое на уровне базы через индекс. Пишется так: CREATE EXTENSION IF NOT EXISTS btree_gist; CREATE TABLE bookings ( id serial PRIMARY KEY, room_id int NOT NULL, period tstzrange NOT NULL, EXCLUDE USING gist ( room_id WITH =, period WITH && ) ); Читается как «не давать двум строкам иметь одновременно одинаковый room_id и пересекающийся period». && — оператор пересечения для range-типов, = — обычное равенство. btree_gist нужен, потому что room_id — обычный int, а GiST по умолчанию понимает только geometric и range. Пробуем: INSERT INTO bookings (room_id, period) VALUES (1, '[2026-08-06 14:00, 2026-08-06 15:00)'); -- OK INSERT INTO bookings (room_id, period) VALUES (1, '[2026-08-06 14:30, 2026-08-06 15:30)'); -- ERROR: conflicting key value violates exclusion constraint Ни триггеров, ни блокировок, ни SERIALIZABLE. Под капотом PostgreSQL проверяет пересечения через GiST-индекс, автоматически создающийся с констрейнтом. При UPDATE та же семантика. Race condition не пролезет, проверка идёт под нужными блокировками. Дальше интереснее — EXCLUDE работает не только с временными диапазонами. Тот же паттерн закроет пересечение IP-диапазонов (inet WITH &&), непересекающиеся 2D-области (box WITH &&), уникальные тарифы на интервалы [valid_from, valid_to) для одной услуги. Один раз добавили — забыли про самописную валидацию. Часто используют partial EXCLUDE с WHERE. Отменённые бронирования не должны блокировать новые: ALTER TABLE bookings ADD EXCLUDE USING gist ( room_id WITH =, period WITH && ) WHERE (status = 'active'); status = 'cancelled' строки в проверке не участвуют, освобождённое окно можно перебронировать. При всём этом GiST-индекс дороже btree на point-lookup, если поиск по room_id частый, заведите отдельный btree, GiST оставьте под EXCLUDE. Если в проекте есть триггер BEFORE INSERT/UPDATE с проверкой пересечений или app-код с SELECT ... FOR UPDATE вокруг вставки — он почти наверняка заменяется одним EXCLUDE constraint, из репозитория уходят сотни строк. #backendvkhub #postgresql
424
13
📢 Неделя до «Митапа с двойной начинкой»! 26 августа собираем Go- и Java-разработчиков на мероприятие от VK Just Tech. Вас жд
📢 Неделя до «Митапа с двойной начинкой»! 26 августа собираем Go- и Java-разработчиков на мероприятие от VK Just Tech. Вас ждут два трека выступлений, общение и, конечно, афтепати с квизом, шавермой и мастер-классом✨ ➡️ В треке Go разберёмся в нестандартной компиляции для ускорения выкатки фичи, расскажем, как в VK выстроили единую платформу для разработки Go-сервисов и как устроена доставка информации об адресах через сетевую инфраструктуру. ➡️ В треке Java поговорим о перформанс-трюках в поисковой платформе, разберём архитектуру современного платёжного движка и обсудим переход от федерации кластеров хранения к мультитенантному решению. 📍 26 августа, Санкт‑Петербург, Крестовский остров, ресторан Royal Beach Встречаемся в 17:00, начало в 18:00.  Участие бесплатное, регистрация обязательна. Зарегистрироваться Ждём вас! #backendvkhub #meetup #go #java #митап
364
14
OneDWH VK: как консолидировали 0,5+ EB данных из 35 бизнес-вертикалей В VK исторически сложилась децентрализованная модель уп+5
OneDWH VK: как консолидировали 0,5+ EB данных из 35 бизнес-вертикалей В VK исторически сложилась децентрализованная модель управления данными: каждая из 35 бизнес-вертикалей развивала аналитику самостоятельно и выбирала технологии независимо от соседей. К моменту решения о переезде в облако это выросло в 10+ железных кластеров под Airflow и Hadoop, 0,5+ EB аналитических данных и тысячи кросс-запросов в месяц между командами. Пока команд было около десяти, расхождения снимались прямыми договорённостями. Когда специалистов стало больше сотни, подход перестал масштабироваться, и в компании начали строить единое хранилище. В карточках — чем именно мешала разрозненная инфраструктура, какие принципы легли в основу новой платформы, как проходила миграция 30 тысяч объектов и что получилось на выходе. ➡️ Полная версия статьи #backendvkhub #dwh #обзорстатьи
408
15
Gel — TypeScript для PostgreSQL Запрос с тремя уровнями вложенности через ORM — это не один SQL, а 5–10 отдельных запросов. P
Gel — TypeScript для PostgreSQL Запрос с тремя уровнями вложенности через ORM — это не один SQL, а 5–10 отдельных запросов. Prisma делает отдельные запросы для каждого уровня, SQLAlchemy требует joinedload() на каждую связь, Hibernate — FETCH JOIN. Если про один уровень забыли — N+1. Gel (бывший EdgeDB) решает проблему на уровне СУБД. Это графово-реляционная БД, которая работает как слой поверх PostgreSQL. Создатели описывают соотношение так: «Gel to Postgres is what TypeScript is to JavaScript». Что такое Gel Вместо таблиц — объектные типы с наследованием и миксинами. Вместо внешних ключей — first-class links. Связи могут быть single или multi, required или optional, с exclusive-ограничением. Обратные ссылки (backlinks) позволяют навигировать в обе стороны без явных JOIN. Пример схемы: abstract type Person { required name: str { constraint exclusive; }; } type Hero extending Person { secret_identity: str; multi link villains := .<nemesis[is Villain]; } type Movie { required title: str; multi characters: Person; } Схема описывается в .esdl-файлах. При изменении схемы gel migration create сам анализирует разницу и генерирует миграцию. Без рассинхронизации между ORM-моделями и реальной схемой. EdgeQL: один запрос вместо N+1 EdgeQL-запрос с тремя уровнями вложенности компилируется в один SQL с JOIN. select Movie { title, actors: { name, awards: { title, year } }, director: { name, birthplace } } filter .title = "Inception" Эквивалент в SQL потребовал бы 4–5 JOIN, а результат был бы плоским списком строк, который нужно собирать в объект на стороне приложения. ORM на такой глубине сделают 5–10 отдельных запросов. В EdgeQL нет ключевого слова JOIN — связи обходятся через links: Movie.actors.name. Нет NULL В EdgeQL нет NULL — только пустые множества {}. NULL = NULL → UNKNOWN, NULL + 5 → NULL, агрегаты игнорируют NULL — в SQL это источник постоянных багов. В EdgeQL каждый тип — это множество значений, и пустое множество явно обозначает отсутствие данных. Устраняет целый класс ошибок, связанных с трёхзначной логикой. AI на уровне БД Расширение ext::ai добавляет векторный поиск и RAG: embedding-индексы на основе OpenAI/Anthropic моделей, поиск по similarity, HTTP-эндпойнт /ai/rag. Поиск и генерация в одной транзакции, без отдельного векторного хранилища. Deferred index пересчитывается автоматически при изменении данных. Ограничения Gel требует изучения EdgeQL, и экосистема пока меньше, чем у Prisma или Drizzle. EdgeQL не поддерживает оконные функции и рекурсивные CTE (хотя в 6.0 можно использовать SQL напрямую). Serializable isolation only — узкое место при высокой конкурентности. Нет кеширования исполняемых планов запросов. Попробуйте Gel на новом проекте со сложной моделью данных. Для всего остального есть PostgreSQL. #backendvkhub #postgresql #gel
452
16
Инвалидация кеша: скорость доступа vs согласованность данных Кеш и источник данных не могут быть строго консистентны. Если бы
Инвалидация кеша: скорость доступа vs согласованность данных  Кеш и источник данных не могут быть строго консистентны. Если бы кеш при каждом чтении ходил в основное хранилище, сам смысл кеширования был бы утрачен. Поэтому любая стратегия инвалидации — компромисс между задержкой чтения, нагрузкой на источник и окном, где клиент видит устаревшие данные. Надо решать не «как сделать правильно», а «какое окно рассинхронизации вы готовы терпеть». Это окно складывается из двух вещей, которые часто путают между собой: как кеш узнаёт, что данные устарели, и что он делает после того, как узнал. Первое определяет, сколько вы вообще будете жить с устаревшими данными. Второе — как быстро от них избавитесь, когда обнаружите.   ➡️ Как кеш узнаёт, что данные устарели TTL. Кеш считает запись невалидной через фиксированное время после записи. TTL не проверяет, изменились ли данные на самом деле — это просто таймер. Плюс: TTL не завязан на запись, живёт в cache-aside, не требует встраивать кеш куда-либо. Минус: окно неконсистентности задаёт произвольное число, а не реальное событие. Write-through. Приложение пишет в кеш, а кеш сам синхронно сохраняет данные в источник. Точка входа для записи — кеш, а не база. Данные актуальны почти всегда. Почти — потому что кеш может уже обновиться, когда запись в источник ещё не подтвердилась. Минус: каждая запись становится медленнее на постоянную величину, даже если этот элемент никому в кеше не нужен. Event-driven (Pub/Sub). Источник публикует событие об изменении, кеш подписан и обновляется асинхронно. Такой подход разводит запись и инвалидацию во времени, но добавляет риски, о которых часто забывают: 🔵брокер может потерять событие, если не даёт гарантию at-least-once 🔵нужно гарантировать сохранение порядка событий. Если придут не по порядку — старое перезапишет свежие данные 🔵пока событие создаётся и пересылается, кеш отдаёт устаревшие данные и не знает об этом ➡️ Что кеш делает после инвалидации Триггер инвалидации не определяет, что происходит с данными дальше. Их обновление — это отдельная, независимая ось. Purge. Кеш точечно удаляет элемент по известному ключу. Данные вернутся в кеш, только когда клиент их запросит. Refresh. Кеш сам идёт в источник за свежим значением сразу после инвалидации — не ждёт запроса клиента. Ban. В отличие от purge, кеш инвалидирует не один ключ, а пачку данных по паттерну или диапазону — даже не зная точных ключей. Запись может ещё какое-то время отдаваться как есть (grace period) и обновиться при следующем обращении. TTL по умолчанию ведёт себя как Ban: невалидная запись просто ждёт следующего запроса. Чтобы получить refresh, добавьте его поверх TTL явно. ➡️ Что выбрать Смотрите на три вещи: 🔵насколько критична консистентность — допустимо ли показать пользователю данные пятисекундной давности 🔵как часто источник меняет данные 🔵какой у вас профиль нагрузки — read-heavy или write-heavy На практике стратегии комбинируют. Например: берёте TTL как страховочный потолок на случай, если событие потерялось, и добавляете event-driven — для быстрой инвалидации горячих ключей. Так надёжнее, чем полагаться на что-то одно. #backendvkhub #кеш #архитектура
365
17
Вам доклады по Go или Java? VK приглашает на «Митап с двойной начинкой» 📢 26 августа собираем Go- и Java-инженеров, чтобы пр
Вам доклады по Go или Java? VK приглашает на «Митап с двойной начинкой» 📢 26 августа собираем Go- и Java-инженеров, чтобы прокачать знания в разработке и архитектуре высоконагруженных сервисов. 💙 Вас ждут: летний Санкт-Петербург, любимый стритфуд, феерический закат, два трека докладов от ведущих инженеров VK и приглашённых экспертов, квиз, мастер-класс и вечеринка! Трек Go 🔹«Компиляция Go в динамическую либу, или Как мы ускорили выкатку фичи в два раза, но нам не понравилось», Михаил Кичигин, бэкенд-разработчик, Avito Tech 🔹«Go-платформа VK: как мы построили единую основу для создания Go-сервисов», Александр Апанасенко, руководитель команды SDK Backend, VK 🔹«Почтальон от Kubernetes до Palo Alto. Как осуществляется доставка информации об адресах», Дарья Кулькина, бэкенд-разработчик, Точка Банк Трек Java 🔹«Перформанс-трюки в поисковой платформе Ozon», Пётр Портнов, ведущий разработчик среднего поиска, Ozon 🔹«Архитектура современного движка платёжной системы и переход к новой архитектуре», Игорь Крикунов, технический лидер разработки DBC, Альфа-Банк 🔹«Переход от федерации S3-кластеров в пользу коммунального и мультитенантного кластера», Кирилл Боблак, ведущий разработчик One-cloud, VK 💙 Афтепати  🔹Квиз «Что? Где? Кебаб?», чтобы окончательно прожарить мозги на технических вопросах 🔹Фуд-корнер 🔹Мастер‑класс, который сделает вас звездой любой вечеринки 📍 26 августа, Санкт‑Петербург, Крестовский остров, ресторан Royal Beach Встречаемся в 17:30, начало в 18:00.  Участие бесплатное, регистрация обязательна. 💙 Ждём вас! #backendvkhub #meetup #go #java #митап
530
18
🔵PostgreSQL 19: Beta 2 Июль в бэкенде — история про PostgreSQL. 16 июля вышла Beta 2 версии 19: Feature freeze означает, что
🔵PostgreSQL 19: Beta 2 Июль в бэкенде — история про PostgreSQL. 16 июля вышла Beta 2 версии 19: Feature freeze означает, что финальный список фич зафиксирован, от RC до GA API уже не поменяется, и то, что тестируется сейчас, реально поедет в прод в сентябре-октябре. 🔵Графовые запросы и REPACK Из вышедшего самое громкое — SQL/PGQ, Property Graph Queries. MATCH-паттерны для графового обхода прямо в SQL. Раньше для такого приходилось либо гонять WITH RECURSIVE с массивом посещённых вершин от циклов, либо тащить отдельную графовую БД типа Neo4j. Теперь запрос вроде «Найди все узлы, до которых от A можно дойти через N связей типа friend» пишется декларативно через MATCH. За компанию — REPACK как встроенная SQL-команда: zero-downtime реорганизация таблиц становится частью базы, pg_repack extension постепенно уходит на пенсию. 🔵Autovacuum, планировщик и auto-scaling AIO Плюс parallel autovacuum через autovacuum_max_parallel_workers (на терабайтных таблицах эффект будет заметный), pg_plan_advice для явного контроля планов и auto-scaling AIO через io_min_workers и io_max_workers. В версии 18 io_method=worker появился, в 19 количество workers само подстраивается под нагрузку. 🔵Что проверить на стейджинге перед GA Feature freeze — как раз то состояние, когда стоит гонять реальный workload по стейджингу. В pg_dumpall, кастомных TOAST-настройках, RADIUS-auth и btree_gist на inet/cidr были правки, поэтому лучше словить регрессии сейчас, чем через два месяца после GA. 🔵Минорные релизы Параллельно вышли минорки всех веток — 18.4, 17.10, 16.14, 15.18, 14.23. Суммарно закрыли 11 проблем с безопасностью и 60+ багов. Стоит обратить внимание: EOL ветки 14 — 12 ноября 2026 года, до конца поддержки полгода. Планировать миграцию стоит начинать сейчас. 🔵Redis Redis провёл июль тихо. Software 7.2.4-154 (1 июля) — внутренние фиксы без громких заголовков. Из общей тенденции: 8.x продолжает интересно развиваться — Vector Sets как native-тип для similarity search (не workaround через ZSET плюс hashes), dev vs production database mode с type-to-confirm при destructive-операциях на проде. Не строго июльская новость, но контекст важный. Новая статья от инженеров VK на Хабре 🔵Сериализация one-nio: от истоков к поддержке JDK 25 #backendvkhub #дайджест
484
19
VK и JUG Ru Group изучили, какими инструментами сейчас пользуются Java-разработчики. Главный вывод: команды активно внедряют
VK и JUG Ru Group изучили, какими инструментами сейчас пользуются Java-разработчики. Главный вывод: команды активно внедряют ИИ, но не спешат менять проверенный стек. Что показало исследование ➡️ Java 21 стала основной версией языка — её используют две трети разработчиков ➡️ 95% специалистов работают с ИИ, больше половины — каждый день ➡️ Spring Boot, PostgreSQL и Kafka остаются основой большинства проектов ➡️ Агентные ИИ-инструменты пока не стали массовыми Получается интересная картина: новые инструменты быстро входят в ежедневную практику, а фундамент технологического стека остаётся прежним. 🚀 Java меняется, но без революций. Полные результаты исследования #backendvkhub #java #исследование
473
20
Нативные lazy objects в PHP Ленивая загрузка в PHP до 8.4 жила на генерации кода. Doctrine отдавала из репозитория не сущност
Нативные lazy objects в PHP Ленивая загрузка в PHP до 8.4 жила на генерации кода. Doctrine отдавала из репозитория не сущность, а сгенерированного наследника, и за это приходилось платить: final на сущность не поставить, get_class() возвращает имя прокси, а в деплое живёт отдельный шаг, который эти классы порождает. В 8.4 всё это переехало в объектную модель. Точек входа две, обе на ReflectionClass. Первая создаёт ghost — объект нужного класса, у которого свойства пустые до первого обращения. $reflector = new ReflectionClass(User::class); $user = $reflector->newLazyGhost(function (User $user) use ($id, $persister): void { $persister->loadById(['id' => $id], $user); }); Возвращается настоящий User, так что instanceof и get_class() отвечают честно, а инициализатор при первом обращении заполняет тот же самый экземпляр. Вторая точка входа делает proxy. Реальный объект создаёт фабрика, а прокси переадресует на него обращения. Применимо там, где экземпляр приходит из чужого кода и собрать его по месту нельзя. $user = $reflector->newLazyProxy( fn (User $user): User => $repository->find($id) ); Объект просыпается от чтения или записи ленивого свойства, а также от ReflectionProperty::getValue() и setValue(). Вызов метода сам по себе триггером не считается, но внутри метода почти всегда читается свойство, и оно-то инициализацию и запускает. Из-за этого обычный getId() сходил бы в базу за значением, которое и так известно. Обходится это записью в обход ленивости: $reflector->getProperty('id') ->setRawValueWithoutLazyInitialization($user, $id); Doctrine раскладывает первичный ключ ровно так, сразу после создания ghost, и getId() после этого отвечает без похода в базу. Рядом лежат два флага для $options: SKIP_INITIALIZATION_ON_SERIALIZE не даёт сериализации разбудить объект, SKIP_DESTRUCTOR отменяет деструктор у экземпляра, который так и не проснулся. Плюс resetAsLazyGhost() и resetAsLazyProxy(), возвращающие в ленивое состояние уже существующий объект. Ограничения простые: ленивым делается пользовательский класс или stdClass, на любом другом внутреннем классе прилетит Error. Объект без свойств ленивым не станет вовсе, откладывать в нём нечего. В экосистеме всё уже на месте. Doctrine ORM умеет нативные объекты с версии 3.4, вышедшей летом 2025, а ORM 4.0 потребует PHP 8.4 и будет построена на них целиком. В Symfony LazyGhostTrait и LazyProxyTrait устарели с 7.3 и удалены в 8.0. На практике переключение выглядит как одна строчка в конфиге: doctrine: orm: enable_native_lazy_objects: true Старый ключ enable_lazy_ghost_objects при этом надо убрать. #backendvkhub #php
527