@@ -207,7 +207,7 @@ impl<'a, RT: Runtime> CronModel<'a, RT> {
207
207
WithHeapSize :: default ( )
208
208
} ;
209
209
210
- let old_crons = self . list ( ) . await ?;
210
+ let old_crons = self . list_metadata ( ) . await ?;
211
211
let mut added_crons: Vec < & CronIdentifier > = vec ! [ ] ;
212
212
let mut updated_crons: Vec < & CronIdentifier > = vec ! [ ] ;
213
213
let mut deleted_crons: Vec < & CronIdentifier > = vec ! [ ] ;
@@ -289,26 +289,50 @@ impl<'a, RT: Runtime> CronModel<'a, RT> {
289
289
. transpose ( )
290
290
}
291
291
292
- pub async fn update (
292
+ async fn update (
293
293
& mut self ,
294
- mut cron_job : CronJob ,
294
+ mut cron_job : ParsedDocument < CronJobMetadata > ,
295
295
new_cron_spec : CronSpec ,
296
296
) -> anyhow:: Result < ( ) > {
297
297
if new_cron_spec. cron_schedule != cron_job. cron_spec . cron_schedule {
298
+ // Skip updating the next run ts, if the runs are close together on the old
299
+ // schedule. This is a heuristic to avoid OCC with existing cron
300
+ // jobs running/changing state. True solution would be to move this
301
+ // logic to the async worker, but quickfix for now is to skip the
302
+ // `update_job_state`.
298
303
let now = self . runtime ( ) . generate_timestamp ( ) ?;
299
- cron_job. next_ts = compute_next_ts ( & new_cron_spec, cron_job. prev_ts , now) ?;
304
+ let next_ts = compute_next_ts ( & cron_job. cron_spec , None , now) ?;
305
+ let next_next_run = compute_next_ts ( & cron_job. cron_spec , Some ( next_ts) , next_ts) ?;
306
+ if next_next_run. secs_since_f64 ( now) > 30.0 {
307
+ // Read in next-run to the readset and update it.
308
+ let mut next_run = self
309
+ . next_run ( cron_job. id ( ) . developer_id )
310
+ . await ?
311
+ . context ( "No next run found" ) ?
312
+ . into_value ( ) ;
313
+
314
+ // Recalculate on the new schedule.
315
+ let now = self . runtime ( ) . generate_timestamp ( ) ?;
316
+ next_run. next_ts = compute_next_ts ( & new_cron_spec, next_run. prev_ts , now) ?;
317
+ self . update_job_state ( next_run) . await ?;
318
+ }
300
319
}
301
320
cron_job. cron_spec = new_cron_spec;
302
- self . update_job_state ( cron_job) . await ?;
321
+ SystemMetadataModel :: new ( self . tx , self . component . into ( ) )
322
+ . replace ( cron_job. id ( ) , cron_job. into_value ( ) . try_into ( ) ?)
323
+ . await ?;
303
324
Ok ( ( ) )
304
325
}
305
326
306
- pub async fn delete ( & mut self , cron_job : CronJob ) -> anyhow:: Result < ( ) > {
327
+ pub async fn delete (
328
+ & mut self ,
329
+ cron_job : ParsedDocument < CronJobMetadata > ,
330
+ ) -> anyhow:: Result < ( ) > {
307
331
SystemMetadataModel :: new ( self . tx , self . component . into ( ) )
308
- . delete ( cron_job. id )
332
+ . delete ( cron_job. id ( ) )
309
333
. await ?;
310
334
let next_run = self
311
- . next_run ( cron_job. id . developer_id )
335
+ . next_run ( cron_job. id ( ) . developer_id )
312
336
. await ?
313
337
. context ( "No next run found" ) ?;
314
338
SystemMetadataModel :: new ( self . tx , self . component . into ( ) )
@@ -319,28 +343,9 @@ impl<'a, RT: Runtime> CronModel<'a, RT> {
319
343
Ok ( ( ) )
320
344
}
321
345
322
- pub async fn update_job_state ( & mut self , job : CronJob ) -> anyhow:: Result < ( ) > {
323
- anyhow:: ensure!( self
324
- . tx
325
- . table_mapping( )
326
- . namespace( self . component. into( ) )
327
- . tablet_matches_name( job. id. tablet_id, & CRON_JOBS_TABLE ) ) ;
328
- let cron_job = CronJobMetadata {
329
- name : job. name ,
330
- cron_spec : job. cron_spec ,
331
- } ;
332
- SystemMetadataModel :: new ( self . tx , self . component . into ( ) )
333
- . replace ( job. id , cron_job. try_into ( ) ?)
334
- . await ?;
335
-
336
- let next_run = CronNextRun {
337
- cron_job_id : job. id . developer_id ,
338
- state : job. state ,
339
- prev_ts : job. prev_ts ,
340
- next_ts : job. next_ts ,
341
- } ;
346
+ pub async fn update_job_state ( & mut self , next_run : CronNextRun ) -> anyhow:: Result < ( ) > {
342
347
let existing_next_run = self
343
- . next_run ( job . id . developer_id )
348
+ . next_run ( next_run . cron_job_id )
344
349
. await ?
345
350
. context ( "No next run found" ) ?;
346
351
SystemMetadataModel :: new ( self . tx , self . component . into ( ) )
@@ -405,6 +410,19 @@ impl<'a, RT: Runtime> CronModel<'a, RT> {
405
410
Ok ( cron_jobs)
406
411
}
407
412
413
+ pub async fn list_metadata (
414
+ & mut self ,
415
+ ) -> anyhow:: Result < BTreeMap < CronIdentifier , ParsedDocument < CronJobMetadata > > > {
416
+ let cron_query = Query :: full_table_scan ( CRON_JOBS_TABLE . clone ( ) , Order :: Asc ) ;
417
+ let mut query_stream = ResolvedQuery :: new ( self . tx , self . component . into ( ) , cron_query) ?;
418
+ let mut cron_jobs = BTreeMap :: new ( ) ;
419
+ while let Some ( job) = query_stream. next ( self . tx , None ) . await ? {
420
+ let cron: ParsedDocument < CronJobMetadata > = job. parse ( ) ?;
421
+ cron_jobs. insert ( cron. name . clone ( ) , cron) ;
422
+ }
423
+ Ok ( cron_jobs)
424
+ }
425
+
408
426
fn runtime ( & self ) -> & RT {
409
427
self . tx . runtime ( )
410
428
}
0 commit comments