Машинный перевод
Эта страница была автоматически переведена ИИ и может содержать ошибки. Если что-то неясно, обращайтесь к английскому источнику.
Инкрементальная синхронизация¶
pplx-export batch создан для частого запуска: каждый запуск экспортирует только то, что новое или
изменённое, устраняет пробелы, оставленные прерванными запусками, и никогда не пересматривает темы, которые
платформа уже завершила. Единственным источником истины для «что было экспортировано» является
index/batch_state.json (BatchState,
pplx_export/core/state.py:63), обновляемый после каждой темы — нет
отдельной теневой копии.
a. Предварительные требования и базовое использование¶
pplx-export index --account alice # refresh index/library_alice.json first
pplx-export batch --account alice # incremental export (early stop, resumable)
batch отказывается работать без индекса
(pplx_export/commands/batch_cmd.py:79-81). --limit N и --mode <mode>
фильтруют строки индекса перед планированием; строки без entryUUID пропускаются
с предупреждением вместо аварийного завершения запуска (batch_cmd.py:95-100).
b. Как работает инкрементальный план¶
- Сортировка. Строки индекса сортируются по
lastUpdated, сначала новые (batch_cmd.py:89). Совершенно новые беседы и возобновлённые старые (чьиlastUpdatedтолько что переместили их вверх) находятся наверху — этот порядок делает безопасной раннюю остановку. - Классификация.
plan_incremental(pplx_export/hooks/incremental.py:36-87) — чистая функция, общая дляbatchиschedule— назначает каждой строке ровно одно действие:
| действие | условие | что делает пакет |
|---|---|---|
new |
uuid никогда не встречался в batch_state |
экспорт |
updated |
lastUpdated отличается от записанного значения, или --force |
повторный экспорт |
done |
статус ok и lastUpdated не изменились |
пропуск |
expired |
платформа вернула ENTRY_EXPIRED при предыдущей попытке |
пропуск — терминальный, никогда не повторяется |
deleted |
sync-deleted подтвердил удаление на стороне |
пропуск — терминальный, никогда не повторяется |
- Ранняя остановка. По умолчанию (ни
--full, ни--force) самая длинная завершающая последовательность терминальных записей (done/expired/deleted) обрезается целиком и учитывается какn_stopped(incremental.py:83-87). Поскольку список отсортирован от новых к старым, всё ниже неизменённой записи обязательно старше и тоже не изменилось — дальнейшее сканирование только тратило бы время.
flowchart TD
IDX["library index rows<br/>sorted by lastUpdated, newest first"] --> PLAN["plan_incremental"]
PLAN --> NEW["new → export"]
PLAN --> UPD["updated → re-export"]
PLAN --> DONE["done → skip"]
PLAN --> TERM["expired / deleted → skip (terminal)"]
DONE --> STOP["early stop:<br/>trailing terminal run trimmed"]
TERM --> STOP
- Выполнение. Каждая экспортированная тема немедленно отмечается (
mark_ok/mark_error/mark_expired/mark_deleted), и файл состояния сохраняется после каждого элемента (batch_cmd.py:154-201);KeyboardInterruptтакже сохраняется перед распространением (batch_cmd.py:158-161). Записи атомарны — временный файл плюсos.replace(state.py:145-152) — поэтому прерванный запуск никогда не оставляет усечённый JSON.
c. Устранение пробелов после прерванных запусков¶
Ранняя остановка никогда не скрывает пробел. Темы, которые завершились ошибкой (статус error) или не были
достигнуты, находятся выше терминального суффикса, поэтому следующий запуск перепланирует их
как updated / new и экспортирует их до достижения точки ранней остановки
(incremental.py:12-14, batch_cmd.py:206-208). В сочетании с сохранением состояния
для каждого элемента пакетный запуск может быть прерван в любой точке и просто перезапущен.
Если сам batch_state.json повреждён, он не затирается молча: оригинал
переименовывается в batch_state.json.corrupt-<timestamp>, чтобы записанные
терминальные состояния не были потеряны и не повторялись без необходимости (state.py:68-81).
d. --full и --force¶
| флаг | эффект | терминальные состояния | когда использовать |
|---|---|---|---|
| (по умолчанию) | ранняя остановка над завершающей терминальной последовательностью | пропущены | каждый обычный / плановый запуск |
--full |
полное сканирование, без ранней остановки; неизменённые темы всё равно пропускаются как done |
пропущены | периодическая подстраховка или при подозрении на пробелы в архиве |
--force |
повторный экспорт всего, даже неизменённых тем | всё ещё исключены — никогда не повторяются | после исправлений конвейера, требующих повторной загрузки сырых данных |
Терминальные состояния исключены из --force по замыслу: повторная попытка для истёкшей или
удалённой на стороне темы только тратит запросы и бюджет отсрочек
(batch_cmd.py:120-127).
См. также status: отчёт без сетевых запросов о
состоянии учёта и плане изменений, вычисленный с той же семантикой plan_incremental
(new/updated/количество ранних остановок).
Сравнение lastUpdated нормализует завершающие нули в
дробной части секунд (.18033Z равно .180330Z; state.py:23-55),
потому что платформа иногда их опускает — точное строковое сравнение могло бы
ошибочно определить «изменено» и вызвать дублирующий экспорт.
e. Терминальные состояния: expired и deleted¶
expired |
deleted |
|
|---|---|---|
| значение | платформа удалила тему (~3-месячное окно хранения); попытка экспорта вернула ENTRY_EXPIRED |
удаление пользователем/удалённо, подтверждено sync-deleted |
| записывается | самим batch (mark_expired, state.py:131-134) |
pplx-export sync-deleted --online (mark_deleted, state.py:136-143) |
| повторяется? | никогда — даже с --force |
никогда — даже с --force |
| доказательство | ответ ENTRY_EXPIRED |
поле note: отсутствие в индексе + GET /rest/thread/<uuid> → ENTRY_DELETED / ENTRY_EXPIRED / HTTP 404 |
e.1 sync-deleted: подтверждение удалений на стороне¶
pplx-export sync-deleted --account alice # offline dry-run: list candidates only
pplx-export sync-deleted --account alice --online # confirm each candidate online
- Кандидаты (офлайн, без сети). Любая тема со статусом
okвbatch_state, отсутствующая в объединенииentryUUIDвсех индексов учётных записейindex/library_*.json, является подозреваемым кандидатом на удаление на стороне (pplx_export/commands/sync_deleted_cmd.py:148-212). Объединение по всем учётным записям необходимо: тема, принадлежащаяbob, но экспортированнаяaliceчерез общее пространство, никогда не появляется в собственном индексеalice— сравнение по одной учётной записи дало бы ложноположительный результат для всего этого набора. Когда нет ни одного пригодного индекса, каждый кандидат безопасно пропускается с записью причины. - Пробный запуск по умолчанию. Без
--onlineкоманда только перечисляет кандидатов — без сети, без изменений файлов. - Подтверждение
--online. Каждый кандидат проверяется с помощьюGET /rest/thread/<uuid>, используя учётную записьexport_viaкандидата изthread.json(cookie переключается автоматически):
| результат | исход |
|---|---|
ENTRY_DELETED / ENTRY_EXPIRED / HTTP 404 |
подтверждено: batch_state помечает как терминальное deleted (note записывает причину), и каждый thread.json этой темы получает метку времени remote_deleted на своё место |
| тема всё ещё существует | ложноположительный результат: сообщается как есть (индекс может быть не полностью обновлён — перезапустите index и проверьте снова), ничего не изменено |
| 5xx / сетевая ошибка | состояние не меняется; кандидат оставляется для следующего раунда |
| 3 последовательных 401/403 | быстрый аварийный останов — истёкший cookie не может восстановиться сам, и продолжение могло бы неправильно пометить активные темы (sync_deleted_cmd.py:333-337) |
Подтверждённые метки сохраняются для каждого элемента, поэтому прерванный запуск --online
ничего не теряет, и повторные запуски идемпотентны (sync_deleted_cmd.py:254-256).
f. Принцип надгробия¶
Локальные архивы никогда не удаляются
Этот архив является резервной копией экспортированных бесед.
sync-deleted только идентифицирует и помечает (надгробие): он никогда не удаляет
и не перемещает ни один файл архива. Подтверждение меняет ровно две вещи —
статус batch_state и один ключ-маркер в thread.json:
"remote_deleted": "2026-07-23T10:20:30Z"
Метка идемпотентна: существующий ключ remote_deleted не
перезаписывается и не заменяется (sync_deleted_cmd.py:215-244).
g. Идемпотентность и офлайн-перерендеринг¶
- Повторный запуск
batchс неизменённым индексом ничего не экспортирует: каждая строка классифицируется какdone, и запуск останавливается в точке ранней остановки. Записи состояния атомарны, метки по каждой теме, и повторно подтверждённые удаления никогда не дублируют меткуremote_deleted. - Архив хранит сырые полезные данные API (
raw_entries.json/raw_blocks.json), поэтому отрендеренные файлы могут быть регенерированы в любое время без доступа к сети:
pplx-export re-render # rebuild conversation.md + turns/ everywhere
pplx-export re-render --dry-run # only list the thread directories
pplx-export re-render --thread-json # also sync interruptions / answer_variants keys
re-render повторно разбирает сырой JSON с текущим рендерером
(pplx_export/commands/rerender_cmd.py:105-190): conversation.md и
turns/turn_*.md перезаписываются, устаревшие файлы поворотов с номерами выше текущего
количества поворотов удаляются, а источники, ресурсы, report.md и thread.json
остаются нетронутыми. Так исправления рендерера распространяются на весь
архив без единого запроса.
h. См. также¶
- pplx-export.md — полная справка по команде
batch(--mode,--limit, задержки) - maintenance-commands.md —
sync-deleted,re-renderи команды обратного заполнения - archive-layout.md — где находятся
batch_state.jsonиthread.json - rate-limiting.md — темп между темами, отсрочка, быстрый аварийный останов при ошибке аутентификации
- ../architecture/export-pipeline.md — полный конвейер экспорта
- ../architecture/offline-operations.md — конвейер офлайн-перестроения в деталях
- ../architecture/rate-limiting-errors.md — таксономия ошибок и обработка терминальных состояний