feat(kpihistory): hourly rollup recorder tick + rollup purge + options (plan #22 T4)
Add a third Akka periodic timer (kpi-rollup) to KpiHistoryRecorderActor that folds the trailing RollupLookbackHours of raw KpiSample rows into the hourly KpiRollupHourly table via the idempotent FoldHourlyRollupsAsync upsert, with the in-progress hour excluded (toHourUtc = current hour start, exclusive). A _rollupInFlight guard coalesces overlapping ticks (mirrors _sampleInFlight), the fold is best-effort (no exception escapes a tick), and a missed/failover tick self-heals via the lookback re-fold. The daily purge now runs BOTH the raw PurgeOlderThanAsync (RetentionDays) and PurgeRollupsOlderThanAsync (RollupRetentionDays), isolated so one failure never skips the other. New KpiHistoryOptions: RollupInterval (1h), RollupLookbackHours (3), RollupRetentionDays (365), RollupThresholdHours (168, consumed by Task 5). Validator adds bounds incl. the coherence rule RollupRetentionDays >= RetentionDays so long-range trends have no data hole. Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -35,4 +35,40 @@ public sealed class KpiHistoryOptions
|
||||
/// <c>[2, 5000]</c>). At least two points are required to draw a line.
|
||||
/// </summary>
|
||||
public int DefaultMaxSeriesPoints { get; set; } = 200;
|
||||
|
||||
/// <summary>
|
||||
/// How often the recorder folds the trailing raw <c>KpiSample</c> rows into the
|
||||
/// hourly <c>KpiRollupHourly</c> table (default 1 hour). Must be strictly
|
||||
/// positive. Each tick re-folds the trailing
|
||||
/// <see cref="RollupLookbackHours"/> hours via an idempotent upsert, so a tick
|
||||
/// missed during a singleton-failover handover self-heals on the next fold.
|
||||
/// </summary>
|
||||
public TimeSpan RollupInterval { get; set; } = TimeSpan.FromHours(1);
|
||||
|
||||
/// <summary>
|
||||
/// How many trailing whole hours each rollup fold re-processes (default 3,
|
||||
/// range <c>[1, 168]</c>). Folding a lookback window rather than just the last
|
||||
/// hour lets an idempotent re-fold recover the one or more complete hours a
|
||||
/// missed/failover tick skipped. Never includes the in-progress hour (the fold's
|
||||
/// upper bound is the current hour start, exclusive).
|
||||
/// </summary>
|
||||
public int RollupLookbackHours { get; set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Central retention window in days for the hourly rollup rows (default 365,
|
||||
/// range <c>[1, 3650]</c>). Rollups outlive raw samples so long-range trend
|
||||
/// charts have data after raw <c>KpiSample</c> rows are purged; the validator
|
||||
/// therefore requires <c>RollupRetentionDays >= <see cref="RetentionDays"/></c>
|
||||
/// so a window never falls into a hole where raw is purged but no rollup was
|
||||
/// written. Dropped by the rollup purge sweep.
|
||||
/// </summary>
|
||||
public int RollupRetentionDays { get; set; } = 365;
|
||||
|
||||
/// <summary>
|
||||
/// Query-routing boundary in hours (default 168 = 7 days, minimum 24). Trend
|
||||
/// queries whose window is <c><=</c> this width read raw <c>KpiSample</c> rows
|
||||
/// (preserving intra-minute detail); wider windows read the pre-aggregated
|
||||
/// hourly rollups. Consumed by the Central UI query service's range routing.
|
||||
/// </summary>
|
||||
public int RollupThresholdHours { get; set; } = 168;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user