@@ -46,12 +46,18 @@ explicit `ValueError` rather than guessing your intended zone.
4646
4747### Server-side vs client-side scheduling
4848
49+ The fetch query compares ` next_attempt_at ` with the database's ` now() ` , so
50+ the database clock decides when a row is due.
51+
4952For ` publish ` with ` activate_in ` , ` next_attempt_at ` is computed server-side
50- via ` now() + make_interval(secs => :s) ` to stay clock-skew-safe. With
51- ` activate_at ` , you supply an absolute instant, so it is stored verbatim and
52- compared against the * worker's* clock at fetch time. Only ` activate_in ` is
53- skew-safe; ` activate_at ` is as accurate as your producers' and workers'
54- clocks agree. For ` publish_batch ` , ` activate_in ` is also client-side
53+ via ` now() + make_interval(secs => :s) ` to stay clock-skew-safe. In
54+ PostgreSQL, ` now() ` is the start time of the current transaction, so the
55+ deadline counts from when the caller's transaction began, not from the
56+ ` publish ` call. Keep publishing transactions short if that gap matters.
57+ With ` activate_at ` , you supply an absolute instant, so it is stored verbatim
58+ and compared against the database clock. Only ` activate_in ` is skew-safe;
59+ ` activate_at ` is as accurate as your producers' and the database's clocks
60+ agree. For ` publish_batch ` , ` activate_in ` is also client-side
5561(` datetime.now(UTC) + activate_in ` ) because executemany doesn't compose
5662cleanly with column-level SQL expressions, and the few-ms drift is harmless
5763for user-supplied scheduling.
0 commit comments