@axinom/mosaic-db-common 0.35.0-rc.11 → 0.35.0-rc.12
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.
|
@@ -1151,3 +1151,172 @@ BEGIN
|
|
|
1151
1151
|
RETURN counter;
|
|
1152
1152
|
END;
|
|
1153
1153
|
$$;
|
|
1154
|
+
|
|
1155
|
+
/*-snippet
|
|
1156
|
+
{
|
|
1157
|
+
"body": [
|
|
1158
|
+
"SELECT ax_define.create_trx_table(",
|
|
1159
|
+
" '${1:app_hidden}',",
|
|
1160
|
+
" '${2|inbox,outbox|}',",
|
|
1161
|
+
" '{${3::DATABASE_ENV_OWNER},${4::DATABASE_GQL_ROLE}}',",
|
|
1162
|
+
" '${5|sequential,parallel|}');"
|
|
1163
|
+
],
|
|
1164
|
+
"description": [
|
|
1165
|
+
"Create a new table for transaction inbox/outbox handling. \n"
|
|
1166
|
+
]
|
|
1167
|
+
}
|
|
1168
|
+
snippet-*/
|
|
1169
|
+
CREATE OR REPLACE FUNCTION ax_define.create_trx_table (
|
|
1170
|
+
trx_schema TEXT,
|
|
1171
|
+
trx_table_name TEXT,
|
|
1172
|
+
additional_roles_to_grant TEXT[],
|
|
1173
|
+
concurrency TEXT
|
|
1174
|
+
) RETURNS VOID
|
|
1175
|
+
LANGUAGE plpgsql
|
|
1176
|
+
AS $$
|
|
1177
|
+
DECLARE
|
|
1178
|
+
role_ TEXT;
|
|
1179
|
+
BEGIN
|
|
1180
|
+
EXECUTE 'DROP TABLE IF EXISTS ' || trx_schema || '.'|| trx_table_name ||' CASCADE;';
|
|
1181
|
+
EXECUTE 'CREATE TABLE ' || trx_schema || '.'|| trx_table_name ||' (
|
|
1182
|
+
id uuid PRIMARY KEY,
|
|
1183
|
+
aggregate_type TEXT NOT NULL,
|
|
1184
|
+
aggregate_id TEXT NOT NULL,
|
|
1185
|
+
message_type TEXT NOT NULL,
|
|
1186
|
+
payload JSONB NOT NULL,
|
|
1187
|
+
metadata JSONB,
|
|
1188
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
1189
|
+
processed_at TIMESTAMPTZ,
|
|
1190
|
+
started_attempts smallint NOT NULL DEFAULT 0,
|
|
1191
|
+
finished_attempts smallint NOT NULL DEFAULT 0,
|
|
1192
|
+
segment TEXT,
|
|
1193
|
+
locked_until TIMESTAMPTZ NOT NULL DEFAULT to_timestamp(0),
|
|
1194
|
+
concurrency TEXT NOT NULL DEFAULT ''' || concurrency || ''',
|
|
1195
|
+
abandoned_at TIMESTAMPTZ
|
|
1196
|
+
);';
|
|
1197
|
+
EXECUTE 'ALTER TABLE ' || trx_schema || '.'|| trx_table_name ||' ADD CONSTRAINT '|| trx_table_name ||'_concurrency_check
|
|
1198
|
+
CHECK (concurrency IN (''sequential'', ''parallel''));';
|
|
1199
|
+
|
|
1200
|
+
-- The owner role has full access - no grants needed
|
|
1201
|
+
-- Grants for additional roles are given here
|
|
1202
|
+
FOREACH role_ IN ARRAY additional_roles_to_grant LOOP
|
|
1203
|
+
EXECUTE 'GRANT SELECT, INSERT, DELETE ON ' || trx_schema || '.'|| trx_table_name ||' TO ' || role_ || ';';
|
|
1204
|
+
EXECUTE 'GRANT UPDATE (locked_until, processed_at, abandoned_at, started_attempts, finished_attempts) ON ' || trx_schema || '.'|| trx_table_name ||' TO ' || role_ || ';';
|
|
1205
|
+
END LOOP;
|
|
1206
|
+
|
|
1207
|
+
EXECUTE 'SELECT ax_define.define_index(''segment'', '''|| trx_table_name ||''', '''||trx_schema||''');';
|
|
1208
|
+
EXECUTE 'SELECT ax_define.define_index(''created_at'', '''|| trx_table_name ||''', ''' || trx_schema || ''');';
|
|
1209
|
+
EXECUTE 'SELECT ax_define.define_index(''processed_at'', '''|| trx_table_name ||''', ''' || trx_schema || ''');';
|
|
1210
|
+
EXECUTE 'SELECT ax_define.define_index(''abandoned_at'', '''|| trx_table_name ||''', ''' || trx_schema || ''');';
|
|
1211
|
+
EXECUTE 'SELECT ax_define.define_index(''locked_until'', '''|| trx_table_name ||''', ''' || trx_schema || ''');';
|
|
1212
|
+
END;
|
|
1213
|
+
$$;
|
|
1214
|
+
|
|
1215
|
+
/*-snippet
|
|
1216
|
+
{
|
|
1217
|
+
"body": [
|
|
1218
|
+
"SELECT ax_define.create_trx_next_messages_function(",
|
|
1219
|
+
" '${1:app_hidden}',",
|
|
1220
|
+
" '${2:app_hidden}',",
|
|
1221
|
+
" '${3|inbox,outbox|}');"
|
|
1222
|
+
],
|
|
1223
|
+
"description": [
|
|
1224
|
+
"Create next_messages function that is used to poll the inbox/outbox tables. \n"
|
|
1225
|
+
]
|
|
1226
|
+
}
|
|
1227
|
+
snippet-*/
|
|
1228
|
+
CREATE OR REPLACE FUNCTION ax_define.create_trx_next_messages_function(
|
|
1229
|
+
trx_next_message_function_schema TEXT,
|
|
1230
|
+
trx_table_schema TEXT,
|
|
1231
|
+
trx_table_name TEXT
|
|
1232
|
+
) RETURNS VOID
|
|
1233
|
+
LANGUAGE plpgsql
|
|
1234
|
+
AS $$
|
|
1235
|
+
BEGIN
|
|
1236
|
+
EXECUTE 'DROP FUNCTION IF EXISTS ' || trx_next_message_function_schema || '.next_'|| trx_table_name ||'_messages(integer, integer);';
|
|
1237
|
+
EXECUTE 'CREATE OR REPLACE FUNCTION ' || trx_next_message_function_schema || '.next_'|| trx_table_name ||'_messages(
|
|
1238
|
+
max_size integer, lock_ms integer)
|
|
1239
|
+
RETURNS SETOF ' || trx_table_schema || '.'|| trx_table_name ||'
|
|
1240
|
+
LANGUAGE ''plpgsql''
|
|
1241
|
+
|
|
1242
|
+
AS $BODY$
|
|
1243
|
+
DECLARE
|
|
1244
|
+
loop_row ' || trx_table_schema || '.'|| trx_table_name ||'%ROWTYPE;
|
|
1245
|
+
message_row ' || trx_table_schema || '.'|| trx_table_name ||'%ROWTYPE;
|
|
1246
|
+
ids uuid[] := ''{}'';
|
|
1247
|
+
BEGIN
|
|
1248
|
+
|
|
1249
|
+
IF max_size < 1 THEN
|
|
1250
|
+
RAISE EXCEPTION ''The max_size for the next messages batch must be at least one.'' using errcode = ''MAXNR'';
|
|
1251
|
+
END IF;
|
|
1252
|
+
|
|
1253
|
+
-- get (only) the oldest message of every segment but only return it if it is not locked
|
|
1254
|
+
FOR loop_row IN
|
|
1255
|
+
SELECT * FROM ' || trx_table_schema || '.'|| trx_table_name ||' m WHERE m.id in (SELECT DISTINCT ON (segment) id
|
|
1256
|
+
FROM ' || trx_table_schema || '.'|| trx_table_name ||'
|
|
1257
|
+
WHERE processed_at IS NULL AND abandoned_at IS NULL
|
|
1258
|
+
ORDER BY segment, created_at) order by created_at
|
|
1259
|
+
LOOP
|
|
1260
|
+
BEGIN
|
|
1261
|
+
EXIT WHEN cardinality(ids) >= max_size;
|
|
1262
|
+
|
|
1263
|
+
SELECT *
|
|
1264
|
+
INTO message_row
|
|
1265
|
+
FROM ' || trx_table_schema || '.'|| trx_table_name ||'
|
|
1266
|
+
WHERE id = loop_row.id
|
|
1267
|
+
FOR NO KEY UPDATE NOWAIT; -- throw/catch error when locked
|
|
1268
|
+
|
|
1269
|
+
IF message_row.locked_until > NOW() THEN
|
|
1270
|
+
CONTINUE;
|
|
1271
|
+
END IF;
|
|
1272
|
+
|
|
1273
|
+
ids := array_append(ids, message_row.id);
|
|
1274
|
+
EXCEPTION
|
|
1275
|
+
WHEN lock_not_available THEN
|
|
1276
|
+
CONTINUE;
|
|
1277
|
+
WHEN serialization_failure THEN
|
|
1278
|
+
CONTINUE;
|
|
1279
|
+
END;
|
|
1280
|
+
END LOOP;
|
|
1281
|
+
|
|
1282
|
+
-- if max_size not reached: get the oldest parallelizable message independent of segment
|
|
1283
|
+
IF cardinality(ids) < max_size THEN
|
|
1284
|
+
FOR loop_row IN
|
|
1285
|
+
SELECT * FROM ' || trx_table_schema || '.'|| trx_table_name ||'
|
|
1286
|
+
WHERE concurrency = ''parallel'' AND processed_at IS NULL AND abandoned_at IS NULL AND locked_until < NOW()
|
|
1287
|
+
AND id NOT IN (SELECT UNNEST(ids))
|
|
1288
|
+
order by created_at
|
|
1289
|
+
LOOP
|
|
1290
|
+
BEGIN
|
|
1291
|
+
EXIT WHEN cardinality(ids) >= max_size;
|
|
1292
|
+
|
|
1293
|
+
SELECT *
|
|
1294
|
+
INTO message_row
|
|
1295
|
+
FROM ' || trx_table_schema || '.'|| trx_table_name ||'
|
|
1296
|
+
WHERE id = loop_row.id
|
|
1297
|
+
FOR NO KEY UPDATE NOWAIT; -- throw/catch error when locked
|
|
1298
|
+
|
|
1299
|
+
ids := array_append(ids, message_row.id);
|
|
1300
|
+
EXCEPTION
|
|
1301
|
+
WHEN lock_not_available THEN
|
|
1302
|
+
CONTINUE;
|
|
1303
|
+
WHEN serialization_failure THEN
|
|
1304
|
+
CONTINUE;
|
|
1305
|
+
END;
|
|
1306
|
+
END LOOP;
|
|
1307
|
+
END IF;
|
|
1308
|
+
|
|
1309
|
+
-- set a short lock value so the the workers can each process a message
|
|
1310
|
+
IF cardinality(ids) > 0 THEN
|
|
1311
|
+
|
|
1312
|
+
RETURN QUERY
|
|
1313
|
+
UPDATE ' || trx_table_schema || '.'|| trx_table_name ||'
|
|
1314
|
+
SET locked_until = clock_timestamp() + (lock_ms || '' milliseconds'')::INTERVAL, started_attempts = started_attempts + 1
|
|
1315
|
+
WHERE ID = ANY(ids)
|
|
1316
|
+
RETURNING *;
|
|
1317
|
+
|
|
1318
|
+
END IF;
|
|
1319
|
+
END;
|
|
1320
|
+
$BODY$;';
|
|
1321
|
+
END;
|
|
1322
|
+
$$;
|
|
@@ -251,5 +251,41 @@
|
|
|
251
251
|
"Make sure the dropped value is not used in other tables.\n",
|
|
252
252
|
"This snippet has a simplified migration sample that might require adjustments."
|
|
253
253
|
]
|
|
254
|
+
},
|
|
255
|
+
"Create Transaction Inbox Outbox Tables and Functions (Ax Custom)": {
|
|
256
|
+
"scope": "sql",
|
|
257
|
+
"prefix": "ax-add-trx-inbox-outbox-tables-and-functions",
|
|
258
|
+
"body": [
|
|
259
|
+
"-- Create Transaction Inbox Table",
|
|
260
|
+
"SELECT ax_define.create_trx_table(",
|
|
261
|
+
"'${1:app_hidden}',",
|
|
262
|
+
"'${2:inbox}',",
|
|
263
|
+
"'{${3::DATABASE_ENV_OWNER},${4::DATABASE_GQL_ROLE}}',",
|
|
264
|
+
"'${5|sequential,parallel|}');",
|
|
265
|
+
"",
|
|
266
|
+
"-- Create Transaction Outbox Table",
|
|
267
|
+
"SELECT ax_define.create_trx_table(",
|
|
268
|
+
"'${1:app_hidden}',",
|
|
269
|
+
"'${6:outbox}',",
|
|
270
|
+
"'{${3::DATABASE_ENV_OWNER},${4::DATABASE_GQL_ROLE}}',",
|
|
271
|
+
"'${7|sequential,parallel|}');",
|
|
272
|
+
"",
|
|
273
|
+
"-- Create Transaction Inbox Next Message function",
|
|
274
|
+
"SELECT ax_define.create_trx_next_messages_function(",
|
|
275
|
+
"'${8:app_hidden}',",
|
|
276
|
+
"'${1:app_hidden}',",
|
|
277
|
+
"'${2:inbox}');",
|
|
278
|
+
"",
|
|
279
|
+
"-- Create Transaction Outbox Next Message function",
|
|
280
|
+
"SELECT ax_define.create_trx_next_messages_function(",
|
|
281
|
+
"'${8:app_hidden}',",
|
|
282
|
+
"'${1:app_hidden}',",
|
|
283
|
+
"'${6:outbox}');"
|
|
284
|
+
],
|
|
285
|
+
"description": [
|
|
286
|
+
"Drops a value from the enum table.\n",
|
|
287
|
+
"Make sure the dropped value is not used in other tables.\n",
|
|
288
|
+
"This snippet has a simplified migration sample that might require adjustments."
|
|
289
|
+
]
|
|
254
290
|
}
|
|
255
291
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-db-common",
|
|
3
|
-
"version": "0.35.0-rc.
|
|
3
|
+
"version": "0.35.0-rc.12",
|
|
4
4
|
"description": "This library encapsulates database-related functionality to develop Mosaic based services.",
|
|
5
5
|
"author": "Axinom",
|
|
6
6
|
"license": "PROPRIETARY",
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"publishConfig": {
|
|
55
55
|
"access": "public"
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "3d1d61402edfb5b7fe5281f95791d31a4fb34174"
|
|
58
58
|
}
|