@opengeni/db 0.28.9 → 0.31.1
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/attached-browser-devices.d.ts +42 -0
- package/dist/attempt-tool-catalogs.d.ts +16 -0
- package/dist/browser-auth.d.ts +91 -0
- package/dist/browser-identities.d.ts +128 -0
- package/dist/browser-sessions.d.ts +272 -0
- package/dist/browser-state-artifacts.d.ts +17 -0
- package/dist/capability-components.d.ts +37 -0
- package/dist/capability-integrations.d.ts +192 -0
- package/dist/chunk-F5FT7BEZ.js +3535 -0
- package/dist/chunk-F5FT7BEZ.js.map +1 -0
- package/dist/chunk-ISI2TIUG.js +639 -0
- package/dist/chunk-ISI2TIUG.js.map +1 -0
- package/dist/chunk-MWDVXQOL.js +11639 -0
- package/dist/chunk-MWDVXQOL.js.map +1 -0
- package/dist/chunk-NBE6CHRI.js +955 -0
- package/dist/chunk-NBE6CHRI.js.map +1 -0
- package/dist/{chunk-VHBFHMPC.js → chunk-YHZXQ7HP.js} +754 -11
- package/dist/chunk-YHZXQ7HP.js.map +1 -0
- package/dist/codemode-operations.d.ts +117 -0
- package/dist/codex-token-resolver.d.ts +2 -2
- package/dist/company-profile-schema.d.ts +798 -0
- package/dist/company-profile.d.ts +268 -0
- package/dist/computer-sessions.d.ts +109 -0
- package/dist/connection-token-resolver.d.ts +9 -4
- package/dist/database.d.ts +52 -3
- package/dist/editable-artifact-durable-export.d.ts +153 -0
- package/dist/editable-artifact-durable-export.js +788 -0
- package/dist/editable-artifact-durable-export.js.map +1 -0
- package/dist/editable-artifact-materialization.d.ts +123 -0
- package/dist/editable-artifacts-schema.d.ts +4152 -0
- package/dist/editable-artifacts.d.ts +610 -0
- package/dist/editable-artifacts.js +26 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/generated-images.d.ts +139 -0
- package/dist/index.d.ts +698 -111
- package/dist/index.js +27573 -10322
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +51 -4
- package/dist/integration-bindings.d.ts +70 -0
- package/dist/integration-features.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +3 -0
- package/dist/interaction-schema.d.ts +5372 -0
- package/dist/knowledge-source-sync-schema.d.ts +1679 -0
- package/dist/knowledge-source-sync.d.ts +274 -0
- package/dist/memory-governance.d.ts +2 -2
- package/dist/memory-slack-delivery.d.ts +101 -0
- package/dist/new-session-drafts.d.ts +2 -2
- package/dist/pack-components.d.ts +76 -0
- package/dist/pack-installations.d.ts +86 -0
- package/dist/persistence-errors.d.ts +3 -3
- package/dist/plugin-packages.d.ts +126 -0
- package/dist/preference-registry.d.ts +1 -1
- package/dist/provision-roles.d.ts +9 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +18 -6
- package/dist/schema.d.ts +18342 -9099
- package/dist/schema.js +165 -1
- package/dist/scoped-knowledge.d.ts +97 -1
- package/dist/session-control.d.ts +4 -4
- package/dist/session-queue-commands.d.ts +19 -11
- package/dist/session-realtime-context.d.ts +2 -2
- package/dist/session-realtime-ledger.d.ts +4 -4
- package/dist/session-realtime-mirror.d.ts +2 -2
- package/dist/session-realtime-state.d.ts +1 -1
- package/dist/session-realtime-terminal.d.ts +1 -1
- package/dist/session-realtime.d.ts +7 -7
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/slack-user-link-access.d.ts +54 -0
- package/dist/transcription-recordings.d.ts +2 -2
- package/dist/turn-initiator.d.ts +2 -2
- package/dist/video-generation.d.ts +216 -0
- package/dist/video-generation.js +66 -0
- package/dist/video-generation.js.map +1 -0
- package/dist/workspace-artifacts.d.ts +1 -1
- package/dist/workspace-instruction-policies.d.ts +1 -1
- package/dist/workspace-learning-policy-schema.d.ts +932 -0
- package/dist/workspace-learning-policy.d.ts +161 -0
- package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
- package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
- package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
- package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
- package/drizzle/0187_generated_image_artifacts.sql +173 -0
- package/drizzle/0188_image_generation_retention_failure.sql +25 -0
- package/drizzle/0189_retained_session_image_formats.sql +13 -0
- package/drizzle/0190_timeline_annotations.sql +31 -0
- package/drizzle/0191_editable_artifact_engine.sql +3853 -0
- package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
- package/drizzle/0193_editable_artifact_authorization.sql +190 -0
- package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
- package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
- package/drizzle/0196_rig_provider_images.sql +152 -0
- package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
- package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
- package/drizzle/0199_workspace_learning_policy.sql +980 -0
- package/drizzle/0201_company_profile_authority.sql +849 -0
- package/drizzle/0202_document_index_checkpoints.sql +85 -0
- package/drizzle/0203_durable_video_generation.sql +353 -0
- package/drizzle/0204_video_generation_funding.sql +44 -0
- package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
- package/drizzle/0206_browser_sessions.sql +646 -0
- package/drizzle/0207_browser_identities.sql +379 -0
- package/drizzle/0208_attached_browser_devices.sql +165 -0
- package/drizzle/0209_computer_sessions.sql +575 -0
- package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
- package/drizzle/0211_editable_artifact_session_links.sql +58 -0
- package/drizzle/0212_slack_installation_bindings.sql +348 -0
- package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
- package/drizzle/0214_session_activity_commit_gate.sql +262 -0
- package/drizzle/0215_capabilities_platform.sql +980 -0
- package/drizzle/0216_pack_component_ownership.sql +235 -0
- package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
- package/package.json +18 -5
- package/src/attached-browser-devices.ts +371 -0
- package/src/attempt-tool-catalogs.ts +131 -0
- package/src/browser-auth.ts +1896 -0
- package/src/browser-identities.ts +1168 -0
- package/src/browser-sessions.ts +2005 -0
- package/src/browser-state-artifacts.ts +59 -0
- package/src/capability-components.ts +166 -0
- package/src/capability-integrations.ts +1663 -0
- package/src/codemode-operations.ts +674 -0
- package/src/company-profile-schema.ts +143 -0
- package/src/company-profile.ts +968 -0
- package/src/computer-sessions.ts +1130 -0
- package/src/connection-token-resolver.ts +270 -25
- package/src/database.ts +427 -23
- package/src/editable-artifact-durable-export.ts +1201 -0
- package/src/editable-artifact-materialization.ts +893 -0
- package/src/editable-artifacts-schema.ts +879 -0
- package/src/editable-artifacts.ts +5404 -0
- package/src/generated-images.ts +740 -0
- package/src/index.ts +5917 -1182
- package/src/insights.ts +160 -13
- package/src/integration-bindings.ts +455 -0
- package/src/integration-features.ts +859 -0
- package/src/interaction-revisions.ts +40 -0
- package/src/interaction-schema.ts +1303 -0
- package/src/knowledge-source-sync-schema.ts +192 -0
- package/src/knowledge-source-sync.ts +1922 -0
- package/src/memory-slack-delivery.ts +801 -0
- package/src/pack-components.ts +954 -0
- package/src/pack-installations.ts +929 -0
- package/src/persistence-errors.ts +4 -7
- package/src/plugin-packages.ts +1186 -0
- package/src/provision-roles.ts +302 -1
- package/src/runtime-posture-cli.ts +9 -0
- package/src/runtime-posture.ts +508 -5
- package/src/schema.ts +5117 -3119
- package/src/scoped-knowledge-schema.ts +1 -1
- package/src/scoped-knowledge.ts +655 -3
- package/src/session-control.ts +8 -5
- package/src/session-queue-commands.ts +187 -14
- package/src/session-realtime-context.ts +2 -2
- package/src/session-realtime-ledger.ts +3 -3
- package/src/session-realtime.ts +7 -7
- package/src/session-tool-call-settlement.ts +9 -0
- package/src/slack-user-link-access.ts +702 -0
- package/src/video-generation.ts +1452 -0
- package/src/workspace-learning-policy-schema.ts +139 -0
- package/src/workspace-learning-policy.ts +595 -0
- package/dist/chunk-P6CUHXQZ.js +0 -7248
- package/dist/chunk-P6CUHXQZ.js.map +0 -1
- package/dist/chunk-VHBFHMPC.js.map +0 -1
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Make the provider-teardown ownership point durable across a mixed worker
|
|
3
|
+
-- rollout. Pre-0184 reapers clear archive_capture_id after publication and
|
|
4
|
+
-- before provider termination; new admission must not re-arm that exact box in
|
|
5
|
+
-- the resulting window. The existing rotation columns are already honored by
|
|
6
|
+
-- every supported old/new acquire path, so a typed teardown_claim marker is a
|
|
7
|
+
-- backward-compatible bridge as well as useful durable lifecycle truth.
|
|
8
|
+
|
|
9
|
+
SET LOCAL lock_timeout = '5s';
|
|
10
|
+
SET LOCAL statement_timeout = '5min';
|
|
11
|
+
|
|
12
|
+
-- A logical operation survives Temporal retries. capture_id changes per DB
|
|
13
|
+
-- owner, while provider_request_id remains the external operation lineage.
|
|
14
|
+
-- replay_safe is explicit: old rolling workers are false; only a caller that
|
|
15
|
+
-- actually supplied a provider-defined idempotency key may retry immediately.
|
|
16
|
+
ALTER TABLE "sandbox_leases"
|
|
17
|
+
ADD COLUMN IF NOT EXISTS "archive_capture_operation_id" uuid,
|
|
18
|
+
ADD COLUMN IF NOT EXISTS "archive_capture_provider_request_id" uuid,
|
|
19
|
+
ADD COLUMN IF NOT EXISTS "archive_capture_provider_replay_safe" boolean
|
|
20
|
+
NOT NULL DEFAULT false,
|
|
21
|
+
ADD COLUMN IF NOT EXISTS "archive_capture_attempt" integer,
|
|
22
|
+
ADD COLUMN IF NOT EXISTS "archive_capture_published_at" timestamptz,
|
|
23
|
+
ADD COLUMN IF NOT EXISTS "reaper_hold_id" uuid,
|
|
24
|
+
ADD COLUMN IF NOT EXISTS "reaper_hold_until" timestamptz,
|
|
25
|
+
ADD COLUMN IF NOT EXISTS "reaper_hold_reason" text;
|
|
26
|
+
|
|
27
|
+
ALTER TABLE "sandbox_leases"
|
|
28
|
+
DROP CONSTRAINT IF EXISTS "sandbox_leases_reaper_hold_check",
|
|
29
|
+
ADD CONSTRAINT "sandbox_leases_reaper_hold_check"
|
|
30
|
+
CHECK (
|
|
31
|
+
(
|
|
32
|
+
"reaper_hold_id" IS NULL
|
|
33
|
+
AND "reaper_hold_until" IS NULL
|
|
34
|
+
AND "reaper_hold_reason" IS NULL
|
|
35
|
+
) OR (
|
|
36
|
+
"reaper_hold_id" IS NOT NULL
|
|
37
|
+
AND "reaper_hold_until" IS NOT NULL
|
|
38
|
+
AND "reaper_hold_reason" IS NOT NULL
|
|
39
|
+
AND length("reaper_hold_reason") BETWEEN 1 AND 500
|
|
40
|
+
)
|
|
41
|
+
) NOT VALID;
|
|
42
|
+
|
|
43
|
+
ALTER TABLE "sandbox_leases"
|
|
44
|
+
VALIDATE CONSTRAINT "sandbox_leases_reaper_hold_check";
|
|
45
|
+
|
|
46
|
+
ALTER TABLE "sandbox_leases"
|
|
47
|
+
DROP CONSTRAINT IF EXISTS "sandbox_leases_rotation_check",
|
|
48
|
+
ADD CONSTRAINT "sandbox_leases_rotation_check"
|
|
49
|
+
CHECK (
|
|
50
|
+
("rotation_requested_at" IS NULL AND "rotation_reason" IS NULL)
|
|
51
|
+
OR (
|
|
52
|
+
"rotation_requested_at" IS NOT NULL
|
|
53
|
+
AND "rotation_reason" IS NOT NULL
|
|
54
|
+
AND "rotation_reason" IN ('provider_deadline', 'operator', 'teardown_claim')
|
|
55
|
+
)
|
|
56
|
+
) NOT VALID;
|
|
57
|
+
|
|
58
|
+
ALTER TABLE "sandbox_leases"
|
|
59
|
+
VALIDATE CONSTRAINT "sandbox_leases_rotation_check";
|
|
60
|
+
|
|
61
|
+
CREATE OR REPLACE FUNCTION opengeni_private.stamp_sandbox_drain_teardown_fence()
|
|
62
|
+
RETURNS trigger
|
|
63
|
+
LANGUAGE plpgsql
|
|
64
|
+
SET search_path = pg_catalog
|
|
65
|
+
AS $function$
|
|
66
|
+
BEGIN
|
|
67
|
+
-- The receipt must predate teardown. This blocks both a new drain capture and
|
|
68
|
+
-- an attempt replacement from an old/new worker after an operator acquired a
|
|
69
|
+
-- live hold. Warm turn-end capture remains allowed: the hold governs only
|
|
70
|
+
-- automatic draining teardown.
|
|
71
|
+
IF NEW.liveness = 'draining'
|
|
72
|
+
AND NEW.archive_capture_id IS NOT NULL
|
|
73
|
+
AND NEW.archive_capture_id IS DISTINCT FROM OLD.archive_capture_id
|
|
74
|
+
AND NEW.reaper_hold_id IS NOT NULL
|
|
75
|
+
AND NEW.reaper_hold_until > pg_catalog.clock_timestamp()
|
|
76
|
+
THEN
|
|
77
|
+
RAISE EXCEPTION 'sandbox reaper hold blocks drain capture for lease %', NEW.id
|
|
78
|
+
USING ERRCODE = '55000';
|
|
79
|
+
END IF;
|
|
80
|
+
|
|
81
|
+
-- Protect old confirmDrainCold implementations too. A hold can be acquired
|
|
82
|
+
-- only before the durable teardown claim, so a draining->cold write during an
|
|
83
|
+
-- active hold is always stale/unsafe.
|
|
84
|
+
IF OLD.liveness = 'draining'
|
|
85
|
+
AND NEW.liveness = 'cold'
|
|
86
|
+
AND NEW.reaper_hold_id IS NOT NULL
|
|
87
|
+
AND NEW.reaper_hold_until > pg_catalog.clock_timestamp()
|
|
88
|
+
THEN
|
|
89
|
+
RAISE EXCEPTION 'sandbox reaper hold blocks cold commit for lease %', NEW.id
|
|
90
|
+
USING ERRCODE = '55000';
|
|
91
|
+
END IF;
|
|
92
|
+
|
|
93
|
+
-- A mixed-version race needs a table-boundary ownership check too. An old
|
|
94
|
+
-- reaper publishes its archive by clearing archive_capture_id, then performs
|
|
95
|
+
-- provider teardown and a later draining->cold write. A new reaper can claim
|
|
96
|
+
-- the row in that window. The old cold write must not atomically erase that
|
|
97
|
+
-- newer claim. New confirmDrainCold sets this transaction-local receipt only
|
|
98
|
+
-- after locking and revalidating the exact capture; old binaries set nothing.
|
|
99
|
+
IF OLD.liveness = 'draining'
|
|
100
|
+
AND NEW.liveness = 'cold'
|
|
101
|
+
AND OLD.archive_capture_id IS NOT NULL
|
|
102
|
+
AND NULLIF(
|
|
103
|
+
pg_catalog.current_setting('opengeni.sandbox_drain_capture_id', true),
|
|
104
|
+
''
|
|
105
|
+
) IS DISTINCT FROM OLD.archive_capture_id::text
|
|
106
|
+
THEN
|
|
107
|
+
RAISE EXCEPTION 'sandbox drain capture ownership blocks cold commit for lease %', NEW.id
|
|
108
|
+
USING ERRCODE = '55000';
|
|
109
|
+
END IF;
|
|
110
|
+
|
|
111
|
+
-- Old binaries know only archive_capture_id. Normalize their writes at the
|
|
112
|
+
-- table boundary so the rolling deployment always retains a complete receipt.
|
|
113
|
+
IF NEW.archive_capture_id IS NULL THEN
|
|
114
|
+
NEW.archive_capture_operation_id := NULL;
|
|
115
|
+
NEW.archive_capture_provider_request_id := NULL;
|
|
116
|
+
NEW.archive_capture_provider_replay_safe := false;
|
|
117
|
+
NEW.archive_capture_attempt := NULL;
|
|
118
|
+
NEW.archive_capture_published_at := NULL;
|
|
119
|
+
ELSIF NEW.archive_capture_id IS DISTINCT FROM OLD.archive_capture_id THEN
|
|
120
|
+
-- Publication belongs to one exact physical capture. A replacement cannot
|
|
121
|
+
-- inherit permission to skip capture from its predecessor.
|
|
122
|
+
NEW.archive_capture_published_at := NULL;
|
|
123
|
+
IF NEW.archive_capture_operation_id IS NOT DISTINCT FROM OLD.archive_capture_operation_id
|
|
124
|
+
AND NEW.archive_capture_attempt IS NOT DISTINCT FROM OLD.archive_capture_attempt
|
|
125
|
+
THEN
|
|
126
|
+
NEW.archive_capture_operation_id := NEW.archive_capture_id;
|
|
127
|
+
NEW.archive_capture_provider_request_id := NEW.archive_capture_id;
|
|
128
|
+
NEW.archive_capture_provider_replay_safe := false;
|
|
129
|
+
NEW.archive_capture_attempt := 1;
|
|
130
|
+
ELSE
|
|
131
|
+
NEW.archive_capture_operation_id := COALESCE(
|
|
132
|
+
NEW.archive_capture_operation_id,
|
|
133
|
+
NEW.archive_capture_id
|
|
134
|
+
);
|
|
135
|
+
NEW.archive_capture_provider_request_id := COALESCE(
|
|
136
|
+
NEW.archive_capture_provider_request_id,
|
|
137
|
+
NEW.archive_capture_id
|
|
138
|
+
);
|
|
139
|
+
NEW.archive_capture_attempt := COALESCE(NEW.archive_capture_attempt, 1);
|
|
140
|
+
END IF;
|
|
141
|
+
ELSE
|
|
142
|
+
NEW.archive_capture_operation_id := COALESCE(
|
|
143
|
+
NEW.archive_capture_operation_id,
|
|
144
|
+
NEW.archive_capture_id
|
|
145
|
+
);
|
|
146
|
+
NEW.archive_capture_provider_request_id := COALESCE(
|
|
147
|
+
NEW.archive_capture_provider_request_id,
|
|
148
|
+
NEW.archive_capture_id
|
|
149
|
+
);
|
|
150
|
+
NEW.archive_capture_attempt := COALESCE(NEW.archive_capture_attempt, 1);
|
|
151
|
+
END IF;
|
|
152
|
+
|
|
153
|
+
IF OLD.liveness = 'draining'
|
|
154
|
+
AND NEW.liveness = 'draining'
|
|
155
|
+
AND OLD.archive_capture_id IS NULL
|
|
156
|
+
AND NEW.archive_capture_id IS NOT NULL
|
|
157
|
+
AND NEW.rotation_requested_at IS NULL
|
|
158
|
+
THEN
|
|
159
|
+
NEW.rotation_requested_at := COALESCE(
|
|
160
|
+
NEW.archive_capture_started_at,
|
|
161
|
+
pg_catalog.clock_timestamp()
|
|
162
|
+
);
|
|
163
|
+
NEW.rotation_reason := 'teardown_claim';
|
|
164
|
+
END IF;
|
|
165
|
+
RETURN NEW;
|
|
166
|
+
END
|
|
167
|
+
$function$;
|
|
168
|
+
|
|
169
|
+
DROP TRIGGER IF EXISTS sandbox_leases_drain_teardown_fence ON "sandbox_leases";
|
|
170
|
+
CREATE TRIGGER sandbox_leases_drain_teardown_fence
|
|
171
|
+
BEFORE UPDATE OF "archive_capture_id", "liveness" ON "sandbox_leases"
|
|
172
|
+
FOR EACH ROW
|
|
173
|
+
EXECUTE FUNCTION opengeni_private.stamp_sandbox_drain_teardown_fence();
|
|
174
|
+
|
|
175
|
+
-- Cover every claim already held when the online migration activates. The
|
|
176
|
+
-- trigger owns every claim that starts after it is installed.
|
|
177
|
+
UPDATE "sandbox_leases"
|
|
178
|
+
SET
|
|
179
|
+
"archive_capture_operation_id" = COALESCE(
|
|
180
|
+
"archive_capture_operation_id",
|
|
181
|
+
"archive_capture_id"
|
|
182
|
+
),
|
|
183
|
+
"archive_capture_provider_request_id" = "archive_capture_id",
|
|
184
|
+
"archive_capture_provider_replay_safe" = false,
|
|
185
|
+
"archive_capture_attempt" = COALESCE("archive_capture_attempt", 1),
|
|
186
|
+
"updated_at" = pg_catalog.clock_timestamp()
|
|
187
|
+
WHERE "archive_capture_id" IS NOT NULL;
|
|
188
|
+
|
|
189
|
+
-- A pre-migration draining claim also needs the old-reader teardown bridge.
|
|
190
|
+
UPDATE "sandbox_leases"
|
|
191
|
+
SET
|
|
192
|
+
"rotation_requested_at" = COALESCE(
|
|
193
|
+
"rotation_requested_at",
|
|
194
|
+
"archive_capture_started_at",
|
|
195
|
+
pg_catalog.clock_timestamp()
|
|
196
|
+
),
|
|
197
|
+
"rotation_reason" = COALESCE("rotation_reason", 'teardown_claim'),
|
|
198
|
+
"updated_at" = pg_catalog.clock_timestamp()
|
|
199
|
+
WHERE "liveness" = 'draining'
|
|
200
|
+
AND "archive_capture_id" IS NOT NULL
|
|
201
|
+
AND "rotation_requested_at" IS NULL;
|
|
202
|
+
|
|
203
|
+
ALTER TABLE "sandbox_leases"
|
|
204
|
+
DROP CONSTRAINT IF EXISTS "sandbox_leases_archive_capture_check",
|
|
205
|
+
ADD CONSTRAINT "sandbox_leases_archive_capture_check"
|
|
206
|
+
CHECK (
|
|
207
|
+
(
|
|
208
|
+
"archive_capture_id" IS NULL
|
|
209
|
+
AND "archive_capture_operation_id" IS NULL
|
|
210
|
+
AND "archive_capture_provider_request_id" IS NULL
|
|
211
|
+
AND "archive_capture_provider_replay_safe" = false
|
|
212
|
+
AND "archive_capture_attempt" IS NULL
|
|
213
|
+
AND "archive_capture_generation" IS NULL
|
|
214
|
+
AND "archive_capture_started_at" IS NULL
|
|
215
|
+
AND "archive_capture_deadline_at" IS NULL
|
|
216
|
+
AND "archive_capture_published_at" IS NULL
|
|
217
|
+
) OR (
|
|
218
|
+
"archive_capture_id" IS NOT NULL
|
|
219
|
+
AND "archive_capture_operation_id" IS NOT NULL
|
|
220
|
+
AND "archive_capture_provider_request_id" IS NOT NULL
|
|
221
|
+
AND (
|
|
222
|
+
"archive_capture_provider_replay_safe" = false
|
|
223
|
+
OR (
|
|
224
|
+
"backend" = 'modal'
|
|
225
|
+
AND COALESCE(
|
|
226
|
+
"resume_state" #>> '{sessionState,providerState,workspacePersistence}',
|
|
227
|
+
"resume_state" #>> '{sessionState,workspacePersistence}'
|
|
228
|
+
) = 'snapshot_filesystem'
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
AND "archive_capture_attempt" IS NOT NULL
|
|
232
|
+
AND "archive_capture_attempt" > 0
|
|
233
|
+
AND "archive_capture_generation" IS NOT NULL
|
|
234
|
+
AND "archive_capture_generation" = "workspace_generation"
|
|
235
|
+
AND "archive_capture_started_at" IS NOT NULL
|
|
236
|
+
AND "archive_capture_deadline_at" IS NOT NULL
|
|
237
|
+
AND "archive_capture_deadline_at" > "archive_capture_started_at"
|
|
238
|
+
AND (
|
|
239
|
+
"archive_capture_published_at" IS NULL
|
|
240
|
+
OR "liveness" = 'draining'
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
) NOT VALID;
|
|
244
|
+
|
|
245
|
+
ALTER TABLE "sandbox_leases"
|
|
246
|
+
VALIDATE CONSTRAINT "sandbox_leases_archive_capture_check";
|
|
247
|
+
|
|
248
|
+
-- The global reaper is database-owned, so replacing it here protects a hold
|
|
249
|
+
-- even while old worker binaries are still running. Held rows may still have
|
|
250
|
+
-- stale holder receipts reconciled, but cannot enter/complete automatic drain.
|
|
251
|
+
DO $hold_aware_reaper$
|
|
252
|
+
DECLARE data_schema text := current_schema();
|
|
253
|
+
BEGIN
|
|
254
|
+
EXECUTE format($create$
|
|
255
|
+
CREATE OR REPLACE FUNCTION opengeni_private.reap_sandbox_leases(
|
|
256
|
+
p_viewer_holder_ttl_ms bigint,
|
|
257
|
+
p_turn_holder_ttl_ms bigint,
|
|
258
|
+
p_idle_grace_ms bigint
|
|
259
|
+
)
|
|
260
|
+
RETURNS TABLE (
|
|
261
|
+
workspace_id uuid,
|
|
262
|
+
sandbox_group_id uuid,
|
|
263
|
+
instance_id text,
|
|
264
|
+
lease_epoch integer
|
|
265
|
+
)
|
|
266
|
+
LANGUAGE plpgsql
|
|
267
|
+
SECURITY DEFINER
|
|
268
|
+
SET search_path = pg_catalog
|
|
269
|
+
AS $function$
|
|
270
|
+
DECLARE locked_ids uuid[];
|
|
271
|
+
BEGIN
|
|
272
|
+
PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
|
|
273
|
+
|
|
274
|
+
SELECT coalesce(
|
|
275
|
+
pg_catalog.array_agg(candidate.id),
|
|
276
|
+
ARRAY[]::uuid[]
|
|
277
|
+
)
|
|
278
|
+
INTO locked_ids
|
|
279
|
+
FROM (
|
|
280
|
+
SELECT lease.id
|
|
281
|
+
FROM %1$I.sandbox_leases lease
|
|
282
|
+
WHERE lease.liveness <> 'cold'
|
|
283
|
+
OR EXISTS (
|
|
284
|
+
SELECT 1
|
|
285
|
+
FROM %1$I.sandbox_lease_holders holder
|
|
286
|
+
WHERE holder.lease_id = lease.id
|
|
287
|
+
)
|
|
288
|
+
ORDER BY lease.updated_at, lease.id
|
|
289
|
+
LIMIT 500
|
|
290
|
+
FOR UPDATE OF lease SKIP LOCKED
|
|
291
|
+
) candidate;
|
|
292
|
+
|
|
293
|
+
IF pg_catalog.cardinality(locked_ids) = 0 THEN
|
|
294
|
+
RETURN;
|
|
295
|
+
END IF;
|
|
296
|
+
|
|
297
|
+
DELETE FROM %1$I.sandbox_lease_holders holder
|
|
298
|
+
USING (
|
|
299
|
+
SELECT stale.id
|
|
300
|
+
FROM %1$I.sandbox_lease_holders stale
|
|
301
|
+
WHERE stale.lease_id = ANY(locked_ids)
|
|
302
|
+
AND stale.kind IN ('viewer', 'direct')
|
|
303
|
+
AND stale.last_heartbeat_at
|
|
304
|
+
< pg_catalog.now()
|
|
305
|
+
- pg_catalog.make_interval(secs => p_viewer_holder_ttl_ms / 1000.0)
|
|
306
|
+
FOR UPDATE OF stale SKIP LOCKED
|
|
307
|
+
) victim
|
|
308
|
+
WHERE holder.id = victim.id;
|
|
309
|
+
|
|
310
|
+
IF p_turn_holder_ttl_ms > 0 THEN
|
|
311
|
+
DELETE FROM %1$I.sandbox_lease_holders holder
|
|
312
|
+
USING (
|
|
313
|
+
SELECT stale.id
|
|
314
|
+
FROM %1$I.sandbox_lease_holders stale
|
|
315
|
+
WHERE stale.lease_id = ANY(locked_ids)
|
|
316
|
+
AND stale.kind = 'turn'
|
|
317
|
+
AND stale.last_heartbeat_at
|
|
318
|
+
< pg_catalog.now()
|
|
319
|
+
- pg_catalog.make_interval(secs => p_turn_holder_ttl_ms / 1000.0)
|
|
320
|
+
FOR UPDATE OF stale SKIP LOCKED
|
|
321
|
+
) victim
|
|
322
|
+
WHERE holder.id = victim.id;
|
|
323
|
+
END IF;
|
|
324
|
+
|
|
325
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
326
|
+
refcount = counts.total,
|
|
327
|
+
turn_holders = counts.turns,
|
|
328
|
+
viewer_holders = counts.viewers,
|
|
329
|
+
liveness = CASE
|
|
330
|
+
WHEN lease.liveness = 'warm'
|
|
331
|
+
AND counts.total = 0
|
|
332
|
+
AND counts.turns = 0
|
|
333
|
+
AND (
|
|
334
|
+
lease.reaper_hold_id IS NULL
|
|
335
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
336
|
+
)
|
|
337
|
+
THEN 'draining' ELSE lease.liveness END,
|
|
338
|
+
expires_at = CASE
|
|
339
|
+
WHEN lease.liveness = 'warm'
|
|
340
|
+
AND counts.total = 0
|
|
341
|
+
AND counts.turns = 0
|
|
342
|
+
AND (
|
|
343
|
+
lease.reaper_hold_id IS NULL
|
|
344
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
345
|
+
)
|
|
346
|
+
THEN CASE
|
|
347
|
+
WHEN lease.rotation_requested_at IS NOT NULL
|
|
348
|
+
THEN pg_catalog.now() - interval '1 millisecond'
|
|
349
|
+
ELSE pg_catalog.now()
|
|
350
|
+
+ pg_catalog.make_interval(secs => p_idle_grace_ms / 1000.0)
|
|
351
|
+
END
|
|
352
|
+
ELSE lease.expires_at END,
|
|
353
|
+
updated_at = pg_catalog.now()
|
|
354
|
+
FROM (
|
|
355
|
+
SELECT candidate.id,
|
|
356
|
+
pg_catalog.count(holder.id)::int AS total,
|
|
357
|
+
pg_catalog.count(holder.id)
|
|
358
|
+
FILTER (WHERE holder.kind = 'turn')::int AS turns,
|
|
359
|
+
pg_catalog.count(holder.id)
|
|
360
|
+
FILTER (WHERE holder.kind = 'viewer')::int AS viewers
|
|
361
|
+
FROM pg_catalog.unnest(locked_ids) candidate(id)
|
|
362
|
+
LEFT JOIN %1$I.sandbox_lease_holders holder
|
|
363
|
+
ON holder.lease_id = candidate.id
|
|
364
|
+
GROUP BY candidate.id
|
|
365
|
+
) counts
|
|
366
|
+
WHERE lease.id = counts.id;
|
|
367
|
+
|
|
368
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
369
|
+
liveness = 'cold',
|
|
370
|
+
instance_id = null,
|
|
371
|
+
lease_epoch = lease.lease_epoch + 1,
|
|
372
|
+
reaper_hold_id = null,
|
|
373
|
+
reaper_hold_until = null,
|
|
374
|
+
reaper_hold_reason = null,
|
|
375
|
+
resume_state = opengeni_private.warming_reset_resume_state_v2(
|
|
376
|
+
lease.backend,
|
|
377
|
+
lease.resume_backend_id,
|
|
378
|
+
lease.resume_state,
|
|
379
|
+
lease.workspace_generation,
|
|
380
|
+
lease.archive_generation,
|
|
381
|
+
pg_catalog.clock_timestamp()
|
|
382
|
+
),
|
|
383
|
+
resume_backend_id = CASE
|
|
384
|
+
WHEN coalesce(
|
|
385
|
+
lease.resume_state #>> '{sessionState,workspaceArchive}', ''
|
|
386
|
+
) <> ''
|
|
387
|
+
OR coalesce(
|
|
388
|
+
lease.resume_state #>> '{sessionState,workspaceArchivePrev}', ''
|
|
389
|
+
) <> ''
|
|
390
|
+
THEN coalesce(lease.resume_backend_id, lease.backend)
|
|
391
|
+
ELSE null END,
|
|
392
|
+
data_plane_url = null,
|
|
393
|
+
terminal_data_plane_url = null,
|
|
394
|
+
provider_created_at = null,
|
|
395
|
+
provider_deadline_at = null,
|
|
396
|
+
rotation_requested_at = null,
|
|
397
|
+
rotation_reason = null,
|
|
398
|
+
updated_at = pg_catalog.now()
|
|
399
|
+
WHERE lease.id = ANY(locked_ids)
|
|
400
|
+
AND lease.liveness = 'warming'
|
|
401
|
+
AND lease.expires_at < pg_catalog.now()
|
|
402
|
+
AND lease.instance_id IS NULL
|
|
403
|
+
AND (
|
|
404
|
+
lease.reaper_hold_id IS NULL
|
|
405
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
409
|
+
liveness = 'draining',
|
|
410
|
+
refcount = 0,
|
|
411
|
+
turn_holders = 0,
|
|
412
|
+
viewer_holders = 0,
|
|
413
|
+
data_plane_url = null,
|
|
414
|
+
terminal_data_plane_url = null,
|
|
415
|
+
lease_epoch = lease.lease_epoch + 1,
|
|
416
|
+
reaper_hold_id = null,
|
|
417
|
+
reaper_hold_until = null,
|
|
418
|
+
reaper_hold_reason = null,
|
|
419
|
+
expires_at = pg_catalog.now() - interval '1 millisecond',
|
|
420
|
+
updated_at = pg_catalog.now()
|
|
421
|
+
WHERE lease.id = ANY(locked_ids)
|
|
422
|
+
AND lease.liveness = 'warming'
|
|
423
|
+
AND lease.expires_at < pg_catalog.now()
|
|
424
|
+
AND lease.instance_id IS NOT NULL
|
|
425
|
+
AND (
|
|
426
|
+
lease.reaper_hold_id IS NULL
|
|
427
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
RETURN QUERY
|
|
431
|
+
SELECT lease.workspace_id, lease.sandbox_group_id,
|
|
432
|
+
lease.instance_id, lease.lease_epoch
|
|
433
|
+
FROM %1$I.sandbox_leases lease
|
|
434
|
+
WHERE lease.id = ANY(locked_ids)
|
|
435
|
+
AND lease.liveness = 'draining'
|
|
436
|
+
AND lease.expires_at < pg_catalog.now()
|
|
437
|
+
AND lease.refcount = 0
|
|
438
|
+
AND (
|
|
439
|
+
lease.reaper_hold_id IS NULL
|
|
440
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
441
|
+
);
|
|
442
|
+
END;
|
|
443
|
+
$function$;
|
|
444
|
+
$create$, data_schema);
|
|
445
|
+
END
|
|
446
|
+
$hold_aware_reaper$;
|
|
447
|
+
|
|
448
|
+
-- Provider-deadline admission is also automatic teardown. Keep the exact
|
|
449
|
+
-- provider available while held; release/expiry makes it eligible next scan.
|
|
450
|
+
DO $hold_aware_rotations$
|
|
451
|
+
DECLARE data_schema text := current_schema();
|
|
452
|
+
BEGIN
|
|
453
|
+
EXECUTE format($create$
|
|
454
|
+
CREATE OR REPLACE FUNCTION opengeni_private.request_due_sandbox_rotations(
|
|
455
|
+
p_lead_ms bigint,
|
|
456
|
+
p_limit integer
|
|
457
|
+
)
|
|
458
|
+
RETURNS integer
|
|
459
|
+
LANGUAGE plpgsql
|
|
460
|
+
SECURITY DEFINER
|
|
461
|
+
SET search_path = pg_catalog
|
|
462
|
+
AS $function$
|
|
463
|
+
DECLARE
|
|
464
|
+
requested integer;
|
|
465
|
+
requested_ids uuid[];
|
|
466
|
+
BEGIN
|
|
467
|
+
IF p_lead_ms IS NULL OR p_lead_ms < 0 OR p_lead_ms > 86400000 THEN
|
|
468
|
+
RAISE EXCEPTION 'sandbox rotation lead must be between 0 and 24h'
|
|
469
|
+
USING ERRCODE = '22023';
|
|
470
|
+
END IF;
|
|
471
|
+
IF p_limit IS NULL OR p_limit < 1 OR p_limit > 500 THEN
|
|
472
|
+
RAISE EXCEPTION 'sandbox rotation batch limit must be between 1 and 500'
|
|
473
|
+
USING ERRCODE = '22023';
|
|
474
|
+
END IF;
|
|
475
|
+
|
|
476
|
+
PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
|
|
477
|
+
|
|
478
|
+
WITH candidates AS MATERIALIZED (
|
|
479
|
+
SELECT lease.id
|
|
480
|
+
FROM %1$I.sandbox_leases lease
|
|
481
|
+
WHERE lease.backend = 'modal'
|
|
482
|
+
AND lease.liveness IN ('warming', 'warm')
|
|
483
|
+
AND lease.provider_deadline_at IS NOT NULL
|
|
484
|
+
AND lease.provider_deadline_at
|
|
485
|
+
<= pg_catalog.now()
|
|
486
|
+
+ pg_catalog.make_interval(secs => p_lead_ms / 1000.0)
|
|
487
|
+
AND lease.rotation_requested_at IS NULL
|
|
488
|
+
AND (
|
|
489
|
+
lease.reaper_hold_id IS NULL
|
|
490
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
491
|
+
)
|
|
492
|
+
ORDER BY lease.provider_deadline_at, lease.id
|
|
493
|
+
FOR UPDATE SKIP LOCKED
|
|
494
|
+
LIMIT p_limit
|
|
495
|
+
),
|
|
496
|
+
due AS (
|
|
497
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
498
|
+
rotation_requested_at = pg_catalog.now(),
|
|
499
|
+
rotation_reason = 'provider_deadline',
|
|
500
|
+
updated_at = pg_catalog.now()
|
|
501
|
+
FROM candidates
|
|
502
|
+
WHERE lease.id = candidates.id
|
|
503
|
+
AND (
|
|
504
|
+
lease.reaper_hold_id IS NULL
|
|
505
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
506
|
+
)
|
|
507
|
+
RETURNING lease.id
|
|
508
|
+
)
|
|
509
|
+
SELECT
|
|
510
|
+
coalesce(pg_catalog.array_agg(due.id), ARRAY[]::uuid[]),
|
|
511
|
+
pg_catalog.count(*)::integer
|
|
512
|
+
INTO requested_ids, requested
|
|
513
|
+
FROM due;
|
|
514
|
+
|
|
515
|
+
DELETE FROM %1$I.sandbox_lease_holders holder
|
|
516
|
+
WHERE holder.lease_id = ANY(requested_ids)
|
|
517
|
+
AND holder.kind = 'viewer';
|
|
518
|
+
|
|
519
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
520
|
+
refcount = counts.total,
|
|
521
|
+
turn_holders = counts.turns,
|
|
522
|
+
viewer_holders = counts.viewers,
|
|
523
|
+
liveness = CASE
|
|
524
|
+
WHEN lease.liveness = 'warm' AND counts.total = 0 THEN 'draining'
|
|
525
|
+
ELSE lease.liveness
|
|
526
|
+
END,
|
|
527
|
+
expires_at = CASE
|
|
528
|
+
WHEN lease.liveness = 'warm' AND counts.total = 0
|
|
529
|
+
THEN pg_catalog.now() - interval '1 millisecond'
|
|
530
|
+
ELSE lease.expires_at
|
|
531
|
+
END,
|
|
532
|
+
updated_at = pg_catalog.now()
|
|
533
|
+
FROM (
|
|
534
|
+
SELECT candidate.id,
|
|
535
|
+
(SELECT pg_catalog.count(*) FROM %1$I.sandbox_lease_holders holder
|
|
536
|
+
WHERE holder.lease_id = candidate.id)::int AS total,
|
|
537
|
+
(SELECT pg_catalog.count(*) FROM %1$I.sandbox_lease_holders holder
|
|
538
|
+
WHERE holder.lease_id = candidate.id AND holder.kind = 'turn')::int AS turns,
|
|
539
|
+
(SELECT pg_catalog.count(*) FROM %1$I.sandbox_lease_holders holder
|
|
540
|
+
WHERE holder.lease_id = candidate.id AND holder.kind = 'viewer')::int AS viewers
|
|
541
|
+
FROM unnest(requested_ids) requested_id(id)
|
|
542
|
+
JOIN %1$I.sandbox_leases candidate ON candidate.id = requested_id.id
|
|
543
|
+
) counts
|
|
544
|
+
WHERE lease.id = counts.id;
|
|
545
|
+
|
|
546
|
+
RETURN requested;
|
|
547
|
+
END;
|
|
548
|
+
$function$;
|
|
549
|
+
$create$, data_schema);
|
|
550
|
+
END
|
|
551
|
+
$hold_aware_rotations$;
|
|
552
|
+
|
|
553
|
+
-- A native checkpoint exists before its candidate receipt can be published to
|
|
554
|
+
-- current/previous. While an archive-capture claim still owns the exact source
|
|
555
|
+
-- lease/epoch/instance/generation, every native candidate produced from that
|
|
556
|
+
-- physical capture is durable recovery state rather than an orphan. Protect it
|
|
557
|
+
-- from generic candidate GC until the claim settles. The provider request id is
|
|
558
|
+
-- deliberately not compared with object_id: Modal's request UUID and returned
|
|
559
|
+
-- Image id are different identifiers.
|
|
560
|
+
DO $capture_aware_checkpoint_gc$
|
|
561
|
+
DECLARE data_schema text := current_schema();
|
|
562
|
+
BEGIN
|
|
563
|
+
EXECUTE format($create$
|
|
564
|
+
CREATE OR REPLACE FUNCTION opengeni_private.claim_sandbox_checkpoint_artifacts(
|
|
565
|
+
p_claim_id uuid,
|
|
566
|
+
p_limit integer,
|
|
567
|
+
p_claim_ttl_ms bigint
|
|
568
|
+
)
|
|
569
|
+
RETURNS TABLE (
|
|
570
|
+
id uuid,
|
|
571
|
+
provider_backend text,
|
|
572
|
+
provider_binding_key text,
|
|
573
|
+
provider_binding jsonb,
|
|
574
|
+
object_kind text,
|
|
575
|
+
object_id text,
|
|
576
|
+
delete_attempts integer
|
|
577
|
+
)
|
|
578
|
+
LANGUAGE plpgsql
|
|
579
|
+
SECURITY DEFINER
|
|
580
|
+
SET search_path = pg_catalog
|
|
581
|
+
AS $function$
|
|
582
|
+
BEGIN
|
|
583
|
+
IF p_claim_id IS NULL THEN
|
|
584
|
+
RAISE EXCEPTION 'checkpoint artifact claim id is required'
|
|
585
|
+
USING ERRCODE = '22023';
|
|
586
|
+
END IF;
|
|
587
|
+
IF p_limit IS NULL OR p_limit < 1 OR p_limit > 500 THEN
|
|
588
|
+
RAISE EXCEPTION 'checkpoint artifact claim limit must be between 1 and 500'
|
|
589
|
+
USING ERRCODE = '22023';
|
|
590
|
+
END IF;
|
|
591
|
+
IF p_claim_ttl_ms IS NULL
|
|
592
|
+
OR p_claim_ttl_ms < 1000
|
|
593
|
+
OR p_claim_ttl_ms > 3600000
|
|
594
|
+
THEN
|
|
595
|
+
RAISE EXCEPTION 'checkpoint artifact claim TTL must be between 1s and 1h'
|
|
596
|
+
USING ERRCODE = '22023';
|
|
597
|
+
END IF;
|
|
598
|
+
|
|
599
|
+
PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
|
|
600
|
+
|
|
601
|
+
WITH stale_claims AS MATERIALIZED (
|
|
602
|
+
SELECT artifact.id
|
|
603
|
+
FROM %1$I.sandbox_checkpoint_artifacts artifact
|
|
604
|
+
WHERE artifact.state = 'deleting'
|
|
605
|
+
AND artifact.delete_claimed_at
|
|
606
|
+
< pg_catalog.now()
|
|
607
|
+
- pg_catalog.make_interval(secs => p_claim_ttl_ms / 1000.0)
|
|
608
|
+
ORDER BY artifact.delete_claimed_at, artifact.id
|
|
609
|
+
FOR UPDATE SKIP LOCKED
|
|
610
|
+
LIMIT p_limit
|
|
611
|
+
)
|
|
612
|
+
UPDATE %1$I.sandbox_checkpoint_artifacts artifact SET
|
|
613
|
+
state = 'delete_failed',
|
|
614
|
+
delete_after = pg_catalog.now(),
|
|
615
|
+
delete_claim_id = null,
|
|
616
|
+
delete_claimed_at = null,
|
|
617
|
+
last_delete_error = coalesce(
|
|
618
|
+
last_delete_error, 'stale delete claim recovered'
|
|
619
|
+
),
|
|
620
|
+
updated_at = pg_catalog.now()
|
|
621
|
+
FROM stale_claims
|
|
622
|
+
WHERE artifact.id = stale_claims.id;
|
|
623
|
+
|
|
624
|
+
RETURN QUERY
|
|
625
|
+
WITH candidates AS (
|
|
626
|
+
SELECT artifact.id
|
|
627
|
+
FROM %1$I.sandbox_checkpoint_artifacts artifact
|
|
628
|
+
WHERE (
|
|
629
|
+
(
|
|
630
|
+
artifact.state = 'candidate'
|
|
631
|
+
AND artifact.created_at < pg_catalog.now() - interval '15 minutes'
|
|
632
|
+
)
|
|
633
|
+
OR (
|
|
634
|
+
artifact.state IN ('current', 'previous')
|
|
635
|
+
AND artifact.created_at < pg_catalog.now() - interval '15 minutes'
|
|
636
|
+
)
|
|
637
|
+
OR (
|
|
638
|
+
artifact.state IN ('delete_pending', 'delete_failed')
|
|
639
|
+
AND coalesce(artifact.delete_after, artifact.created_at)
|
|
640
|
+
<= pg_catalog.now()
|
|
641
|
+
)
|
|
642
|
+
)
|
|
643
|
+
AND NOT EXISTS (
|
|
644
|
+
SELECT 1 FROM %1$I.sandbox_leases lease
|
|
645
|
+
WHERE lease.current_checkpoint_artifact_id = artifact.id
|
|
646
|
+
OR lease.previous_checkpoint_artifact_id = artifact.id
|
|
647
|
+
OR (
|
|
648
|
+
artifact.provenance = 'native_capture'
|
|
649
|
+
AND lease.id = artifact.source_lease_id
|
|
650
|
+
AND lease.lease_epoch = artifact.source_lease_epoch
|
|
651
|
+
AND lease.instance_id = artifact.source_instance_id
|
|
652
|
+
AND lease.backend = artifact.provider_backend
|
|
653
|
+
AND lease.archive_capture_id IS NOT NULL
|
|
654
|
+
AND lease.archive_capture_generation
|
|
655
|
+
= artifact.source_workspace_generation
|
|
656
|
+
)
|
|
657
|
+
)
|
|
658
|
+
ORDER BY coalesce(artifact.delete_after, artifact.created_at), artifact.id
|
|
659
|
+
FOR UPDATE SKIP LOCKED
|
|
660
|
+
LIMIT p_limit
|
|
661
|
+
)
|
|
662
|
+
UPDATE %1$I.sandbox_checkpoint_artifacts artifact SET
|
|
663
|
+
state = 'deleting',
|
|
664
|
+
delete_attempts = artifact.delete_attempts + 1,
|
|
665
|
+
delete_claim_id = p_claim_id,
|
|
666
|
+
delete_claimed_at = pg_catalog.now(),
|
|
667
|
+
updated_at = pg_catalog.now()
|
|
668
|
+
FROM candidates
|
|
669
|
+
WHERE artifact.id = candidates.id
|
|
670
|
+
RETURNING artifact.id, artifact.provider_backend, artifact.provider_binding_key,
|
|
671
|
+
artifact.provider_binding, artifact.object_kind, artifact.object_id,
|
|
672
|
+
artifact.delete_attempts;
|
|
673
|
+
END;
|
|
674
|
+
$function$;
|
|
675
|
+
$create$, data_schema);
|
|
676
|
+
END
|
|
677
|
+
$capture_aware_checkpoint_gc$;
|
|
678
|
+
|
|
679
|
+
REVOKE ALL ON FUNCTION opengeni_private.stamp_sandbox_drain_teardown_fence() FROM PUBLIC;
|
|
680
|
+
|
|
681
|
+
RESET statement_timeout;
|
|
682
|
+
RESET lock_timeout;
|