11import {
2- claimReadyRetentionJobs ,
2+ claimReadyDailyMaintenanceJobs ,
33 createDb ,
4+ type DailyMaintenanceJobLease ,
45 type Database ,
5- deferRetentionJob ,
6- listLiveRetentionJobLeases ,
7- purgeCompletedRetentionJobs ,
8- type RetentionJobLease ,
9- registerDailyRetentionJob ,
6+ deferDailyMaintenanceJob ,
7+ listLiveDailyMaintenanceJobLeases ,
8+ purgeCompletedDailyMaintenanceJobs ,
9+ registerDailyMaintenanceJob ,
1010} from "@cheatcode/db" ;
1111import { createLogger , emitErrorEvent , safeErrorTelemetry } from "@cheatcode/observability" ;
1212import { z } from "zod" ;
13- import { assertReleaseOpen } from "./release-gate" ;
1413import {
15- activeRetentionReleaseVersion ,
16- createRetentionInstance ,
14+ activeDailyMaintenanceReleaseVersion ,
15+ createDailyMaintenanceInstance ,
16+ type DailyMaintenanceEnv ,
1717 previousUtcDay ,
18- type RetentionMaintenanceEnv ,
19- } from "./retention-maintenance " ;
18+ } from "./daily-maintenance-workflow" ;
19+ import { assertReleaseOpen } from "./release-gate " ;
2020import type { DeterministicWorkflowResult } from "./workflow-instance" ;
2121
22- const RETENTION_RECONCILIATION_LIMIT = 25 ;
23- const RETENTION_CREATION_CONCURRENCY = 5 ;
24- const RETENTION_TOMBSTONE_MS = 32 * 24 * 60 * 60 * 1000 ;
25- const CREATION_ERROR_CODE = "retention_instance_creation_failed" ;
26- const ORPHANED_COMPLETION_ERROR_CODE = "retention_instance_completed_without_job_completion" ;
22+ const MAINTENANCE_RECONCILIATION_LIMIT = 25 ;
23+ const MAINTENANCE_CREATION_CONCURRENCY = 5 ;
24+ const MAINTENANCE_TOMBSTONE_MS = 32 * 24 * 60 * 60 * 1000 ;
25+ const CREATION_ERROR_CODE = "daily_maintenance_instance_creation_failed" ;
26+ const ORPHANED_COMPLETION_ERROR_CODE =
27+ "daily_maintenance_instance_completed_without_job_completion" ;
2728const ScheduledTimeSchema = z . number ( ) . int ( ) . nonnegative ( ) . max ( 8_640_000_000_000_000 ) ;
2829
2930type ReconciliationSource = "claimed" | "live" ;
3031
3132interface ReconciliationCandidate {
32- lease : RetentionJobLease ;
33+ lease : DailyMaintenanceJobLease ;
3334 source : ReconciliationSource ;
3435}
3536
36- export interface RetentionReconciliationSummary {
37+ export interface DailyMaintenanceReconciliationSummary {
3738 claimed : number ;
3839 created : number ;
3940 deferred : number ;
@@ -44,24 +45,24 @@ export interface RetentionReconciliationSummary {
4445}
4546
4647/** Register a daily job, then use the same recovery path as the five-minute reconciler. */
47- export async function enqueueDailyRetentionMetrics (
48- env : RetentionMaintenanceEnv ,
48+ export async function enqueueDailyMaintenance (
49+ env : DailyMaintenanceEnv ,
4950 scheduledTimeInput : number ,
50- ) : Promise < RetentionReconciliationSummary > {
51+ ) : Promise < DailyMaintenanceReconciliationSummary > {
5152 assertReleaseOpen ( env ) ;
5253 const scheduledAt = new Date ( ScheduledTimeSchema . parse ( scheduledTimeInput ) ) ;
5354 await withDatabase ( env , ( db ) =>
54- registerDailyRetentionJob ( db , { day : previousUtcDay ( scheduledAt ) , scheduledAt } ) ,
55+ registerDailyMaintenanceJob ( db , { day : previousUtcDay ( scheduledAt ) , scheduledAt } ) ,
5556 ) ;
56- return reconcileDailyRetentionWorkflows ( env ) ;
57+ return reconcileDailyMaintenanceWorkflows ( env ) ;
5758}
5859
5960/** Recreate live reserved generations and lease every due/expired day in bounded batches. */
60- export async function reconcileDailyRetentionWorkflows (
61- env : RetentionMaintenanceEnv ,
62- ) : Promise < RetentionReconciliationSummary > {
61+ export async function reconcileDailyMaintenanceWorkflows (
62+ env : DailyMaintenanceEnv ,
63+ ) : Promise < DailyMaintenanceReconciliationSummary > {
6364 assertReleaseOpen ( env ) ;
64- const releaseVersionId = activeRetentionReleaseVersion ( env ) ;
65+ const releaseVersionId = activeDailyMaintenanceReleaseVersion ( env ) ;
6566 const now = new Date ( ) ;
6667 const state = await loadReconciliationState ( env , now , releaseVersionId ) ;
6768 const eligibleLive = state . live . filter ( ( lease ) => lease . releaseVersionId === releaseVersionId ) ;
@@ -79,22 +80,26 @@ export async function reconcileDailyRetentionWorkflows(
7980}
8081
8182async function loadReconciliationState (
82- env : RetentionMaintenanceEnv ,
83+ env : DailyMaintenanceEnv ,
8384 now : Date ,
8485 releaseVersionId : string ,
85- ) : Promise < { claimed : RetentionJobLease [ ] ; live : RetentionJobLease [ ] ; purged : number } > {
86+ ) : Promise < {
87+ claimed : DailyMaintenanceJobLease [ ] ;
88+ live : DailyMaintenanceJobLease [ ] ;
89+ purged : number ;
90+ } > {
8691 return withDatabase ( env , async ( db ) => {
87- const purged = await purgeCompletedRetentionJobs (
92+ const purged = await purgeCompletedDailyMaintenanceJobs (
8893 db ,
89- new Date ( now . getTime ( ) - RETENTION_TOMBSTONE_MS ) ,
94+ new Date ( now . getTime ( ) - MAINTENANCE_TOMBSTONE_MS ) ,
9095 ) ;
91- const live = await listLiveRetentionJobLeases ( db , {
92- limit : RETENTION_RECONCILIATION_LIMIT ,
96+ const live = await listLiveDailyMaintenanceJobLeases ( db , {
97+ limit : MAINTENANCE_RECONCILIATION_LIMIT ,
9398 now,
9499 } ) ;
95- const claimed = await claimReadyRetentionJobs ( db , {
100+ const claimed = await claimReadyDailyMaintenanceJobs ( db , {
96101 leaseToken : crypto . randomUUID ( ) ,
97- limit : RETENTION_RECONCILIATION_LIMIT ,
102+ limit : MAINTENANCE_RECONCILIATION_LIMIT ,
98103 now,
99104 releaseVersionId,
100105 } ) ;
@@ -103,30 +108,30 @@ async function loadReconciliationState(
103108}
104109
105110async function createReconciledInstances (
106- env : RetentionMaintenanceEnv ,
111+ env : DailyMaintenanceEnv ,
107112 candidates : ReconciliationCandidate [ ] ,
108- ) : Promise < Omit < RetentionReconciliationSummary , "claimed" | "purged" | "staleRelease" > > {
113+ ) : Promise < Omit < DailyMaintenanceReconciliationSummary , "claimed" | "purged" | "staleRelease" > > {
109114 const summary = { created : 0 , deferred : 0 , restarted : 0 , reused : 0 } ;
110- for ( let offset = 0 ; offset < candidates . length ; offset += RETENTION_CREATION_CONCURRENCY ) {
111- const batch = candidates . slice ( offset , offset + RETENTION_CREATION_CONCURRENCY ) ;
115+ for ( let offset = 0 ; offset < candidates . length ; offset += MAINTENANCE_CREATION_CONCURRENCY ) {
116+ const batch = candidates . slice ( offset , offset + MAINTENANCE_CREATION_CONCURRENCY ) ;
112117 const settled = await Promise . allSettled (
113- batch . map ( ( { lease } ) => createRetentionInstance ( env , lease ) ) ,
118+ batch . map ( ( { lease } ) => createDailyMaintenanceInstance ( env , lease ) ) ,
114119 ) ;
115120 await accountForReconciliationBatch ( env , batch , settled , summary ) ;
116121 }
117122 return summary ;
118123}
119124
120125async function accountForReconciliationBatch (
121- env : RetentionMaintenanceEnv ,
126+ env : DailyMaintenanceEnv ,
122127 candidates : ReconciliationCandidate [ ] ,
123128 settled : PromiseSettledResult < DeterministicWorkflowResult > [ ] ,
124129 summary : { created : number ; deferred : number ; restarted : number ; reused : number } ,
125130) : Promise < void > {
126131 for ( const [ index , result ] of settled . entries ( ) ) {
127132 const candidate = candidates [ index ] ;
128133 if ( ! candidate ) {
129- throw new Error ( "Retention reconciliation lost a lease identity" ) ;
134+ throw new Error ( "Daily maintenance reconciliation lost a lease identity" ) ;
130135 }
131136 if ( result . status === "rejected" ) {
132137 await accountForCreationFailure ( env , candidate , result . reason , summary ) ;
@@ -137,7 +142,7 @@ async function accountForReconciliationBatch(
137142}
138143
139144async function accountForCreationFailure (
140- env : RetentionMaintenanceEnv ,
145+ env : DailyMaintenanceEnv ,
141146 candidate : ReconciliationCandidate ,
142147 error : unknown ,
143148 summary : { deferred : number } ,
@@ -146,7 +151,7 @@ async function accountForCreationFailure(
146151 const deferred = await tryDeferLease ( env , candidate . lease , CREATION_ERROR_CODE ) ;
147152 summary . deferred += deferred ? 1 : 0 ;
148153 }
149- createLogger ( ) . error ( "retention_instance_reconciliation_failed " , {
154+ createLogger ( ) . error ( "daily_maintenance_instance_reconciliation_failed " , {
150155 continuation : candidate . lease . continuation ,
151156 day : candidate . lease . day ,
152157 source : candidate . source ,
@@ -155,13 +160,13 @@ async function accountForCreationFailure(
155160 emitErrorEvent ( env , {
156161 errorCategory : "workflow" ,
157162 errorCode : CREATION_ERROR_CODE ,
158- route : "retention -admission" ,
163+ route : "daily-maintenance -admission" ,
159164 workerName : "webhooks" ,
160165 } ) ;
161166}
162167
163168async function accountForCreationResult (
164- env : RetentionMaintenanceEnv ,
169+ env : DailyMaintenanceEnv ,
165170 candidate : ReconciliationCandidate ,
166171 result : DeterministicWorkflowResult ,
167172 summary : { created : number ; deferred : number ; restarted : number ; reused : number } ,
@@ -176,17 +181,17 @@ async function accountForCreationResult(
176181}
177182
178183async function tryDeferLease (
179- env : RetentionMaintenanceEnv ,
180- lease : RetentionJobLease ,
184+ env : DailyMaintenanceEnv ,
185+ lease : DailyMaintenanceJobLease ,
181186 errorCode : string ,
182187) : Promise < boolean > {
183188 try {
184189 const deferred = await withDatabase ( env , ( db ) =>
185- deferRetentionJob ( db , { ...lease , errorCode } ) ,
190+ deferDailyMaintenanceJob ( db , { ...lease , errorCode } ) ,
186191 ) ;
187192 return deferred !== null ;
188193 } catch ( error ) {
189- createLogger ( ) . error ( "retention_reconciliation_defer_failed " , {
194+ createLogger ( ) . error ( "daily_maintenance_reconciliation_defer_failed " , {
190195 continuation : lease . continuation ,
191196 day : lease . day ,
192197 errorCode,
@@ -197,7 +202,7 @@ async function tryDeferLease(
197202}
198203
199204async function withDatabase < T > (
200- env : RetentionMaintenanceEnv ,
205+ env : DailyMaintenanceEnv ,
201206 operation : ( db : Database ) => Promise < T > ,
202207) : Promise < T > {
203208 const { db, close } = createDb ( env . HYPERDRIVE , {
0 commit comments