feat: complete remaining Go backend modules
This commit is contained in:
69
database/migrations/0002_generation_lifecycle_fencing.sql
Normal file
69
database/migrations/0002_generation_lifecycle_fencing.sql
Normal file
@@ -0,0 +1,69 @@
|
||||
-- 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;
|
||||
Reference in New Issue
Block a user