Как реализовать удаление устаревших записей из хранилища?
Что проверяют
Понимаете ли вы, что удалить старые записи легко, а удалить их без просадки прода трудно. Интервьюер ждёт разговора о пачках, индексах, нескольких репликах сервиса и о том, что часть работы умеет делать само хранилище. Следом спрашивают, что будет, если сервис запущен в трёх экземплярах.
Как отвечать
Начните с выбора механизма под хранилище:
- встроенный TTL: Redis (
EXPIRE,SET ... EX), MongoDB (TTL-индекс), Cassandra (TTL при записи), ClickHouse (выражениеTTLв описании таблицы). Если он есть, свой воркер не нужен; - PostgreSQL: TTL нет. Для логов и событий лучше всего партиционирование по времени, тогда старые данные уходят через
DROPилиDETACH PARTITIONпочти мгновенно и без мёртвых строк; - удаление пачками:
DELETE FROM records WHERE id IN (SELECT id FROM records WHERE created_at < $1 LIMIT 5000)в цикле, покаRowsAffected()не вернёт ноль. Нужен индекс поcreated_at, иначе каждая пачка это полный просмотр.
Сам воркер на Go:
time.Tickerиcontext.Context: цикл сselectпоticker.Cиctx.Done(), запросы черезExecContextс таймаутом, чтобы сервис корректно останавливался по сигналу;- одна копия на кластер: при нескольких репликах берите
pg_try_advisory_lockперед проходом или выносите задачу в CronJob Kubernetes, иначе все экземпляры будут бороться за одни и те же строки; - метрики: число удалённых строк и длительность прохода, чтобы видеть, успевает ли очистка за притоком данных.
Для кэша в памяти подход другой: map под sync.Mutex с фоновой зачисткой плюс проверка срока при чтении, чтобы протухшее значение не отдалось между проходами.
Где спотыкаются
Пишут один DELETE ... WHERE created_at < now() - interval '30 days' на всю таблицу. На десятках миллионов строк это долгая транзакция, блокировки, всплеск WAL и отставание реплик.
Запускают горутину без контекста и держат процесс через select {}. Её нельзя корректно остановить, а ошибки просто пишутся в лог без повторов и алертов.
Забывают, что в PostgreSQL удаление не освобождает место сразу. Строки остаются мёртвыми до прохода автовакуума, и при агрессивной очистке таблица и индексы распухают.