70 lines
2.4 KiB
PL/PgSQL
70 lines
2.4 KiB
PL/PgSQL
-- Recoverable generation finalization and charge-before-dispatch fencing.
|
|
-- Additive so the previous release remains rollback-compatible.
|
|
|
|
alter table public.generation_jobs
|
|
add column if not exists dispatch_ready_at timestamptz;
|
|
|
|
alter table public.generation_jobs
|
|
add column if not exists finalized_at timestamptz;
|
|
|
|
alter table public.generation_jobs
|
|
add column if not exists provider_dispatch_started_at timestamptz;
|
|
|
|
-- Existing rows were created before dispatch fencing and are therefore ready.
|
|
update public.generation_jobs
|
|
set dispatch_ready_at = coalesce(dispatch_ready_at, created_at, now())
|
|
where dispatch_ready_at is null;
|
|
|
|
-- Rows already terminal before this migration have completed their legacy
|
|
-- finalization path and must not be replayed into billing/webhook side effects.
|
|
update public.generation_jobs
|
|
set finalized_at = coalesce(finalized_at, completed_at, updated_at, now())
|
|
where finalized_at is null
|
|
and status in ('succeeded', 'failed', 'expired', 'cancelled');
|
|
|
|
create index if not exists generation_jobs_finalize_claim_idx
|
|
on public.generation_jobs(finalized_at, scheduled_at, locked_at, priority desc);
|
|
|
|
create or replace function public.claim_generation_jobs(
|
|
p_worker_id text,
|
|
p_limit integer default 1,
|
|
p_lock_timeout_seconds integer default 300
|
|
)
|
|
returns setof public.generation_jobs
|
|
language plpgsql
|
|
set search_path = public, pg_temp
|
|
as $$
|
|
declare
|
|
v_now timestamptz := now();
|
|
begin
|
|
return query
|
|
with candidates as (
|
|
select id
|
|
from public.generation_jobs
|
|
where dispatch_ready_at is not null
|
|
and finalized_at is null
|
|
and status in ('queued', 'running', 'succeeded', 'failed', 'expired', 'cancelled')
|
|
and coalesce(scheduled_at, created_at) <= v_now
|
|
and (
|
|
locked_at is null
|
|
or locked_at < v_now - make_interval(secs => p_lock_timeout_seconds)
|
|
)
|
|
order by coalesce(priority, 0) desc, coalesce(scheduled_at, created_at) asc, created_at asc
|
|
limit greatest(1, least(coalesce(p_limit, 1), 20))
|
|
for update skip locked
|
|
),
|
|
updated as (
|
|
update public.generation_jobs
|
|
set locked_at = v_now,
|
|
locked_by = p_worker_id,
|
|
started_at = coalesce(public.generation_jobs.started_at, v_now),
|
|
updated_at = v_now
|
|
where id in (select id from candidates)
|
|
returning public.generation_jobs.*
|
|
)
|
|
select * from updated;
|
|
end;
|
|
$$;
|
|
|
|
revoke all on function public.claim_generation_jobs(text, integer, integer) from public;
|