- Add per-cycle correlation ID ([cyc:xxxxxxxx]) threaded through Python
logs and SQL audit tables for end-to-end traceability of any divergence.
- New ProductionDataBaseSync.SyncApplyRunLog table + @CycleID on usp_SyncApply
for per-table apply auditing (pending/merged/deleted/applied, dead/error
counts, duration). Audit write isolated in its own TRY/CATCH outside txn.
- Capture/cleanup phases now emit full detail: Insert->Delete downgrade
warning with record_id/log_id, dedup_skipped as apply-stall signal,
per-file summary, and access log-id ranges on cleanup.
- Queue health check surfaces error/dead rows with recent samples instead of
silent accumulation (previously the top cause of data divergence).
- sql_writer uses INSERT...SELECT...WHERE NOT EXISTS for observable dedup;
idle cycles lowered to DEBUG with periodic heartbeat.
- Backward compatible: old proc callers still work (CycleID nullable; legacy
coarse-grained logging with one-time notice).
Excluded from this commit: CODE.md, build_code_doc.py (doc generation).
At startup, previously produced logs are relocated into an Archive/
subfolder next to the active log: the project sync.log gets a
-YYYY-MM-DD suffix when archived, and NSSM's nssm_*.log captures are
moved as-is. The log root then only shows the current day's sync.log.
Idempotent handler setup is preserved.
Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
setup_logging unconditionally addHandler'd on every call. Under the new
main.py entry the function was invoked twice for --loop mode (main.py and
service.run both called it), stacking two file + two console handlers so
every log line was written twice.
Clear any pre-existing root handlers before re-adding so repeated calls
always yield exactly one file handler + one console handler, regardless of
the caller. Preserves the standalone python -m sync.service entry point.
Wires capture -> apply -> cleanup into cycle(cfg): per-file capture and
cleanup each wrapped in try/except + log.exception so one file's failure
does not abort the cycle; apply failure does not block cleanup; writer is
always closed in finally. run(cfg) loops cycle with sleep; main() loads
config from argv. logging_setup uses RotatingFileHandler 10MBx5 + console.
Unit tests cover all three error-isolation branches via mocks (no real
end-to-end smoke; integration deferred to Task 9 pilot).
Co-Authored-By: Claude <noreply@anthropic.com>