Твои Postgres-серваки сейчас плавятся? Поздравляю. И да, тебе нужен PG Dog.
Его основатель Lev один из самых сильных ребят в теме масштабирования и шардинга БД. Он делал это для Instacart, и его софт может сделать это и для тебя.
Если реплики начинают отставать, а IOPS упёрся в потолок, значит, пора.
https://pgdog.dev/
👉 @BackendPortal
Его основатель Lev один из самых сильных ребят в теме масштабирования и шардинга БД. Он делал это для Instacart, и его софт может сделать это и для тебя.
Если реплики начинают отставать, а IOPS упёрся в потолок, значит, пора.
https://pgdog.dev/
Please open Telegram to view this post
VIEW IN TELEGRAM
PgDog
Scale PostgreSQL horizontally
❤3👍2
Не пиши в SQL = NULL или != NULL
Для NULL всегда используй IS NULL / IS NOT NULL
NULL это “нет значения / неизвестно”, и обычные операторы сравнения (=, !=, <, >) с ним не работают, потому что NULL не равен, не больше и не меньше вообще ничего.
= / != сравнивают значения. А NULL это не значение, а отсутствие значения. Поэтому IS / IS NOT проверяют именно наличие или отсутствие значения.
👉 @BackendPortal
Для NULL всегда используй IS NULL / IS NOT NULL
NULL это “нет значения / неизвестно”, и обычные операторы сравнения (=, !=, <, >) с ним не работают, потому что NULL не равен, не больше и не меньше вообще ничего.
= / != сравнивают значения. А NULL это не значение, а отсутствие значения. Поэтому IS / IS NOT проверяют именно наличие или отсутствие значения.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8
В этом туториале объясняется, что такое JWT, как они работают, какие бывают техники подписи и какие есть best practices по безопасности.
👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Dockerfile доктор уже тут: переписывает файлы Dockerfile для уменьшения размера и повышения безопасности
👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12🔥3
Строки в PostgreSQL неизменяемые и хранятся в heap. У каждой строки есть скрытый идентификатор CTID, который указывает на ее физическое местоположение на диске.
CTID это идентификатор-кортеж из двух частей: номер страницы и индекс (позиция) внутри этой страницы. Посмотреть его можно простым запросом:
Вывод будет выглядеть примерно так:
Что в CTID интересного: когда ты создаешь индекс по первичному ключу, индекс не хранит всю строку целиком. Он хранит значение ключа и CTID, который указывает на реальное место строки в heap.
Из-за этого выборки быстрые. Индекс дает CTID, и PostgreSQL прыгает напрямую в нужную физическую точку в heap, чтобы достать строку.
Но есть нюанс: CTID меняется при обновлении строки. PostgreSQL использует MVCC (Multi-Version Concurrency Control), поэтому update создает новую версию строки в другом физическом месте. Старый CTID становится невалидным, а у строки появляется новый.
Из-за этого индексы нужно обновлять при каждом обновлении строки. Значение ключа может остаться тем же, но CTID, на который оно указывает, меняется.
Интересные архитектурные решения. PostgreSQL сэкономил одно лишнее разыменование, сохраняя CTID, но теперь индекс нужно переписывать даже если меняются неиндексируемые колонки.
Вот почему я обожаю копаться во внутренностях СУБД :) Там сплошь интересные решения и компромиссы.
👉 @BackendPortal
CTID это идентификатор-кортеж из двух частей: номер страницы и индекс (позиция) внутри этой страницы. Посмотреть его можно простым запросом:
SELECT ctid, * FROM your_table;
Вывод будет выглядеть примерно так:
(0,1), (0,2), (1,1), где первое число это страница, а второе это позиция.Что в CTID интересного: когда ты создаешь индекс по первичному ключу, индекс не хранит всю строку целиком. Он хранит значение ключа и CTID, который указывает на реальное место строки в heap.
Из-за этого выборки быстрые. Индекс дает CTID, и PostgreSQL прыгает напрямую в нужную физическую точку в heap, чтобы достать строку.
Но есть нюанс: CTID меняется при обновлении строки. PostgreSQL использует MVCC (Multi-Version Concurrency Control), поэтому update создает новую версию строки в другом физическом месте. Старый CTID становится невалидным, а у строки появляется новый.
Из-за этого индексы нужно обновлять при каждом обновлении строки. Значение ключа может остаться тем же, но CTID, на который оно указывает, меняется.
Интересные архитектурные решения. PostgreSQL сэкономил одно лишнее разыменование, сохраняя CTID, но теперь индекс нужно переписывать даже если меняются неиндексируемые колонки.
Вот почему я обожаю копаться во внутренностях СУБД :) Там сплошь интересные решения и компромиссы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍5❤3
Нашёл крутой способ прокачать Go😎
Это go-kata — набор маленьких задачек, каждая тренирует конкретный навык: от работы с ошибками до конкуррентности и HTTP. Берёшь, решаешь, смотришь, как можно было лучше.
Брюс Ли говорил, что боится не того, кто учит 10 000 ударов, а того, кто повторил «каты» по Go 10 000 раз😂
👉 @BackendPortal
Это go-kata — набор маленьких задачек, каждая тренирует конкретный навык: от работы с ошибками до конкуррентности и HTTP. Берёшь, решаешь, смотришь, как можно было лучше.
Брюс Ли говорил, что боится не того, кто учит 10 000 ударов, а того, кто повторил «каты» по Go 10 000 раз
Please open Telegram to view this post
VIEW IN TELEGRAM
GitHub
GitHub - MedUnes/go-kata: A collection of daily coding challenges designed to help you master idiomatic Go through deliberate,…
A collection of daily coding challenges designed to help you master idiomatic Go through deliberate, repetitive practice. - MedUnes/go-kata
❤4
This media is not supported in your browser
VIEW IN TELEGRAM
Бесплатная опенсорсная игра, которая буквально учит вас писать эмулятор NES с нуля на JavaScript 😊
👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Хватит сваливать всё в один файл CLAUDE.md.
Вложенные файлы недооценены просто преступно.
Большинство команд думают, что ИИ косячит потому, что модели недостаточно умные.
Чаще всего проблема вообще не в этом.
ИИ ломается из-за того, что у него ноль понимания границ контекста.
Когда ты запихиваешь все правила, соглашения и архитектурные решения в один огромный CLAUDE-файл…
Ты получаешь:
• шум в контексте
• конфликтующие инструкции
• более низкую точность
• более медленную работу с ИИ
Это как засунуть всю систему в один класс и назвать это модульной архитектурой.
Вложенные CLAUDE-файлы решают эту проблему. Они вводят границы контекста.
Ровно так же, как нормальная архитектура в коде.
Глобальные правила наверху.
Более специфичные правила по мере приближения ИИ к коду.
Глубокие файлы переопределяют верхние.
Вложенные файлы подгружаются только когда Claude работает в этом каталоге.
В монорепах это становится особенно мощно. Это даёт:
И вот главный вывод, к которому я пришёл, работая с ИИ при написании кода:
ИИ-кодинг — это не проблема prompt engineering.
Это проблема архитектуры контекста.
Если бы тебе нужно было определить ОДНО жёсткое, не подлежащее обсуждению правило в CLAUDE.md, каким бы оно было?
👉 @BackendPortal
Вложенные файлы недооценены просто преступно.
Большинство команд думают, что ИИ косячит потому, что модели недостаточно умные.
Чаще всего проблема вообще не в этом.
ИИ ломается из-за того, что у него ноль понимания границ контекста.
Когда ты запихиваешь все правила, соглашения и архитектурные решения в один огромный CLAUDE-файл…
Ты получаешь:
• шум в контексте
• конфликтующие инструкции
• более низкую точность
• более медленную работу с ИИ
Это как засунуть всю систему в один класс и назвать это модульной архитектурой.
Вложенные CLAUDE-файлы решают эту проблему. Они вводят границы контекста.
Ровно так же, как нормальная архитектура в коде.
Глобальные правила наверху.
Более специфичные правила по мере приближения ИИ к коду.
Глубокие файлы переопределяют верхние.
Вложенные файлы подгружаются только когда Claude работает в этом каталоге.
В монорепах это становится особенно мощно. Это даёт:
1. Эффективность по токенам
Каждый токен в контексте стоит производительности. Зачем грузить правила для React, если ты пишешь Python-воркер?
2. Масштабирование команды
Frontend-команда владеет apps/web/CLAUDE.md.
Backend-команда владеет apps/api/CLAUDE.md.
Без конфликтов мерджа. Без наступания друг другу на ноги. Чёткая зона ответственности.
3. Более высокая точность ИИ
Claude загружает только релевантные правила.
Меньше конфликтующих инструкций.
Гораздо более консистентный результат.
И вот главный вывод, к которому я пришёл, работая с ИИ при написании кода:
ИИ-кодинг — это не проблема prompt engineering.
Это проблема архитектуры контекста.
Если бы тебе нужно было определить ОДНО жёсткое, не подлежащее обсуждению правило в CLAUDE.md, каким бы оно было?
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤3💊1
Растеры тут? Ловите совет по Rust 🦀
При этом оно ленивое — замыкание выполняется только если условие
👉 @BackendPortal
bool::then: ленивое создание Optionbool::then можно использовать как альтернативу if-else, когда нужно условно построить Option<T>.При этом оно ленивое — замыкание выполняется только если условие
true, пропуская всю работу (выделение памяти, I/O, тяжёлые вычисления) при false.Please open Telegram to view this post
VIEW IN TELEGRAM
❤7
Недавно наткнулся на отличный пример реализации паттерна Outbox для e-commerce сервиса заказов на Postgres с Redis Pub/Sub. Сразу уточню, это не моя работа, просто делюсь как наглядным примером.
Суть паттерна в том, чтобы не терять события между записью в базу и публикацией в брокер, что часто становится проблемой в event-driven системах. В этом примере, когда создается заказ, транзакция Postgres одновременно записывает строку в таблицу заказов и в таблицу outbox. В outbox хранится топик и JSON с текущим состоянием, например pending. Отдельный процесс-диспетчер опрашивает эти записи, публикует их в Redis канал вроде orders.created и только после успешной публикации помечает запись как обработанную.
Такой подход обеспечивает хотя бы одну доставку события и сохраняет консистентность системы. Консьюмеры downstream, например для управления запасами, email-уведомлений или аналитики, делаются идемпотентными, поэтому спокойно справляются с редкими дубликатами.
Паттерн нравится своей простотой и надежностью: используется проверенная база данных для атомарности, нет сложных распределенных транзакций, при этом система остается устойчивой и легко расширяемой.
Исходники
👉 @BackendPortal
Суть паттерна в том, чтобы не терять события между записью в базу и публикацией в брокер, что часто становится проблемой в event-driven системах. В этом примере, когда создается заказ, транзакция Postgres одновременно записывает строку в таблицу заказов и в таблицу outbox. В outbox хранится топик и JSON с текущим состоянием, например pending. Отдельный процесс-диспетчер опрашивает эти записи, публикует их в Redis канал вроде orders.created и только после успешной публикации помечает запись как обработанную.
Такой подход обеспечивает хотя бы одну доставку события и сохраняет консистентность системы. Консьюмеры downstream, например для управления запасами, email-уведомлений или аналитики, делаются идемпотентными, поэтому спокойно справляются с редкими дубликатами.
Паттерн нравится своей простотой и надежностью: используется проверенная база данных для атомарности, нет сложных распределенных транзакций, при этом система остается устойчивой и легко расширяемой.
Исходники
Please open Telegram to view this post
VIEW IN TELEGRAM
Переименование файла - это атомарная операция, и именно эта гарантия сильно упрощает реализацию многих баз данных. Вот один практический пример, разберёмся детальнее…
Когда мы записываем данные в базу, мы не можем просто перезаписать файл напрямую. Если во время записи внезапно пропадёт питание, файл окажется повреждённым: часть старых данных, часть новых, короче полный беспорядок. Кошмар.
Поэтому большинство баз используют умную стратегию с двумя файлами для безопасности. Как это работает:
Новые данные сначала пишутся во временный файл. Это может быть запись в журнал write-ahead или новая страница базы данных. Старый файл при этом остаётся нетронутым и содержит последнее корректное состояние.
Когда новые данные полностью записаны во временный файл, система вызывает
Эта операция переименования атомарна на уровне файловой системы: либо всё прошло, либо ничего. После сбоя мы получаем ровно одно из двух состояний: старый файл остался нетронутым или новый файл полностью заменил старый. Никаких частичных записей или повреждённых данных.
Без этой атомарной гарантии всё было бы ужасно сложно: нужны сложные протоколы восстановления, версии, контрольные суммы на каждом шагу, и даже тогда остаются крайние случаи, когда данные могут повредиться.
Надёжность большинства баз данных держится именно на этой простой файловой гарантии. Довольно элегантно, если честно :)
👉 @BackendPortal
Когда мы записываем данные в базу, мы не можем просто перезаписать файл напрямую. Если во время записи внезапно пропадёт питание, файл окажется повреждённым: часть старых данных, часть новых, короче полный беспорядок. Кошмар.
Поэтому большинство баз используют умную стратегию с двумя файлами для безопасности. Как это работает:
Новые данные сначала пишутся во временный файл. Это может быть запись в журнал write-ahead или новая страница базы данных. Старый файл при этом остаётся нетронутым и содержит последнее корректное состояние.
Когда новые данные полностью записаны во временный файл, система вызывает
fsync(), чтобы гарантировать, что они реально оказались на диске (а не только в буфере). Затем выполняется атомарная операция rename(temp -> real_file).Эта операция переименования атомарна на уровне файловой системы: либо всё прошло, либо ничего. После сбоя мы получаем ровно одно из двух состояний: старый файл остался нетронутым или новый файл полностью заменил старый. Никаких частичных записей или повреждённых данных.
Без этой атомарной гарантии всё было бы ужасно сложно: нужны сложные протоколы восстановления, версии, контрольные суммы на каждом шагу, и даже тогда остаются крайние случаи, когда данные могут повредиться.
Надёжность большинства баз данных держится именно на этой простой файловой гарантии. Довольно элегантно, если честно :)
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12😁7❤2
Git tip
Используй эту команду, чтобы убрать из репозитория файлы, которые ты случайно закоммитил, хотя они должны были быть в
После этого файлы станут untracked. Добавь их в
👉 @BackendPortal
Используй эту команду, чтобы убрать из репозитория файлы, которые ты случайно закоммитил, хотя они должны были быть в
.gitignore git rm --cached <file>
После этого файлы станут untracked. Добавь их в
.gitignore и сделай новый коммит, чтобы полностью прекратить их трекинг.Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
GPT-5 это не одна модель.
Это единая система из нескольких моделей, защитных контуров и роутера в реальном времени.
Когда ты отправляешь запрос, выбранный режим определяет, какую модель использовать и сколько работы система сделает.
Instant mode отправляет запрос напрямую в быструю нерезонирующую модель GPT-5-main. Она оптимизирована под минимальную задержку и подходит для простых или низкорисковых задач типа коротких объяснений или переписываний текста.
Thinking mode использует рассуждающую модель GPT-5-thinking, которая делает несколько внутренних шагов перед финальным ответом. Это повышает корректность на сложных задачах вроде математики или планирования.
Auto mode добавляет роутер в реальном времени. Легковесный классификатор смотрит на запрос и решает, использовать GPT-5-main или GPT-5-thinking, когда нужно более глубокое рассуждение.
Pro mode не использует другую модель. Он берет GPT-5-thinking, но сэмплирует несколько попыток рассуждения и выбирает лучшую с помощью reward model.
Во всех режимах safeguards работают параллельно на разных стадиях. Быстрый тематический классификатор определяет, высокорисковая ли тема, затем reasoning monitor применяет более строгие проверки, чтобы заблокировать небезопасные ответы.
👉 @BackendPortal
Это единая система из нескольких моделей, защитных контуров и роутера в реальном времени.
Когда ты отправляешь запрос, выбранный режим определяет, какую модель использовать и сколько работы система сделает.
Instant mode отправляет запрос напрямую в быструю нерезонирующую модель GPT-5-main. Она оптимизирована под минимальную задержку и подходит для простых или низкорисковых задач типа коротких объяснений или переписываний текста.
Thinking mode использует рассуждающую модель GPT-5-thinking, которая делает несколько внутренних шагов перед финальным ответом. Это повышает корректность на сложных задачах вроде математики или планирования.
Auto mode добавляет роутер в реальном времени. Легковесный классификатор смотрит на запрос и решает, использовать GPT-5-main или GPT-5-thinking, когда нужно более глубокое рассуждение.
Pro mode не использует другую модель. Он берет GPT-5-thinking, но сэмплирует несколько попыток рассуждения и выбирает лучшую с помощью reward model.
Во всех режимах safeguards работают параллельно на разных стадиях. Быстрый тематический классификатор определяет, высокорисковая ли тема, затем reasoning monitor применяет более строгие проверки, чтобы заблокировать небезопасные ответы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7❤2😁1
Большинство систем детектят падение ноды или мастера простым polling-ом, и хотя звучит это очевидно, у подхода есть интересная проблема с надежностью
Типичный сценарий: есть наблюдатель, который смотрит на ноду напрямую. Обычно это ping, проверка что порт открыт, или легкий запрос, чтобы убедиться, что нода жива.
На бумаге все ок, но у всех этих методов одна и та же слабость: а что если ошибается сам наблюдатель?
В распределенной системе сетевые глюки это норма. Временная потеря пакетов, косяки маршрутизации или частичные сетевые партиции легко делают здоровую ноду “недоступной” именно для наблюдателя. Обычно это фиксят ретраями: повторяют проверку несколько раз и объявляют фейл после n-го подряд провала.
И тут появляется классический компромисс.
Если n маленькое (или polling очень частый), детект получается быстрым, но растет число false positive. Короткий сетевой чих может запустить ненужный failover, который иногда разрушительнее исходной проблемы.
Если n большое (или интервалы polling длиннее), false positive меньше, но реальные падения детектятся дольше. А эта задержка напрямую увеличивает даунтайм.
Но если у тебя уже есть кластер, можно мыслить надежнее.
Вместо того чтобы один наблюдатель постоянно дергал целевую ноду, можно дать нескольким нодам в кластере независимо делать health-check. И считать ноду упавшей только тогда, когда большинство наблюдателей согласны, что она недоступна.
Такой consensus-подход снижает риск false positive из-за сетевых партиций. Даже если один наблюдатель потерял связность, остальная часть кластера все равно дает более точную картину здоровья системы.
Консенсус стоит дорого, так что это не самый экономичный вариант. Но он точно полезен, если система достаточно большая и размазана по нескольким географиям.
👉 @BackendPortal
Типичный сценарий: есть наблюдатель, который смотрит на ноду напрямую. Обычно это ping, проверка что порт открыт, или легкий запрос, чтобы убедиться, что нода жива.
На бумаге все ок, но у всех этих методов одна и та же слабость: а что если ошибается сам наблюдатель?
В распределенной системе сетевые глюки это норма. Временная потеря пакетов, косяки маршрутизации или частичные сетевые партиции легко делают здоровую ноду “недоступной” именно для наблюдателя. Обычно это фиксят ретраями: повторяют проверку несколько раз и объявляют фейл после n-го подряд провала.
И тут появляется классический компромисс.
Если n маленькое (или polling очень частый), детект получается быстрым, но растет число false positive. Короткий сетевой чих может запустить ненужный failover, который иногда разрушительнее исходной проблемы.
Если n большое (или интервалы polling длиннее), false positive меньше, но реальные падения детектятся дольше. А эта задержка напрямую увеличивает даунтайм.
Но если у тебя уже есть кластер, можно мыслить надежнее.
Вместо того чтобы один наблюдатель постоянно дергал целевую ноду, можно дать нескольким нодам в кластере независимо делать health-check. И считать ноду упавшей только тогда, когда большинство наблюдателей согласны, что она недоступна.
Такой consensus-подход снижает риск false positive из-за сетевых партиций. Даже если один наблюдатель потерял связность, остальная часть кластера все равно дает более точную картину здоровья системы.
Консенсус стоит дорого, так что это не самый экономичный вариант. Но он точно полезен, если система достаточно большая и размазана по нескольким географиям.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5
This media is not supported in your browser
VIEW IN TELEGRAM
Когда один из серверов падает, дальше все зависит от того, есть ли у тебя load balancer и как он настроен.
▪️ Если есть load balancer
1. Health checks. Балансер постоянно пингует бэкенды (HTTP endpoint типа
2. Трафик на него перестают слать. Балансер выкидывает упавший инстанс из пула и перенаправляет запросы на живые сервера.
3. Что увидит пользователь
Если живых серверов достаточно, пользователь почти ничего не заметит (максимум краткий пик задержек).
Если это был единственный сервер, или остальные уже под нагрузкой, будет ошибка/таймаут (обычно 502/503/504).
4. Сессии и состояние
Если у тебя sticky sessions (привязка пользователя к одному серверу) и именно он умер, у пользователя может “слететь” сессия, если она хранилась в памяти сервера.
Правильнее хранить сессии/кеш/стейт внешне (Redis, DB), тогда падение инстанса не ломает логин и корзину.
5. Автовосстановление. В нормальной прод-схеме оркестратор (Kubernetes/ASG/VM supervisor) поднимет новый инстанс, и балансер снова начнет слать на него трафик после успешных health checks.
▪️ Если load balancer нет
Все запросы идут в один сервер.
Сервер упал = приложение лежит полностью до ручного/автоматического рестарта.
Load balancer делает так, чтобы падение одного сервера было “не трагедия”, а “минус один из пула”. Без балансера падение сервера почти всегда равно даунтайму.
👉 @BackendPortal
1. Health checks. Балансер постоянно пингует бэкенды (HTTP endpoint типа
/health, TCP-check, gRPC-check). Если сервер не отвечает или возвращает плохой статус, он помечается как unhealthy.2. Трафик на него перестают слать. Балансер выкидывает упавший инстанс из пула и перенаправляет запросы на живые сервера.
3. Что увидит пользователь
Если живых серверов достаточно, пользователь почти ничего не заметит (максимум краткий пик задержек).
Если это был единственный сервер, или остальные уже под нагрузкой, будет ошибка/таймаут (обычно 502/503/504).
4. Сессии и состояние
Если у тебя sticky sessions (привязка пользователя к одному серверу) и именно он умер, у пользователя может “слететь” сессия, если она хранилась в памяти сервера.
Правильнее хранить сессии/кеш/стейт внешне (Redis, DB), тогда падение инстанса не ломает логин и корзину.
5. Автовосстановление. В нормальной прод-схеме оркестратор (Kubernetes/ASG/VM supervisor) поднимет новый инстанс, и балансер снова начнет слать на него трафик после успешных health checks.
Все запросы идут в один сервер.
Сервер упал = приложение лежит полностью до ручного/автоматического рестарта.
Load balancer делает так, чтобы падение одного сервера было “не трагедия”, а “минус один из пула”. Без балансера падение сервера почти всегда равно даунтайму.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍6