using System; using Microsoft.Data.SqlClient; using Serilog; using SyncEngine.Configuration; namespace SyncEngine.Sync { public sealed class SoftDeleteTracker { private readonly SyncOptions _options; private readonly ILogger _logger; public SoftDeleteTracker(SyncOptions options, ILogger logger) { _options = options; _logger = logger; } public int MarkMissingRows(SqlConnection connection, string qualifiedTable, string pkColumn, DateTime runStart) { if (!_options.SoftDeleteEnabled || _options.DryRun) { return 0; } // Compare at millisecond precision so stored DATETIME2 values match the runStart written by MarkRowsSeen. var sql = string.Format(@" UPDATE {0} SET _sync_is_deleted = 1, _sync_updated_at = @now WHERE _sync_is_deleted = 0 AND ( _sync_last_seen_at IS NULL OR DATEDIFF(millisecond, _sync_last_seen_at, @runStart) > 0 )", qualifiedTable); using (var cmd = new SqlCommand(sql, connection)) { cmd.CommandTimeout = _options.CommandTimeoutSeconds; cmd.Parameters.AddWithValue("@now", DateTime.UtcNow); cmd.Parameters.AddWithValue("@runStart", runStart); var count = cmd.ExecuteNonQuery(); if (count > 0) { _logger.Information("Soft-deleted {Count} rows in {Table}", count, qualifiedTable); } return count; } } } }