feat(datasync): add custom interval support to DataUpdateRepository
Add optional customIntervals parameter to GetSyncStatusAsync to allow per-pipeline interval overrides instead of hardcoded defaults. This enables tables like MisData to use longer sync intervals (e.g., 70 days) while other tables use standard intervals. Key changes: - IDataUpdateRepository.GetSyncStatusAsync now accepts an optional Dictionary<string, int> for custom intervals keyed by "TableName_UpdateType" - GetExpectedInterval and IsOverdue made public static for testing and reuse - Added GetDefaultInterval method for accessing default values - Updated DataSyncHealthCheck to use new signature - Added comprehensive unit tests for custom interval behavior
This commit is contained in:
@@ -64,9 +64,16 @@ public interface IDataUpdateRepository
|
||||
/// <summary>
|
||||
/// Gets sync status for health check purposes.
|
||||
/// </summary>
|
||||
/// <param name="customIntervals">
|
||||
/// Optional dictionary of custom intervals per table/updateType.
|
||||
/// Key format: "{TableName}_{UpdateType}" where UpdateType is the numeric enum value (e.g., "MisData_3" for Mass).
|
||||
/// Value: interval in minutes.
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>List of table sync status records.</returns>
|
||||
Task<List<TableSyncStatus>> GetSyncStatusAsync(CancellationToken cancellationToken = default);
|
||||
Task<List<TableSyncStatus>> GetSyncStatusAsync(
|
||||
Dictionary<string, int>? customIntervals = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -25,7 +25,7 @@ public class DataSyncHealthCheck : IHealthCheck
|
||||
{
|
||||
try
|
||||
{
|
||||
var statuses = await _repository.GetSyncStatusAsync(cancellationToken);
|
||||
var statuses = await _repository.GetSyncStatusAsync(customIntervals: null, cancellationToken);
|
||||
var data = new Dictionary<string, object>();
|
||||
|
||||
foreach (var status in statuses)
|
||||
|
||||
@@ -158,7 +158,9 @@ WHERE StartDT < DATEADD(DAY, -@retentionDays, GETUTCDATE())";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<TableSyncStatus>> GetSyncStatusAsync(CancellationToken cancellationToken = default)
|
||||
public async Task<List<TableSyncStatus>> GetSyncStatusAsync(
|
||||
Dictionary<string, int>? customIntervals = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
const string sql = @"
|
||||
WITH LastSuccessful AS (
|
||||
@@ -182,16 +184,18 @@ FROM LastSuccessful";
|
||||
(UpdateTypes)r.UpdateType,
|
||||
r.LastSuccessfulSync,
|
||||
r.LastSuccessfulSync.HasValue,
|
||||
GetExpectedInterval((UpdateTypes)r.UpdateType),
|
||||
IsOverdue(r.LastSuccessfulSync, (UpdateTypes)r.UpdateType),
|
||||
GetExpectedInterval(r.TableName, (UpdateTypes)r.UpdateType, customIntervals),
|
||||
IsOverdue(r.LastSuccessfulSync, r.TableName, (UpdateTypes)r.UpdateType, customIntervals),
|
||||
r.RecentFailures))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the expected interval in minutes for an update type.
|
||||
/// Gets the default interval in minutes for an update type.
|
||||
/// </summary>
|
||||
private static int GetExpectedInterval(UpdateTypes updateType)
|
||||
/// <param name="updateType">The update type.</param>
|
||||
/// <returns>The default interval in minutes.</returns>
|
||||
public static int GetDefaultInterval(UpdateTypes updateType)
|
||||
{
|
||||
return updateType switch
|
||||
{
|
||||
@@ -202,20 +206,54 @@ FROM LastSuccessful";
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the expected interval in minutes for a table and update type.
|
||||
/// Uses custom interval if provided, otherwise falls back to default.
|
||||
/// </summary>
|
||||
/// <param name="tableName">The table name.</param>
|
||||
/// <param name="updateType">The update type.</param>
|
||||
/// <param name="customIntervals">Optional dictionary of custom intervals per table/updateType.</param>
|
||||
/// <returns>The expected interval in minutes.</returns>
|
||||
public static int GetExpectedInterval(
|
||||
string tableName,
|
||||
UpdateTypes updateType,
|
||||
Dictionary<string, int>? customIntervals)
|
||||
{
|
||||
if (customIntervals is not null)
|
||||
{
|
||||
var key = $"{tableName}_{(int)updateType}";
|
||||
if (customIntervals.TryGetValue(key, out var customInterval))
|
||||
{
|
||||
return customInterval;
|
||||
}
|
||||
}
|
||||
|
||||
return GetDefaultInterval(updateType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a sync is overdue based on last successful sync time.
|
||||
/// </summary>
|
||||
private static bool IsOverdue(DateTime? lastSync, UpdateTypes updateType)
|
||||
/// <param name="lastSync">The last successful sync time.</param>
|
||||
/// <param name="tableName">The table name.</param>
|
||||
/// <param name="updateType">The update type.</param>
|
||||
/// <param name="customIntervals">Optional dictionary of custom intervals per table/updateType.</param>
|
||||
/// <returns>True if the sync is overdue; otherwise, false.</returns>
|
||||
public static bool IsOverdue(
|
||||
DateTime? lastSync,
|
||||
string tableName,
|
||||
UpdateTypes updateType,
|
||||
Dictionary<string, int>? customIntervals)
|
||||
{
|
||||
if (!lastSync.HasValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var expectedInterval = GetExpectedInterval(updateType);
|
||||
var expectedInterval = GetExpectedInterval(tableName, updateType, customIntervals);
|
||||
var grace = expectedInterval * 0.5; // 50% grace period
|
||||
var overdueTreshold = DateTime.UtcNow.AddMinutes(-(expectedInterval + grace));
|
||||
var overdueThreshold = DateTime.UtcNow.AddMinutes(-(expectedInterval + grace));
|
||||
|
||||
return lastSync.Value < overdueTreshold;
|
||||
return lastSync.Value < overdueThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user