Migrate sync objects into ProductionDataBaseSync schema + add permanent audit archive

- Add sql/00_schema.sql: create dedicated ProductionDataBaseSync schema (idempotent)
- Move SyncQueue and usp_SyncApply from dbo into ProductionDataBaseSync
- Add sql/03_sync_log_archive.sql: permanent, append-only SyncLogArchive that
  records both OriginalOperateType and ProcessedOperateType plus the Access log
  OriginalTime, so pipeline divergences (e.g. Insert applied as Delete) stay
  reconstructible forever (SyncQueue is transient and only keeps processed type)
- config.py: inject sync_queue_table / archive_table / apply_proc (default to the
  new schema); SqlWriter takes these names instead of hardcoding dbo
- sql_writer.py: add ArchiveRow + insert_archive_row (dedup on source keys),
  parametrize queue/archive/proc names throughout
- capture.py: archive every consumed log row before enqueue (preserves evidence
  before cleanup deletes the Access log)
- service.py: pass the three names into SqlWriter
- tests: read queue/proc names from config instead of hardcoding dbo.SyncQueue
This commit is contained in:
Misaka_Company
2026-07-16 12:32:22 +08:00
parent 4179d3e232
commit d71b7eab62
11 changed files with 242 additions and 61 deletions

View File

@@ -1,9 +1,10 @@
-- SyncQueue: staging table for the Access -> SQL Server one-way sync.
-- Lives under the ProductionDataBaseSync schema (run 00_schema.sql first).
-- Idempotent: safe to re-run (table created only if absent; indexes only if absent).
IF OBJECT_ID('dbo.SyncQueue', 'U') IS NULL
IF OBJECT_ID('ProductionDataBaseSync.SyncQueue', 'U') IS NULL
BEGIN
CREATE TABLE dbo.SyncQueue (
CREATE TABLE ProductionDataBaseSync.SyncQueue (
QueueID bigint IDENTITY(1,1) NOT NULL,
SourceFile nvarchar(255) NOT NULL,
SourceTable nvarchar(255) NOT NULL,
@@ -18,6 +19,7 @@ BEGIN
ErrorMsg nvarchar(max) NULL,
CapturedAt datetime2 NOT NULL CONSTRAINT DF_SyncQueue_Captured DEFAULT sysdatetime(),
AppliedAt datetime2 NULL,
CleanedAt datetime2 NULL,
CONSTRAINT PK_SyncQueue PRIMARY KEY CLUSTERED (QueueID)
);
END
@@ -25,38 +27,27 @@ GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes
WHERE name = 'UX_SyncQueue_Dedup'
AND object_id = OBJECT_ID('dbo.SyncQueue'))
AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncQueue'))
BEGIN
CREATE UNIQUE INDEX UX_SyncQueue_Dedup
ON dbo.SyncQueue(SourceFile, SourceTable, SourceLogID);
ON ProductionDataBaseSync.SyncQueue(SourceFile, SourceTable, SourceLogID);
END
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes
WHERE name = 'IX_SyncQueue_Pending'
AND object_id = OBJECT_ID('dbo.SyncQueue'))
AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncQueue'))
BEGIN
CREATE INDEX IX_SyncQueue_Pending
ON dbo.SyncQueue(Status, TargetSchema, TargetTable);
END
GO
-- Cleanup bookkeeping: track when a queue row's Access log counterpart has
-- been physically deleted, so cleanup never re-deletes the same IDs and the
-- table can be purged to bound its growth.
IF NOT EXISTS (SELECT 1 FROM sys.columns
WHERE object_id = OBJECT_ID('dbo.SyncQueue')
AND name = 'CleanedAt')
BEGIN
ALTER TABLE dbo.SyncQueue ADD CleanedAt datetime2 NULL;
ON ProductionDataBaseSync.SyncQueue(Status, TargetSchema, TargetTable);
END
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes
WHERE name = 'IX_SyncQueue_Cleaned'
AND object_id = OBJECT_ID('dbo.SyncQueue'))
AND object_id = OBJECT_ID('ProductionDataBaseSync.SyncQueue'))
BEGIN
CREATE INDEX IX_SyncQueue_Cleaned
ON dbo.SyncQueue(Status, CleanedAt);
ON ProductionDataBaseSync.SyncQueue(Status, CleanedAt);
END
GO