diff --git a/sql/bgw_scheduler.sql b/sql/bgw_scheduler.sql index 0a294f103..11f164ccc 100644 --- a/sql/bgw_scheduler.sql +++ b/sql/bgw_scheduler.sql @@ -45,7 +45,9 @@ CREATE OR REPLACE FUNCTION alter_job_schedule( max_runtime INTERVAL = NULL, max_retries INTEGER = NULL, retry_period INTERVAL = NULL, - if_exists BOOL = FALSE) -RETURNS TABLE (job_id INTEGER, schedule_interval INTERVAL, max_runtime INTERVAL, max_retries INTEGER, retry_period INTERVAL) + if_exists BOOL = FALSE, + next_start TIMESTAMPTZ = NULL +) +RETURNS TABLE (job_id INTEGER, schedule_interval INTERVAL, max_runtime INTERVAL, max_retries INTEGER, retry_period INTERVAL, next_start TIMESTAMPTZ) AS '@MODULE_PATHNAME@', 'ts_alter_job_schedule' LANGUAGE C VOLATILE; diff --git a/sql/updates/latest-dev.sql b/sql/updates/latest-dev.sql index 6612bb99f..4e073252f 100644 --- a/sql/updates/latest-dev.sql +++ b/sql/updates/latest-dev.sql @@ -58,3 +58,5 @@ INSERT INTO _timescaledb_config.bgw_policy_drop_chunks SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_policy_drop_chunks', ''); DROP TABLE _timescaledb_config.bgw_policy_drop_chunks_tmp; GRANT SELECT ON _timescaledb_config.bgw_policy_drop_chunks TO PUBLIC; + +DROP FUNCTION IF EXISTS alter_job_schedule(INTEGER, INTERVAL, INTERVAL, INTEGER, INTERVAL, BOOL); diff --git a/src/bgw/job.c b/src/bgw/job.c index 275d24834..0cdd21851 100644 --- a/src/bgw/job.c +++ b/src/bgw/job.c @@ -771,7 +771,10 @@ bgw_job_tuple_update_by_id(TupleInfo *ti, void *const data) DirectFunctionCall2(timestamptz_pl_interval, TimestampTzGetDatum(stat->fd.last_finish), IntervalPGetDatum(&updated_job->fd.schedule_interval))); - ts_bgw_job_stat_update_next_start(updated_job, next_start); + /* allow DT_NOBEGIN for next_start here through allow_unset=true in the case that + * last_finish is DT_NOBEGIN, + * This means the value is counted as unset which is what we want */ + ts_bgw_job_stat_update_next_start(updated_job, next_start, true); } fd->schedule_interval = updated_job->fd.schedule_interval; } diff --git a/src/bgw/job_stat.c b/src/bgw/job_stat.c index 2c2ad133a..986df81ea 100644 --- a/src/bgw/job_stat.c +++ b/src/bgw/job_stat.c @@ -296,7 +296,8 @@ bgw_job_stat_tuple_set_next_start(TupleInfo *ti, void *const data) } static bool -bgw_job_stat_insert_mark_start_relation(Relation rel, int32 bgw_job_id) +bgw_job_stat_insert_relation(Relation rel, int32 bgw_job_id, bool mark_start, + TimestampTz next_start) { TupleDesc desc = RelationGetDescr(rel); Datum values[Natts_bgw_job_stat]; @@ -307,21 +308,35 @@ bgw_job_stat_insert_mark_start_relation(Relation rel, int32 bgw_job_id) }; values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_job_id)] = Int32GetDatum(bgw_job_id); - values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_last_start)] = - TimestampGetDatum(ts_timer_get_current_timestamp()); + if (mark_start) + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_last_start)] = + TimestampGetDatum(ts_timer_get_current_timestamp()); + else + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_last_start)] = + TimestampGetDatum(DT_NOBEGIN); values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_last_finish)] = TimestampGetDatum(DT_NOBEGIN); - values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_next_start)] = TimestampGetDatum(DT_NOBEGIN); - values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_total_runs)] = Int64GetDatum(1); + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_next_start)] = TimestampGetDatum(next_start); + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_total_runs)] = + Int64GetDatum((mark_start ? 1 : 0)); values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_total_duration)] = IntervalPGetDatum(&zero_ival); values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_total_success)] = Int64GetDatum(0); values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_total_failures)] = Int64GetDatum(0); values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_consecutive_failures)] = Int32GetDatum(0); - /* This is udone by any of the end marks */ - values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_last_run_success)] = BoolGetDatum(false); - values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_total_crashes)] = Int64GetDatum(1); - values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_consecutive_crashes)] = Int32GetDatum(1); + if (mark_start) + { + /* This is udone by any of the end marks */ + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_last_run_success)] = BoolGetDatum(false); + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_total_crashes)] = Int64GetDatum(1); + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_consecutive_crashes)] = Int32GetDatum(1); + } + else + { + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_last_run_success)] = BoolGetDatum(true); + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_total_crashes)] = Int64GetDatum(0); + values[AttrNumberGetAttrOffset(Anum_bgw_job_stat_consecutive_crashes)] = Int32GetDatum(0); + } ts_catalog_database_info_become_owner(ts_catalog_database_info_get(), &sec_ctx); ts_catalog_insert_values(rel, desc, values, nulls); @@ -330,29 +345,27 @@ bgw_job_stat_insert_mark_start_relation(Relation rel, int32 bgw_job_id) return true; } -static bool -bgw_job_stat_insert_mark_start(int32 bgw_job_id) -{ - Catalog *catalog = ts_catalog_get(); - Relation rel; - bool result; - - rel = heap_open(catalog_get_table_id(catalog, BGW_JOB_STAT), RowExclusiveLock); - result = bgw_job_stat_insert_mark_start_relation(rel, bgw_job_id); - heap_close(rel, RowExclusiveLock); - - return result; -} - void ts_bgw_job_stat_mark_start(int32 bgw_job_id) { + /* Use double-check locking */ if (!bgw_job_stat_scan_job_id(bgw_job_id, bgw_job_stat_tuple_mark_start, NULL, NULL, RowExclusiveLock)) - bgw_job_stat_insert_mark_start(bgw_job_id); + { + Relation rel = + heap_open(catalog_get_table_id(ts_catalog_get(), BGW_JOB_STAT), ShareRowExclusiveLock); + /* Recheck while having a self-exclusive lock */ + if (!bgw_job_stat_scan_job_id(bgw_job_id, + bgw_job_stat_tuple_mark_start, + NULL, + NULL, + RowExclusiveLock)) + bgw_job_stat_insert_relation(rel, bgw_job_id, true, DT_NOBEGIN); + heap_close(rel, ShareRowExclusiveLock); + } } void @@ -394,11 +407,11 @@ ts_bgw_job_stat_set_next_start(BgwJob *job, TimestampTz next_start) /* update next_start if job stat exists */ TSDLLEXPORT bool -ts_bgw_job_stat_update_next_start(BgwJob *job, TimestampTz next_start) +ts_bgw_job_stat_update_next_start(BgwJob *job, TimestampTz next_start, bool allow_unset) { bool found = false; /* Cannot use DT_NOBEGIN as that's the value used to indicate "not set" */ - if (next_start == DT_NOBEGIN) + if (!allow_unset && next_start == DT_NOBEGIN) elog(ERROR, "cannot set next start to -infinity"); found = bgw_job_stat_scan_job_id(job->fd.id, @@ -409,6 +422,33 @@ ts_bgw_job_stat_update_next_start(BgwJob *job, TimestampTz next_start) return found; } +TSDLLEXPORT void +ts_bgw_job_stat_upsert_next_start(int32 bgw_job_id, TimestampTz next_start) +{ + /* Cannot use DT_NOBEGIN as that's the value used to indicate "not set" */ + if (next_start == DT_NOBEGIN) + elog(ERROR, "cannot set next start to -infinity"); + + /* Use double-check locking */ + if (!bgw_job_stat_scan_job_id(bgw_job_id, + bgw_job_stat_tuple_set_next_start, + NULL, + &next_start, + RowExclusiveLock)) + { + Relation rel = + heap_open(catalog_get_table_id(ts_catalog_get(), BGW_JOB_STAT), ShareRowExclusiveLock); + /* Recheck while having a self-exclusive lock */ + if (!bgw_job_stat_scan_job_id(bgw_job_id, + bgw_job_stat_tuple_set_next_start, + NULL, + &next_start, + RowExclusiveLock)) + bgw_job_stat_insert_relation(rel, bgw_job_id, true, next_start); + heap_close(rel, ShareRowExclusiveLock); + } +} + bool ts_bgw_job_stat_should_execute(BgwJobStat *jobstat, BgwJob *job) { diff --git a/src/bgw/job_stat.h b/src/bgw/job_stat.h index f8593afe3..d9754895c 100644 --- a/src/bgw/job_stat.h +++ b/src/bgw/job_stat.h @@ -27,7 +27,10 @@ extern void ts_bgw_job_stat_mark_end(BgwJob *job, JobResult result); extern bool ts_bgw_job_stat_end_was_marked(BgwJobStat *jobstat); extern TSDLLEXPORT void ts_bgw_job_stat_set_next_start(BgwJob *job, TimestampTz next_start); -extern TSDLLEXPORT bool ts_bgw_job_stat_update_next_start(BgwJob *job, TimestampTz next_start); +extern TSDLLEXPORT bool ts_bgw_job_stat_update_next_start(BgwJob *job, TimestampTz next_start, + bool allow_unset); + +extern TSDLLEXPORT void ts_bgw_job_stat_upsert_next_start(int32 bgw_job_id, TimestampTz next_start); extern bool ts_bgw_job_stat_should_execute(BgwJobStat *jobstat, BgwJob *job); diff --git a/tsl/src/bgw_policy/job.c b/tsl/src/bgw_policy/job.c index 49118a24e..155bf3f0a 100644 --- a/tsl/src/bgw_policy/job.c +++ b/tsl/src/bgw_policy/job.c @@ -36,7 +36,7 @@ #include "drop_chunks_api.h" #include "interval.h" -#define ALTER_JOB_SCHEDULE_NUM_COLS 5 +#define ALTER_JOB_SCHEDULE_NUM_COLS 6 #define REORDER_SKIP_RECENT_DIM_SLICES_N 3 static void @@ -289,17 +289,16 @@ Datum bgw_policy_alter_job_schedule(PG_FUNCTION_ARGS) { BgwJob *job; + BgwJobStat *stat; TupleDesc tupdesc; Datum values[ALTER_JOB_SCHEDULE_NUM_COLS]; bool nulls[ALTER_JOB_SCHEDULE_NUM_COLS] = { false }; HeapTuple tuple; + TimestampTz next_start; int job_id = PG_GETARG_INT32(0); bool if_exists = PG_GETARG_BOOL(5); - license_enforce_enterprise_enabled(); - license_print_expiration_warning_if_needed(); - /* First get the job */ job = ts_bgw_job_find(job_id, CurrentMemoryContext, false); @@ -318,6 +317,10 @@ bgw_policy_alter_job_schedule(PG_FUNCTION_ARGS) errmsg("cannot alter policy schedule, policy #%d not found", job_id))); } + if (bgw_policy_job_requires_enterprise_license(job)) + license_enforce_enterprise_enabled(); + license_print_expiration_warning_if_needed(); + ts_bgw_job_permission_check(job); if (!PG_ARGISNULL(1)) @@ -331,6 +334,9 @@ bgw_policy_alter_job_schedule(PG_FUNCTION_ARGS) ts_bgw_job_update_by_id(job_id, job); + if (!PG_ARGISNULL(6)) + ts_bgw_job_stat_upsert_next_start(job_id, PG_GETARG_TIMESTAMPTZ(6)); + /* Now look up the job and return it */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) ereport(ERROR, @@ -338,12 +344,19 @@ bgw_policy_alter_job_schedule(PG_FUNCTION_ARGS) errmsg("function returning record called in context " "that cannot accept type record"))); + stat = ts_bgw_job_stat_find(job_id); + if (stat != NULL) + next_start = stat->fd.next_start; + else + next_start = DT_NOBEGIN; + tupdesc = BlessTupleDesc(tupdesc); values[0] = Int32GetDatum(job->fd.id); values[1] = IntervalPGetDatum(&job->fd.schedule_interval); values[2] = IntervalPGetDatum(&job->fd.max_runtime); values[3] = Int32GetDatum(job->fd.max_retries); values[4] = IntervalPGetDatum(&job->fd.retry_period); + values[5] = TimestampTzGetDatum(next_start); tuple = heap_form_tuple(tupdesc, values, nulls); return HeapTupleGetDatum(tuple); diff --git a/tsl/test/expected/bgw_reorder_drop_chunks.out b/tsl/test/expected/bgw_reorder_drop_chunks.out index 6bac0cc3d..1c3d814bd 100644 --- a/tsl/test/expected/bgw_reorder_drop_chunks.out +++ b/tsl/test/expected/bgw_reorder_drop_chunks.out @@ -440,9 +440,9 @@ SELECT json_object_field(get_telemetry_report(always_display_report := true)::js (1 row) SELECT alter_job_schedule(:drop_chunks_job_id, schedule_interval => INTERVAL '1 second'); - alter_job_schedule ---------------------------------------------- - (1001,"@ 1 sec","@ 5 mins",-1,"@ 12 hours") + alter_job_schedule +------------------------------------------------------- + (1001,"@ 1 sec","@ 5 mins",-1,"@ 12 hours",-infinity) (1 row) select * from _timescaledb_config.bgw_policy_drop_chunks where job_id=:drop_chunks_job_id; diff --git a/tsl/test/expected/continuous_aggs_bgw.out b/tsl/test/expected/continuous_aggs_bgw.out index c8f9d0876..6365ee868 100644 --- a/tsl/test/expected/continuous_aggs_bgw.out +++ b/tsl/test/expected/continuous_aggs_bgw.out @@ -285,6 +285,28 @@ WHERE job_id=:job_id; 1000 | @ 1 min | 3 (1 row) +--change next run to be after 30s instead +SELECT (next_start - '30s'::interval) AS "NEW_NEXT_START" +FROM _timescaledb_internal.bgw_job_stat +WHERE job_id=:job_id \gset +SELECT alter_job_schedule(:job_id, next_start => :'NEW_NEXT_START'); + alter_job_schedule +-------------------------------------------------------------------- + (1000,"@ 1 min","@ 0",-1,"@ 1 min","Sat Jan 01 04:02:30 2000 PST") +(1 row) + +SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint * 1000000)+(extract(epoch from interval '2 minute 30 seconds')::bigint * 1000000), true); + ts_bgw_params_reset_time +-------------------------- + +(1 row) + +SELECT wait_for_job_to_run(:job_id, 4); + wait_for_job_to_run +--------------------- + t +(1 row) + --advance clock to quit scheduler SELECT ts_bgw_params_reset_time(extract(epoch from interval '25 hour')::bigint * 1000000, true); ts_bgw_params_reset_time diff --git a/tsl/test/expected/tsl_tables.out b/tsl/test/expected/tsl_tables.out index 3aaf200b7..f04b8f6c6 100644 --- a/tsl/test/expected/tsl_tables.out +++ b/tsl/test/expected/tsl_tables.out @@ -776,73 +776,152 @@ select add_reorder_policy('test_table', 'test_table_time_idx') as job_id \gset -- No change select * from alter_job_schedule(:job_id); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+-------------+-------------+-------------- - 1014 | @ 84 hours | @ 0 | -1 | @ 1 day + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+-------------+-------------+--------------+------------ + 1014 | @ 84 hours | @ 0 | -1 | @ 1 day | -infinity (1 row) -- Changes expected select * from alter_job_schedule(:job_id, INTERVAL '3 years', INTERVAL '5 min', 5, INTERVAL '123 sec'); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+-------------+-------------+----------------- - 1014 | @ 3 years | @ 5 mins | 5 | @ 2 mins 3 secs + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+-------------+-------------+-----------------+------------ + 1014 | @ 3 years | @ 5 mins | 5 | @ 2 mins 3 secs | -infinity (1 row) select * from alter_job_schedule(:job_id, INTERVAL '123 years'); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+-------------+-------------+----------------- - 1014 | @ 123 years | @ 5 mins | 5 | @ 2 mins 3 secs + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+-------------+-------------+-----------------+------------ + 1014 | @ 123 years | @ 5 mins | 5 | @ 2 mins 3 secs | -infinity (1 row) select * from alter_job_schedule(:job_id, retry_period => INTERVAL '33 hours'); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+-------------+-------------+-------------- - 1014 | @ 123 years | @ 5 mins | 5 | @ 33 hours + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+-------------+-------------+--------------+------------ + 1014 | @ 123 years | @ 5 mins | 5 | @ 33 hours | -infinity (1 row) select * from alter_job_schedule(:job_id, max_runtime => INTERVAL '456 sec'); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+------------------+-------------+-------------- - 1014 | @ 123 years | @ 7 mins 36 secs | 5 | @ 33 hours + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 123 years | @ 7 mins 36 secs | 5 | @ 33 hours | -infinity (1 row) select * from alter_job_schedule(:job_id, max_retries => 0); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+------------------+-------------+-------------- - 1014 | @ 123 years | @ 7 mins 36 secs | 0 | @ 33 hours + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 123 years | @ 7 mins 36 secs | 0 | @ 33 hours | -infinity (1 row) select * from alter_job_schedule(:job_id, max_retries => -1); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+------------------+-------------+-------------- - 1014 | @ 123 years | @ 7 mins 36 secs | -1 | @ 33 hours + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 123 years | @ 7 mins 36 secs | -1 | @ 33 hours | -infinity (1 row) select * from alter_job_schedule(:job_id, max_retries => 20); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+------------------+-------------+-------------- - 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | -infinity (1 row) -- No change select * from alter_job_schedule(:job_id, max_runtime => NULL); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+------------------+-------------+-------------- - 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | -infinity (1 row) select * from alter_job_schedule(:job_id, max_retries => NULL); - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+------------------+-------------+-------------- - 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | -infinity (1 row) +--change schedule_interval when bgw_job_stat does not exist +select * from alter_job_schedule(:job_id, schedule_interval=>'1 min'); + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 1 min | @ 7 mins 36 secs | 20 | @ 33 hours | -infinity +(1 row) + +select count(*) = 0 from _timescaledb_internal.bgw_job_stat where job_id = :job_id; + ?column? +---------- + t +(1 row) + +--set next_start when bgw_job_stat does not exist +select * from alter_job_schedule(:job_id, next_start=>'2001-01-01 01:01:01'); + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------------------------ + 1014 | @ 1 min | @ 7 mins 36 secs | 20 | @ 33 hours | Mon Jan 01 01:01:01 2001 PST +(1 row) + +--change schedule_interval when no last_finish set +select * from alter_job_schedule(:job_id, schedule_interval=>'10 min'); + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 10 mins | @ 7 mins 36 secs | 20 | @ 33 hours | -infinity +(1 row) + +--next_start overrides any schedule_interval changes +select * from alter_job_schedule(:job_id, schedule_interval=>'20 min', next_start=>'2002-01-01 01:01:01'); + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------------------------ + 1014 | @ 20 mins | @ 7 mins 36 secs | 20 | @ 33 hours | Tue Jan 01 01:01:01 2002 PST +(1 row) + +--set the last_finish manually +\c :TEST_DBNAME :ROLE_SUPERUSER +UPDATE _timescaledb_internal.bgw_job_stat SET last_finish = '2003-01-01:01:01:01' WHERE job_id = :job_id; +\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER +--not changing the interval doesn't change the next_start +select * from alter_job_schedule(:job_id, schedule_interval=>'20 min'); +WARNING: Timescale License expired + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------------------------ + 1014 | @ 20 mins | @ 7 mins 36 secs | 20 | @ 33 hours | Tue Jan 01 01:01:01 2002 PST +(1 row) + +--changing the interval changes next_start +select * from alter_job_schedule(:job_id, schedule_interval=>'30 min'); + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------------------------ + 1014 | @ 30 mins | @ 7 mins 36 secs | 20 | @ 33 hours | Wed Jan 01 01:31:01 2003 PST +(1 row) + +--explicit next start overrides. +select * from alter_job_schedule(:job_id, schedule_interval=>'40 min', next_start=>'2004-01-01 01:01:01'); + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------------------------ + 1014 | @ 40 mins | @ 7 mins 36 secs | 20 | @ 33 hours | Thu Jan 01 01:01:01 2004 PST +(1 row) + +--test pausing +select * from alter_job_schedule(:job_id, next_start=>'infinity'); + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+------------------+-------------+--------------+------------ + 1014 | @ 40 mins | @ 7 mins 36 secs | 20 | @ 33 hours | infinity +(1 row) + +--test that you can use now() to unpause +select next_start = now() from alter_job_schedule(:job_id, next_start=>now()); + ?column? +---------- + t +(1 row) + +\set ON_ERROR_STOP 0 +-- negative infinity disallowed (used as special value) +select * from alter_job_schedule(:job_id, next_start=>'-infinity'); +ERROR: cannot set next start to -infinity +\set ON_ERROR_STOP 1 -- Check if_exists boolean works correctly select * from alter_job_schedule(1234, if_exists => TRUE); NOTICE: cannot alter policy schedule, policy #1234 not found, skipping - job_id | schedule_interval | max_runtime | max_retries | retry_period ---------+-------------------+-------------+-------------+-------------- - | | | | + job_id | schedule_interval | max_runtime | max_retries | retry_period | next_start +--------+-------------------+-------------+-------------+--------------+------------ + | | | | | (1 row) \set ON_ERROR_STOP 0 @@ -868,7 +947,7 @@ ERROR: cannot execute an enterprise function with an invalid enterprise license select remove_drop_chunks_policy('test_table'); ERROR: cannot execute an enterprise function with an invalid enterprise license select alter_job_schedule(12345); -ERROR: cannot execute an enterprise function with an invalid enterprise license +ERROR: cannot alter policy schedule, policy #12345 not found \set ON_ERROR_STOP 1 \c :TEST_DBNAME :ROLE_SUPERUSER select add_reorder_policy('test_table', 'test_table_time_idx') as reorder_job_id \gset diff --git a/tsl/test/sql/continuous_aggs_bgw.sql b/tsl/test/sql/continuous_aggs_bgw.sql index 99617e198..4e487a5b8 100644 --- a/tsl/test/sql/continuous_aggs_bgw.sql +++ b/tsl/test/sql/continuous_aggs_bgw.sql @@ -196,6 +196,15 @@ SELECT job_id, next_start-last_finish as until_next, total_runs FROM _timescaledb_internal.bgw_job_stat WHERE job_id=:job_id; +--change next run to be after 30s instead +SELECT (next_start - '30s'::interval) AS "NEW_NEXT_START" +FROM _timescaledb_internal.bgw_job_stat +WHERE job_id=:job_id \gset +SELECT alter_job_schedule(:job_id, next_start => :'NEW_NEXT_START'); + +SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint * 1000000)+(extract(epoch from interval '2 minute 30 seconds')::bigint * 1000000), true); +SELECT wait_for_job_to_run(:job_id, 4); + --advance clock to quit scheduler SELECT ts_bgw_params_reset_time(extract(epoch from interval '25 hour')::bigint * 1000000, true); select ts_bgw_db_scheduler_test_wait_for_scheduler_finish(); diff --git a/tsl/test/sql/tsl_tables.sql b/tsl/test/sql/tsl_tables.sql index e9bfb8cd9..592a3f273 100644 --- a/tsl/test/sql/tsl_tables.sql +++ b/tsl/test/sql/tsl_tables.sql @@ -309,6 +309,37 @@ select * from alter_job_schedule(:job_id, max_retries => 20); select * from alter_job_schedule(:job_id, max_runtime => NULL); select * from alter_job_schedule(:job_id, max_retries => NULL); +--change schedule_interval when bgw_job_stat does not exist +select * from alter_job_schedule(:job_id, schedule_interval=>'1 min'); +select count(*) = 0 from _timescaledb_internal.bgw_job_stat where job_id = :job_id; +--set next_start when bgw_job_stat does not exist +select * from alter_job_schedule(:job_id, next_start=>'2001-01-01 01:01:01'); +--change schedule_interval when no last_finish set +select * from alter_job_schedule(:job_id, schedule_interval=>'10 min'); +--next_start overrides any schedule_interval changes +select * from alter_job_schedule(:job_id, schedule_interval=>'20 min', next_start=>'2002-01-01 01:01:01'); + +--set the last_finish manually +\c :TEST_DBNAME :ROLE_SUPERUSER +UPDATE _timescaledb_internal.bgw_job_stat SET last_finish = '2003-01-01:01:01:01' WHERE job_id = :job_id; +\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER + +--not changing the interval doesn't change the next_start +select * from alter_job_schedule(:job_id, schedule_interval=>'20 min'); +--changing the interval changes next_start +select * from alter_job_schedule(:job_id, schedule_interval=>'30 min'); +--explicit next start overrides. +select * from alter_job_schedule(:job_id, schedule_interval=>'40 min', next_start=>'2004-01-01 01:01:01'); +--test pausing +select * from alter_job_schedule(:job_id, next_start=>'infinity'); +--test that you can use now() to unpause +select next_start = now() from alter_job_schedule(:job_id, next_start=>now()); + +\set ON_ERROR_STOP 0 +-- negative infinity disallowed (used as special value) +select * from alter_job_schedule(:job_id, next_start=>'-infinity'); +\set ON_ERROR_STOP 1 + -- Check if_exists boolean works correctly select * from alter_job_schedule(1234, if_exists => TRUE);