save_listings и upsert_listing_snapshot писали observed_at/scraped_at/last_seen_at
через NOW() == transaction_timestamp(). Коммит у писателя один, в конце всего batch'а,
поэтому все строки вызова несли одну метку: прод 2026-08-06 — run 3303 219 строк на
1 метку при семи минутах работы, run 3229 2643 строки на 55 меток (метка на вызов
save_listings, не на строку).
Цена — не фильтры свежести: смещение равно длительности прогона (минуты; худший
случай 17.3 ч у cian_full_load) против окна в 14 суток, то есть 0.03-5%. Теряется
разрешение во времени — темп сбора и порядок строк внутри прогона по данным не
восстановить, отсюда же следовала невозможность бэкфилла run_id (#2701).
statement_timestamp(), а НЕ clock_timestamp() как в #2702/#2718: здесь одним
statement'ом пишутся ДВЕ колонки, обязанные совпадать (после #2206 scraped_at =
last_seen_at у 100% строк, на этом равенстве стоит предикат миграции 161).
Прод-проверка: clock_timestamp() = clock_timestamp() → false, а
statement_timestamp() = statement_timestamp() → true; при этом statement_timestamp()
двигается от запроса к запросу внутри одной транзакции, а каждый upsert — свой запрос.
Set-based писатели (listing_source_snapshots 89827 строк одним statement'ом,
deactivate_stale_avito) намеренно НЕ тронуты: там одна метка — правда о statement'е,
а clock_timestamp() дал бы ложное разрешение (прод: 27783 разных значения на 200000
строк одного statement'а, кодирующих порядок обработки, а не наблюдения).
Миграция 232 — только COMMENT ON COLUMN: фиксирует дату перехода и то, что
исторические строки невосстановимы (внутрипрогонное время нигде не сохранялось).
Refs #2731