fix(purge): short first tick on all three purge timers so daily-recycled nodes still purge
This commit is contained in:
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using ZB.MOM.WW.ScadaBridge.AuditLog.Configuration;
|
using ZB.MOM.WW.ScadaBridge.AuditLog.Configuration;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||||
|
|
||||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||||
|
|
||||||
@@ -93,7 +94,10 @@ public class AuditLogPurgeActor : ReceiveActor
|
|||||||
base.PreStart();
|
base.PreStart();
|
||||||
var interval = _purgeOptions.Interval;
|
var interval = _purgeOptions.Interval;
|
||||||
_timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(
|
_timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(
|
||||||
initialDelay: interval,
|
// Short first tick (min(interval, 5min)): purges are idempotent, and using the
|
||||||
|
// full interval as the initial delay meant a daily-recycled node never purged
|
||||||
|
// (arch-review 04, S9). Fast test cadences (interval < 5min) are unchanged.
|
||||||
|
initialDelay: PurgeTimerSchedule.InitialDelay(interval),
|
||||||
interval: interval,
|
interval: interval,
|
||||||
receiver: Self,
|
receiver: Self,
|
||||||
message: PurgeTick.Instance,
|
message: PurgeTick.Instance,
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
namespace ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shared initial-delay rule for the central retention-purge singletons
|
||||||
|
/// (Audit Log partition purge, Site Call Audit terminal purge, KPI History
|
||||||
|
/// sample purge). All three run on a long (up to 24 h) interval; scheduling the
|
||||||
|
/// FIRST tick after a full interval meant a node that recycles more often than
|
||||||
|
/// the interval — e.g. a daily container restart against a 24 h cadence — would
|
||||||
|
/// never actually purge (arch-review 04, S9).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Purges are idempotent, so an early first sweep is always safe. Taking
|
||||||
|
/// <c>min(interval, <see cref="ShortFirstTick"/>)</c> guarantees the first purge
|
||||||
|
/// runs within five minutes of startup while leaving the steady-state cadence —
|
||||||
|
/// and the millisecond intervals the actor test suites use — unchanged.
|
||||||
|
/// </remarks>
|
||||||
|
public static class PurgeTimerSchedule
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Upper bound on the delay before the first purge tick. Short enough that a
|
||||||
|
/// frequently-recycled node still purges, long enough not to add startup churn.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly TimeSpan ShortFirstTick = TimeSpan.FromMinutes(5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The initial delay a purge timer should use for its first tick given its
|
||||||
|
/// steady-state <paramref name="interval"/>: the interval itself when it is
|
||||||
|
/// already shorter than <see cref="ShortFirstTick"/> (preserving fast test
|
||||||
|
/// cadences), otherwise capped at <see cref="ShortFirstTick"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static TimeSpan InitialDelay(TimeSpan interval) =>
|
||||||
|
interval < ShortFirstTick ? interval : ShortFirstTick;
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Kpi;
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Kpi;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Kpi;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Kpi;
|
||||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||||
|
|
||||||
namespace ZB.MOM.WW.ScadaBridge.KpiHistory;
|
namespace ZB.MOM.WW.ScadaBridge.KpiHistory;
|
||||||
|
|
||||||
@@ -119,12 +120,15 @@ public class KpiHistoryRecorderActor : ReceiveActor, IWithTimers
|
|||||||
initialDelay: TimeSpan.FromSeconds(5),
|
initialDelay: TimeSpan.FromSeconds(5),
|
||||||
interval: _options.SampleInterval);
|
interval: _options.SampleInterval);
|
||||||
|
|
||||||
// The purge is daily and idempotent — no initial fast tick; the first sweep
|
// The purge is idempotent, so it takes a short first tick (min(PurgeInterval, 5min)):
|
||||||
// fires after a full PurgeInterval.
|
// scheduling the first sweep a full PurgeInterval out meant a node recycled more often
|
||||||
|
// than the interval (e.g. daily restart vs. daily cadence) would never purge
|
||||||
|
// (arch-review 04, S9). Fast test cadences (interval < 5min) are unchanged.
|
||||||
Timers.StartPeriodicTimer(
|
Timers.StartPeriodicTimer(
|
||||||
PurgeTimerKey,
|
PurgeTimerKey,
|
||||||
PurgeTick.Instance,
|
PurgeTick.Instance,
|
||||||
_options.PurgeInterval);
|
initialDelay: PurgeTimerSchedule.InitialDelay(_options.PurgeInterval),
|
||||||
|
interval: _options.PurgeInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -415,7 +415,10 @@ public class SiteCallAuditActor : ReceiveActor
|
|||||||
|
|
||||||
var interval = _options.ResolvedPurgeInterval;
|
var interval = _options.ResolvedPurgeInterval;
|
||||||
_purgeTimer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(
|
_purgeTimer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(
|
||||||
initialDelay: interval,
|
// Short first tick (min(interval, 5min)): purges are idempotent, and a full-interval
|
||||||
|
// initial delay meant a daily-recycled node never purged (arch-review 04, S9). Fast
|
||||||
|
// test cadences (interval < 5min) are unchanged.
|
||||||
|
initialDelay: PurgeTimerSchedule.InitialDelay(interval),
|
||||||
interval: interval,
|
interval: interval,
|
||||||
receiver: Self,
|
receiver: Self,
|
||||||
message: PurgeTick.Instance,
|
message: PurgeTick.Instance,
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||||
|
|
||||||
|
namespace ZB.MOM.WW.ScadaBridge.Commons.Tests.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Covers the shared purge-timer initial-delay rule (arch-review 04, S9): the
|
||||||
|
/// three central purge singletons must NOT wait a full (up to 24 h) interval
|
||||||
|
/// before their first tick, or a node recycled daily would never purge. The
|
||||||
|
/// initial delay is min(interval, 5 min).
|
||||||
|
/// </summary>
|
||||||
|
public class PurgeTimerScheduleTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void InitialDelay_LongInterval_IsCappedAtShortFirstTick()
|
||||||
|
{
|
||||||
|
// A daily (24 h) cadence must still fire its first purge within 5 minutes.
|
||||||
|
Assert.Equal(PurgeTimerSchedule.ShortFirstTick, PurgeTimerSchedule.InitialDelay(TimeSpan.FromHours(24)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InitialDelay_IntervalShorterThanShortFirstTick_IsUnchanged()
|
||||||
|
{
|
||||||
|
// A millisecond test cadence must be preserved verbatim (min picks the interval),
|
||||||
|
// so the existing fast-tick actor suites keep their sub-second timing.
|
||||||
|
var fast = TimeSpan.FromMilliseconds(100);
|
||||||
|
Assert.Equal(fast, PurgeTimerSchedule.InitialDelay(fast));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InitialDelay_IntervalEqualToShortFirstTick_IsThatValue()
|
||||||
|
{
|
||||||
|
Assert.Equal(PurgeTimerSchedule.ShortFirstTick, PurgeTimerSchedule.InitialDelay(PurgeTimerSchedule.ShortFirstTick));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ShortFirstTick_IsFiveMinutes()
|
||||||
|
{
|
||||||
|
Assert.Equal(TimeSpan.FromMinutes(5), PurgeTimerSchedule.ShortFirstTick);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user