create-protobase 0.23.13 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ask-preset.js +25 -0
- package/dist/create-project.js +7 -6
- package/dist/index.js +19 -9
- package/dist/next-steps.js +8 -2
- package/dist/presets.js +17 -0
- package/dist/templates/erp/.build-test-abf2a11f/protobase.config.ts +6 -0
- package/dist/templates/erp/.build-test-abf2a11f/resources.ts +2 -0
- package/dist/templates/erp/.build-test-b878b26f/config/index.ts +2 -0
- package/dist/templates/erp/.build-test-b878b26f/config/things/data.ts +2 -0
- package/dist/templates/erp/.build-test-b878b26f/config/things/ui.ts +2 -0
- package/dist/templates/erp/.env.example +14 -0
- package/dist/templates/erp/AGENTS.md +24 -0
- package/dist/templates/erp/auth/auth.ts +68 -0
- package/dist/templates/erp/components/orders-per-day.tsx +21 -0
- package/dist/templates/erp/config/bottom-bar.ts +9 -0
- package/dist/templates/erp/config/categories/data.ts +20 -0
- package/dist/templates/erp/config/categories/ui.ts +11 -0
- package/dist/templates/erp/config/companies/data.ts +23 -0
- package/dist/templates/erp/config/companies/ui.ts +22 -0
- package/dist/templates/erp/config/company_tags/data.ts +16 -0
- package/dist/templates/erp/config/company_tags/ui.ts +7 -0
- package/dist/templates/erp/config/countries/data.ts +13 -0
- package/dist/templates/erp/config/countries/ui.ts +6 -0
- package/dist/templates/erp/config/currencies/data.ts +14 -0
- package/dist/templates/erp/config/currencies/ui.ts +6 -0
- package/dist/templates/erp/config/emp_master/data.ts +19 -0
- package/dist/templates/erp/config/emp_master/ui.ts +7 -0
- package/dist/templates/erp/config/index.ts +42 -0
- package/dist/templates/erp/config/invoice_lines/data.ts +23 -0
- package/dist/templates/erp/config/invoice_lines/ui.ts +7 -0
- package/dist/templates/erp/config/invoices/data.ts +26 -0
- package/dist/templates/erp/config/invoices/ui.ts +38 -0
- package/dist/templates/erp/config/jobs.test.ts +23 -0
- package/dist/templates/erp/config/jobs.ts +29 -0
- package/dist/templates/erp/config/locations/data.ts +18 -0
- package/dist/templates/erp/config/locations/ui.ts +6 -0
- package/dist/templates/erp/config/order_lines/data.ts +20 -0
- package/dist/templates/erp/config/order_lines/ui.ts +7 -0
- package/dist/templates/erp/config/orders/data.ts +27 -0
- package/dist/templates/erp/config/orders/ui.ts +41 -0
- package/dist/templates/erp/config/organizations/data.ts +17 -0
- package/dist/templates/erp/config/organizations/ui.ts +6 -0
- package/dist/templates/erp/config/overview/page.tsx +80 -0
- package/dist/templates/erp/config/pages.test.ts +14 -0
- package/dist/templates/erp/config/people/data.ts +21 -0
- package/dist/templates/erp/config/people/ui.ts +6 -0
- package/dist/templates/erp/config/products/data.ts +25 -0
- package/dist/templates/erp/config/products/ui.ts +6 -0
- package/dist/templates/erp/config/roles.test.ts +56 -0
- package/dist/templates/erp/config/roles.ts +54 -0
- package/dist/templates/erp/config/shell.test.ts +24 -0
- package/dist/templates/erp/config/stock_levels/data.ts +16 -0
- package/dist/templates/erp/config/stock_levels/ui.ts +6 -0
- package/dist/templates/erp/config/stock_moves/data.ts +22 -0
- package/dist/templates/erp/config/stock_moves/ui.ts +25 -0
- package/dist/templates/erp/config/tags/data.ts +17 -0
- package/dist/templates/erp/config/tags/ui.ts +6 -0
- package/dist/templates/erp/config/user-menu.ts +9 -0
- package/dist/templates/erp/config/users/data.ts +19 -0
- package/dist/templates/erp/config/users/ui.ts +9 -0
- package/dist/templates/erp/config/warehouses/data.ts +19 -0
- package/dist/templates/erp/config/warehouses/ui.ts +6 -0
- package/dist/templates/erp/data.ts +3 -0
- package/dist/templates/erp/db/connection.ts +7 -0
- package/dist/templates/erp/db/drop.ts +9 -0
- package/dist/templates/erp/db/kysely.ts +13 -0
- package/dist/templates/erp/db/migrations/001_core.sql +48 -0
- package/dist/templates/erp/db/migrations/002_crm.sql +59 -0
- package/dist/templates/erp/db/migrations/003_catalog.sql +38 -0
- package/dist/templates/erp/db/migrations/004_sales.sql +86 -0
- package/dist/templates/erp/db/migrations/005_inventory.sql +65 -0
- package/dist/templates/erp/db/migrations/006_hr_legacy.sql +16 -0
- package/dist/templates/erp/db/migrations/007_filter_indexes.sql +4 -0
- package/dist/templates/erp/db/migrations/008_calculated_totals.sql +98 -0
- package/dist/templates/erp/db/migrations/011_auth_organizations.sql +22 -0
- package/dist/templates/erp/db/migrations/012_jobs.sql +531 -0
- package/dist/templates/erp/db/migrations/013_jobs.sql +98 -0
- package/dist/templates/erp/db/migrations/016_jobs.sql +99 -0
- package/dist/templates/erp/db/migrations/017_filter_indexes.sql +5 -0
- package/dist/templates/erp/db/migrations/018_sort_indexes.sql +4 -0
- package/dist/templates/erp/db/migrations/019_auth_users.sql +18 -0
- package/dist/templates/erp/functions/unpaid-orders.ts +13 -0
- package/dist/templates/erp/package.json +38 -0
- package/dist/templates/erp/protobase.config.ts +22 -0
- package/dist/templates/erp/protobase.ui.tsx +14 -0
- package/dist/templates/erp/seed/auth-organizations.ts +11 -0
- package/dist/templates/erp/seed/base-organization.test.ts +17 -0
- package/dist/templates/erp/seed/base-organization.ts +10 -0
- package/dist/templates/erp/seed/base.ts +22 -0
- package/dist/templates/erp/seed/builders/categories.ts +18 -0
- package/dist/templates/erp/seed/builders/company.ts +61 -0
- package/dist/templates/erp/seed/builders/invoice.ts +31 -0
- package/dist/templates/erp/seed/builders/order.ts +66 -0
- package/dist/templates/erp/seed/builders/product.ts +46 -0
- package/dist/templates/erp/seed/calendar.ts +60 -0
- package/dist/templates/erp/seed/cli.ts +54 -0
- package/dist/templates/erp/seed/copy.ts +22 -0
- package/dist/templates/erp/seed/data/catalogTree.ts +72 -0
- package/dist/templates/erp/seed/data/layout.ts +19 -0
- package/dist/templates/erp/seed/data/names.ts +44 -0
- package/dist/templates/erp/seed/data/reference.ts +53 -0
- package/dist/templates/erp/seed/format.ts +24 -0
- package/dist/templates/erp/seed/maintenance.ts +63 -0
- package/dist/templates/erp/seed/rng.ts +36 -0
- package/dist/templates/erp/seed/scales.ts +9 -0
- package/dist/templates/erp/seed/steps/catalog.ts +34 -0
- package/dist/templates/erp/seed/steps/crm.ts +62 -0
- package/dist/templates/erp/seed/steps/hr.ts +26 -0
- package/dist/templates/erp/seed/steps/ledger.ts +88 -0
- package/dist/templates/erp/seed/steps/reference.ts +24 -0
- package/dist/templates/erp/seed/steps/sales.ts +58 -0
- package/dist/templates/erp/seed/steps/warehouses.ts +42 -0
- package/dist/templates/erp/seed/world.ts +86 -0
- package/dist/templates/erp/vitest.config.ts +4 -0
- package/dist/templates/real-estate/.env.example +9 -0
- package/dist/templates/real-estate/AGENTS.md +24 -0
- package/dist/templates/real-estate/_.gitignore +3 -0
- package/dist/templates/real-estate/_bunfig.toml +4 -0
- package/dist/templates/real-estate/auth/auth.ts +36 -0
- package/dist/templates/real-estate/config/amenities/data.ts +12 -0
- package/dist/templates/real-estate/config/amenities/ui.ts +7 -0
- package/dist/templates/real-estate/config/arrears/data.ts +19 -0
- package/dist/templates/real-estate/config/arrears/ui.ts +21 -0
- package/dist/templates/real-estate/config/deposits/data.ts +21 -0
- package/dist/templates/real-estate/config/deposits/ui.ts +17 -0
- package/dist/templates/real-estate/config/index.ts +41 -0
- package/dist/templates/real-estate/config/inspections/data.ts +22 -0
- package/dist/templates/real-estate/config/inspections/ui.ts +15 -0
- package/dist/templates/real-estate/config/lease_tenants/data.ts +16 -0
- package/dist/templates/real-estate/config/lease_tenants/ui.ts +8 -0
- package/dist/templates/real-estate/config/leases/data.ts +29 -0
- package/dist/templates/real-estate/config/leases/ui.ts +33 -0
- package/dist/templates/real-estate/config/organizations/data.ts +17 -0
- package/dist/templates/real-estate/config/organizations/ui.ts +7 -0
- package/dist/templates/real-estate/config/overview/page.tsx +57 -0
- package/dist/templates/real-estate/config/owners/data.ts +22 -0
- package/dist/templates/real-estate/config/owners/ui.ts +17 -0
- package/dist/templates/real-estate/config/pages.test.ts +49 -0
- package/dist/templates/real-estate/config/payments/data.ts +20 -0
- package/dist/templates/real-estate/config/payments/ui.ts +14 -0
- package/dist/templates/real-estate/config/properties/data.ts +26 -0
- package/dist/templates/real-estate/config/properties/ui.ts +21 -0
- package/dist/templates/real-estate/config/rent_charges/data.ts +22 -0
- package/dist/templates/real-estate/config/rent_charges/ui.ts +27 -0
- package/dist/templates/real-estate/config/roles.test.ts +49 -0
- package/dist/templates/real-estate/config/roles.ts +45 -0
- package/dist/templates/real-estate/config/shell.test.ts +18 -0
- package/dist/templates/real-estate/config/tenants/data.ts +23 -0
- package/dist/templates/real-estate/config/tenants/ui.ts +27 -0
- package/dist/templates/real-estate/config/tickets/data.ts +33 -0
- package/dist/templates/real-estate/config/tickets/ui.ts +58 -0
- package/dist/templates/real-estate/config/unit_amenities/data.ts +15 -0
- package/dist/templates/real-estate/config/unit_amenities/ui.ts +8 -0
- package/dist/templates/real-estate/config/units/data.ts +24 -0
- package/dist/templates/real-estate/config/units/ui.ts +41 -0
- package/dist/templates/real-estate/config/user-menu.ts +9 -0
- package/dist/templates/real-estate/config/users/data.ts +19 -0
- package/dist/templates/real-estate/config/users/ui.ts +11 -0
- package/dist/templates/real-estate/config/valuations/data.ts +19 -0
- package/dist/templates/real-estate/config/valuations/ui.ts +13 -0
- package/dist/templates/real-estate/config/vendors/data.ts +21 -0
- package/dist/templates/real-estate/config/vendors/ui.ts +18 -0
- package/dist/templates/real-estate/config/work_orders/data.ts +22 -0
- package/dist/templates/real-estate/config/work_orders/ui.ts +18 -0
- package/dist/templates/real-estate/db/connection.ts +7 -0
- package/dist/templates/real-estate/db/drop.ts +9 -0
- package/dist/templates/real-estate/db/kysely.ts +13 -0
- package/dist/templates/real-estate/db/migrate.ts +30 -0
- package/dist/templates/real-estate/db/migrations/001_core.sql +33 -0
- package/dist/templates/real-estate/db/migrations/002_portfolio.sql +97 -0
- package/dist/templates/real-estate/db/migrations/003_leasing.sql +78 -0
- package/dist/templates/real-estate/db/migrations/004_billing.sql +88 -0
- package/dist/templates/real-estate/db/migrations/005_maintenance.sql +105 -0
- package/dist/templates/real-estate/db/migrations/006_outstanding.sql +14 -0
- package/dist/templates/real-estate/db/migrations/007_auth.sql +36 -0
- package/dist/templates/real-estate/db/migrations/008_auth_snake_case.sql +109 -0
- package/dist/templates/real-estate/db/migrations/009_auth.sql +6 -0
- package/dist/templates/real-estate/db/migrations/010_auth.sql +4 -0
- package/dist/templates/real-estate/db/migrations/011_filter_indexes.sql +8 -0
- package/dist/templates/real-estate/db/migrations/012_sort_indexes.sql +2 -0
- package/dist/templates/real-estate/db/migrations/013_auth_users.sql +17 -0
- package/dist/templates/real-estate/node-types.d.ts +1 -0
- package/dist/templates/real-estate/package.json +38 -0
- package/dist/templates/real-estate/protobase.config.ts +5 -0
- package/dist/templates/real-estate/protobase.ui.tsx +16 -0
- package/dist/templates/real-estate/seed/base-organization.test.ts +17 -0
- package/dist/templates/real-estate/seed/base-organization.ts +10 -0
- package/dist/templates/real-estate/seed/base.ts +18 -0
- package/dist/templates/real-estate/seed/builders/allocation.ts +47 -0
- package/dist/templates/real-estate/seed/builders/ledger.ts +52 -0
- package/dist/templates/real-estate/seed/builders/property.ts +98 -0
- package/dist/templates/real-estate/seed/builders/tenancy.ts +99 -0
- package/dist/templates/real-estate/seed/builders/ticket.ts +115 -0
- package/dist/templates/real-estate/seed/calendar.ts +77 -0
- package/dist/templates/real-estate/seed/cli.ts +50 -0
- package/dist/templates/real-estate/seed/copy.ts +22 -0
- package/dist/templates/real-estate/seed/data/names.ts +31 -0
- package/dist/templates/real-estate/seed/data/reference.ts +63 -0
- package/dist/templates/real-estate/seed/format.ts +24 -0
- package/dist/templates/real-estate/seed/iban.ts +15 -0
- package/dist/templates/real-estate/seed/maintenance.ts +63 -0
- package/dist/templates/real-estate/seed/rng.ts +36 -0
- package/dist/templates/real-estate/seed/scales.ts +9 -0
- package/dist/templates/real-estate/seed/steps/billing.ts +44 -0
- package/dist/templates/real-estate/seed/steps/leasing.ts +60 -0
- package/dist/templates/real-estate/seed/steps/maintenance.ts +77 -0
- package/dist/templates/real-estate/seed/steps/portfolio.ts +97 -0
- package/dist/templates/real-estate/seed/steps/reference.ts +21 -0
- package/dist/templates/real-estate/seed/world.ts +92 -0
- package/dist/templates/real-estate/tsconfig.json +16 -0
- package/dist/templates/real-estate/vitest.config.ts +4 -0
- package/dist/templates/scratch/_.gitignore +3 -0
- package/dist/templates/scratch/_bunfig.toml +4 -0
- package/dist/templates/scratch/db/migrate.ts +30 -0
- package/dist/templates/scratch/db/migrations/001_auth.sql +36 -0
- package/dist/templates/scratch/db/migrations/002_auth_snake_case.sql +109 -0
- package/dist/templates/scratch/db/migrations/003_auth.sql +6 -0
- package/dist/templates/scratch/db/migrations/004_auth.sql +4 -0
- package/dist/templates/scratch/node-types.d.ts +1 -0
- package/dist/{template → templates/scratch}/package.json +5 -5
- package/dist/templates/scratch/tsconfig.json +16 -0
- package/package.json +2 -2
- /package/dist/{template → templates/erp}/_.gitignore +0 -0
- /package/dist/{template → templates/erp}/_bunfig.toml +0 -0
- /package/dist/{template → templates/erp}/db/migrate.ts +0 -0
- /package/dist/{template/db/migrations/001_auth.sql → templates/erp/db/migrations/009_auth.sql} +0 -0
- /package/dist/{template/db/migrations/002_auth_snake_case.sql → templates/erp/db/migrations/010_auth_snake_case.sql} +0 -0
- /package/dist/{template/db/migrations/003_auth.sql → templates/erp/db/migrations/014_auth.sql} +0 -0
- /package/dist/{template/db/migrations/004_auth.sql → templates/erp/db/migrations/015_auth.sql} +0 -0
- /package/dist/{template → templates/erp}/node-types.d.ts +0 -0
- /package/dist/{template → templates/erp}/tsconfig.json +0 -0
- /package/dist/{template → templates/scratch}/.env.example +0 -0
- /package/dist/{template → templates/scratch}/AGENTS.md +0 -0
- /package/dist/{template → templates/scratch}/auth/auth.ts +0 -0
- /package/dist/{template → templates/scratch}/config/index.ts +0 -0
- /package/dist/{template → templates/scratch}/config/user-menu.ts +0 -0
- /package/dist/{template → templates/scratch}/db/connection.ts +0 -0
- /package/dist/{template → templates/scratch}/db/migrations/.gitkeep +0 -0
- /package/dist/{template → templates/scratch}/protobase.config.ts +0 -0
- /package/dist/{template → templates/scratch}/vitest.config.ts +0 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
-- The jobs schema this version of Protobase needs, written by `protobase migration`.
|
|
2
|
+
-- Applied with the project's other migrations; edit or remove statements the project handles differently.
|
|
3
|
+
|
|
4
|
+
SELECT pg_advisory_xact_lock(('x' || encode(sha224((current_database() || '.pgboss.jobs')::bytea), 'hex'))::bit(64)::bigint);
|
|
5
|
+
CREATE SCHEMA IF NOT EXISTS jobs;
|
|
6
|
+
|
|
7
|
+
CREATE TYPE jobs.job_state AS ENUM (
|
|
8
|
+
'created',
|
|
9
|
+
'retry',
|
|
10
|
+
'active',
|
|
11
|
+
'completed',
|
|
12
|
+
'cancelled',
|
|
13
|
+
'failed'
|
|
14
|
+
)
|
|
15
|
+
;
|
|
16
|
+
|
|
17
|
+
CREATE FUNCTION jobs.job_now()
|
|
18
|
+
RETURNS timestamp with time zone AS
|
|
19
|
+
$$
|
|
20
|
+
SELECT pg_catalog.now();
|
|
21
|
+
$$
|
|
22
|
+
LANGUAGE sql STABLE;
|
|
23
|
+
;
|
|
24
|
+
|
|
25
|
+
CREATE TABLE jobs.version (
|
|
26
|
+
version int primary key,
|
|
27
|
+
cron_on timestamp with time zone,
|
|
28
|
+
bam_on timestamp with time zone,
|
|
29
|
+
flow_on timestamp with time zone,
|
|
30
|
+
reindex_on timestamp with time zone,
|
|
31
|
+
monitor_backoff_on timestamp with time zone
|
|
32
|
+
)
|
|
33
|
+
;
|
|
34
|
+
|
|
35
|
+
CREATE TABLE jobs.queue (
|
|
36
|
+
name text NOT NULL,
|
|
37
|
+
policy text NOT NULL,
|
|
38
|
+
retry_limit int NOT NULL,
|
|
39
|
+
retry_delay int NOT NULL,
|
|
40
|
+
retry_backoff bool NOT NULL,
|
|
41
|
+
retry_delay_max int,
|
|
42
|
+
expire_seconds int NOT NULL,
|
|
43
|
+
retention_seconds int NOT NULL,
|
|
44
|
+
deletion_seconds int NOT NULL,
|
|
45
|
+
dead_letter text REFERENCES jobs.queue (name) CHECK (dead_letter IS DISTINCT FROM name),
|
|
46
|
+
partition bool NOT NULL,
|
|
47
|
+
table_name text NOT NULL,
|
|
48
|
+
deferred_count int NOT NULL default 0,
|
|
49
|
+
blocked_count int NOT NULL default 0,
|
|
50
|
+
queued_count int NOT NULL default 0,
|
|
51
|
+
ready_count int NOT NULL default 0,
|
|
52
|
+
warning_queued int NOT NULL default 0,
|
|
53
|
+
active_count int NOT NULL default 0,
|
|
54
|
+
failed_count int NOT NULL default 0,
|
|
55
|
+
total_count int NOT NULL default 0,
|
|
56
|
+
created_delta int NOT NULL default 0,
|
|
57
|
+
completed_delta int NOT NULL default 0,
|
|
58
|
+
failed_delta int NOT NULL default 0,
|
|
59
|
+
delta_on timestamp with time zone,
|
|
60
|
+
delta_seconds int,
|
|
61
|
+
wait_bins int[],
|
|
62
|
+
run_bins int[],
|
|
63
|
+
ready_oldest_seconds int,
|
|
64
|
+
ready_history int[] NOT NULL default '{}',
|
|
65
|
+
heartbeat_seconds int,
|
|
66
|
+
notify bool NOT NULL DEFAULT false,
|
|
67
|
+
singletons_active text[],
|
|
68
|
+
monitor_claim_on timestamp with time zone,
|
|
69
|
+
monitor_on timestamp with time zone,
|
|
70
|
+
maintain_on timestamp with time zone,
|
|
71
|
+
created_on timestamp with time zone not null default now(),
|
|
72
|
+
updated_on timestamp with time zone not null default now(),
|
|
73
|
+
PRIMARY KEY (name)
|
|
74
|
+
)
|
|
75
|
+
;
|
|
76
|
+
|
|
77
|
+
CREATE TABLE jobs.schedule (
|
|
78
|
+
name text REFERENCES jobs.queue ON DELETE CASCADE,
|
|
79
|
+
key text not null DEFAULT '',
|
|
80
|
+
kind text not null DEFAULT 'cron' CHECK (kind IN ('cron', 'rrule')),
|
|
81
|
+
cron text not null,
|
|
82
|
+
timezone text DEFAULT 'UTC',
|
|
83
|
+
data jsonb,
|
|
84
|
+
options jsonb,
|
|
85
|
+
created_on timestamp with time zone not null default now(),
|
|
86
|
+
updated_on timestamp with time zone not null default now(),
|
|
87
|
+
last_job_id uuid,
|
|
88
|
+
PRIMARY KEY (name, key)
|
|
89
|
+
)
|
|
90
|
+
;
|
|
91
|
+
|
|
92
|
+
CREATE TABLE jobs.subscription (
|
|
93
|
+
event text not null,
|
|
94
|
+
name text not null REFERENCES jobs.queue ON DELETE CASCADE,
|
|
95
|
+
created_on timestamp with time zone not null default now(),
|
|
96
|
+
updated_on timestamp with time zone not null default now(),
|
|
97
|
+
PRIMARY KEY(event, name)
|
|
98
|
+
)
|
|
99
|
+
;
|
|
100
|
+
|
|
101
|
+
CREATE TABLE jobs.bam (
|
|
102
|
+
id uuid PRIMARY KEY default gen_random_uuid(),
|
|
103
|
+
name text NOT NULL,
|
|
104
|
+
version int NOT NULL,
|
|
105
|
+
status text NOT NULL DEFAULT 'pending',
|
|
106
|
+
queue text,
|
|
107
|
+
table_name text NOT NULL,
|
|
108
|
+
command text NOT NULL,
|
|
109
|
+
error text,
|
|
110
|
+
created_on timestamp with time zone NOT NULL DEFAULT clock_timestamp(),
|
|
111
|
+
started_on timestamp with time zone,
|
|
112
|
+
completed_on timestamp with time zone
|
|
113
|
+
)
|
|
114
|
+
;
|
|
115
|
+
|
|
116
|
+
CREATE FUNCTION jobs.job_table_format(command text, table_name text)
|
|
117
|
+
RETURNS text AS
|
|
118
|
+
$$
|
|
119
|
+
SELECT format(
|
|
120
|
+
regexp_replace(
|
|
121
|
+
regexp_replace(command, '\.job\y', '.%1$I', 'g'),
|
|
122
|
+
'\yjob_i(\d+)', '%1$s_i\1', 'g'
|
|
123
|
+
),
|
|
124
|
+
table_name
|
|
125
|
+
);
|
|
126
|
+
$$
|
|
127
|
+
LANGUAGE sql IMMUTABLE;
|
|
128
|
+
;
|
|
129
|
+
|
|
130
|
+
CREATE FUNCTION jobs.job_table_run(command text, tbl_name text DEFAULT NULL, queue_name text DEFAULT NULL)
|
|
131
|
+
RETURNS VOID AS
|
|
132
|
+
$$
|
|
133
|
+
DECLARE
|
|
134
|
+
tbl RECORD;
|
|
135
|
+
BEGIN
|
|
136
|
+
IF queue_name IS NOT NULL THEN
|
|
137
|
+
SELECT table_name INTO tbl_name FROM jobs.queue WHERE name = queue_name;
|
|
138
|
+
END IF;
|
|
139
|
+
|
|
140
|
+
IF tbl_name IS NOT NULL THEN
|
|
141
|
+
EXECUTE jobs.job_table_format(command, tbl_name);
|
|
142
|
+
RETURN;
|
|
143
|
+
END IF;
|
|
144
|
+
|
|
145
|
+
EXECUTE jobs.job_table_format(command, 'job_common');
|
|
146
|
+
|
|
147
|
+
FOR tbl IN SELECT table_name FROM jobs.queue WHERE partition = true
|
|
148
|
+
LOOP
|
|
149
|
+
EXECUTE jobs.job_table_format(command, tbl.table_name);
|
|
150
|
+
END LOOP;
|
|
151
|
+
END;
|
|
152
|
+
$$
|
|
153
|
+
LANGUAGE plpgsql;
|
|
154
|
+
;
|
|
155
|
+
|
|
156
|
+
CREATE FUNCTION jobs.job_table_run_async(command_name text, version int, command text, tbl_name text DEFAULT NULL, queue_name text DEFAULT NULL)
|
|
157
|
+
RETURNS VOID AS
|
|
158
|
+
$$
|
|
159
|
+
BEGIN
|
|
160
|
+
IF queue_name IS NOT NULL THEN
|
|
161
|
+
SELECT table_name INTO tbl_name FROM jobs.queue WHERE name = queue_name;
|
|
162
|
+
END IF;
|
|
163
|
+
|
|
164
|
+
IF tbl_name IS NOT NULL THEN
|
|
165
|
+
INSERT INTO jobs.bam (name, version, status, queue, table_name, command)
|
|
166
|
+
VALUES (
|
|
167
|
+
command_name,
|
|
168
|
+
version,
|
|
169
|
+
'pending',
|
|
170
|
+
queue_name,
|
|
171
|
+
tbl_name,
|
|
172
|
+
jobs.job_table_format(command, tbl_name)
|
|
173
|
+
);
|
|
174
|
+
RETURN;
|
|
175
|
+
END IF;
|
|
176
|
+
|
|
177
|
+
INSERT INTO jobs.bam (name, version, status, queue, table_name, command)
|
|
178
|
+
SELECT
|
|
179
|
+
command_name,
|
|
180
|
+
version,
|
|
181
|
+
'pending',
|
|
182
|
+
NULL,
|
|
183
|
+
'job_common',
|
|
184
|
+
jobs.job_table_format(command, 'job_common')
|
|
185
|
+
UNION ALL
|
|
186
|
+
SELECT
|
|
187
|
+
command_name,
|
|
188
|
+
version,
|
|
189
|
+
'pending',
|
|
190
|
+
queue.name,
|
|
191
|
+
queue.table_name,
|
|
192
|
+
jobs.job_table_format(command, queue.table_name)
|
|
193
|
+
FROM jobs.queue
|
|
194
|
+
WHERE partition = true;
|
|
195
|
+
END;
|
|
196
|
+
$$
|
|
197
|
+
LANGUAGE plpgsql;
|
|
198
|
+
;
|
|
199
|
+
|
|
200
|
+
CREATE TABLE jobs.job (
|
|
201
|
+
id uuid not null default gen_random_uuid(),
|
|
202
|
+
name text not null,
|
|
203
|
+
priority integer not null default(0),
|
|
204
|
+
data jsonb,
|
|
205
|
+
state jobs.job_state not null default 'created',
|
|
206
|
+
retry_limit integer not null default 2,
|
|
207
|
+
retry_count integer not null default 0,
|
|
208
|
+
retry_delay integer not null default 0,
|
|
209
|
+
retry_backoff boolean not null default false,
|
|
210
|
+
retry_delay_max integer,
|
|
211
|
+
expire_seconds int not null default 900,
|
|
212
|
+
deletion_seconds int not null default 604800,
|
|
213
|
+
singleton_key text,
|
|
214
|
+
singleton_on timestamp without time zone,
|
|
215
|
+
group_id text,
|
|
216
|
+
group_tier text,
|
|
217
|
+
start_after timestamp with time zone not null default now(),
|
|
218
|
+
created_on timestamp with time zone not null default now(),
|
|
219
|
+
started_on timestamp with time zone,
|
|
220
|
+
completed_on timestamp with time zone,
|
|
221
|
+
keep_until timestamp with time zone NOT NULL default now() + interval '1209600',
|
|
222
|
+
output jsonb,
|
|
223
|
+
dead_letter text,
|
|
224
|
+
policy text,
|
|
225
|
+
heartbeat_on timestamp with time zone,
|
|
226
|
+
heartbeat_seconds int,
|
|
227
|
+
blocked boolean not null default false,
|
|
228
|
+
blocking boolean not null default false,
|
|
229
|
+
pending_dependencies int not null default 0,
|
|
230
|
+
source_name text,
|
|
231
|
+
source_id uuid,
|
|
232
|
+
source_created_on timestamp with time zone,
|
|
233
|
+
source_retry_count int,
|
|
234
|
+
source_output jsonb,
|
|
235
|
+
source_root_id uuid,
|
|
236
|
+
trace_context jsonb,
|
|
237
|
+
upsert_by_key bool
|
|
238
|
+
) PARTITION BY LIST (name)
|
|
239
|
+
;
|
|
240
|
+
ALTER TABLE jobs.job ADD PRIMARY KEY (name, id);
|
|
241
|
+
|
|
242
|
+
CREATE TABLE jobs.job_common (LIKE jobs.job INCLUDING GENERATED INCLUDING DEFAULTS);
|
|
243
|
+
|
|
244
|
+
SELECT jobs.job_table_run($cmd$ALTER TABLE jobs.job ADD PRIMARY KEY (name, id)$cmd$, 'job_common');
|
|
245
|
+
SELECT jobs.job_table_run($cmd$ALTER TABLE jobs.job ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES jobs.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, 'job_common');
|
|
246
|
+
SELECT jobs.job_table_run($cmd$ALTER TABLE jobs.job ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES jobs.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, 'job_common');
|
|
247
|
+
SELECT jobs.job_table_run($cmd$CREATE UNIQUE INDEX job_i1 ON jobs.job (name, COALESCE(singleton_key, '')) WHERE state = 'created' AND policy = 'short'$cmd$, 'job_common');
|
|
248
|
+
SELECT jobs.job_table_run($cmd$CREATE UNIQUE INDEX job_i2 ON jobs.job (name, COALESCE(singleton_key, '')) WHERE state = 'active' AND policy = 'singleton'$cmd$, 'job_common');
|
|
249
|
+
SELECT jobs.job_table_run($cmd$CREATE UNIQUE INDEX job_i3 ON jobs.job (name, state, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'stately'$cmd$, 'job_common');
|
|
250
|
+
SELECT jobs.job_table_run($cmd$CREATE UNIQUE INDEX job_i6 ON jobs.job (name, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'exclusive'$cmd$, 'job_common');
|
|
251
|
+
SELECT jobs.job_table_run($cmd$CREATE UNIQUE INDEX job_i8 ON jobs.job (name, singleton_key) WHERE state IN ('active', 'retry', 'failed') AND policy = 'key_strict_fifo'$cmd$, 'job_common');
|
|
252
|
+
SELECT jobs.job_table_run($cmd$CREATE INDEX job_i10 ON jobs.job (name, singleton_key, state DESC, created_on, id) INCLUDE (start_after) WHERE state < 'active' AND NOT blocked AND policy = 'key_strict_fifo'$cmd$, 'job_common');
|
|
253
|
+
SELECT jobs.job_table_run($cmd$ALTER TABLE jobs.job ADD CONSTRAINT job_key_strict_fifo_singleton_key_check CHECK (NOT (policy = 'key_strict_fifo' AND singleton_key IS NULL))$cmd$, 'job_common');
|
|
254
|
+
SELECT jobs.job_table_run($cmd$CREATE UNIQUE INDEX job_i4 ON jobs.job (name, singleton_on, COALESCE(singleton_key, '')) WHERE state <> 'cancelled' AND singleton_on IS NOT NULL$cmd$, 'job_common');
|
|
255
|
+
SELECT jobs.job_table_run($cmd$CREATE INDEX job_i11 ON jobs.job (name, priority DESC, created_on, start_after) WHERE state < 'active' AND NOT blocked$cmd$, 'job_common');
|
|
256
|
+
SELECT jobs.job_table_run($cmd$CREATE INDEX job_i7 ON jobs.job (name, group_id) WHERE state = 'active' AND group_id IS NOT NULL$cmd$, 'job_common');
|
|
257
|
+
SELECT jobs.job_table_run($cmd$CREATE INDEX job_i9 ON jobs.job (name, id) WHERE blocking AND state = 'completed'$cmd$, 'job_common');
|
|
258
|
+
SELECT jobs.job_table_run($cmd$CREATE INDEX job_i12 ON jobs.job (source_root_id) WHERE source_root_id IS NOT NULL$cmd$, 'job_common');
|
|
259
|
+
SELECT jobs.job_table_run($cmd$CREATE UNIQUE INDEX job_i13 ON jobs.job (name, singleton_key) WHERE state = 'created' AND upsert_by_key$cmd$, 'job_common');
|
|
260
|
+
|
|
261
|
+
ALTER TABLE jobs.job ATTACH PARTITION jobs.job_common DEFAULT;
|
|
262
|
+
;
|
|
263
|
+
|
|
264
|
+
CREATE TABLE jobs.warning (
|
|
265
|
+
id uuid PRIMARY KEY default gen_random_uuid(),
|
|
266
|
+
type text NOT NULL,
|
|
267
|
+
message text NOT NULL,
|
|
268
|
+
data jsonb,
|
|
269
|
+
created_on timestamp with time zone NOT NULL DEFAULT now()
|
|
270
|
+
)
|
|
271
|
+
;
|
|
272
|
+
CREATE INDEX warning_i1 ON jobs.warning (created_on DESC);
|
|
273
|
+
|
|
274
|
+
CREATE TABLE jobs.queue_stats (
|
|
275
|
+
id uuid NOT NULL DEFAULT gen_random_uuid(),
|
|
276
|
+
name text NOT NULL,
|
|
277
|
+
deferred_count int NOT NULL DEFAULT 0,
|
|
278
|
+
queued_count int NOT NULL DEFAULT 0,
|
|
279
|
+
ready_count int NOT NULL DEFAULT 0,
|
|
280
|
+
active_count int NOT NULL DEFAULT 0,
|
|
281
|
+
failed_count int NOT NULL DEFAULT 0,
|
|
282
|
+
total_count int NOT NULL DEFAULT 0,
|
|
283
|
+
created_delta int,
|
|
284
|
+
completed_delta int,
|
|
285
|
+
failed_delta int,
|
|
286
|
+
delta_seconds int,
|
|
287
|
+
delta_on timestamptz,
|
|
288
|
+
wait_bins int[],
|
|
289
|
+
run_bins int[],
|
|
290
|
+
ready_oldest_seconds int,
|
|
291
|
+
captured_on timestamptz NOT NULL DEFAULT now(),
|
|
292
|
+
PRIMARY KEY (id, captured_on)
|
|
293
|
+
) PARTITION BY RANGE (captured_on)
|
|
294
|
+
;
|
|
295
|
+
CREATE INDEX queue_stats_i1 ON jobs.queue_stats (name, captured_on DESC) INCLUDE (deferred_count, queued_count, ready_count, active_count, failed_count, total_count);
|
|
296
|
+
|
|
297
|
+
DO $$
|
|
298
|
+
DECLARE
|
|
299
|
+
d date;
|
|
300
|
+
i int;
|
|
301
|
+
part_name text;
|
|
302
|
+
BEGIN
|
|
303
|
+
FOR i IN 0..1 LOOP
|
|
304
|
+
d := (jobs.job_now() AT TIME ZONE 'UTC')::date + i;
|
|
305
|
+
part_name := 'queue_stats_' || to_char(d, 'YYYYMMDD');
|
|
306
|
+
IF NOT EXISTS (
|
|
307
|
+
SELECT 1 FROM pg_class c
|
|
308
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
309
|
+
WHERE n.nspname = 'jobs' AND c.relname = part_name
|
|
310
|
+
) THEN
|
|
311
|
+
EXECUTE format(
|
|
312
|
+
'CREATE TABLE jobs.%I PARTITION OF jobs.queue_stats FOR VALUES FROM (%L) TO (%L)',
|
|
313
|
+
part_name,
|
|
314
|
+
to_char(d, 'YYYY-MM-DD') || ' 00:00:00+00',
|
|
315
|
+
to_char(d + 1, 'YYYY-MM-DD') || ' 00:00:00+00'
|
|
316
|
+
);
|
|
317
|
+
END IF;
|
|
318
|
+
END LOOP;
|
|
319
|
+
END;
|
|
320
|
+
$$
|
|
321
|
+
;
|
|
322
|
+
|
|
323
|
+
CREATE TABLE jobs.job_dependency (
|
|
324
|
+
child_name text NOT NULL,
|
|
325
|
+
child_id uuid NOT NULL,
|
|
326
|
+
parent_name text NOT NULL,
|
|
327
|
+
parent_id uuid NOT NULL,
|
|
328
|
+
PRIMARY KEY (child_name, child_id, parent_name, parent_id)
|
|
329
|
+
)
|
|
330
|
+
;
|
|
331
|
+
CREATE INDEX IF NOT EXISTS job_dep_parent_idx ON jobs.job_dependency (parent_name, parent_id);
|
|
332
|
+
|
|
333
|
+
CREATE TABLE jobs.instance (
|
|
334
|
+
id uuid PRIMARY KEY,
|
|
335
|
+
name text,
|
|
336
|
+
host text NOT NULL,
|
|
337
|
+
pid int NOT NULL,
|
|
338
|
+
version text NOT NULL,
|
|
339
|
+
node_version text NOT NULL,
|
|
340
|
+
application_name text,
|
|
341
|
+
heartbeat_seconds int NOT NULL,
|
|
342
|
+
supervise bool NOT NULL,
|
|
343
|
+
schedule bool NOT NULL,
|
|
344
|
+
migrate bool NOT NULL,
|
|
345
|
+
persist_queue_stats bool NOT NULL,
|
|
346
|
+
persist_warnings bool NOT NULL,
|
|
347
|
+
pool_max int,
|
|
348
|
+
pool_total int,
|
|
349
|
+
pool_idle int,
|
|
350
|
+
pool_waiting int,
|
|
351
|
+
workers jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
352
|
+
metrics jsonb,
|
|
353
|
+
config jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
354
|
+
crash_restarts int NOT NULL DEFAULT 0,
|
|
355
|
+
crash_restarts_since timestamptz,
|
|
356
|
+
started_on timestamptz NOT NULL DEFAULT now(),
|
|
357
|
+
heartbeat_on timestamptz NOT NULL DEFAULT now(),
|
|
358
|
+
stopped_on timestamptz
|
|
359
|
+
)
|
|
360
|
+
;
|
|
361
|
+
|
|
362
|
+
CREATE FUNCTION jobs.create_queue(queue_name text, options jsonb)
|
|
363
|
+
RETURNS VOID AS
|
|
364
|
+
$$
|
|
365
|
+
DECLARE
|
|
366
|
+
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
367
|
+
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
368
|
+
ELSE 'job_common'
|
|
369
|
+
END;
|
|
370
|
+
queue_created_on timestamptz;
|
|
371
|
+
BEGIN
|
|
372
|
+
|
|
373
|
+
WITH q as (
|
|
374
|
+
INSERT INTO jobs.queue (
|
|
375
|
+
name,
|
|
376
|
+
policy,
|
|
377
|
+
retry_limit,
|
|
378
|
+
retry_delay,
|
|
379
|
+
retry_backoff,
|
|
380
|
+
retry_delay_max,
|
|
381
|
+
expire_seconds,
|
|
382
|
+
retention_seconds,
|
|
383
|
+
deletion_seconds,
|
|
384
|
+
warning_queued,
|
|
385
|
+
dead_letter,
|
|
386
|
+
partition,
|
|
387
|
+
table_name,
|
|
388
|
+
heartbeat_seconds,
|
|
389
|
+
notify,
|
|
390
|
+
created_on,
|
|
391
|
+
updated_on
|
|
392
|
+
)
|
|
393
|
+
VALUES (
|
|
394
|
+
queue_name,
|
|
395
|
+
options->>'policy',
|
|
396
|
+
COALESCE((options->>'retryLimit')::int, 2),
|
|
397
|
+
COALESCE((options->>'retryDelay')::int, 0),
|
|
398
|
+
COALESCE((options->>'retryBackoff')::bool, false),
|
|
399
|
+
(options->>'retryDelayMax')::int,
|
|
400
|
+
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
401
|
+
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
402
|
+
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
403
|
+
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
404
|
+
options->>'deadLetter',
|
|
405
|
+
COALESCE((options->>'partition')::bool, false),
|
|
406
|
+
tablename,
|
|
407
|
+
(options->>'heartbeatSeconds')::int,
|
|
408
|
+
COALESCE((options->>'notify')::bool, false),
|
|
409
|
+
jobs.job_now(),
|
|
410
|
+
jobs.job_now()
|
|
411
|
+
)
|
|
412
|
+
ON CONFLICT DO NOTHING
|
|
413
|
+
RETURNING created_on
|
|
414
|
+
)
|
|
415
|
+
SELECT created_on into queue_created_on from q;
|
|
416
|
+
|
|
417
|
+
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
418
|
+
RETURN;
|
|
419
|
+
END IF;
|
|
420
|
+
|
|
421
|
+
EXECUTE format('CREATE TABLE jobs.%I (LIKE jobs.job INCLUDING DEFAULTS)', tablename);
|
|
422
|
+
|
|
423
|
+
EXECUTE jobs.job_table_format($cmd$ALTER TABLE jobs.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
424
|
+
EXECUTE jobs.job_table_format($cmd$ALTER TABLE jobs.job ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES jobs.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, tablename);
|
|
425
|
+
EXECUTE jobs.job_table_format($cmd$ALTER TABLE jobs.job ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES jobs.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, tablename);
|
|
426
|
+
|
|
427
|
+
EXECUTE jobs.job_table_format($cmd$CREATE INDEX job_i11 ON jobs.job (name, priority DESC, created_on, start_after) WHERE state < 'active' AND NOT blocked$cmd$, tablename);
|
|
428
|
+
EXECUTE jobs.job_table_format($cmd$CREATE UNIQUE INDEX job_i4 ON jobs.job (name, singleton_on, COALESCE(singleton_key, '')) WHERE state <> 'cancelled' AND singleton_on IS NOT NULL$cmd$, tablename);
|
|
429
|
+
EXECUTE jobs.job_table_format($cmd$CREATE INDEX job_i7 ON jobs.job (name, group_id) WHERE state = 'active' AND group_id IS NOT NULL$cmd$, tablename);
|
|
430
|
+
EXECUTE jobs.job_table_format($cmd$CREATE INDEX job_i9 ON jobs.job (name, id) WHERE blocking AND state = 'completed'$cmd$, tablename);
|
|
431
|
+
EXECUTE jobs.job_table_format($cmd$CREATE INDEX job_i12 ON jobs.job (source_root_id) WHERE source_root_id IS NOT NULL$cmd$, tablename);
|
|
432
|
+
EXECUTE jobs.job_table_format($cmd$CREATE UNIQUE INDEX job_i13 ON jobs.job (name, singleton_key) WHERE state = 'created' AND upsert_by_key$cmd$, tablename);
|
|
433
|
+
|
|
434
|
+
IF options->>'policy' = 'short' THEN
|
|
435
|
+
EXECUTE jobs.job_table_format($cmd$CREATE UNIQUE INDEX job_i1 ON jobs.job (name, COALESCE(singleton_key, '')) WHERE state = 'created' AND policy = 'short'$cmd$, tablename);
|
|
436
|
+
ELSIF options->>'policy' = 'singleton' THEN
|
|
437
|
+
EXECUTE jobs.job_table_format($cmd$CREATE UNIQUE INDEX job_i2 ON jobs.job (name, COALESCE(singleton_key, '')) WHERE state = 'active' AND policy = 'singleton'$cmd$, tablename);
|
|
438
|
+
ELSIF options->>'policy' = 'stately' THEN
|
|
439
|
+
EXECUTE jobs.job_table_format($cmd$CREATE UNIQUE INDEX job_i3 ON jobs.job (name, state, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'stately'$cmd$, tablename);
|
|
440
|
+
ELSIF options->>'policy' = 'exclusive' THEN
|
|
441
|
+
EXECUTE jobs.job_table_format($cmd$CREATE UNIQUE INDEX job_i6 ON jobs.job (name, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'exclusive'$cmd$, tablename);
|
|
442
|
+
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
443
|
+
EXECUTE jobs.job_table_format($cmd$CREATE UNIQUE INDEX job_i8 ON jobs.job (name, singleton_key) WHERE state IN ('active', 'retry', 'failed') AND policy = 'key_strict_fifo'$cmd$, tablename);
|
|
444
|
+
EXECUTE jobs.job_table_format($cmd$CREATE INDEX job_i10 ON jobs.job (name, singleton_key, state DESC, created_on, id) INCLUDE (start_after) WHERE state < 'active' AND NOT blocked AND policy = 'key_strict_fifo'$cmd$, tablename);
|
|
445
|
+
EXECUTE jobs.job_table_format($cmd$ALTER TABLE jobs.job ADD CONSTRAINT job_key_strict_fifo_singleton_key_check CHECK (NOT (policy = 'key_strict_fifo' AND singleton_key IS NULL))$cmd$, tablename);
|
|
446
|
+
END IF;
|
|
447
|
+
|
|
448
|
+
EXECUTE format('ALTER TABLE jobs.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
449
|
+
EXECUTE format('ALTER TABLE jobs.job ATTACH PARTITION jobs.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
450
|
+
END;
|
|
451
|
+
$$
|
|
452
|
+
LANGUAGE plpgsql;
|
|
453
|
+
;
|
|
454
|
+
|
|
455
|
+
CREATE FUNCTION jobs.delete_queue(queue_name text)
|
|
456
|
+
RETURNS VOID AS
|
|
457
|
+
$$
|
|
458
|
+
DECLARE
|
|
459
|
+
v_table varchar;
|
|
460
|
+
v_partition bool;
|
|
461
|
+
BEGIN
|
|
462
|
+
|
|
463
|
+
SELECT table_name, partition
|
|
464
|
+
FROM jobs.queue
|
|
465
|
+
WHERE name = queue_name
|
|
466
|
+
INTO v_table, v_partition;
|
|
467
|
+
|
|
468
|
+
IF v_partition THEN
|
|
469
|
+
EXECUTE format('DROP TABLE IF EXISTS jobs.%I', v_table);
|
|
470
|
+
ELSE
|
|
471
|
+
EXECUTE format('DELETE FROM jobs.%I WHERE name = %L', v_table, queue_name);
|
|
472
|
+
END IF;
|
|
473
|
+
|
|
474
|
+
DELETE FROM jobs.queue WHERE name = queue_name;
|
|
475
|
+
END;
|
|
476
|
+
$$
|
|
477
|
+
LANGUAGE plpgsql;
|
|
478
|
+
;
|
|
479
|
+
INSERT INTO jobs.version(version) VALUES ('45');
|
|
480
|
+
|
|
481
|
+
create or replace function jobs.enqueue(
|
|
482
|
+
job_name text,
|
|
483
|
+
payload jsonb default null,
|
|
484
|
+
run_at timestamptz default null,
|
|
485
|
+
key text default null,
|
|
486
|
+
group_key text default null,
|
|
487
|
+
priority integer default 0
|
|
488
|
+
) returns uuid
|
|
489
|
+
language plpgsql as $$
|
|
490
|
+
declare
|
|
491
|
+
queue jobs.queue;
|
|
492
|
+
start timestamptz := coalesce(run_at, jobs.job_now());
|
|
493
|
+
job_id uuid;
|
|
494
|
+
begin
|
|
495
|
+
if key is not null and group_key is not null then
|
|
496
|
+
raise exception 'Job "%": an ordered job takes a group_key and no key', job_name;
|
|
497
|
+
end if;
|
|
498
|
+
perform jobs.create_queue(job_name, jsonb_build_object('policy', case when group_key is null then 'standard' else 'key_strict_fifo' end));
|
|
499
|
+
select * into queue from jobs.queue where name = job_name;
|
|
500
|
+
if (queue.policy = 'key_strict_fifo') <> (group_key is not null) then
|
|
501
|
+
raise exception 'Job "%" %', job_name, case when group_key is null then 'is ordered: pass its group_key' else 'is not ordered: it takes no group_key' end;
|
|
502
|
+
end if;
|
|
503
|
+
if key is not null then
|
|
504
|
+
select id into job_id from jobs.job
|
|
505
|
+
where name = job_name and singleton_key = key and state < 'active'
|
|
506
|
+
order by created_on desc limit 1;
|
|
507
|
+
if found then
|
|
508
|
+
return job_id;
|
|
509
|
+
end if;
|
|
510
|
+
end if;
|
|
511
|
+
execute format(
|
|
512
|
+
'insert into jobs.%I (name, data, priority, start_after, singleton_key, retry_limit, retry_delay, retry_backoff,
|
|
513
|
+
retry_delay_max, expire_seconds, deletion_seconds, keep_until, policy, dead_letter, heartbeat_seconds, upsert_by_key)
|
|
514
|
+
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $4 + $12 * interval ''1s'', $13, $14, $15, $16)
|
|
515
|
+
on conflict do nothing
|
|
516
|
+
returning id',
|
|
517
|
+
queue.table_name)
|
|
518
|
+
using job_name, payload, priority, start, coalesce(key, group_key), queue.retry_limit, queue.retry_delay,
|
|
519
|
+
queue.retry_backoff, queue.retry_delay_max, queue.expire_seconds, queue.deletion_seconds, queue.retention_seconds,
|
|
520
|
+
queue.policy, queue.dead_letter, queue.heartbeat_seconds, key is not null
|
|
521
|
+
into job_id;
|
|
522
|
+
-- A concurrent enqueue of the same key inserted first: its job is the one waiting.
|
|
523
|
+
if job_id is null and key is not null then
|
|
524
|
+
select id into job_id from jobs.job
|
|
525
|
+
where name = job_name and singleton_key = key and state < 'active'
|
|
526
|
+
order by created_on desc limit 1;
|
|
527
|
+
end if;
|
|
528
|
+
return job_id;
|
|
529
|
+
end
|
|
530
|
+
$$;
|
|
531
|
+
comment on function jobs.enqueue(text, jsonb, timestamptz, text, text, integer) is 'Protobase jobs 1';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
-- The jobs schema this version of Protobase needs, written by `protobase migration`.
|
|
2
|
+
-- Applied with the project's other migrations; edit or remove statements the project handles differently.
|
|
3
|
+
|
|
4
|
+
create or replace function jobs.enqueue(
|
|
5
|
+
job_name text,
|
|
6
|
+
payload jsonb default null,
|
|
7
|
+
run_at timestamptz default null,
|
|
8
|
+
key text default null,
|
|
9
|
+
group_key text default null,
|
|
10
|
+
priority integer default 0
|
|
11
|
+
) returns uuid
|
|
12
|
+
language plpgsql as $$
|
|
13
|
+
declare
|
|
14
|
+
queue jobs.queue;
|
|
15
|
+
start timestamptz := coalesce(run_at, jobs.job_now());
|
|
16
|
+
job_id uuid;
|
|
17
|
+
begin
|
|
18
|
+
if key is not null and group_key is not null then
|
|
19
|
+
raise exception 'Job "%": an ordered job takes a group_key and no key', job_name;
|
|
20
|
+
end if;
|
|
21
|
+
perform jobs.create_queue(job_name, jsonb_build_object('policy', case when group_key is null then 'standard' else 'key_strict_fifo' end));
|
|
22
|
+
select * into queue from jobs.queue where name = job_name;
|
|
23
|
+
if (queue.policy = 'key_strict_fifo') <> (group_key is not null) then
|
|
24
|
+
raise exception 'Job "%" %', job_name, case when group_key is null then 'is ordered: pass its group_key' else 'is not ordered: it takes no group_key' end;
|
|
25
|
+
end if;
|
|
26
|
+
if key is not null then
|
|
27
|
+
select id into job_id from jobs.job
|
|
28
|
+
where name = job_name and singleton_key = key and state < 'active'
|
|
29
|
+
order by created_on desc limit 1;
|
|
30
|
+
if found then
|
|
31
|
+
return job_id;
|
|
32
|
+
end if;
|
|
33
|
+
end if;
|
|
34
|
+
execute format(
|
|
35
|
+
'insert into jobs.%I (name, data, priority, start_after, singleton_key, retry_limit, retry_delay, retry_backoff,
|
|
36
|
+
retry_delay_max, expire_seconds, deletion_seconds, keep_until, policy, dead_letter, heartbeat_seconds, upsert_by_key)
|
|
37
|
+
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $4 + $12 * interval ''1s'', $13, $14, $15, $16)
|
|
38
|
+
on conflict do nothing
|
|
39
|
+
returning id',
|
|
40
|
+
queue.table_name)
|
|
41
|
+
using job_name, payload, priority, start, coalesce(key, group_key), queue.retry_limit, queue.retry_delay,
|
|
42
|
+
queue.retry_backoff, queue.retry_delay_max, queue.expire_seconds, queue.deletion_seconds, queue.retention_seconds,
|
|
43
|
+
queue.policy, queue.dead_letter, queue.heartbeat_seconds, key is not null
|
|
44
|
+
into job_id;
|
|
45
|
+
-- A concurrent enqueue of the same key inserted first: its job is the one waiting.
|
|
46
|
+
if job_id is null and key is not null then
|
|
47
|
+
select id into job_id from jobs.job
|
|
48
|
+
where name = job_name and singleton_key = key and state < 'active'
|
|
49
|
+
order by created_on desc limit 1;
|
|
50
|
+
end if;
|
|
51
|
+
return job_id;
|
|
52
|
+
end
|
|
53
|
+
$$;
|
|
54
|
+
comment on function jobs.enqueue(text, jsonb, timestamptz, text, text, integer) is 'Protobase jobs 2';
|
|
55
|
+
|
|
56
|
+
create or replace function jobs.next_due() returns timestamptz
|
|
57
|
+
language sql stable as $$
|
|
58
|
+
select min(due) from (
|
|
59
|
+
select j.start_after as due from jobs.job j
|
|
60
|
+
where j.state < 'active' and not j.blocked
|
|
61
|
+
and not (j.policy = 'key_strict_fifo' and exists (
|
|
62
|
+
select 1 from jobs.job h where h.name = j.name and h.singleton_key = j.singleton_key and h.state = 'failed'))
|
|
63
|
+
union all
|
|
64
|
+
select started_on + expire_seconds * interval '1s' from jobs.job where state = 'active'
|
|
65
|
+
) jobs
|
|
66
|
+
$$;
|
|
67
|
+
comment on function jobs.next_due() is 'Protobase jobs 2';
|
|
68
|
+
|
|
69
|
+
create index if not exists job_failed_i on jobs.job (completed_on) where state = 'failed';
|
|
70
|
+
drop view if exists jobs.failed;
|
|
71
|
+
create view jobs.failed as
|
|
72
|
+
select id, name, data as payload, retry_count + 1 as attempts,
|
|
73
|
+
coalesce(output ->> 'message', output -> 'value' ->> 'message', output ->> 'value') as error,
|
|
74
|
+
output, completed_on as failed_at, created_on as created_at, singleton_key as key, false as retry
|
|
75
|
+
from jobs.job where state = 'failed';
|
|
76
|
+
create or replace function jobs.failed_retry() returns trigger
|
|
77
|
+
language plpgsql as $$
|
|
78
|
+
begin
|
|
79
|
+
if new.retry then
|
|
80
|
+
update jobs.job
|
|
81
|
+
set state = 'retry', retry_limit = retry_limit + 1, completed_on = null,
|
|
82
|
+
start_after = greatest(start_after, jobs.job_now()), keep_until = greatest(keep_until, jobs.job_now() + interval '14 days')
|
|
83
|
+
where name = old.name and id = old.id and state = 'failed';
|
|
84
|
+
end if;
|
|
85
|
+
return new;
|
|
86
|
+
end
|
|
87
|
+
$$;
|
|
88
|
+
comment on function jobs.failed_retry() is 'Protobase jobs 2';
|
|
89
|
+
create or replace function jobs.failed_delete() returns trigger
|
|
90
|
+
language plpgsql as $$
|
|
91
|
+
begin
|
|
92
|
+
delete from jobs.job where name = old.name and id = old.id and state = 'failed';
|
|
93
|
+
return old;
|
|
94
|
+
end
|
|
95
|
+
$$;
|
|
96
|
+
comment on function jobs.failed_delete() is 'Protobase jobs 2';
|
|
97
|
+
create trigger failed_retry instead of update on jobs.failed for each row execute function jobs.failed_retry();
|
|
98
|
+
create trigger failed_delete instead of delete on jobs.failed for each row execute function jobs.failed_delete();
|