Skip to content

Commit 1983cfa

Browse files
authored
refactor: rename heartbeat_timeout_seconds to debounce with interval type (#528)
# Replace `heartbeat_timeout_seconds` with `debounce` interval in worker management This PR changes the worker function debounce mechanism from using an integer seconds field to a proper PostgreSQL interval type. The change: - Renames `heartbeat_timeout_seconds` to `debounce` in the `worker_functions` table - Uses the native interval type instead of converting integers to intervals - Adds a check constraint ensuring debounce is at least 1 second - Updates all references in functions and comments to use the new field name - Updates TypeScript type definitions to reflect the schema change This improves type safety and makes the code more semantically clear by using the proper PostgreSQL data type for time intervals.
1 parent 7889c2a commit 1983cfa

File tree

7 files changed

+29
-27
lines changed

7 files changed

+29
-27
lines changed

pkgs/core/schemas/0056_table_worker_functions.sql

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
create table if not exists pgflow.worker_functions (
55
function_name text not null primary key,
66
enabled boolean not null default true,
7-
heartbeat_timeout_seconds integer not null default 6,
7+
debounce interval not null default '6 seconds'
8+
check (debounce >= '1 second'),
89
last_invoked_at timestamptz,
910
created_at timestamptz not null default now(),
1011
updated_at timestamptz not null default now()
@@ -19,8 +20,8 @@ comment on column pgflow.worker_functions.function_name is
1920
comment on column pgflow.worker_functions.enabled is
2021
'Whether ensure_workers() should ping this function';
2122

22-
comment on column pgflow.worker_functions.heartbeat_timeout_seconds is
23-
'How long before considering a worker dead (no heartbeat)';
23+
comment on column pgflow.worker_functions.debounce is
24+
'Minimum interval between invocation attempts for this function';
2425

2526
comment on column pgflow.worker_functions.last_invoked_at is
2627
'When ensure_workers() last pinged this function (used for debouncing)';

pkgs/core/schemas/0059_function_ensure_workers.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ as $$
2828

2929
-- Find functions that pass the debounce check
3030
debounce_passed as (
31-
select wf.function_name, wf.heartbeat_timeout_seconds
31+
select wf.function_name, wf.debounce
3232
from pgflow.worker_functions as wf
3333
where wf.enabled = true
3434
and (
3535
wf.last_invoked_at is null
36-
or wf.last_invoked_at < now() - (wf.heartbeat_timeout_seconds || ' seconds')::interval
36+
or wf.last_invoked_at < now() - wf.debounce
3737
)
3838
),
3939

@@ -44,7 +44,7 @@ as $$
4444
inner join debounce_passed as dp on w.function_name = dp.function_name
4545
where w.stopped_at is null
4646
and w.deprecated_at is null
47-
and w.last_heartbeat_at > now() - (dp.heartbeat_timeout_seconds || ' seconds')::interval
47+
and w.last_heartbeat_at > now() - dp.debounce
4848
),
4949

5050
-- Determine which functions should be invoked
@@ -104,7 +104,7 @@ comment on function pgflow.ensure_workers() is
104104
'Ensures worker functions are running by pinging them via HTTP when needed.
105105
In local mode: pings ALL enabled functions (ignores debounce AND alive workers check).
106106
In production mode: only pings functions that pass debounce AND have no alive workers.
107-
Debounce: skips functions pinged within their heartbeat_timeout_seconds window (production only).
107+
Debounce: skips functions pinged within their debounce interval (production only).
108108
Credentials: Uses Vault secrets (supabase_service_role_key, supabase_project_id) or local fallbacks.
109109
URL is built from project_id: https://{project_id}.supabase.co/functions/v1
110110
Returns request_id from pg_net for each HTTP request made.';

pkgs/core/src/database-types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,25 +317,25 @@ export type Database = {
317317
worker_functions: {
318318
Row: {
319319
created_at: string
320+
debounce: unknown
320321
enabled: boolean
321322
function_name: string
322-
heartbeat_timeout_seconds: number
323323
last_invoked_at: string | null
324324
updated_at: string
325325
}
326326
Insert: {
327327
created_at?: string
328+
debounce?: unknown
328329
enabled?: boolean
329330
function_name: string
330-
heartbeat_timeout_seconds?: number
331331
last_invoked_at?: string | null
332332
updated_at?: string
333333
}
334334
Update: {
335335
created_at?: string
336+
debounce?: unknown
336337
enabled?: boolean
337338
function_name?: string
338-
heartbeat_timeout_seconds?: number
339339
last_invoked_at?: string | null
340340
updated_at?: string
341341
}

pkgs/core/supabase/migrations/20251209074533_pgflow_worker_management.sql

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@ ALTER TABLE "pgflow"."workers" ADD COLUMN "stopped_at" timestamptz NULL;
88
CREATE TABLE "pgflow"."worker_functions" (
99
"function_name" text NOT NULL,
1010
"enabled" boolean NOT NULL DEFAULT true,
11-
"heartbeat_timeout_seconds" integer NOT NULL DEFAULT 6,
11+
"debounce" interval NOT NULL DEFAULT '00:00:06'::interval,
1212
"last_invoked_at" timestamptz NULL,
1313
"created_at" timestamptz NOT NULL DEFAULT now(),
1414
"updated_at" timestamptz NOT NULL DEFAULT now(),
15-
PRIMARY KEY ("function_name")
15+
PRIMARY KEY ("function_name"),
16+
CONSTRAINT "worker_functions_debounce_check" CHECK (debounce >= '00:00:01'::interval)
1617
);
1718
-- Set comment to table: "worker_functions"
1819
COMMENT ON TABLE "pgflow"."worker_functions" IS 'Registry of edge functions that run pgflow workers, used by ensure_workers() cron';
1920
-- Set comment to column: "function_name" on table: "worker_functions"
2021
COMMENT ON COLUMN "pgflow"."worker_functions"."function_name" IS 'Name of the Supabase Edge Function';
2122
-- Set comment to column: "enabled" on table: "worker_functions"
2223
COMMENT ON COLUMN "pgflow"."worker_functions"."enabled" IS 'Whether ensure_workers() should ping this function';
23-
-- Set comment to column: "heartbeat_timeout_seconds" on table: "worker_functions"
24-
COMMENT ON COLUMN "pgflow"."worker_functions"."heartbeat_timeout_seconds" IS 'How long before considering a worker dead (no heartbeat)';
24+
-- Set comment to column: "debounce" on table: "worker_functions"
25+
COMMENT ON COLUMN "pgflow"."worker_functions"."debounce" IS 'Minimum interval between invocation attempts for this function';
2526
-- Set comment to column: "last_invoked_at" on table: "worker_functions"
2627
COMMENT ON COLUMN "pgflow"."worker_functions"."last_invoked_at" IS 'When ensure_workers() last pinged this function (used for debouncing)';
2728
-- Create "cleanup_ensure_workers_logs" function
@@ -122,12 +123,12 @@ with
122123

123124
-- Find functions that pass the debounce check
124125
debounce_passed as (
125-
select wf.function_name, wf.heartbeat_timeout_seconds
126+
select wf.function_name, wf.debounce
126127
from pgflow.worker_functions as wf
127128
where wf.enabled = true
128129
and (
129130
wf.last_invoked_at is null
130-
or wf.last_invoked_at < now() - (wf.heartbeat_timeout_seconds || ' seconds')::interval
131+
or wf.last_invoked_at < now() - wf.debounce
131132
)
132133
),
133134

@@ -138,7 +139,7 @@ with
138139
inner join debounce_passed as dp on w.function_name = dp.function_name
139140
where w.stopped_at is null
140141
and w.deprecated_at is null
141-
and w.last_heartbeat_at > now() - (dp.heartbeat_timeout_seconds || ' seconds')::interval
142+
and w.last_heartbeat_at > now() - dp.debounce
142143
),
143144

144145
-- Determine which functions should be invoked
@@ -197,7 +198,7 @@ $$;
197198
COMMENT ON FUNCTION "pgflow"."ensure_workers" IS 'Ensures worker functions are running by pinging them via HTTP when needed.
198199
In local mode: pings ALL enabled functions (ignores debounce AND alive workers check).
199200
In production mode: only pings functions that pass debounce AND have no alive workers.
200-
Debounce: skips functions pinged within their heartbeat_timeout_seconds window (production only).
201+
Debounce: skips functions pinged within their debounce interval (production only).
201202
Credentials: Uses Vault secrets (supabase_service_role_key, supabase_project_id) or local fallbacks.
202203
URL is built from project_id: https://{project_id}.supabase.co/functions/v1
203204
Returns request_id from pg_net for each HTTP request made.';

pkgs/core/supabase/migrations/atlas.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
h1:+LFk0wyD7sBjvwZW93t7THECms67qWV2Vm0E5GHTCxQ=
1+
h1:s6n1PLk8xdvwsEbFcEbLUMaRj0zxGmAfBcaVkk3GKak=
22
20250429164909_pgflow_initial.sql h1:I3n/tQIg5Q5nLg7RDoU3BzqHvFVjmumQxVNbXTPG15s=
33
20250517072017_pgflow_fix_poll_for_tasks_to_use_separate_statement_for_polling.sql h1:wTuXuwMxVniCr3ONCpodpVWJcHktoQZIbqMZ3sUHKMY=
44
20250609105135_pgflow_add_start_tasks_and_started_status.sql h1:ggGanW4Wyt8Kv6TWjnZ00/qVb3sm+/eFVDjGfT8qyPg=
@@ -12,4 +12,4 @@ h1:+LFk0wyD7sBjvwZW93t7THECms67qWV2Vm0E5GHTCxQ=
1212
20251103222045_pgflow_fix_broadcast_order_and_timestamp_handling.sql h1:K/XnZpOmxfelsaNoJbR5HxhBrs/oW4aYja222h5cps4=
1313
20251104080523_pgflow_upgrade_pgmq_1_5_1.sql h1:Fw7zpMWnjhAHQ0qBJAprAvGl7dJMd8ExNHg8aKvkzTg=
1414
20251130000000_pgflow_auto_compilation.sql h1:qs+3qq1Vsyo0ETzbxDnmkVtSUa6XHkd/K9wF/3W46jM=
15-
20251209074533_pgflow_worker_management.sql h1:Yi+WZEgL+AcK0eAfKJD8v/uPBVBM8WcL+ZOL4ilkYz8=
15+
20251209074533_pgflow_worker_management.sql h1:ozFkYM1EvEH7dGvW+1pWgpzbwdWlwuQoddqLomL1GXw=

pkgs/core/supabase/tests/ensure_workers/debounce.test.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,16 @@ select is(
5353
'Function with NULL last_invoked_at IS returned'
5454
);
5555

56-
-- TEST: Debounce uses heartbeat_timeout_seconds from function config
56+
-- TEST: Debounce uses debounce interval from function config
5757
update pgflow.worker_functions
58-
set heartbeat_timeout_seconds = 3,
58+
set debounce = '3 seconds'::interval,
5959
last_invoked_at = now() - interval '4 seconds'
6060
where function_name = 'my-function';
6161

6262
select is(
6363
(select count(*) from pgflow.ensure_workers()),
6464
1::bigint,
65-
'Debounce respects function-specific heartbeat_timeout_seconds'
65+
'Debounce respects function-specific debounce interval'
6666
);
6767

6868
select finish();

pkgs/core/supabase/tests/track_worker_function/basic.test.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ select is(
1717
'New worker function has enabled=true by default'
1818
);
1919

20-
-- TEST: New worker function has default heartbeat_timeout_seconds
20+
-- TEST: New worker function has default debounce interval
2121
select is(
22-
(select heartbeat_timeout_seconds from pgflow.worker_functions where function_name = 'my-edge-function'),
23-
6,
24-
'New worker function has heartbeat_timeout_seconds=6 by default'
22+
(select debounce from pgflow.worker_functions where function_name = 'my-edge-function'),
23+
'6 seconds'::interval,
24+
'New worker function has debounce=6 seconds by default'
2525
);
2626

2727
-- TEST: Does NOT set last_invoked_at on insert (debounce handled by ensure_workers)

0 commit comments

Comments
 (0)