feat: implement strict mode for max_records_per_run to prevent offset overshoot with composite keys#1
Conversation
… overshoot with composite keys
|
/codex review |
There was a problem hiding this comment.
Вердикт: есть блокер.
PR добавляет strict mode для max_records_per_run, обертку индекса, изменения SQL для error records, тесты и CI-пины. Главный риск: частичный батч теперь помечает весь transform_key обработанным, из-за чего последующие записи того же ключа могут быть навсегда отфильтрованы.
Критичные проблемы
Найдена потеря данных при повторных запусках strict mode для ключей, у которых записи выходят за пределы первого LIMIT.
Важные замечания
Есть риск неправильного чтения входов с join_keys: strict branch обходит существующую логику filtered join.
Тестовое покрытие
Новый тест проверяет только первый запуск и offset, но не проверяет последующие прогоны и финальный результат для той же пары ключей. Тесты не запускал: по правилам ревью нельзя выполнять код из репозитория.
Правила репозитория
Repo rules не предоставлены, шаг N/A.
| changed_idx_full_pk = cast(IndexDF, df) | ||
|
|
||
| # Группируем по transform_keys для обратной совместимости | ||
| idx_grouped = data_to_index(df, self.transform_keys) |
There was a problem hiding this comment.
Сценарий: max_records_per_run берет только часть full-PK строк для profile_id/post_id, а здесь они снова сворачиваются до transform_keys.
Влияние: store_batch_result пометит весь transform_key успешным с текущим process_ts; следующие строки того же ключа с update_ts < process_ts больше не попадут в выборку, и агрегаты вроде count будут потеряны/перезаписаны.
Фикс: не отмечать частичный transform_key как полностью обработанный либо хранить прогресс на уровне full PK/watermark и добавить тест с 2-3 run_full, проверяющий финальный результат для ключа, разбитого лимитом.
| # changed_idx содержит полный PK ограниченного батча, что позволяет | ||
| # читать именно те записи которые попали в LIMIT запроса. | ||
| assert changed_idx is not None # for mypy | ||
| data = inp.dt.get_data(changed_idx) |
There was a problem hiding this comment.
Сценарий: у второго input есть join_keys={"user_id": "id"}, а changed_idx содержит user_id, но не PK id этой таблицы.
Влияние: inp.dt.get_data(changed_idx) не применит mapping join_keys; для таблицы без пересечения PK это превращается в чтение всей таблицы и может дать неверные join/агрегаты или OOM на больших справочниках.
Фикс: для каждого inp строить отдельный индекс: для join_keys использовать старую filtered-join логику с rename(columns=inp.join_keys), и покрыть это тестом max_records_per_run + JoinSpec.
Summary
Implements strict mode for
max_records_per_runto fix offset overshoot bug when using composite keys. Whenmax_records_per_runlimits the batch size, the offset calculation now uses only the actually processed records instead of all events for the processed key pairs.Problem
When transform_keys differ from the input table's primary key:
get_batch_input_dfsreads ALL events for those pairs (not just N records)Solution
Strict mode behavior when
max_records_per_runis set:_get_additional_idx_columns)IndexDFWithMetadata(idx_grouped, changed_idx_full_pk)get_batch_input_dfsreads ONLY the specific records fromchanged_idx(strict mode)changed_idxinstead of querying DBlimit_hitguard - safe to update offset with strict modeChanges
datapipe/index_metadata.py- wrapper class for passing metadata with idxdatapipe/step/batch_transform.py- strict mode implementationtests/test_overshoot_composite_keys.py- demonstrates bug and verifies fixtests/test_batch_transform_with_offset_optimization.py- updated for strict mode behaviordocs/max_records_per_run_associativity_plan.md- implementation planTest Results
test_offset_overshoot_with_composite_keys- PASSED (bug fixed)Assumptions
When
max_records_per_runis set, the transformation is assumed to be associative:T(A) + T(B) = T(A+B). No validation mechanism implemented yet.