@opengeni/db 3.7.4-canary.0 → 3.8.2-canary.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/canonical-human-identities.js +3 -3
- package/dist/{chunk-DXJ2WY6W.js → chunk-53A2PEHT.js} +241 -31
- package/dist/chunk-53A2PEHT.js.map +1 -0
- package/dist/{chunk-JS3AWDKZ.js → chunk-5S4BQAHX.js} +3 -3
- package/dist/{chunk-JP6IYGYI.js → chunk-CITYUGMW.js} +3 -3
- package/dist/{chunk-6FIVOJ3V.js → chunk-F5YOZEYQ.js} +45 -1
- package/dist/chunk-F5YOZEYQ.js.map +1 -0
- package/dist/{chunk-ZTSJPFDH.js → chunk-HZV7O6JR.js} +2 -2
- package/dist/{chunk-VDXHLVDE.js → chunk-RLQVPOVR.js} +2 -2
- package/dist/{chunk-LHKB62GC.js → chunk-UKOGHUGC.js} +2 -2
- package/dist/{chunk-QL4AHBYD.js → chunk-X6TO5HII.js} +2 -2
- package/dist/{chunk-3ZMN4EXC.js → chunk-Y53WEFYB.js} +2 -2
- package/dist/connection-token-resolver.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +59 -12
- package/dist/index.js +5259 -4374
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +9 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/new-session-drafts.d.ts +12 -0
- package/dist/organization-membership-lifecycle.d.ts +8 -1
- package/dist/organization-model-providers.d.ts +101 -0
- package/dist/pack-components.d.ts +1 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +5 -5
- package/dist/schema.d.ts +645 -0
- package/dist/schema.js +7 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/drizzle/0390_organization_model_provider_connections.sql +197 -0
- package/drizzle/0391_sandbox_provider_deadline_interaction_followup.sql +295 -0
- package/drizzle/0392_context_compaction_pending_observability.sql +198 -0
- package/drizzle/0393_workspace_memory_and_learning_defaults.sql +102 -0
- package/drizzle/0394_session_selected_skill_activation.sql +20 -0
- package/drizzle/0395_scheduled_task_unclaimed_occurrence_invalidation.sql +154 -0
- package/drizzle/0396_model_call_equivalent_credit_cost.sql +28 -0
- package/drizzle/0397_sandbox_deadline_rotation_preemption.sql +113 -0
- package/drizzle/0398_organization_workspace_management_entry.sql +206 -0
- package/drizzle/0399_additional_managed_organization_creation.sql +691 -0
- package/package.json +6 -6
- package/src/connection-token-resolver.ts +12 -0
- package/src/index.ts +701 -155
- package/src/insights-model-bundle.ts +54 -5
- package/src/insights.ts +32 -0
- package/src/new-session-drafts.ts +66 -21
- package/src/organization-membership-lifecycle.ts +47 -5
- package/src/organization-model-providers.ts +614 -0
- package/src/pack-components.ts +15 -2
- package/src/provision-roles.ts +2 -0
- package/src/runtime-posture.ts +44 -0
- package/src/schema.ts +250 -30
- package/dist/chunk-6FIVOJ3V.js.map +0 -1
- package/dist/chunk-DXJ2WY6W.js.map +0 -1
- /package/dist/{chunk-JS3AWDKZ.js.map → chunk-5S4BQAHX.js.map} +0 -0
- /package/dist/{chunk-JP6IYGYI.js.map → chunk-CITYUGMW.js.map} +0 -0
- /package/dist/{chunk-ZTSJPFDH.js.map → chunk-HZV7O6JR.js.map} +0 -0
- /package/dist/{chunk-VDXHLVDE.js.map → chunk-RLQVPOVR.js.map} +0 -0
- /package/dist/{chunk-LHKB62GC.js.map → chunk-UKOGHUGC.js.map} +0 -0
- /package/dist/{chunk-QL4AHBYD.js.map → chunk-X6TO5HII.js.map} +0 -0
- /package/dist/{chunk-3ZMN4EXC.js.map → chunk-Y53WEFYB.js.map} +0 -0
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
settleClaimedConnectedMachineBackgroundCommand,
|
|
13
13
|
settleConnectedMachineSessionBackgroundCommand,
|
|
14
14
|
settleSessionBackgroundCommandForRetainedProcess
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
15
|
+
} from "./chunk-CITYUGMW.js";
|
|
16
|
+
import "./chunk-HZV7O6JR.js";
|
|
17
|
+
import "./chunk-53A2PEHT.js";
|
|
18
18
|
import "./chunk-PZ5AY32C.js";
|
|
19
19
|
export {
|
|
20
20
|
backgroundCommandActivityForSessions,
|
package/dist/session-tenancy.js
CHANGED
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
replayAppliedSessionFork,
|
|
15
15
|
sessionTenancyProductActivated,
|
|
16
16
|
transitionSessionVisibility
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-RLQVPOVR.js";
|
|
18
|
+
import "./chunk-HZV7O6JR.js";
|
|
19
|
+
import "./chunk-53A2PEHT.js";
|
|
20
20
|
import "./chunk-PZ5AY32C.js";
|
|
21
21
|
export {
|
|
22
22
|
SessionTenancyAccessError,
|
package/dist/video-generation.js
CHANGED
|
@@ -29,9 +29,9 @@ import {
|
|
|
29
29
|
settleVideoGenerationFailure,
|
|
30
30
|
settleVideoGenerationReady,
|
|
31
31
|
updateWorkspaceVideoGenerationPolicy
|
|
32
|
-
} from "./chunk-
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-
|
|
32
|
+
} from "./chunk-5S4BQAHX.js";
|
|
33
|
+
import "./chunk-HZV7O6JR.js";
|
|
34
|
+
import "./chunk-53A2PEHT.js";
|
|
35
35
|
import "./chunk-PZ5AY32C.js";
|
|
36
36
|
export {
|
|
37
37
|
ACTIVE_VIDEO_GENERATION_STATUSES,
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Organization-owned Vercel AI Gateway/OpenRouter credentials and model catalog.
|
|
3
|
+
-- Personal workspaces are deliberately excluded from runtime inheritance.
|
|
4
|
+
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '5min';
|
|
7
|
+
|
|
8
|
+
DO $scope_visibility$
|
|
9
|
+
DECLARE data_schema text := current_schema();
|
|
10
|
+
BEGIN
|
|
11
|
+
EXECUTE format($ddl$
|
|
12
|
+
CREATE OR REPLACE FUNCTION opengeni_private.organization_model_provider_scope_visible(
|
|
13
|
+
p_account_id uuid
|
|
14
|
+
) RETURNS boolean
|
|
15
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
16
|
+
SET search_path = pg_catalog
|
|
17
|
+
AS $function$
|
|
18
|
+
DECLARE
|
|
19
|
+
workspace_value uuid := opengeni_private.current_workspace_id();
|
|
20
|
+
subject_value text := opengeni_private.current_subject_id();
|
|
21
|
+
visible boolean := false;
|
|
22
|
+
previous_lifecycle text := pg_catalog.current_setting(
|
|
23
|
+
'opengeni.organization_tenancy_lifecycle', true
|
|
24
|
+
);
|
|
25
|
+
BEGIN
|
|
26
|
+
IF p_account_id IS NULL
|
|
27
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
28
|
+
THEN RETURN false; END IF;
|
|
29
|
+
|
|
30
|
+
PERFORM pg_catalog.set_config(
|
|
31
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
32
|
+
'organization_membership_lifecycle', true
|
|
33
|
+
);
|
|
34
|
+
IF workspace_value IS NOT NULL THEN
|
|
35
|
+
SELECT EXISTS (
|
|
36
|
+
SELECT 1 FROM %1$I.workspaces workspace
|
|
37
|
+
WHERE workspace.account_id = p_account_id AND workspace.id = workspace_value
|
|
38
|
+
) AND NOT EXISTS (
|
|
39
|
+
SELECT 1 FROM %1$I.organization_memberships membership
|
|
40
|
+
WHERE membership.account_id = p_account_id
|
|
41
|
+
AND membership.personal_workspace_id = workspace_value
|
|
42
|
+
) INTO visible;
|
|
43
|
+
ELSIF subject_value IS NOT NULL AND (
|
|
44
|
+
subject_value LIKE 'user:%%'
|
|
45
|
+
OR (
|
|
46
|
+
subject_value = 'dev'
|
|
47
|
+
AND EXISTS (
|
|
48
|
+
SELECT 1 FROM %1$I.managed_accounts account
|
|
49
|
+
WHERE account.id = p_account_id
|
|
50
|
+
AND account.external_source = 'opengeni:local'
|
|
51
|
+
AND account.external_id = 'default'
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
) THEN
|
|
55
|
+
SELECT EXISTS (
|
|
56
|
+
SELECT 1 FROM %1$I.organization_memberships membership
|
|
57
|
+
WHERE membership.account_id = p_account_id
|
|
58
|
+
AND membership.subject_id = subject_value
|
|
59
|
+
AND membership.status = 'active'
|
|
60
|
+
AND membership.role IN ('owner', 'admin')
|
|
61
|
+
) INTO visible;
|
|
62
|
+
END IF;
|
|
63
|
+
PERFORM pg_catalog.set_config(
|
|
64
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
65
|
+
);
|
|
66
|
+
RETURN coalesce(visible, false);
|
|
67
|
+
EXCEPTION WHEN OTHERS THEN
|
|
68
|
+
PERFORM pg_catalog.set_config(
|
|
69
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
70
|
+
);
|
|
71
|
+
RAISE;
|
|
72
|
+
END
|
|
73
|
+
$function$;
|
|
74
|
+
$ddl$, data_schema);
|
|
75
|
+
END
|
|
76
|
+
$scope_visibility$;
|
|
77
|
+
|
|
78
|
+
CREATE TABLE organization_model_provider_connections (
|
|
79
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
80
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
81
|
+
provider_kind text NOT NULL CHECK (provider_kind IN ('vercel_gateway', 'openrouter')),
|
|
82
|
+
status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'revoked')),
|
|
83
|
+
credential_encrypted text NOT NULL,
|
|
84
|
+
version integer NOT NULL DEFAULT 1 CHECK (version > 0),
|
|
85
|
+
operation_id uuid NOT NULL,
|
|
86
|
+
request_hash text NOT NULL CHECK (request_hash ~ '^[a-f0-9]{64}$'),
|
|
87
|
+
updated_by_subject_id text NOT NULL CHECK (octet_length(updated_by_subject_id) BETWEEN 1 AND 1024),
|
|
88
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
89
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
90
|
+
UNIQUE (account_id, provider_kind),
|
|
91
|
+
UNIQUE (account_id, provider_kind, operation_id)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
CREATE TABLE organization_model_provider_connection_operations (
|
|
95
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
96
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
97
|
+
provider_kind text NOT NULL CHECK (provider_kind IN ('vercel_gateway', 'openrouter')),
|
|
98
|
+
operation_id uuid NOT NULL,
|
|
99
|
+
request_hash text NOT NULL CHECK (request_hash ~ '^[a-f0-9]{64}$'),
|
|
100
|
+
result_status text NOT NULL CHECK (result_status IN ('active', 'revoked')),
|
|
101
|
+
result_version integer NOT NULL CHECK (result_version > 0),
|
|
102
|
+
result_created_at timestamptz NOT NULL,
|
|
103
|
+
result_updated_at timestamptz NOT NULL,
|
|
104
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
105
|
+
UNIQUE (account_id, provider_kind, operation_id)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
CREATE TABLE organization_model_provider_custom_models (
|
|
109
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
110
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
111
|
+
provider_kind text NOT NULL CHECK (provider_kind IN ('vercel_gateway', 'openrouter')),
|
|
112
|
+
upstream_model_id text NOT NULL CHECK (
|
|
113
|
+
octet_length(upstream_model_id) BETWEEN 1 AND 238
|
|
114
|
+
AND upstream_model_id ~ '^[!-~]+$' AND upstream_model_id !~ '[|]'
|
|
115
|
+
),
|
|
116
|
+
label text CHECK (
|
|
117
|
+
label IS NULL OR (octet_length(label) BETWEEN 1 AND 128 AND label !~ '[\r\n|]')
|
|
118
|
+
),
|
|
119
|
+
version integer NOT NULL DEFAULT 1 CHECK (version > 0),
|
|
120
|
+
create_operation_id uuid NOT NULL,
|
|
121
|
+
create_request_hash text NOT NULL CHECK (create_request_hash ~ '^[a-f0-9]{64}$'),
|
|
122
|
+
delete_operation_id uuid,
|
|
123
|
+
delete_request_hash text,
|
|
124
|
+
created_by_subject_id text NOT NULL CHECK (octet_length(created_by_subject_id) BETWEEN 1 AND 1024),
|
|
125
|
+
retired_at timestamptz,
|
|
126
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
127
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
128
|
+
CHECK (
|
|
129
|
+
(delete_operation_id IS NULL AND delete_request_hash IS NULL)
|
|
130
|
+
OR (delete_operation_id IS NOT NULL AND delete_request_hash ~ '^[a-f0-9]{64}$' AND retired_at IS NOT NULL)
|
|
131
|
+
),
|
|
132
|
+
UNIQUE (account_id, provider_kind, create_operation_id)
|
|
133
|
+
);
|
|
134
|
+
CREATE UNIQUE INDEX organization_model_provider_custom_models_active_uq
|
|
135
|
+
ON organization_model_provider_custom_models(account_id, provider_kind, upstream_model_id)
|
|
136
|
+
WHERE retired_at IS NULL;
|
|
137
|
+
CREATE UNIQUE INDEX organization_model_provider_custom_models_delete_operation_uq
|
|
138
|
+
ON organization_model_provider_custom_models(account_id, provider_kind, delete_operation_id)
|
|
139
|
+
WHERE delete_operation_id IS NOT NULL;
|
|
140
|
+
|
|
141
|
+
ALTER TABLE organization_model_provider_connections ENABLE ROW LEVEL SECURITY;
|
|
142
|
+
ALTER TABLE organization_model_provider_connections FORCE ROW LEVEL SECURITY;
|
|
143
|
+
ALTER TABLE organization_model_provider_connection_operations ENABLE ROW LEVEL SECURITY;
|
|
144
|
+
ALTER TABLE organization_model_provider_connection_operations FORCE ROW LEVEL SECURITY;
|
|
145
|
+
ALTER TABLE organization_model_provider_custom_models ENABLE ROW LEVEL SECURITY;
|
|
146
|
+
ALTER TABLE organization_model_provider_custom_models FORCE ROW LEVEL SECURITY;
|
|
147
|
+
|
|
148
|
+
CREATE POLICY organization_model_provider_scope
|
|
149
|
+
ON organization_model_provider_connections
|
|
150
|
+
USING (opengeni_private.organization_model_provider_scope_visible(account_id))
|
|
151
|
+
WITH CHECK (opengeni_private.organization_model_provider_scope_visible(account_id));
|
|
152
|
+
CREATE POLICY organization_model_provider_scope
|
|
153
|
+
ON organization_model_provider_connection_operations
|
|
154
|
+
USING (opengeni_private.organization_model_provider_scope_visible(account_id))
|
|
155
|
+
WITH CHECK (opengeni_private.organization_model_provider_scope_visible(account_id));
|
|
156
|
+
CREATE POLICY organization_model_provider_scope
|
|
157
|
+
ON organization_model_provider_custom_models
|
|
158
|
+
USING (opengeni_private.organization_model_provider_scope_visible(account_id))
|
|
159
|
+
WITH CHECK (opengeni_private.organization_model_provider_scope_visible(account_id));
|
|
160
|
+
|
|
161
|
+
REVOKE ALL ON organization_model_provider_connections FROM PUBLIC;
|
|
162
|
+
REVOKE ALL ON organization_model_provider_connection_operations FROM PUBLIC;
|
|
163
|
+
REVOKE ALL ON organization_model_provider_custom_models FROM PUBLIC;
|
|
164
|
+
REVOKE ALL ON FUNCTION opengeni_private.organization_model_provider_scope_visible(uuid) FROM PUBLIC;
|
|
165
|
+
|
|
166
|
+
DO $application_grants$
|
|
167
|
+
DECLARE
|
|
168
|
+
data_schema text := current_schema();
|
|
169
|
+
application_role text;
|
|
170
|
+
BEGIN
|
|
171
|
+
FOR application_role IN
|
|
172
|
+
SELECT role_value.rolname
|
|
173
|
+
FROM pg_catalog.jsonb_array_elements_text(
|
|
174
|
+
coalesce(nullif(current_setting('opengeni.migration_application_roles', true), ''), '[]')::jsonb
|
|
175
|
+
) configured(value)
|
|
176
|
+
JOIN pg_catalog.pg_roles role_value ON role_value.rolname = configured.value
|
|
177
|
+
UNION SELECT 'opengeni_app'
|
|
178
|
+
WHERE EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'opengeni_app')
|
|
179
|
+
LOOP
|
|
180
|
+
EXECUTE format(
|
|
181
|
+
'GRANT SELECT, INSERT, UPDATE ON TABLE %1$I.organization_model_provider_connections, %1$I.organization_model_provider_connection_operations, %1$I.organization_model_provider_custom_models TO %2$I',
|
|
182
|
+
data_schema, application_role
|
|
183
|
+
);
|
|
184
|
+
EXECUTE format(
|
|
185
|
+
'GRANT EXECUTE ON FUNCTION opengeni_private.organization_model_provider_scope_visible(uuid) TO %I',
|
|
186
|
+
application_role
|
|
187
|
+
);
|
|
188
|
+
END LOOP;
|
|
189
|
+
END
|
|
190
|
+
$application_grants$;
|
|
191
|
+
|
|
192
|
+
COMMENT ON TABLE organization_model_provider_connections IS
|
|
193
|
+
'Encrypted organization-owned Vercel AI Gateway/OpenRouter credentials inherited by shared workspaces only.';
|
|
194
|
+
COMMENT ON TABLE organization_model_provider_connection_operations IS
|
|
195
|
+
'Durable secret-free idempotency receipts for organization provider connection mutations.';
|
|
196
|
+
COMMENT ON TABLE organization_model_provider_custom_models IS
|
|
197
|
+
'Retained organization provider model generations; active rows are fresh-selection authority.';
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Repair the post-0388 provider-deadline interaction lifecycle without
|
|
3
|
+
-- widening ordinary interaction expiry or weakening the session-tenancy fence.
|
|
4
|
+
SET LOCAL lock_timeout = '5s';
|
|
5
|
+
SET LOCAL statement_timeout = '10min';
|
|
6
|
+
|
|
7
|
+
-- The TTL-aware fence inventory below must discover due lease-free controller
|
|
8
|
+
-- transitions before the SECURITY DEFINER reaper opens its fenced owner scope.
|
|
9
|
+
-- Keep this as inventory-only SELECT authority; the 0388 fenced owner policies
|
|
10
|
+
-- remain the sole mutation path after every workspace advisory lock is held.
|
|
11
|
+
DO $interaction_reaper_inventory_policies$
|
|
12
|
+
DECLARE
|
|
13
|
+
target_schema text := pg_catalog.current_schema();
|
|
14
|
+
target_schema_oid oid := pg_catalog.current_schema()::pg_catalog.regnamespace;
|
|
15
|
+
migration_owner text := current_user;
|
|
16
|
+
table_name text;
|
|
17
|
+
BEGIN
|
|
18
|
+
FOREACH table_name IN ARRAY ARRAY[
|
|
19
|
+
'browser_sessions',
|
|
20
|
+
'computer_sessions',
|
|
21
|
+
'interaction_operations'
|
|
22
|
+
] LOOP
|
|
23
|
+
EXECUTE pg_catalog.format(
|
|
24
|
+
'DROP POLICY IF EXISTS session_tenancy_fence_inventory_read ON %I.%I',
|
|
25
|
+
target_schema,
|
|
26
|
+
table_name
|
|
27
|
+
);
|
|
28
|
+
EXECUTE pg_catalog.format(
|
|
29
|
+
'CREATE POLICY session_tenancy_fence_inventory_read ON %I.%I '
|
|
30
|
+
|| 'FOR SELECT USING ('
|
|
31
|
+
|| '%I.session_tenancy_fence_owner_policy_active('
|
|
32
|
+
|| 'current_user::text, %L::text, %s::oid, workspace_id, true))',
|
|
33
|
+
target_schema,
|
|
34
|
+
table_name,
|
|
35
|
+
target_schema,
|
|
36
|
+
migration_owner,
|
|
37
|
+
target_schema_oid
|
|
38
|
+
);
|
|
39
|
+
END LOOP;
|
|
40
|
+
END
|
|
41
|
+
$interaction_reaper_inventory_policies$;
|
|
42
|
+
|
|
43
|
+
-- The no-argument helper remains installed for reap_sandbox_leases() and old
|
|
44
|
+
-- in-flight callers. The interaction reaper uses this overload so its bounded
|
|
45
|
+
-- stale-operation batch can also see lease-free Connected Machine and attached-
|
|
46
|
+
-- device workspaces. Because SKIP LOCKED can advance beyond any preselected
|
|
47
|
+
-- operation row, fence every currently due workspace, but no healthy lease-free
|
|
48
|
+
-- interaction workspace, in canonical UUID order before taking row locks.
|
|
49
|
+
CREATE OR REPLACE FUNCTION acquire_sandbox_reaper_session_tenancy_fences(
|
|
50
|
+
p_interaction_holder_ttl_ms bigint
|
|
51
|
+
)
|
|
52
|
+
RETURNS integer
|
|
53
|
+
LANGUAGE plpgsql
|
|
54
|
+
SECURITY DEFINER
|
|
55
|
+
SET search_path FROM CURRENT
|
|
56
|
+
AS $interaction_reaper_fences$
|
|
57
|
+
DECLARE
|
|
58
|
+
inventory_capability_id uuid;
|
|
59
|
+
workspace_id_value uuid;
|
|
60
|
+
locked_count integer := 0;
|
|
61
|
+
BEGIN
|
|
62
|
+
IF p_interaction_holder_ttl_ms <= 0 THEN
|
|
63
|
+
RETURN 0;
|
|
64
|
+
END IF;
|
|
65
|
+
PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
|
|
66
|
+
inventory_capability_id :=
|
|
67
|
+
opengeni_private.open_session_tenancy_fence_inventory(
|
|
68
|
+
session_tenancy_fence_target_schema()
|
|
69
|
+
);
|
|
70
|
+
FOR workspace_id_value IN
|
|
71
|
+
SELECT candidate.workspace_id
|
|
72
|
+
FROM (
|
|
73
|
+
SELECT lease.workspace_id
|
|
74
|
+
FROM sandbox_leases lease
|
|
75
|
+
WHERE lease.workspace_id IS NOT NULL
|
|
76
|
+
AND (
|
|
77
|
+
lease.liveness <> 'cold'
|
|
78
|
+
OR EXISTS (
|
|
79
|
+
SELECT 1
|
|
80
|
+
FROM sandbox_lease_holders holder
|
|
81
|
+
WHERE holder.lease_id = lease.id
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
UNION
|
|
85
|
+
SELECT operation.workspace_id
|
|
86
|
+
FROM interaction_operations operation
|
|
87
|
+
WHERE operation.workspace_id IS NOT NULL
|
|
88
|
+
-- Lease-free prepared operations remain replayable. A dispatched
|
|
89
|
+
-- operation is the first controller-side effect and is the only
|
|
90
|
+
-- lease-free transition the ordinary stale-transition reaper settles.
|
|
91
|
+
AND operation.state = 'dispatched'
|
|
92
|
+
AND operation.updated_at
|
|
93
|
+
< pg_catalog.now()
|
|
94
|
+
- pg_catalog.make_interval(
|
|
95
|
+
secs => p_interaction_holder_ttl_ms / 1000.0
|
|
96
|
+
)
|
|
97
|
+
AND (
|
|
98
|
+
EXISTS (
|
|
99
|
+
SELECT 1
|
|
100
|
+
FROM browser_sessions browser
|
|
101
|
+
WHERE operation.resource_kind = 'browser_session'
|
|
102
|
+
AND operation.account_id = browser.account_id
|
|
103
|
+
AND operation.workspace_id = browser.workspace_id
|
|
104
|
+
AND operation.resource_id = browser.id
|
|
105
|
+
AND browser.lifecycle IN (
|
|
106
|
+
'starting', 'suspending', 'restoring', 'ending'
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
OR EXISTS (
|
|
110
|
+
SELECT 1
|
|
111
|
+
FROM computer_sessions computer
|
|
112
|
+
WHERE operation.resource_kind = 'computer_session'
|
|
113
|
+
AND operation.account_id = computer.account_id
|
|
114
|
+
AND operation.workspace_id = computer.workspace_id
|
|
115
|
+
AND operation.resource_id = computer.id
|
|
116
|
+
AND computer.lifecycle IN ('starting', 'ending')
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
) candidate
|
|
120
|
+
ORDER BY candidate.workspace_id
|
|
121
|
+
LOOP
|
|
122
|
+
PERFORM acquire_session_tenancy_fence(workspace_id_value);
|
|
123
|
+
locked_count := locked_count + 1;
|
|
124
|
+
END LOOP;
|
|
125
|
+
PERFORM opengeni_private.close_session_tenancy_fence_inventory(
|
|
126
|
+
inventory_capability_id
|
|
127
|
+
);
|
|
128
|
+
RETURN locked_count;
|
|
129
|
+
EXCEPTION WHEN OTHERS THEN
|
|
130
|
+
PERFORM opengeni_private.close_session_tenancy_fence_inventory(
|
|
131
|
+
inventory_capability_id
|
|
132
|
+
);
|
|
133
|
+
RAISE;
|
|
134
|
+
END
|
|
135
|
+
$interaction_reaper_fences$;
|
|
136
|
+
|
|
137
|
+
REVOKE ALL ON FUNCTION
|
|
138
|
+
acquire_sandbox_reaper_session_tenancy_fences(bigint)
|
|
139
|
+
FROM PUBLIC;
|
|
140
|
+
|
|
141
|
+
-- CREATE OR REPLACE records `SET search_path FROM CURRENT` from the migrator
|
|
142
|
+
-- connection. Re-pin this overload to the exact target schema before callers
|
|
143
|
+
-- can invoke the enclosing reaper, so a caller-created temp relation cannot
|
|
144
|
+
-- shadow its lease-free inventory before workspace fences are acquired.
|
|
145
|
+
DO $pin_interaction_reaper_fence_helper$
|
|
146
|
+
DECLARE
|
|
147
|
+
target_schema text := pg_catalog.current_schema();
|
|
148
|
+
BEGIN
|
|
149
|
+
EXECUTE pg_catalog.format(
|
|
150
|
+
'ALTER FUNCTION %I.acquire_sandbox_reaper_session_tenancy_fences(bigint) '
|
|
151
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp',
|
|
152
|
+
target_schema,
|
|
153
|
+
target_schema
|
|
154
|
+
);
|
|
155
|
+
END
|
|
156
|
+
$pin_interaction_reaper_fence_helper$;
|
|
157
|
+
|
|
158
|
+
-- Patch only the exact 0388 definition. The interaction reaper keeps the
|
|
159
|
+
-- canonical advisory-fence -> owner capability -> operation -> lease -> holder
|
|
160
|
+
-- ordering. The holder EXISTS is an unlocked eligibility read before LIMIT;
|
|
161
|
+
-- lease rows are still the first ownership rows locked by the deadline phase.
|
|
162
|
+
DO $provider_deadline_interaction_followup$
|
|
163
|
+
DECLARE
|
|
164
|
+
data_schema text := pg_catalog.current_schema();
|
|
165
|
+
definition text;
|
|
166
|
+
patched text;
|
|
167
|
+
fence_anchor constant text :=
|
|
168
|
+
E' PERFORM acquire_sandbox_reaper_session_tenancy_fences();';
|
|
169
|
+
fence_replacement constant text :=
|
|
170
|
+
E' -- 0391 provider-deadline interaction follow-up: inventory due\n'
|
|
171
|
+
|| E' -- lease-free transitions before any interaction row lock.\n'
|
|
172
|
+
|| E' PERFORM acquire_sandbox_reaper_session_tenancy_fences(\n'
|
|
173
|
+
|| E' p_interaction_holder_ttl_ms\n'
|
|
174
|
+
|| E' );';
|
|
175
|
+
liveness_anchor constant text :=
|
|
176
|
+
E' AND lease.liveness IN (''warming'', ''warm'')';
|
|
177
|
+
liveness_replacement constant text :=
|
|
178
|
+
E' AND lease.liveness IN (''warming'', ''warm'', ''draining'')';
|
|
179
|
+
deadline_anchor constant text :=
|
|
180
|
+
E' AND lease.provider_deadline_at <= pg_catalog.now()';
|
|
181
|
+
deadline_replacement text;
|
|
182
|
+
occurrences integer;
|
|
183
|
+
BEGIN
|
|
184
|
+
SELECT pg_catalog.pg_get_functiondef(
|
|
185
|
+
'opengeni_private.reap_stale_interaction_transitions(bigint)'::regprocedure
|
|
186
|
+
) INTO definition;
|
|
187
|
+
|
|
188
|
+
IF definition IS NULL
|
|
189
|
+
OR pg_catalog.strpos(definition, 'deadline_lease_ids uuid[]') = 0
|
|
190
|
+
OR pg_catalog.strpos(definition, 'provider_deadline_rotation') = 0
|
|
191
|
+
OR pg_catalog.strpos(definition, 'open_session_tenancy_fenced_access') = 0
|
|
192
|
+
OR pg_catalog.strpos(definition, 'LIMIT 500') = 0
|
|
193
|
+
THEN
|
|
194
|
+
RAISE EXCEPTION '0391 interaction reaper prerequisite definition drift'
|
|
195
|
+
USING ERRCODE = '55000';
|
|
196
|
+
END IF;
|
|
197
|
+
|
|
198
|
+
IF pg_catalog.strpos(
|
|
199
|
+
definition,
|
|
200
|
+
'0391 provider-deadline interaction follow-up'
|
|
201
|
+
) > 0 THEN
|
|
202
|
+
IF pg_catalog.strpos(
|
|
203
|
+
definition,
|
|
204
|
+
'acquire_sandbox_reaper_session_tenancy_fences('
|
|
205
|
+
) = 0
|
|
206
|
+
OR pg_catalog.strpos(
|
|
207
|
+
definition,
|
|
208
|
+
'p_interaction_holder_ttl_ms'
|
|
209
|
+
) = 0
|
|
210
|
+
OR pg_catalog.strpos(
|
|
211
|
+
definition,
|
|
212
|
+
'lease.liveness IN (''warming'', ''warm'', ''draining'')'
|
|
213
|
+
) = 0
|
|
214
|
+
OR pg_catalog.strpos(
|
|
215
|
+
definition,
|
|
216
|
+
'interaction_holder.kind = ''interaction'''
|
|
217
|
+
) = 0
|
|
218
|
+
THEN
|
|
219
|
+
RAISE EXCEPTION '0391 interaction reaper replay definition drift'
|
|
220
|
+
USING ERRCODE = '55000';
|
|
221
|
+
END IF;
|
|
222
|
+
ELSE
|
|
223
|
+
patched := definition;
|
|
224
|
+
|
|
225
|
+
occurrences := (
|
|
226
|
+
pg_catalog.length(patched)
|
|
227
|
+
- pg_catalog.length(pg_catalog.replace(patched, fence_anchor, ''))
|
|
228
|
+
) / pg_catalog.length(fence_anchor);
|
|
229
|
+
IF occurrences <> 1 THEN
|
|
230
|
+
RAISE EXCEPTION '0391 interaction reaper fence anchor drift'
|
|
231
|
+
USING ERRCODE = '55000';
|
|
232
|
+
END IF;
|
|
233
|
+
patched := pg_catalog.replace(patched, fence_anchor, fence_replacement);
|
|
234
|
+
|
|
235
|
+
occurrences := (
|
|
236
|
+
pg_catalog.length(patched)
|
|
237
|
+
- pg_catalog.length(pg_catalog.replace(patched, liveness_anchor, ''))
|
|
238
|
+
) / pg_catalog.length(liveness_anchor);
|
|
239
|
+
IF occurrences <> 1 THEN
|
|
240
|
+
RAISE EXCEPTION '0391 interaction reaper liveness anchor drift'
|
|
241
|
+
USING ERRCODE = '55000';
|
|
242
|
+
END IF;
|
|
243
|
+
patched := pg_catalog.replace(
|
|
244
|
+
patched,
|
|
245
|
+
liveness_anchor,
|
|
246
|
+
liveness_replacement
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
occurrences := (
|
|
250
|
+
pg_catalog.length(patched)
|
|
251
|
+
- pg_catalog.length(pg_catalog.replace(patched, deadline_anchor, ''))
|
|
252
|
+
) / pg_catalog.length(deadline_anchor);
|
|
253
|
+
IF occurrences <> 1 THEN
|
|
254
|
+
RAISE EXCEPTION '0391 interaction reaper deadline anchor drift'
|
|
255
|
+
USING ERRCODE = '55000';
|
|
256
|
+
END IF;
|
|
257
|
+
deadline_replacement := deadline_anchor || pg_catalog.format(
|
|
258
|
+
E'\n AND EXISTS (\n'
|
|
259
|
+
|| E' SELECT 1\n'
|
|
260
|
+
|| E' FROM %I.sandbox_lease_holders interaction_holder\n'
|
|
261
|
+
|| E' WHERE interaction_holder.lease_id = lease.id\n'
|
|
262
|
+
|| E' AND interaction_holder.kind = ''interaction''\n'
|
|
263
|
+
|| E' )',
|
|
264
|
+
data_schema
|
|
265
|
+
);
|
|
266
|
+
patched := pg_catalog.replace(
|
|
267
|
+
patched,
|
|
268
|
+
deadline_anchor,
|
|
269
|
+
deadline_replacement
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
IF pg_catalog.strpos(
|
|
273
|
+
patched,
|
|
274
|
+
'0391 provider-deadline interaction follow-up'
|
|
275
|
+
) = 0
|
|
276
|
+
OR pg_catalog.strpos(
|
|
277
|
+
patched,
|
|
278
|
+
'lease.liveness IN (''warming'', ''warm'', ''draining'')'
|
|
279
|
+
) = 0
|
|
280
|
+
OR pg_catalog.strpos(
|
|
281
|
+
patched,
|
|
282
|
+
'interaction_holder.kind = ''interaction'''
|
|
283
|
+
) = 0
|
|
284
|
+
THEN
|
|
285
|
+
RAISE EXCEPTION '0391 interaction reaper patch failed'
|
|
286
|
+
USING ERRCODE = '55000';
|
|
287
|
+
END IF;
|
|
288
|
+
|
|
289
|
+
EXECUTE patched;
|
|
290
|
+
END IF;
|
|
291
|
+
END
|
|
292
|
+
$provider_deadline_interaction_followup$;
|
|
293
|
+
|
|
294
|
+
RESET statement_timeout;
|
|
295
|
+
RESET lock_timeout;
|