BeepSyncManager is the modern replacement for the legacy DataSyncManager. It is a partial class split across nine focused files and orchestrates one-way or bidirectional data synchronization between any two registered IDataSource instances.
When set, unlocks rule-engine gating for every policy decision point.
var ctx = new SyncIntegrationContext
{
RuleEngine = myRuleEngine, // IRuleEngine — drives all rule policy gates
DefaultsManager = myDefaults, // IDefaultsManager — fills default field values
RunInitiatedBy = "scheduler", // stamped on schema versions & audit logs
// MappingPlanState = ... // optional: shared mapping plan state
};
var syncManager = new BeepSyncManager(editor, ctx);
Properties
Property
Type
Description
Editor
IDMEEditor
Data management engine provided at construction
SyncSchemas
List<DataSyncSchema>
All registered schemas
Filepath
string
Path used for schema JSON persistence
IntegrationContext
SyncIntegrationContext
Optional rule/defaults wiring
LastRunConflicts
List<ConflictEvidence>
Populated by bidirectional runs when CaptureEvidence = true
LastRunCheckpoint
SyncCheckpoint
Latest checkpoint persisted during the last run
LastRunReconciliationReport
ReconciliationReport
DQ reconciliation results from the last run
Schema Management
CRUD methods
// Add a new schema to the in-memory list
syncManager.AddSyncSchema(schema);
// Update an existing schema by Id
syncManager.UpdateSyncSchema(schema);
// Remove by Id
syncManager.RemoveSyncSchema(schemaId);
// Manual schema validation (returns IErrorsInfo)
IErrorsInfo result = syncManager.ValidateSchema(schema);
Filters & field mappings
// Add a filter clause (AppFilter wraps a field/operator/value predicate)
syncManager.AddFilter(schemaId, new AppFilter
{
FieldName = "Region",
Operator = "=",
FilterValue = "EU"
});
// Remove a filter by field name
syncManager.RemoveFilter(schemaId, "Region");
// Add a single field mapping
syncManager.AddFieldMapping(schemaId, new FieldSyncData
{
SourceField = "CustomerName",
DestinationField = "Name",
});
Delegates to DataImportManager.RunImportAsync with the CDC-enriched import config
9
Bidirectional reverse import
If SyncDirection = "Bidirectional", swaps source ↔ dest and re-imports
10
Conflict gate
TryEvaluateConflictGate runs built-in or rule-driven resolution; quarantines if required
Cancellation — Pass a CancellationToken from a UI button or scheduler. The sync manager propagates it to RunImportAsync and all awaited helpers. On cancellation the schema SyncStatus is set to "Cancelled".
Bulk Sync
When you have multiple schemas registered, the bulk helpers iterate them in one call.
Sequential
// Async — processes schemas one after another
await syncManager.SyncAllDataAsync(CancellationToken.None, progress);
// Sync fallback
syncManager.SyncAllData();
Parallel
// Runs schemas in parallel, bounded by SyncPerformanceProfile.MaxDegreeOfParallelism
await syncManager.SyncAllDataParallelAsync(CancellationToken.None, progress);
The semaphore count is read from the schema's performance profile rule result (default 4). This avoids overwhelming shared data sources under load.
Watermark policy validation — field name set, mode recognised
Warning
5
Conflict policy rule existence — if ResolutionRuleKey set, engine must have it
Warning
SyncPreflightReport
var report = await syncManager.RunPreflightAsync(schema);
if (!report.IsApproved)
{
foreach (var issue in report.Issues)
Console.WriteLine($"[{issue.Severity}] {issue.Code}: {issue.Message}");
return; // block the run
}
Console.WriteLine($"Rules passed : {report.RulesPassed}");
Console.WriteLine($"Defaults ready: {report.DefaultsReady}");
CDC & Watermark (Incremental Sync)
Set SyncType = "Incremental" and configure WatermarkPolicy to restrict each sync run to only records changed since the previous run.
WatermarkPolicy
schema.WatermarkPolicy = new WatermarkPolicy
{
WatermarkMode = "Timestamp", // "Timestamp" | "Sequence" | "CompositeKey"
WatermarkField = "UpdatedAt", // column that advances monotonically
LastWatermarkValue = null, // null = start from beginning; engine updates this
OverlapWindowSeconds = 300, // re-scan last 5 min to catch late arrivals
DedupeStrategy = "LastWrite", // "LastWrite" | "FirstWrite" | "None"
ReplayEnabled = true, // idempotent: safe to re-run the same window
// Optional: rule keys override the built-in filter/late-arrival logic
FilterRuleKey = "sync.watermark.filter",
LateArrivalRuleKey = "sync.watermark.late-arrival",
};
WatermarkMode values
Mode
Typical field type
Use case
Timestamp
DateTime / DateTimeOffset
Audit timestamp, UpdatedAt, ModifiedAt
Sequence
long / int
Auto-increment IDs, sequence numbers, offsets
CompositeKey
Multiple columns
Change-version tables with composite change keys
CdcFilterContext (output)
Returned by BuildCdcFilterContext — the runtime filter envelope passed to DataImportManager.
Field
Type
Description
ResolvedFilters
List<AppFilter>
Computed filter predicates using the watermark window
WindowStart
object
Effective lower-bound (includes overlap window)
WindowEnd
object
Effective upper-bound (current watermark)
NewWatermarkValue
object
Value to be persisted as LastWatermarkValue after a successful run
LateArrivalAction
string
"Include" / "Warn" / "Quarantine" per rule result
Conflict Resolution
Applies only when SyncDirection = "Bidirectional". When a record exists in both source and destination with different values, TryEvaluateConflictGate decides which value wins.
ConflictPolicy
schema.ConflictPolicy = new ConflictPolicy
{
// Built-in rule keys (no rule engine required):
// "sync.conflict.source-wins"
// "sync.conflict.destination-wins"
// "sync.conflict.latest-timestamp-wins"
// "sync.conflict.fail-on-conflict"
ResolutionRuleKey = "sync.conflict.latest-timestamp-wins",
CaptureEvidence = true, // populate BeepSyncManager.LastRunConflicts
// Quarantine records whose conflict could not be resolved:
QuarantineDsName = "QuarantineDB",
QuarantineEntity = "ConflictQuarantine",
MaxConflictsPerRun = 500,
OnMaxExceededAction = "QuarantineRest", // "Abort" | "Continue" | "QuarantineRest"
};
Built-in strategies
Rule key
Behaviour
sync.conflict.source-wins
Source record always overwrites destination
sync.conflict.destination-wins
Destination record is kept unchanged (default fallback)
sync.conflict.latest-timestamp-wins
Record with the later timestamp wins; requires WatermarkField
sync.conflict.fail-on-conflict
Any conflict halts the run immediately
(custom key)
Rule engine evaluates your custom rule; output must be "source", "destination", or "quarantine"
ConflictEvidence (audit)
// After a bidirectional run:
var conflicts = syncManager.LastRunConflicts;
Console.WriteLine($"Conflicts this run: {conflicts.Count}");
foreach (var ev in conflicts)
{
Console.WriteLine($" Key={ev.RecordKey} " +
$"Resolution={ev.Resolution} " +
$"Rule={ev.AppliedRuleKey}");
}
Retry & Checkpoint
Configure RetryPolicy to automatically re-attempt on transient errors and resume from a mid-run checkpoint without re-processing already-committed batches.
RetryPolicy
schema.RetryPolicy = new RetryPolicy
{
MaxAttempts = 4,
BaseDelayMs = 2000,
BackoffMode = "Exponential", // "Fixed" | "Linear" | "Exponential"
MaxResumeWindowHours = 48, // checkpoint invalidated after 48 h
CheckpointEnabled = true, // save offset after each committed batch
// Rule Engine key that classifies errors into categories
ErrorCategoryRuleKey = "sync.error.classify",
NonRetryableCategories = new List<string> { "Fatal", "Validation" },
};
Error categories
Category
Default action
Description
Transient
Retry
Network timeout, transient DB lock
Validation
Quarantine
Schema mismatch, constraint violation
Conflict
Retry
Optimistic-concurrency write conflict
Fatal
Abort
Authentication failure, data source not found
If the rule key ErrorCategoryRuleKey is not set or the rule engine is disabled, TryClassifyError applies keyword heuristics (e.g. "timeout" → Transient, "constraint" → Validation).
Checkpoint resume
// After a partial failure the checkpoint is stored in schema.ActiveCheckpoint.
// On the next call to SyncDataAsync, the manager calls IsCheckpointResumeSafe()
// which evaluates the rule "sync.checkpoint.resume-safe" or checks the age window.
// Force a full re-run by clearing the checkpoint:
schema.ActiveCheckpoint = null;
await syncManager.SyncDataAsync(schema, CancellationToken.None, progress);
Schema Governance & Promotion
PromoteSchemaAsync advances a schema through an approval lifecycle (e.g. Draft → Review → Approved). A rule engine gate can block promotion if the mapping quality or approval evidence is insufficient.
var fieldHelper = new FieldMappingHelper(editor);
// Returns List<FieldSyncData> for all columns with matching names
var mappings = fieldHelper.AutoMapFields(
"SourceDB", "Customers",
"DestDB", "Customers");
// Override specific mappings after auto-map:
var nameMapping = mappings.First(m => m.SourceField == "ContactName");
nameMapping.DestinationField = "FullName";
ValidateWatermarkPolicy
var validationHelper = new SyncValidationHelper(editor);
var result = validationHelper.ValidateWatermarkPolicy(schema);
if (result.Flag == Errors.Failed)
Console.WriteLine($"[ERROR] {result.Message}");
Examples
Eight runnable examples live in Editor/BeepSync/Examples/. Below is a representative selection.
Example 01 — Basic full sync
// Example01_BasicFullSync.cs
var syncManager = new BeepSyncManager(editor);
var schema = new DataSyncSchema
{
Id = Guid.NewGuid().ToString(),
SourceDataSourceName = "SourceDB",
DestinationDataSourceName = "DestDB",
SourceEntityName = "Customers",
DestinationEntityName = "Customers",
SourceKeyField = "CustomerId",
DestinationKeyField = "CustomerId",
SyncType = "Full",
SyncDirection = "OneWay",
BatchSize = 500,
};
var fieldHelper = new FieldMappingHelper(editor);
foreach (var m in fieldHelper.AutoMapFields("SourceDB","Customers","DestDB","Customers"))
schema.MappedFields.Add(m);
syncManager.AddSyncSchema(schema);
await syncManager.SyncDataAsync(schema, CancellationToken.None,
new Progress<PassedArgs>(p => Console.WriteLine(p.Messege)));
Console.WriteLine($"Status: {schema.SyncStatus} LastSync: {schema.LastSyncDate:u}");
Example 03 — Incremental sync with watermark
// Example03_IncrementalSync.cs
var schema = new DataSyncSchema
{
// ... source/dest/entity/key fields ...
SyncType = "Incremental",
WatermarkPolicy = new WatermarkPolicy
{
WatermarkMode = "Timestamp",
WatermarkField = "UpdatedAt",
OverlapWindowSeconds = 300,
DedupeStrategy = "LastWrite",
ReplayEnabled = true,
},
};
// First run — syncs records changed since epoch:
await syncManager.SyncDataAsync(schema, CancellationToken.None, null);
// Second run — only picks up records changed since the first run:
await syncManager.SyncDataAsync(schema, CancellationToken.None, null);
// Example05_RetryAndCheckpoint.cs
schema.RetryPolicy = new RetryPolicy
{
MaxAttempts = 4,
BaseDelayMs = 2000,
BackoffMode = "Exponential",
MaxResumeWindowHours = 48,
CheckpointEnabled = true,
ErrorCategoryRuleKey = "sync.error.classify",
NonRetryableCategories = new List<string> { "Fatal", "Validation" },
};
// Run 1 — saves a checkpoint if it fails mid-way
await syncManager.SyncDataAsync(schema, CancellationToken.None, progress);
// Run 2 — resumes from the checkpoint automatically if it is still within 48 h
await syncManager.SyncDataAsync(schema, CancellationToken.None, progress);
// Force full re-run:
schema.ActiveCheckpoint = null;
await syncManager.SyncDataAsync(schema, CancellationToken.None, progress);
Example 02 — Schema versioning & promotion
// Example02_SchemaVersioning.cs
var ctx = new SyncIntegrationContext
{
RuleEngine = myRuleEngine,
RunInitiatedBy = "data-steward@example.com",
};
var syncManager = new BeepSyncManager(editor, ctx);
// ... add schema, configure mappings ...
var report = await syncManager.PromoteSchemaAsync(
schema,
targetState : "Approved",
approver : "data-steward@example.com",
changeNotes : "All mappings reviewed and complete."
);
if (report.IsApproved)
Console.WriteLine("Schema is now Approved and versioned.");
else
foreach (var issue in report.Issues)
Console.WriteLine($"[BLOCKED] {issue.Code}: {issue.Message}");