@opengeni/db 0.28.9 → 0.36.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/attached-browser-devices.d.ts +42 -0
- package/dist/attempt-tool-catalogs.d.ts +16 -0
- package/dist/browser-auth.d.ts +299 -0
- package/dist/browser-downloads.d.ts +51 -0
- package/dist/browser-identities.d.ts +137 -0
- package/dist/browser-sessions.d.ts +354 -0
- package/dist/browser-state-artifacts.d.ts +91 -0
- package/dist/canonical-human-identities.d.ts +48 -0
- package/dist/canonical-human-identities.js +28 -0
- package/dist/canonical-human-identities.js.map +1 -0
- package/dist/capability-components.d.ts +37 -0
- package/dist/capability-integrations.d.ts +194 -0
- package/dist/chunk-5KTIEDX7.js +13200 -0
- package/dist/chunk-5KTIEDX7.js.map +1 -0
- package/dist/chunk-CQZ4QSP5.js +995 -0
- package/dist/chunk-CQZ4QSP5.js.map +1 -0
- package/dist/chunk-F32YT7MP.js +117 -0
- package/dist/chunk-F32YT7MP.js.map +1 -0
- package/dist/chunk-JC5QPLUM.js +666 -0
- package/dist/chunk-JC5QPLUM.js.map +1 -0
- package/dist/chunk-P3RE4D2B.js +3535 -0
- package/dist/chunk-P3RE4D2B.js.map +1 -0
- package/dist/chunk-TAUTWOS5.js +259 -0
- package/dist/chunk-TAUTWOS5.js.map +1 -0
- package/dist/chunk-VKLB2ZGI.js +2389 -0
- package/dist/chunk-VKLB2ZGI.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 +57 -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 +1122 -126
- package/dist/index.js +42329 -18407
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +73 -10
- package/dist/integration-bindings.d.ts +70 -0
- package/dist/integration-facets.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +14 -0
- package/dist/interaction-schema.d.ts +6036 -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 +19 -6
- package/dist/schema.d.ts +22654 -9359
- package/dist/schema.js +209 -3
- package/dist/scoped-knowledge.d.ts +97 -1
- package/dist/session-control.d.ts +33 -7
- 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-tenancy.d.ts +44 -0
- package/dist/session-tenancy.js +16 -0
- package/dist/session-tenancy.js.map +1 -0
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/slack-task-policy-schema.d.ts +847 -0
- package/dist/slack-task-policy.d.ts +65 -0
- package/dist/slack-user-link-access.d.ts +54 -0
- package/dist/task-notes-schema.d.ts +924 -0
- package/dist/task-notes.d.ts +85 -0
- package/dist/transcription-recordings.d.ts +2 -2
- package/dist/turn-initiator.d.ts +2 -2
- package/dist/video-generation.d.ts +225 -0
- package/dist/video-generation.js +68 -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/dist/xai-subscription.d.ts +326 -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_browser_state_transfer_hardening.sql +560 -0
- package/drizzle/0212_slack_installation_bindings.sql +348 -0
- package/drizzle/0213_browser_interaction_authority.sql +67 -0
- package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
- package/drizzle/0214_browser_download_saves.sql +34 -0
- package/drizzle/0214_session_activity_commit_gate.sql +262 -0
- package/drizzle/0215_browser_controller_host.sql +385 -0
- package/drizzle/0215_capabilities_platform.sql +980 -0
- package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
- package/drizzle/0216_pack_component_ownership.sql +235 -0
- package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
- package/drizzle/0217_external_browser_auth_operations.sql +22 -0
- package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
- package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
- package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
- package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
- package/drizzle/0220_session_channels.sql +54 -0
- package/drizzle/0221_sessions_channel_index.sql +5 -0
- package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
- package/drizzle/0222_sessions_channel_fk.sql +89 -0
- package/drizzle/0223_pending_tool_event_output.sql +55 -0
- package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
- package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
- package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
- package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
- package/drizzle/0227_slack_native_actions.sql +145 -0
- package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
- package/drizzle/0228_slack_task_policy.sql +400 -0
- package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
- package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
- package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
- package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
- package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
- package/drizzle/0234_xai_subscription_authority.sql +1139 -0
- package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
- package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
- package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
- package/drizzle/0237_interaction_transition_reaper.sql +263 -0
- package/drizzle/0238_goal_persistence_policy.sql +471 -0
- package/drizzle/0238_supergrok_realtime_model.sql +29 -0
- package/drizzle/0239_supergrok_video_funding.sql +50 -0
- package/drizzle/0239_task_tree_notes.sql +990 -0
- package/package.json +26 -5
- package/src/attached-browser-devices.ts +371 -0
- package/src/attempt-tool-catalogs.ts +131 -0
- package/src/browser-auth.ts +4373 -0
- package/src/browser-downloads.ts +517 -0
- package/src/browser-identities.ts +1385 -0
- package/src/browser-sessions.ts +2294 -0
- package/src/browser-state-artifacts.ts +449 -0
- package/src/canonical-human-identities.ts +317 -0
- package/src/capability-components.ts +166 -0
- package/src/capability-integrations.ts +1571 -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 +468 -24
- 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 +9963 -1465
- package/src/insights.ts +251 -23
- package/src/integration-bindings.ts +455 -0
- package/src/integration-facets.ts +862 -0
- package/src/interaction-revisions.ts +70 -0
- package/src/interaction-schema.ts +1606 -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 +886 -0
- package/src/persistence-errors.ts +4 -7
- package/src/plugin-packages.ts +1186 -0
- package/src/provision-roles.ts +534 -1
- package/src/runtime-posture-cli.ts +9 -0
- package/src/runtime-posture.ts +721 -5
- package/src/schema.ts +6270 -3159
- package/src/scoped-knowledge-schema.ts +1 -1
- package/src/scoped-knowledge.ts +655 -3
- package/src/session-control.ts +130 -42
- package/src/session-queue-commands.ts +265 -15
- 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-tenancy.ts +188 -0
- package/src/session-tool-call-settlement.ts +18 -3
- package/src/slack-task-policy-schema.ts +130 -0
- package/src/slack-task-policy.ts +341 -0
- package/src/slack-user-link-access.ts +702 -0
- package/src/task-notes-schema.ts +135 -0
- package/src/task-notes.ts +178 -0
- package/src/video-generation.ts +1530 -0
- package/src/workspace-artifacts.ts +1 -1
- package/src/workspace-learning-policy-schema.ts +139 -0
- package/src/workspace-learning-policy.ts +595 -0
- package/src/xai-subscription.ts +1887 -0
- package/dist/chunk-P6CUHXQZ.js +0 -7248
- package/dist/chunk-P6CUHXQZ.js.map +0 -1
- package/dist/chunk-VHBFHMPC.js +0 -1219
- package/dist/chunk-VHBFHMPC.js.map +0 -1
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Replace the hidden per-row activity-counter allocator with one explicit
|
|
3
|
+
-- once-per-transaction commit gate. Readers become ordinary MVCC reads; writers
|
|
4
|
+
-- settle every other constraint before briefly advancing the workspace clock.
|
|
5
|
+
-- The durable ledger remains the source of truth; no application-local cache participates.
|
|
6
|
+
|
|
7
|
+
SET lock_timeout = '5s';
|
|
8
|
+
SET statement_timeout = '10min';
|
|
9
|
+
|
|
10
|
+
ALTER TABLE "sessions"
|
|
11
|
+
ADD COLUMN "activity_revision_pending_xid" bigint;
|
|
12
|
+
|
|
13
|
+
CREATE INDEX "sessions_workspace_activity_pending_idx"
|
|
14
|
+
ON "sessions" ("workspace_id", "activity_revision_pending_xid", "id")
|
|
15
|
+
WHERE "activity_revision_pending_xid" IS NOT NULL;
|
|
16
|
+
|
|
17
|
+
CREATE OR REPLACE FUNCTION opengeni_private.ensure_workspace_session_activity_revision(
|
|
18
|
+
target_schema text,
|
|
19
|
+
target_workspace_id uuid,
|
|
20
|
+
target_account_id uuid
|
|
21
|
+
)
|
|
22
|
+
RETURNS void
|
|
23
|
+
LANGUAGE plpgsql
|
|
24
|
+
SECURITY DEFINER
|
|
25
|
+
SET search_path = pg_catalog
|
|
26
|
+
AS $function$
|
|
27
|
+
DECLARE
|
|
28
|
+
previous_account_id text := current_setting('opengeni.account_id', true);
|
|
29
|
+
previous_workspace_id text := current_setting('opengeni.workspace_id', true);
|
|
30
|
+
BEGIN
|
|
31
|
+
-- FORCE RLS applies to supported non-bypass table owners too. Establish only
|
|
32
|
+
-- the row's own trusted scope, then restore the caller's transaction-local
|
|
33
|
+
-- context so structural initialization cannot leak authority downstream.
|
|
34
|
+
PERFORM set_config('opengeni.account_id', target_account_id::text, true);
|
|
35
|
+
PERFORM set_config('opengeni.workspace_id', target_workspace_id::text, true);
|
|
36
|
+
EXECUTE format(
|
|
37
|
+
'INSERT INTO %I.workspace_session_activity_revisions '
|
|
38
|
+
|| '(workspace_id, account_id, revision) VALUES ($1, $2, 0) '
|
|
39
|
+
|| 'ON CONFLICT (workspace_id) DO NOTHING',
|
|
40
|
+
target_schema
|
|
41
|
+
)
|
|
42
|
+
USING target_workspace_id, target_account_id;
|
|
43
|
+
PERFORM set_config('opengeni.account_id', coalesce(previous_account_id, ''), true);
|
|
44
|
+
PERFORM set_config('opengeni.workspace_id', coalesce(previous_workspace_id, ''), true);
|
|
45
|
+
EXCEPTION WHEN OTHERS THEN
|
|
46
|
+
PERFORM set_config('opengeni.account_id', coalesce(previous_account_id, ''), true);
|
|
47
|
+
PERFORM set_config('opengeni.workspace_id', coalesce(previous_workspace_id, ''), true);
|
|
48
|
+
RAISE;
|
|
49
|
+
END;
|
|
50
|
+
$function$;
|
|
51
|
+
|
|
52
|
+
REVOKE ALL ON FUNCTION opengeni_private.ensure_workspace_session_activity_revision(
|
|
53
|
+
text, uuid, uuid
|
|
54
|
+
) FROM PUBLIC;
|
|
55
|
+
|
|
56
|
+
CREATE OR REPLACE FUNCTION opengeni_private.initialize_workspace_session_activity_revision()
|
|
57
|
+
RETURNS trigger
|
|
58
|
+
LANGUAGE plpgsql
|
|
59
|
+
SECURITY DEFINER
|
|
60
|
+
SET search_path = pg_catalog
|
|
61
|
+
AS $function$
|
|
62
|
+
BEGIN
|
|
63
|
+
PERFORM opengeni_private.ensure_workspace_session_activity_revision(
|
|
64
|
+
TG_TABLE_SCHEMA,
|
|
65
|
+
NEW.id,
|
|
66
|
+
NEW.account_id
|
|
67
|
+
);
|
|
68
|
+
RETURN NEW;
|
|
69
|
+
END;
|
|
70
|
+
$function$;
|
|
71
|
+
|
|
72
|
+
REVOKE ALL ON FUNCTION opengeni_private.initialize_workspace_session_activity_revision()
|
|
73
|
+
FROM PUBLIC;
|
|
74
|
+
|
|
75
|
+
CREATE TRIGGER workspaces_initialize_session_activity_revision
|
|
76
|
+
AFTER INSERT ON "workspaces"
|
|
77
|
+
FOR EACH ROW
|
|
78
|
+
EXECUTE FUNCTION opengeni_private.initialize_workspace_session_activity_revision();
|
|
79
|
+
|
|
80
|
+
-- Install the structural initializer before taking the existing-workspace
|
|
81
|
+
-- snapshot. CREATE TRIGGER fences concurrent workspace writers; the subsequent
|
|
82
|
+
-- conflict-safe backfill covers every row that committed before the trigger,
|
|
83
|
+
-- while later rows are covered by the trigger itself. The same private helper
|
|
84
|
+
-- keeps this path valid when the schema owner is subject to FORCE RLS.
|
|
85
|
+
SELECT opengeni_private.ensure_workspace_session_activity_revision(
|
|
86
|
+
current_schema(),
|
|
87
|
+
"id",
|
|
88
|
+
"account_id"
|
|
89
|
+
)
|
|
90
|
+
FROM "workspaces";
|
|
91
|
+
|
|
92
|
+
-- Counter lifecycle is now structural: the workspace trigger creates rows and
|
|
93
|
+
-- the foreign-key cascade removes them. The application may only observe and
|
|
94
|
+
-- advance an existing counter at the explicit commit gate.
|
|
95
|
+
DO $grants$
|
|
96
|
+
DECLARE target_schema text := current_schema();
|
|
97
|
+
BEGIN
|
|
98
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
99
|
+
EXECUTE format(
|
|
100
|
+
'REVOKE INSERT, DELETE ON TABLE %I.workspace_session_activity_revisions FROM opengeni_app',
|
|
101
|
+
target_schema
|
|
102
|
+
);
|
|
103
|
+
END IF;
|
|
104
|
+
END $grants$;
|
|
105
|
+
|
|
106
|
+
-- UPDATE remains necessary for the runtime finalizer, but the table itself
|
|
107
|
+
-- rejects every other write shape. This catches a forgotten wrapper even when
|
|
108
|
+
-- application code reaches for the counter directly instead of a session row.
|
|
109
|
+
CREATE OR REPLACE FUNCTION opengeni_private.guard_workspace_session_activity_revision()
|
|
110
|
+
RETURNS trigger
|
|
111
|
+
LANGUAGE plpgsql
|
|
112
|
+
SET search_path = pg_catalog
|
|
113
|
+
AS $function$
|
|
114
|
+
DECLARE
|
|
115
|
+
gate_state text := nullif(current_setting('opengeni.session_activity_gate_state', true), '');
|
|
116
|
+
gate_workspace_id uuid := nullif(
|
|
117
|
+
current_setting('opengeni.session_activity_gate_workspace_id', true),
|
|
118
|
+
''
|
|
119
|
+
)::uuid;
|
|
120
|
+
BEGIN
|
|
121
|
+
IF gate_state <> 'finalizing'
|
|
122
|
+
OR gate_workspace_id IS DISTINCT FROM OLD.workspace_id
|
|
123
|
+
OR NEW.workspace_id IS DISTINCT FROM OLD.workspace_id
|
|
124
|
+
OR NEW.account_id IS DISTINCT FROM OLD.account_id
|
|
125
|
+
OR NEW.revision IS DISTINCT FROM OLD.revision + 1
|
|
126
|
+
THEN
|
|
127
|
+
RAISE EXCEPTION USING
|
|
128
|
+
ERRCODE = '55000',
|
|
129
|
+
MESSAGE = 'Workspace session activity revision requires the finalizing commit gate';
|
|
130
|
+
END IF;
|
|
131
|
+
RETURN NEW;
|
|
132
|
+
END;
|
|
133
|
+
$function$;
|
|
134
|
+
|
|
135
|
+
REVOKE ALL ON FUNCTION opengeni_private.guard_workspace_session_activity_revision()
|
|
136
|
+
FROM PUBLIC;
|
|
137
|
+
|
|
138
|
+
CREATE TRIGGER workspace_session_activity_revision_commit_gate
|
|
139
|
+
BEFORE UPDATE ON "workspace_session_activity_revisions"
|
|
140
|
+
FOR EACH ROW
|
|
141
|
+
EXECUTE FUNCTION opengeni_private.guard_workspace_session_activity_revision();
|
|
142
|
+
|
|
143
|
+
DROP TRIGGER sessions_assign_activity_revision ON "sessions";
|
|
144
|
+
DROP FUNCTION opengeni_private.assign_session_activity_revision();
|
|
145
|
+
|
|
146
|
+
CREATE OR REPLACE FUNCTION opengeni_private.mark_session_activity_pending()
|
|
147
|
+
RETURNS trigger
|
|
148
|
+
LANGUAGE plpgsql
|
|
149
|
+
SET search_path = pg_catalog
|
|
150
|
+
AS $function$
|
|
151
|
+
DECLARE
|
|
152
|
+
gate_state text := nullif(current_setting('opengeni.session_activity_gate_state', true), '');
|
|
153
|
+
gate_workspace_id uuid := nullif(
|
|
154
|
+
current_setting('opengeni.session_activity_gate_workspace_id', true),
|
|
155
|
+
''
|
|
156
|
+
)::uuid;
|
|
157
|
+
current_xid bigint := pg_current_xact_id()::text::bigint;
|
|
158
|
+
BEGIN
|
|
159
|
+
IF gate_state = 'finalizing' THEN
|
|
160
|
+
IF TG_OP <> 'UPDATE'
|
|
161
|
+
OR NEW.workspace_id IS DISTINCT FROM OLD.workspace_id
|
|
162
|
+
OR NEW.updated_at IS DISTINCT FROM OLD.updated_at
|
|
163
|
+
OR OLD.activity_revision_pending_xid IS DISTINCT FROM current_xid
|
|
164
|
+
OR NEW.activity_revision_pending_xid IS NOT NULL
|
|
165
|
+
OR NEW.activity_revision <= OLD.activity_revision
|
|
166
|
+
THEN
|
|
167
|
+
RAISE EXCEPTION USING
|
|
168
|
+
ERRCODE = '55000',
|
|
169
|
+
MESSAGE = 'Invalid session activity finalization';
|
|
170
|
+
END IF;
|
|
171
|
+
RETURN NEW;
|
|
172
|
+
END IF;
|
|
173
|
+
|
|
174
|
+
IF gate_state <> 'open' OR gate_workspace_id IS DISTINCT FROM NEW.workspace_id THEN
|
|
175
|
+
RAISE EXCEPTION USING
|
|
176
|
+
ERRCODE = '55000',
|
|
177
|
+
MESSAGE = 'Session activity write requires an open workspace commit gate';
|
|
178
|
+
END IF;
|
|
179
|
+
|
|
180
|
+
IF TG_OP = 'INSERT' THEN
|
|
181
|
+
IF NEW.activity_revision <> 0 OR NEW.activity_revision_pending_xid IS NOT NULL THEN
|
|
182
|
+
RAISE EXCEPTION USING
|
|
183
|
+
ERRCODE = '55000',
|
|
184
|
+
MESSAGE = 'Session activity revision is managed by the commit gate';
|
|
185
|
+
END IF;
|
|
186
|
+
ELSIF NEW.activity_revision IS DISTINCT FROM OLD.activity_revision
|
|
187
|
+
OR NEW.activity_revision_pending_xid IS DISTINCT FROM OLD.activity_revision_pending_xid
|
|
188
|
+
THEN
|
|
189
|
+
RAISE EXCEPTION USING
|
|
190
|
+
ERRCODE = '55000',
|
|
191
|
+
MESSAGE = 'Session activity revision is managed by the commit gate';
|
|
192
|
+
END IF;
|
|
193
|
+
|
|
194
|
+
NEW.activity_revision_pending_xid := current_xid;
|
|
195
|
+
RETURN NEW;
|
|
196
|
+
END;
|
|
197
|
+
$function$;
|
|
198
|
+
|
|
199
|
+
REVOKE ALL ON FUNCTION opengeni_private.mark_session_activity_pending()
|
|
200
|
+
FROM PUBLIC;
|
|
201
|
+
|
|
202
|
+
CREATE TRIGGER sessions_mark_activity_pending
|
|
203
|
+
BEFORE INSERT OR UPDATE OF "updated_at", "activity_revision", "activity_revision_pending_xid"
|
|
204
|
+
ON "sessions"
|
|
205
|
+
FOR EACH ROW
|
|
206
|
+
EXECUTE FUNCTION opengeni_private.mark_session_activity_pending();
|
|
207
|
+
|
|
208
|
+
-- A transaction that opens the gate manually but omits the finalizer still
|
|
209
|
+
-- cannot commit. The application gate switches to `preparing` before forcing
|
|
210
|
+
-- all deferred checks, then clears every pending marker in its final write.
|
|
211
|
+
CREATE OR REPLACE FUNCTION opengeni_private.reject_unfinalized_session_activity()
|
|
212
|
+
RETURNS trigger
|
|
213
|
+
LANGUAGE plpgsql
|
|
214
|
+
SET search_path = pg_catalog
|
|
215
|
+
AS $function$
|
|
216
|
+
DECLARE
|
|
217
|
+
gate_state text := nullif(current_setting('opengeni.session_activity_gate_state', true), '');
|
|
218
|
+
still_pending_xid bigint;
|
|
219
|
+
BEGIN
|
|
220
|
+
IF gate_state = 'preparing' THEN
|
|
221
|
+
RETURN NULL;
|
|
222
|
+
END IF;
|
|
223
|
+
EXECUTE format(
|
|
224
|
+
'SELECT activity_revision_pending_xid FROM %I.sessions WHERE id = $1',
|
|
225
|
+
TG_TABLE_SCHEMA
|
|
226
|
+
)
|
|
227
|
+
INTO still_pending_xid
|
|
228
|
+
USING NEW.id;
|
|
229
|
+
IF still_pending_xid IS NOT NULL THEN
|
|
230
|
+
RAISE EXCEPTION USING
|
|
231
|
+
ERRCODE = '55000',
|
|
232
|
+
MESSAGE = 'Session activity transaction reached commit without finalization';
|
|
233
|
+
END IF;
|
|
234
|
+
RETURN NULL;
|
|
235
|
+
END;
|
|
236
|
+
$function$;
|
|
237
|
+
|
|
238
|
+
REVOKE ALL ON FUNCTION opengeni_private.reject_unfinalized_session_activity()
|
|
239
|
+
FROM PUBLIC;
|
|
240
|
+
|
|
241
|
+
CREATE CONSTRAINT TRIGGER sessions_activity_insert_commit_guard
|
|
242
|
+
AFTER INSERT ON "sessions"
|
|
243
|
+
DEFERRABLE INITIALLY DEFERRED
|
|
244
|
+
FOR EACH ROW
|
|
245
|
+
WHEN (NEW.activity_revision_pending_xid IS NOT NULL)
|
|
246
|
+
EXECUTE FUNCTION opengeni_private.reject_unfinalized_session_activity();
|
|
247
|
+
|
|
248
|
+
-- The clearing write must queue a second check as well. The application first
|
|
249
|
+
-- flushes all existing deferred constraints, re-defers these two guards, stamps
|
|
250
|
+
-- every pending row, and then forces both guards against the final row state.
|
|
251
|
+
CREATE CONSTRAINT TRIGGER sessions_activity_update_commit_guard
|
|
252
|
+
AFTER UPDATE ON "sessions"
|
|
253
|
+
DEFERRABLE INITIALLY DEFERRED
|
|
254
|
+
FOR EACH ROW
|
|
255
|
+
WHEN (
|
|
256
|
+
NEW.activity_revision_pending_xid IS NOT NULL
|
|
257
|
+
OR OLD.activity_revision_pending_xid IS NOT NULL
|
|
258
|
+
)
|
|
259
|
+
EXECUTE FUNCTION opengeni_private.reject_unfinalized_session_activity();
|
|
260
|
+
|
|
261
|
+
RESET statement_timeout;
|
|
262
|
+
RESET lock_timeout;
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Separate a remote browser's logical placement from the sandbox that hosts
|
|
3
|
+
-- its sole browserd mutation controller. Existing managed sessions preserve
|
|
4
|
+
-- their current home placement; remote rows are backfilled from their immutable
|
|
5
|
+
-- creation association.
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '30s';
|
|
8
|
+
|
|
9
|
+
ALTER TABLE "browser_sessions"
|
|
10
|
+
ADD COLUMN "controller_host_sandbox_group_id" uuid;
|
|
11
|
+
|
|
12
|
+
UPDATE "browser_sessions"
|
|
13
|
+
SET "controller_host_sandbox_group_id" = "sandbox_group_id"
|
|
14
|
+
WHERE "placement_kind" = 'sandbox_group';
|
|
15
|
+
|
|
16
|
+
UPDATE "browser_sessions" browser
|
|
17
|
+
SET "controller_host_sandbox_group_id" = source."sandbox_group_id"
|
|
18
|
+
FROM "browser_session_associations" association
|
|
19
|
+
JOIN "sessions" source
|
|
20
|
+
ON source."workspace_id" = association."workspace_id"
|
|
21
|
+
AND source."id" = association."session_id"
|
|
22
|
+
WHERE browser."workspace_id" = association."workspace_id"
|
|
23
|
+
AND browser."id" = association."browser_session_id"
|
|
24
|
+
AND browser."placement_kind" = 'external_provider'
|
|
25
|
+
AND association."relationship" = 'created';
|
|
26
|
+
|
|
27
|
+
ALTER TABLE "browser_sessions"
|
|
28
|
+
ADD CONSTRAINT "browser_sessions_controller_host_check" CHECK (
|
|
29
|
+
("placement_kind" = 'sandbox_group'
|
|
30
|
+
AND "controller_host_sandbox_group_id" = "sandbox_group_id")
|
|
31
|
+
OR
|
|
32
|
+
("placement_kind" = 'external_provider'
|
|
33
|
+
AND "controller_host_sandbox_group_id" IS NOT NULL)
|
|
34
|
+
OR
|
|
35
|
+
("placement_kind" IN ('connected_machine', 'attached_device')
|
|
36
|
+
AND "controller_host_sandbox_group_id" IS NULL)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX "browser_sessions_controller_host_sandbox_group_idx"
|
|
40
|
+
ON "browser_sessions"
|
|
41
|
+
("workspace_id", "controller_host_sandbox_group_id", "lifecycle");
|
|
42
|
+
|
|
43
|
+
-- Reinstall the interaction-aware lease reaper against controller locality,
|
|
44
|
+
-- not logical browser locality. This keeps a remote browser alive exactly while
|
|
45
|
+
-- its home browserd lease and holder remain authoritative.
|
|
46
|
+
DO $interaction_aware_reaper$
|
|
47
|
+
DECLARE data_schema text := current_schema();
|
|
48
|
+
BEGIN
|
|
49
|
+
EXECUTE format($create$
|
|
50
|
+
CREATE OR REPLACE FUNCTION opengeni_private.reap_sandbox_leases(
|
|
51
|
+
p_viewer_holder_ttl_ms bigint,
|
|
52
|
+
p_turn_holder_ttl_ms bigint,
|
|
53
|
+
p_interaction_holder_ttl_ms bigint,
|
|
54
|
+
p_idle_grace_ms bigint
|
|
55
|
+
)
|
|
56
|
+
RETURNS TABLE (
|
|
57
|
+
workspace_id uuid,
|
|
58
|
+
sandbox_group_id uuid,
|
|
59
|
+
instance_id text,
|
|
60
|
+
lease_epoch integer
|
|
61
|
+
)
|
|
62
|
+
LANGUAGE plpgsql
|
|
63
|
+
SECURITY DEFINER
|
|
64
|
+
SET search_path = pg_catalog
|
|
65
|
+
AS $function$
|
|
66
|
+
DECLARE
|
|
67
|
+
locked_ids uuid[];
|
|
68
|
+
stale_interaction_ids uuid[];
|
|
69
|
+
changed_interaction_workspaces uuid[];
|
|
70
|
+
BEGIN
|
|
71
|
+
PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
|
|
72
|
+
|
|
73
|
+
SELECT coalesce(pg_catalog.array_agg(candidate.id), ARRAY[]::uuid[])
|
|
74
|
+
INTO locked_ids
|
|
75
|
+
FROM (
|
|
76
|
+
SELECT lease.id
|
|
77
|
+
FROM %1$I.sandbox_leases lease
|
|
78
|
+
WHERE lease.liveness <> 'cold'
|
|
79
|
+
OR EXISTS (
|
|
80
|
+
SELECT 1
|
|
81
|
+
FROM %1$I.sandbox_lease_holders holder
|
|
82
|
+
WHERE holder.lease_id = lease.id
|
|
83
|
+
)
|
|
84
|
+
ORDER BY lease.updated_at, lease.id
|
|
85
|
+
LIMIT 500
|
|
86
|
+
FOR UPDATE OF lease SKIP LOCKED
|
|
87
|
+
) candidate;
|
|
88
|
+
|
|
89
|
+
IF pg_catalog.cardinality(locked_ids) = 0 THEN
|
|
90
|
+
RETURN;
|
|
91
|
+
END IF;
|
|
92
|
+
|
|
93
|
+
DELETE FROM %1$I.sandbox_lease_holders holder
|
|
94
|
+
USING (
|
|
95
|
+
SELECT stale.id
|
|
96
|
+
FROM %1$I.sandbox_lease_holders stale
|
|
97
|
+
WHERE stale.lease_id = ANY(locked_ids)
|
|
98
|
+
AND stale.kind IN ('viewer', 'direct')
|
|
99
|
+
AND stale.last_heartbeat_at
|
|
100
|
+
< pg_catalog.now()
|
|
101
|
+
- pg_catalog.make_interval(secs => p_viewer_holder_ttl_ms / 1000.0)
|
|
102
|
+
FOR UPDATE OF stale SKIP LOCKED
|
|
103
|
+
) victim
|
|
104
|
+
WHERE holder.id = victim.id;
|
|
105
|
+
|
|
106
|
+
IF p_turn_holder_ttl_ms > 0 THEN
|
|
107
|
+
DELETE FROM %1$I.sandbox_lease_holders holder
|
|
108
|
+
USING (
|
|
109
|
+
SELECT stale.id
|
|
110
|
+
FROM %1$I.sandbox_lease_holders stale
|
|
111
|
+
WHERE stale.lease_id = ANY(locked_ids)
|
|
112
|
+
AND stale.kind = 'turn'
|
|
113
|
+
AND (
|
|
114
|
+
stale.last_heartbeat_at
|
|
115
|
+
< pg_catalog.now()
|
|
116
|
+
- pg_catalog.make_interval(secs => p_turn_holder_ttl_ms / 1000.0)
|
|
117
|
+
OR (
|
|
118
|
+
stale.holder_id ~* '^turn-attempt:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
|
|
119
|
+
AND NOT EXISTS (
|
|
120
|
+
SELECT 1
|
|
121
|
+
FROM %1$I.session_turn_attempts attempt
|
|
122
|
+
JOIN %1$I.session_turns turn
|
|
123
|
+
ON turn.account_id = attempt.account_id
|
|
124
|
+
AND turn.workspace_id = attempt.workspace_id
|
|
125
|
+
AND turn.session_id = attempt.session_id
|
|
126
|
+
AND turn.id = attempt.turn_id
|
|
127
|
+
AND turn.execution_generation = attempt.execution_generation
|
|
128
|
+
AND turn.active_attempt_id = attempt.id
|
|
129
|
+
WHERE stale.holder_id = ('turn-attempt:' || attempt.id::text)
|
|
130
|
+
AND attempt.account_id = stale.account_id
|
|
131
|
+
AND attempt.workspace_id = stale.workspace_id
|
|
132
|
+
AND attempt.session_id = stale.subject_id
|
|
133
|
+
AND attempt.state IN ('claimed', 'running')
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
FOR UPDATE OF stale SKIP LOCKED
|
|
138
|
+
) victim
|
|
139
|
+
WHERE holder.id = victim.id;
|
|
140
|
+
END IF;
|
|
141
|
+
|
|
142
|
+
IF p_interaction_holder_ttl_ms > 0 THEN
|
|
143
|
+
SELECT coalesce(pg_catalog.array_agg(candidate.id), ARRAY[]::uuid[])
|
|
144
|
+
INTO stale_interaction_ids
|
|
145
|
+
FROM (
|
|
146
|
+
SELECT stale.id
|
|
147
|
+
FROM %1$I.sandbox_lease_holders stale
|
|
148
|
+
JOIN %1$I.sandbox_leases lease ON lease.id = stale.lease_id
|
|
149
|
+
WHERE stale.lease_id = ANY(locked_ids)
|
|
150
|
+
AND stale.kind = 'interaction'
|
|
151
|
+
AND (
|
|
152
|
+
stale.last_heartbeat_at
|
|
153
|
+
< pg_catalog.now()
|
|
154
|
+
- pg_catalog.make_interval(
|
|
155
|
+
secs => p_interaction_holder_ttl_ms / 1000.0
|
|
156
|
+
)
|
|
157
|
+
OR NOT (
|
|
158
|
+
EXISTS (
|
|
159
|
+
SELECT 1
|
|
160
|
+
FROM %1$I.browser_sessions browser
|
|
161
|
+
WHERE stale.holder_id = ('browser-session:' || browser.id::text)
|
|
162
|
+
AND browser.account_id = stale.account_id
|
|
163
|
+
AND browser.workspace_id = stale.workspace_id
|
|
164
|
+
AND browser.controller_host_sandbox_group_id = lease.sandbox_group_id
|
|
165
|
+
AND browser.lifecycle IN (
|
|
166
|
+
'starting', 'active', 'suspending', 'restoring', 'ending'
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
OR EXISTS (
|
|
170
|
+
SELECT 1
|
|
171
|
+
FROM %1$I.computer_sessions computer
|
|
172
|
+
WHERE stale.holder_id = ('computer-session:' || computer.id::text)
|
|
173
|
+
AND computer.account_id = stale.account_id
|
|
174
|
+
AND computer.workspace_id = stale.workspace_id
|
|
175
|
+
AND computer.sandbox_group_id = lease.sandbox_group_id
|
|
176
|
+
AND computer.lifecycle IN (
|
|
177
|
+
'starting', 'active', 'suspending', 'restoring', 'ending'
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
FOR UPDATE OF stale SKIP LOCKED
|
|
183
|
+
) candidate;
|
|
184
|
+
|
|
185
|
+
IF pg_catalog.cardinality(stale_interaction_ids) > 0 THEN
|
|
186
|
+
WITH updated_browser AS (
|
|
187
|
+
UPDATE %1$I.browser_sessions browser
|
|
188
|
+
SET lifecycle = 'lost',
|
|
189
|
+
controller_id = null,
|
|
190
|
+
controller_generation = null,
|
|
191
|
+
placement_instance_id = null,
|
|
192
|
+
controller_heartbeat_at = null,
|
|
193
|
+
failure_code = 'controller_heartbeat_expired',
|
|
194
|
+
updated_at = pg_catalog.now()
|
|
195
|
+
FROM %1$I.sandbox_lease_holders holder
|
|
196
|
+
WHERE holder.id = ANY(stale_interaction_ids)
|
|
197
|
+
AND holder.holder_id = ('browser-session:' || browser.id::text)
|
|
198
|
+
AND holder.account_id = browser.account_id
|
|
199
|
+
AND holder.workspace_id = browser.workspace_id
|
|
200
|
+
AND browser.lifecycle IN (
|
|
201
|
+
'starting', 'active', 'suspending', 'restoring', 'ending'
|
|
202
|
+
)
|
|
203
|
+
RETURNING browser.workspace_id
|
|
204
|
+
), updated_computer AS (
|
|
205
|
+
UPDATE %1$I.computer_sessions computer
|
|
206
|
+
SET lifecycle = 'lost',
|
|
207
|
+
controller_id = null,
|
|
208
|
+
controller_generation = null,
|
|
209
|
+
placement_instance_id = null,
|
|
210
|
+
controller_heartbeat_at = null,
|
|
211
|
+
failure_code = 'controller_heartbeat_expired',
|
|
212
|
+
updated_at = pg_catalog.now()
|
|
213
|
+
FROM %1$I.sandbox_lease_holders holder
|
|
214
|
+
WHERE holder.id = ANY(stale_interaction_ids)
|
|
215
|
+
AND holder.holder_id = ('computer-session:' || computer.id::text)
|
|
216
|
+
AND holder.account_id = computer.account_id
|
|
217
|
+
AND holder.workspace_id = computer.workspace_id
|
|
218
|
+
AND computer.lifecycle IN (
|
|
219
|
+
'starting', 'active', 'suspending', 'restoring', 'ending'
|
|
220
|
+
)
|
|
221
|
+
RETURNING computer.workspace_id
|
|
222
|
+
), updated AS (
|
|
223
|
+
SELECT updated_browser.workspace_id AS interaction_workspace_id
|
|
224
|
+
FROM updated_browser
|
|
225
|
+
UNION
|
|
226
|
+
SELECT updated_computer.workspace_id AS interaction_workspace_id
|
|
227
|
+
FROM updated_computer
|
|
228
|
+
)
|
|
229
|
+
SELECT coalesce(
|
|
230
|
+
pg_catalog.array_agg(DISTINCT updated.interaction_workspace_id),
|
|
231
|
+
ARRAY[]::uuid[]
|
|
232
|
+
)
|
|
233
|
+
INTO changed_interaction_workspaces
|
|
234
|
+
FROM updated;
|
|
235
|
+
|
|
236
|
+
DELETE FROM %1$I.sandbox_lease_holders holder
|
|
237
|
+
WHERE holder.id = ANY(stale_interaction_ids);
|
|
238
|
+
|
|
239
|
+
UPDATE %1$I.workspace_interaction_revisions revision
|
|
240
|
+
SET revision = revision.revision + 1,
|
|
241
|
+
updated_at = pg_catalog.now()
|
|
242
|
+
WHERE revision.workspace_id = ANY(changed_interaction_workspaces);
|
|
243
|
+
END IF;
|
|
244
|
+
END IF;
|
|
245
|
+
|
|
246
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
247
|
+
refcount = counts.total,
|
|
248
|
+
turn_holders = counts.turns,
|
|
249
|
+
viewer_holders = counts.viewers,
|
|
250
|
+
liveness = CASE
|
|
251
|
+
WHEN lease.liveness = 'warm'
|
|
252
|
+
AND counts.total = 0
|
|
253
|
+
AND counts.turns = 0
|
|
254
|
+
AND (
|
|
255
|
+
lease.reaper_hold_id IS NULL
|
|
256
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
257
|
+
)
|
|
258
|
+
THEN 'draining' ELSE lease.liveness END,
|
|
259
|
+
expires_at = CASE
|
|
260
|
+
WHEN lease.liveness = 'warm'
|
|
261
|
+
AND counts.total = 0
|
|
262
|
+
AND counts.turns = 0
|
|
263
|
+
AND (
|
|
264
|
+
lease.reaper_hold_id IS NULL
|
|
265
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
266
|
+
)
|
|
267
|
+
THEN CASE
|
|
268
|
+
WHEN lease.rotation_requested_at IS NOT NULL
|
|
269
|
+
OR lease.archive_capture_id IS NOT NULL
|
|
270
|
+
THEN pg_catalog.now() - interval '1 millisecond'
|
|
271
|
+
ELSE pg_catalog.now()
|
|
272
|
+
+ pg_catalog.make_interval(secs => p_idle_grace_ms / 1000.0)
|
|
273
|
+
END
|
|
274
|
+
ELSE lease.expires_at END,
|
|
275
|
+
updated_at = pg_catalog.now()
|
|
276
|
+
FROM (
|
|
277
|
+
SELECT candidate.id,
|
|
278
|
+
pg_catalog.count(holder.id)::int AS total,
|
|
279
|
+
pg_catalog.count(holder.id)
|
|
280
|
+
FILTER (WHERE holder.kind = 'turn')::int AS turns,
|
|
281
|
+
pg_catalog.count(holder.id)
|
|
282
|
+
FILTER (WHERE holder.kind = 'viewer')::int AS viewers
|
|
283
|
+
FROM pg_catalog.unnest(locked_ids) candidate(id)
|
|
284
|
+
LEFT JOIN %1$I.sandbox_lease_holders holder
|
|
285
|
+
ON holder.lease_id = candidate.id
|
|
286
|
+
GROUP BY candidate.id
|
|
287
|
+
) counts
|
|
288
|
+
WHERE lease.id = counts.id;
|
|
289
|
+
|
|
290
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
291
|
+
liveness = 'cold',
|
|
292
|
+
instance_id = null,
|
|
293
|
+
lease_epoch = lease.lease_epoch + 1,
|
|
294
|
+
reaper_hold_id = null,
|
|
295
|
+
reaper_hold_until = null,
|
|
296
|
+
reaper_hold_reason = null,
|
|
297
|
+
resume_state = opengeni_private.warming_reset_resume_state_v2(
|
|
298
|
+
lease.backend,
|
|
299
|
+
lease.resume_backend_id,
|
|
300
|
+
lease.resume_state,
|
|
301
|
+
lease.workspace_generation,
|
|
302
|
+
lease.archive_generation,
|
|
303
|
+
pg_catalog.clock_timestamp()
|
|
304
|
+
),
|
|
305
|
+
resume_backend_id = CASE
|
|
306
|
+
WHEN coalesce(
|
|
307
|
+
lease.resume_state #>> '{sessionState,workspaceArchive}', ''
|
|
308
|
+
) <> ''
|
|
309
|
+
OR coalesce(
|
|
310
|
+
lease.resume_state #>> '{sessionState,workspaceArchivePrev}', ''
|
|
311
|
+
) <> ''
|
|
312
|
+
THEN coalesce(lease.resume_backend_id, lease.backend)
|
|
313
|
+
ELSE null END,
|
|
314
|
+
data_plane_url = null,
|
|
315
|
+
terminal_data_plane_url = null,
|
|
316
|
+
provider_created_at = null,
|
|
317
|
+
provider_deadline_at = null,
|
|
318
|
+
rotation_requested_at = null,
|
|
319
|
+
rotation_reason = null,
|
|
320
|
+
updated_at = pg_catalog.now()
|
|
321
|
+
WHERE lease.id = ANY(locked_ids)
|
|
322
|
+
AND lease.liveness = 'warming'
|
|
323
|
+
AND lease.expires_at < pg_catalog.now()
|
|
324
|
+
AND lease.instance_id IS NULL
|
|
325
|
+
AND (
|
|
326
|
+
lease.reaper_hold_id IS NULL
|
|
327
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
331
|
+
liveness = 'draining',
|
|
332
|
+
refcount = 0,
|
|
333
|
+
turn_holders = 0,
|
|
334
|
+
viewer_holders = 0,
|
|
335
|
+
data_plane_url = null,
|
|
336
|
+
terminal_data_plane_url = null,
|
|
337
|
+
lease_epoch = lease.lease_epoch + 1,
|
|
338
|
+
reaper_hold_id = null,
|
|
339
|
+
reaper_hold_until = null,
|
|
340
|
+
reaper_hold_reason = null,
|
|
341
|
+
expires_at = pg_catalog.now() - interval '1 millisecond',
|
|
342
|
+
updated_at = pg_catalog.now()
|
|
343
|
+
WHERE lease.id = ANY(locked_ids)
|
|
344
|
+
AND lease.liveness = 'warming'
|
|
345
|
+
AND lease.expires_at < pg_catalog.now()
|
|
346
|
+
AND lease.instance_id IS NOT NULL
|
|
347
|
+
AND (
|
|
348
|
+
lease.reaper_hold_id IS NULL
|
|
349
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
RETURN QUERY
|
|
353
|
+
SELECT lease.workspace_id, lease.sandbox_group_id,
|
|
354
|
+
lease.instance_id, lease.lease_epoch
|
|
355
|
+
FROM %1$I.sandbox_leases lease
|
|
356
|
+
WHERE lease.id = ANY(locked_ids)
|
|
357
|
+
AND lease.liveness = 'draining'
|
|
358
|
+
AND lease.expires_at < pg_catalog.now()
|
|
359
|
+
AND lease.refcount = 0
|
|
360
|
+
AND (
|
|
361
|
+
lease.reaper_hold_id IS NULL
|
|
362
|
+
OR lease.reaper_hold_until <= pg_catalog.now()
|
|
363
|
+
);
|
|
364
|
+
END;
|
|
365
|
+
$function$;
|
|
366
|
+
$create$, data_schema);
|
|
367
|
+
END
|
|
368
|
+
$interaction_aware_reaper$;
|
|
369
|
+
|
|
370
|
+
REVOKE ALL ON FUNCTION opengeni_private.reap_sandbox_leases(
|
|
371
|
+
bigint, bigint, bigint, bigint
|
|
372
|
+
) FROM PUBLIC;
|
|
373
|
+
|
|
374
|
+
DO $reaper_grant$
|
|
375
|
+
BEGIN
|
|
376
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
377
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.reap_sandbox_leases(
|
|
378
|
+
bigint, bigint, bigint, bigint
|
|
379
|
+
) TO opengeni_app;
|
|
380
|
+
END IF;
|
|
381
|
+
END
|
|
382
|
+
$reaper_grant$;
|
|
383
|
+
|
|
384
|
+
RESET statement_timeout;
|
|
385
|
+
RESET lock_timeout;
|