@keetanetwork/anchor 0.0.58 → 0.0.60

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.
Files changed (54) hide show
  1. package/lib/chaining.d.ts +3 -3
  2. package/lib/chaining.d.ts.map +1 -1
  3. package/lib/chaining.js.map +1 -1
  4. package/lib/queue/drivers/queue_postgres.d.ts +20 -3
  5. package/lib/queue/drivers/queue_postgres.d.ts.map +1 -1
  6. package/lib/queue/drivers/queue_postgres.js +152 -40
  7. package/lib/queue/drivers/queue_postgres.js.map +1 -1
  8. package/lib/resolver.d.ts +1 -1
  9. package/lib/resolver.d.ts.map +1 -1
  10. package/lib/resolver.js +214 -244
  11. package/lib/resolver.js.map +1 -1
  12. package/npm-shrinkwrap.json +2 -2
  13. package/package.json +1 -1
  14. package/services/asset-movement/client.d.ts +20 -8
  15. package/services/asset-movement/client.d.ts.map +1 -1
  16. package/services/asset-movement/client.js +63 -22
  17. package/services/asset-movement/client.js.map +1 -1
  18. package/services/asset-movement/common.d.ts +33 -12
  19. package/services/asset-movement/common.d.ts.map +1 -1
  20. package/services/asset-movement/common.generated.d.ts +3 -0
  21. package/services/asset-movement/common.generated.d.ts.map +1 -1
  22. package/services/asset-movement/common.generated.js +24320 -7253
  23. package/services/asset-movement/common.generated.js.map +1 -1
  24. package/services/asset-movement/common.js +3 -0
  25. package/services/asset-movement/common.js.map +1 -1
  26. package/services/asset-movement/lib/data/addresses/bank-account/ca.d.ts +4 -0
  27. package/services/asset-movement/lib/data/addresses/bank-account/ca.d.ts.map +1 -0
  28. package/services/asset-movement/lib/data/addresses/bank-account/ca.js +49 -0
  29. package/services/asset-movement/lib/data/addresses/bank-account/ca.js.map +1 -0
  30. package/services/asset-movement/lib/data/addresses/bank-account/index.generated.d.ts +4 -0
  31. package/services/asset-movement/lib/data/addresses/bank-account/index.generated.d.ts.map +1 -1
  32. package/services/asset-movement/lib/data/addresses/bank-account/index.generated.js +4 -0
  33. package/services/asset-movement/lib/data/addresses/bank-account/index.generated.js.map +1 -1
  34. package/services/asset-movement/lib/data/addresses/bank-account/interac.d.ts.map +1 -1
  35. package/services/asset-movement/lib/data/addresses/bank-account/interac.js +9 -20
  36. package/services/asset-movement/lib/data/addresses/bank-account/interac.js.map +1 -1
  37. package/services/asset-movement/lib/data/addresses/bank-account/pix.d.ts.map +1 -1
  38. package/services/asset-movement/lib/data/addresses/bank-account/pix.js +3 -2
  39. package/services/asset-movement/lib/data/addresses/bank-account/pix.js.map +1 -1
  40. package/services/asset-movement/lib/data/addresses/bank-account/uae.d.ts +4 -0
  41. package/services/asset-movement/lib/data/addresses/bank-account/uae.d.ts.map +1 -0
  42. package/services/asset-movement/lib/data/addresses/bank-account/uae.js +45 -0
  43. package/services/asset-movement/lib/data/addresses/bank-account/uae.js.map +1 -0
  44. package/services/asset-movement/lib/data/addresses/types.generated.d.ts +260 -52
  45. package/services/asset-movement/lib/data/addresses/types.generated.d.ts.map +1 -1
  46. package/services/asset-movement/lib/data/addresses/types.generated.js +252 -35
  47. package/services/asset-movement/lib/data/addresses/types.generated.js.map +1 -1
  48. package/services/asset-movement/lib/location.generated.js +3 -3
  49. package/services/asset-movement/server.d.ts +5 -1
  50. package/services/asset-movement/server.d.ts.map +1 -1
  51. package/services/asset-movement/server.js +23 -2
  52. package/services/asset-movement/server.js.map +1 -1
  53. package/services/storage/clients/contacts.generated.js +636 -312
  54. package/services/storage/clients/contacts.generated.js.map +1 -1
@@ -1,18 +1,34 @@
1
1
  import type { KeetaAnchorQueueStorageDriver, KeetaAnchorQueueStorageDriverConstructor, KeetaAnchorQueueRequest, KeetaAnchorQueueRequestID, KeetaAnchorQueueEntry, KeetaAnchorQueueEntryExtra, KeetaAnchorQueueEntryAncillaryData, KeetaAnchorQueueStatus, KeetaAnchorQueueFilter } from '../index.ts';
2
2
  import type { JSONSerializable } from '../../utils/json.js';
3
3
  import type * as pg from 'pg';
4
+ type KeetaAnchorQueueStorageDriverPostgresOptions = {
5
+ /**
6
+ * Function that returns a Postgres connection pool.
7
+ */
8
+ pool: () => Promise<pg.Pool>;
9
+ /**
10
+ * The prefix to use for the database tables. Defaults to 'queue',
11
+ * resulting in table names 'queue_entries', 'queue_idempotent_keys',
12
+ * and `queue_schema_version`.
13
+ */
14
+ tablePrefix?: string | undefined;
15
+ };
4
16
  export default class KeetaAnchorQueueStorageDriverPostgres<QueueRequest extends JSONSerializable = JSONSerializable, QueueResult extends JSONSerializable = JSONSerializable> implements KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult> {
5
17
  private readonly logger;
6
18
  private poolInternal;
19
+ private tablePrefix;
20
+ private tableNameEntries;
21
+ private tableNameSchemaVersion;
22
+ private tableNameIdempotentKeys;
7
23
  private dbInitializationPromise;
24
+ private serializationRetryCount;
25
+ private debugForceIndexScan;
8
26
  readonly name = "KeetaAnchorQueueStorageDriverPostgres";
9
27
  readonly id: string;
10
28
  readonly path: string[];
11
29
  private readonly pathStr;
12
30
  private toctouDelay;
13
- constructor(options: NonNullable<ConstructorParameters<KeetaAnchorQueueStorageDriverConstructor<QueueRequest, QueueResult>>[0]> & {
14
- pool: () => Promise<pg.Pool>;
15
- });
31
+ constructor(options: NonNullable<ConstructorParameters<KeetaAnchorQueueStorageDriverConstructor<QueueRequest, QueueResult>>[0]> & KeetaAnchorQueueStorageDriverPostgresOptions);
16
32
  private initializeDBConnection;
17
33
  private methodLogger;
18
34
  private runWithRetry;
@@ -26,4 +42,5 @@ export default class KeetaAnchorQueueStorageDriverPostgres<QueueRequest extends
26
42
  destroy(): Promise<void>;
27
43
  [Symbol.asyncDispose](): Promise<void>;
28
44
  }
45
+ export {};
29
46
  //# sourceMappingURL=queue_postgres.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"queue_postgres.d.ts","sourceRoot":"","sources":["../../../../src/lib/queue/drivers/queue_postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,6BAA6B,EAC7B,wCAAwC,EACxC,uBAAuB,EACvB,yBAAyB,EACzB,qBAAqB,EACrB,0BAA0B,EAC1B,kCAAkC,EAClC,sBAAsB,EACtB,sBAAsB,EAEtB,MAAM,aAAa,CAAC;AAWrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,KAAK,EAAE,MAAM,IAAI,CAAC;AAkB9B,MAAM,CAAC,OAAO,OAAO,qCAAqC,CAAC,YAAY,SAAS,gBAAgB,GAAG,gBAAgB,EAAE,WAAW,SAAS,gBAAgB,GAAG,gBAAgB,CAAE,YAAW,6BAA6B,CAAC,YAAY,EAAE,WAAW,CAAC;IAChP,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,YAAY,CAAyC;IAC7D,OAAO,CAAC,uBAAuB,CAAiC;IAEhE,QAAQ,CAAC,IAAI,2CAA2C;IACxD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAM;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,WAAW,CAAgD;gBAEvD,OAAO,EAAE,WAAW,CAAC,qBAAqB,CAAC,wCAAwC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;KAAE;YAWrJ,sBAAsB;IAyDpC,OAAO,CAAC,YAAY;YASN,YAAY;YA2CZ,eAAe;YAiBf,aAAa;IAoCrB,GAAG,CAAC,OAAO,EAAE,uBAAuB,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA6D1H,SAAS,CAAC,EAAE,EAAE,yBAAyB,EAAE,MAAM,EAAE,sBAAsB,EAAE,SAAS,CAAC,EAAE,kCAAkC,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAyDpJ,GAAG,CAAC,EAAE,EAAE,yBAAyB,GAAG,OAAO,CAAC,qBAAqB,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC;IA8CpG,KAAK,CAAC,MAAM,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,CAAC;IAuEnG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAI,OAAO,CAAC,6BAA6B,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAiB3F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxB,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAwB5C"}
1
+ {"version":3,"file":"queue_postgres.d.ts","sourceRoot":"","sources":["../../../../src/lib/queue/drivers/queue_postgres.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,6BAA6B,EAC7B,wCAAwC,EACxC,uBAAuB,EACvB,yBAAyB,EACzB,qBAAqB,EACrB,0BAA0B,EAC1B,kCAAkC,EAClC,sBAAsB,EACtB,sBAAsB,EAEtB,MAAM,aAAa,CAAC;AAWrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,KAAK,EAAE,MAAM,IAAI,CAAC;AAkB9B,KAAK,4CAA4C,GAAG;IACnD;;OAEG;IACH,IAAI,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;IAE7B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,qCAAqC,CAAC,YAAY,SAAS,gBAAgB,GAAG,gBAAgB,EAAE,WAAW,SAAS,gBAAgB,GAAG,gBAAgB,CAAE,YAAW,6BAA6B,CAAC,YAAY,EAAE,WAAW,CAAC;IAChP,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,YAAY,CAAyC;IAC7D,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,uBAAuB,CAAiC;IAChE,OAAO,CAAC,uBAAuB,CAAK;IACpC,OAAO,CAAC,mBAAmB,CAAS;IAEpC,QAAQ,CAAC,IAAI,2CAA2C;IACxD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAM;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,WAAW,CAAgD;gBAEvD,OAAO,EAAE,WAAW,CAAC,qBAAqB,CAAC,wCAAwC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,4CAA4C;YAgBhK,sBAAsB;IAgJpC,OAAO,CAAC,YAAY;YASN,YAAY;YA8CZ,eAAe;YAiBf,aAAa;IA0CrB,GAAG,CAAC,OAAO,EAAE,uBAAuB,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA6D1H,SAAS,CAAC,EAAE,EAAE,yBAAyB,EAAE,MAAM,EAAE,sBAAsB,EAAE,SAAS,CAAC,EAAE,kCAAkC,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAyDpJ,GAAG,CAAC,EAAE,EAAE,yBAAyB,GAAG,OAAO,CAAC,qBAAqB,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC;IA8CpG,KAAK,CAAC,MAAM,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,CAAC;IA2EnG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAI,OAAO,CAAC,6BAA6B,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAkB3F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxB,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAwC5C"}
@@ -4,7 +4,13 @@ import { asleep } from '../../utils/asleep.js';
4
4
  export default class KeetaAnchorQueueStorageDriverPostgres {
5
5
  logger;
6
6
  poolInternal = null;
7
+ tablePrefix;
8
+ tableNameEntries;
9
+ tableNameSchemaVersion;
10
+ tableNameIdempotentKeys;
7
11
  dbInitializationPromise = null;
12
+ serializationRetryCount = 0;
13
+ debugForceIndexScan = false;
8
14
  name = 'KeetaAnchorQueueStorageDriverPostgres';
9
15
  id;
10
16
  path = [];
@@ -13,10 +19,14 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
13
19
  constructor(options) {
14
20
  this.id = options?.id ?? crypto.randomUUID();
15
21
  this.logger = options?.logger;
22
+ this.tablePrefix = options?.tablePrefix;
16
23
  this.poolInternal = options.pool;
17
24
  this.path = options.path ?? [];
18
25
  this.pathStr = ['root', ...this.path].join('.');
19
26
  Object.freeze(this.path);
27
+ this.tableNameEntries = `${this.tablePrefix ?? 'queue'}_entries`;
28
+ this.tableNameIdempotentKeys = `${this.tablePrefix ?? 'queue'}_idempotent_keys`;
29
+ this.tableNameSchemaVersion = `${this.tablePrefix ?? 'queue'}_schema_version`;
20
30
  this.methodLogger('new')?.debug('Initialized Postgres queue storage driver');
21
31
  }
22
32
  async initializeDBConnection(pool) {
@@ -27,35 +37,115 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
27
37
  return (pool);
28
38
  }
29
39
  this.dbInitializationPromise = (async () => {
30
- logger?.debug('Initializing DB schema for queue storage driver');
40
+ logger?.debug('Initializing DB schema for queue storage driver on tables', [this.tableNameEntries, this.tableNameIdempotentKeys]);
31
41
  const client = await pool.connect();
32
42
  try {
33
- await client.query(`
34
- CREATE TABLE IF NOT EXISTS queue_entries (
35
- id TEXT NOT NULL,
36
- path TEXT NOT NULL,
37
- request TEXT NOT NULL,
38
- output TEXT,
39
- last_error TEXT,
40
- status TEXT NOT NULL,
41
- created BIGINT NOT NULL,
42
- updated BIGINT NOT NULL,
43
- worker BIGINT,
44
- failures INTEGER NOT NULL DEFAULT 0,
45
- PRIMARY KEY (id, path)
46
- )`);
47
- await client.query(`
48
- CREATE TABLE IF NOT EXISTS queue_idempotent_keys (
49
- entry_id TEXT NOT NULL,
50
- idempotent_id TEXT NOT NULL,
51
- path TEXT NOT NULL,
52
- UNIQUE (idempotent_id, path),
53
- PRIMARY KEY (entry_id, idempotent_id, path),
54
- FOREIGN KEY (entry_id, path) REFERENCES queue_entries(id, path)
55
- )`);
56
- await client.query('CREATE INDEX IF NOT EXISTS idx_queue_entries_status ON queue_entries(status)');
57
- await client.query('CREATE INDEX IF NOT EXISTS idx_queue_entries_updated ON queue_entries(updated)');
58
- await client.query('CREATE INDEX IF NOT EXISTS idx_queue_idempotent_keys_idempotent_id ON queue_idempotent_keys(idempotent_id)');
43
+ /*
44
+ * Random lock key (32-bit integer), to ensure
45
+ * that multiple instances of this driver
46
+ * (potentially across different
47
+ * instances/process) will run the migration
48
+ * sequentially to avoid multiple concurrent
49
+ * migrations from competing with each other
50
+ * and causing delays
51
+ */
52
+ const lockId = 0x24995E48;
53
+ logger?.debug('Acquiring advisory lock for schema migration');
54
+ await client.query('SELECT pg_advisory_lock($1)', [lockId]);
55
+ try {
56
+ // Create schema version table if it doesn't exist
57
+ await client.query(`
58
+ CREATE TABLE IF NOT EXISTS ${this.tableNameSchemaVersion} (
59
+ version INTEGER NOT NULL,
60
+ applied_at BIGINT NOT NULL,
61
+ PRIMARY KEY (version)
62
+ )`);
63
+ // Check current schema version
64
+ const versionResult = await client.query(`SELECT MAX(version) as version FROM ${this.tableNameSchemaVersion}`);
65
+ const currentVersion = versionResult.rows[0]?.version ?? 0;
66
+ logger?.debug(`Current queue schema version: ${currentVersion}`);
67
+ // Version 1: Initial schema
68
+ if (currentVersion < 1) {
69
+ logger?.debug('Applying schema version 1: Initial tables and indexes');
70
+ await client.query('BEGIN');
71
+ try {
72
+ await client.query(`
73
+ CREATE TABLE IF NOT EXISTS ${this.tableNameEntries} (
74
+ id TEXT NOT NULL,
75
+ path TEXT NOT NULL,
76
+ request TEXT NOT NULL,
77
+ output TEXT,
78
+ last_error TEXT,
79
+ status TEXT NOT NULL,
80
+ created BIGINT NOT NULL,
81
+ updated BIGINT NOT NULL,
82
+ worker BIGINT,
83
+ failures INTEGER NOT NULL DEFAULT 0,
84
+ PRIMARY KEY (id, path)
85
+ )`);
86
+ await client.query(`
87
+ CREATE TABLE IF NOT EXISTS ${this.tableNameIdempotentKeys} (
88
+ entry_id TEXT NOT NULL,
89
+ idempotent_id TEXT NOT NULL,
90
+ path TEXT NOT NULL,
91
+ UNIQUE (idempotent_id, path),
92
+ PRIMARY KEY (entry_id, idempotent_id, path),
93
+ FOREIGN KEY (entry_id, path) REFERENCES ${this.tableNameEntries}(id, path)
94
+ )`);
95
+ // Old single-column indexes (for pre-version-2 schemas)
96
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_${this.tableNameEntries}_status ON ${this.tableNameEntries}(status)`);
97
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_${this.tableNameEntries}_updated ON ${this.tableNameEntries}(updated)`);
98
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_${this.tableNameIdempotentKeys}_idempotent_id ON ${this.tableNameIdempotentKeys}(idempotent_id)`);
99
+ await client.query(`INSERT INTO ${this.tableNameSchemaVersion} (version, applied_at) VALUES (1, $1)`, [Date.now()]);
100
+ await client.query('COMMIT');
101
+ logger?.debug('Applied schema version 1');
102
+ }
103
+ catch (error) {
104
+ await client.query('ROLLBACK');
105
+ throw (error);
106
+ }
107
+ }
108
+ // Version 2: Partition-aware composite indexes
109
+ if (currentVersion < 2) {
110
+ logger?.debug('Applying schema version 2: Partition-aware composite indexes');
111
+ // Create new partition-aware indexes (CONCURRENTLY must be outside transaction)
112
+ logger?.debug('Creating partition-aware indexes...');
113
+ await client.query(`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_${this.tableNameEntries}_path_status ON ${this.tableNameEntries}(path, status)`);
114
+ await client.query(`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_${this.tableNameEntries}_path_updated ON ${this.tableNameEntries}(path, updated)`);
115
+ await client.query(`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_${this.tableNameEntries}_path_status_updated ON ${this.tableNameEntries}(path, status, updated)`);
116
+ await client.query(`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_${this.tableNameIdempotentKeys}_path_idempotent_id ON ${this.tableNameIdempotentKeys}(path, idempotent_id)`);
117
+ // Now drop old indexes and record version
118
+ await client.query('BEGIN');
119
+ try {
120
+ // Drop old indexes that are now redundant (these will fail gracefully if indexes don't exist)
121
+ logger?.debug('Dropping old single-column indexes...');
122
+ await client.query(`DROP INDEX IF EXISTS idx_${this.tableNameEntries}_status`);
123
+ await client.query(`DROP INDEX IF EXISTS idx_${this.tableNameEntries}_updated`);
124
+ await client.query(`DROP INDEX IF EXISTS idx_${this.tableNameIdempotentKeys}_idempotent_id`);
125
+ await client.query(`INSERT INTO ${this.tableNameSchemaVersion} (version, applied_at) VALUES (2, $1)`, [Date.now()]);
126
+ await client.query('COMMIT');
127
+ logger?.debug('Applied schema version 2');
128
+ }
129
+ catch (error) {
130
+ await client.query('ROLLBACK');
131
+ throw (error);
132
+ }
133
+ }
134
+ logger?.debug('Schema is up to date');
135
+ }
136
+ finally {
137
+ // Always release the advisory lock
138
+ // Note: Advisory locks are session-based and auto-release on disconnect,
139
+ // but we explicitly unlock for clarity and to avoid holding locks longer than needed
140
+ try {
141
+ logger?.debug('Releasing advisory lock');
142
+ await client.query('SELECT pg_advisory_unlock($1)', [lockId]);
143
+ }
144
+ catch (unlockError) {
145
+ // Log but don't throw - the lock will be auto-released when connection closes
146
+ logger?.debug('Failed to explicitly release advisory lock (will auto-release on disconnect):', unlockError);
147
+ }
148
+ }
59
149
  }
60
150
  finally {
61
151
  client.release();
@@ -95,6 +185,8 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
95
185
  const errorCode = 'code' in error ? error.code : null;
96
186
  if (errorCode === '40001' || errorCode === '40P01') {
97
187
  logger?.debug('Serialization failure or deadlock detected');
188
+ // Track serialization retries for instrumentation
189
+ this.serializationRetryCount++;
98
190
  const minBackoff = 100;
99
191
  const maxBackoff = 30_000;
100
192
  const backoffIntervalSize = Math.min(maxBackoff - minBackoff, (retry + 50) ** 2);
@@ -125,12 +217,16 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
125
217
  async dbTransaction(className, fn) {
126
218
  const pool = await this.newDBConnection();
127
219
  const logger = this.methodLogger(className);
220
+ const debugForceIndexScan = this.debugForceIndexScan;
128
221
  const result = await this.runWithRetry(async function () {
129
222
  const client = await pool.connect();
130
223
  try {
131
224
  logger?.debug('Starting DB transaction');
132
225
  await client.query('BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE');
133
226
  logger?.debug('DB transaction started');
227
+ if (debugForceIndexScan) {
228
+ await client.query('SET LOCAL enable_seqscan TO off');
229
+ }
134
230
  const retval = await fn(client, logger);
135
231
  logger?.debug('Committing DB transaction');
136
232
  await client.query('COMMIT');
@@ -158,7 +254,7 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
158
254
  return (await this.dbTransaction('add', async (client, logger) => {
159
255
  let entryID = ConvertStringToRequestID(info?.id);
160
256
  if (entryID) {
161
- const existingEntry = await client.query('SELECT id FROM queue_entries WHERE id = $1 AND path = $2', [entryID, this.pathStr]);
257
+ const existingEntry = await client.query(`SELECT id FROM ${this.tableNameEntries} WHERE id = $1 AND path = $2`, [entryID, this.pathStr]);
162
258
  if (existingEntry.rows.length > 0) {
163
259
  logger?.debug(`Request with id ${String(entryID)} already exists, ignoring`);
164
260
  return (entryID);
@@ -169,7 +265,7 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
169
265
  if (idempotentIDs) {
170
266
  const matchingIdempotentEntries = new Set();
171
267
  for (const idempotentID of idempotentIDs) {
172
- const idempotentEntryExists = await client.query('SELECT idempotent_id FROM queue_idempotent_keys WHERE idempotent_id = $1 AND path = $2', [idempotentID, this.pathStr]);
268
+ const idempotentEntryExists = await client.query(`SELECT idempotent_id FROM ${this.tableNameIdempotentKeys} WHERE idempotent_id = $1 AND path = $2`, [idempotentID, this.pathStr]);
173
269
  if (idempotentEntryExists.rows.length > 0) {
174
270
  matchingIdempotentEntries.add(idempotentID);
175
271
  }
@@ -187,11 +283,11 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
187
283
  * The status to use for the new entry
188
284
  */
189
285
  const status = info?.status ?? 'pending';
190
- await client.query(`INSERT INTO queue_entries (id, path, request, output, last_error, status, created, updated, worker, failures)
286
+ await client.query(`INSERT INTO ${this.tableNameEntries} (id, path, request, output, last_error, status, created, updated, worker, failures)
191
287
  VALUES ($1, $2, $3, NULL, NULL, $4, $5, $6, NULL, 0)`, [entryID, this.pathStr, requestJSON, status, currentTime, currentTime]);
192
288
  if (idempotentIDs && idempotentIDs.size > 0) {
193
289
  for (const idempotentID of idempotentIDs) {
194
- await client.query('INSERT INTO queue_idempotent_keys (entry_id, path, idempotent_id) VALUES ($1, $2, $3)', [entryID, this.pathStr, idempotentID]);
290
+ await client.query(`INSERT INTO ${this.tableNameIdempotentKeys} (entry_id, path, idempotent_id) VALUES ($1, $2, $3)`, [entryID, this.pathStr, idempotentID]);
195
291
  }
196
292
  }
197
293
  return (entryID);
@@ -200,7 +296,7 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
200
296
  async setStatus(id, status, ancillary) {
201
297
  const { oldStatus } = ancillary ?? {};
202
298
  return (await this.dbTransaction('setStatus', async (client, logger) => {
203
- const existingEntry = await client.query('SELECT status, failures, last_error, output FROM queue_entries WHERE id = $1 AND path = $2', [id, this.pathStr]);
299
+ const existingEntry = await client.query(`SELECT status, failures, last_error, output FROM ${this.tableNameEntries} WHERE id = $1 AND path = $2 FOR UPDATE`, [id, this.pathStr]);
204
300
  if (existingEntry.rows.length === 0) {
205
301
  throw (new Error(`Request with ID ${String(id)} not found`));
206
302
  }
@@ -218,13 +314,13 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
218
314
  let updateQuery;
219
315
  let updateParams;
220
316
  if (oldStatus) {
221
- updateQuery = `UPDATE queue_entries
317
+ updateQuery = `UPDATE ${this.tableNameEntries}
222
318
  SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6
223
319
  WHERE id = $7 AND path = $8 AND status = $9`;
224
320
  updateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr, oldStatus];
225
321
  }
226
322
  else {
227
- updateQuery = `UPDATE queue_entries
323
+ updateQuery = `UPDATE ${this.tableNameEntries}
228
324
  SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6
229
325
  WHERE id = $7 AND path = $8`;
230
326
  updateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr];
@@ -232,7 +328,7 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
232
328
  const result = await client.query(updateQuery, updateParams);
233
329
  await this.toctouDelay?.();
234
330
  if (oldStatus && result.rowCount === 0) {
235
- const currentEntry = await client.query('SELECT status FROM queue_entries WHERE id = $1 AND path = $2', [id, this.pathStr]);
331
+ const currentEntry = await client.query(`SELECT status FROM ${this.tableNameEntries} WHERE id = $1 AND path = $2`, [id, this.pathStr]);
236
332
  const currentStatus = currentEntry.rows[0]?.status;
237
333
  if (currentEntry.rows.length > 0) {
238
334
  if (currentStatus === undefined) {
@@ -249,7 +345,7 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
249
345
  async get(id) {
250
346
  return (await this.dbTransaction('get', async (client) => {
251
347
  const row = await client.query(`SELECT id, request, output, last_error, status, created, updated, worker, failures
252
- FROM queue_entries WHERE id = $1 AND path = $2`, [id, this.pathStr]);
348
+ FROM ${this.tableNameEntries} WHERE id = $1 AND path = $2`, [id, this.pathStr]);
253
349
  if (row.rows.length === 0) {
254
350
  return (null);
255
351
  }
@@ -257,7 +353,7 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
257
353
  if (!entry) {
258
354
  return (null);
259
355
  }
260
- const idempotentRows = await client.query('SELECT idempotent_id FROM queue_idempotent_keys WHERE entry_id = $1 AND path = $2', [id, this.pathStr]);
356
+ const idempotentRows = await client.query(`SELECT idempotent_id FROM ${this.tableNameIdempotentKeys} WHERE entry_id = $1 AND path = $2`, [id, this.pathStr]);
261
357
  const idempotentKeys = idempotentRows.rows.length > 0
262
358
  ? new Set(idempotentRows.rows.map(function (idempotentRow) {
263
359
  return (ConvertStringToRequestID(idempotentRow.idempotent_id));
@@ -296,10 +392,13 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
296
392
  conditions.push(`updated < $${paramIndex++}`);
297
393
  params.push(filter.updatedBefore.getTime());
298
394
  }
299
- let query = 'SELECT id, request, output, last_error, status, created, updated, worker, failures FROM queue_entries';
395
+ let query = `SELECT id, request, output, last_error, status, created, updated, worker, failures FROM ${this.tableNameEntries}`;
300
396
  if (conditions.length > 0) {
301
397
  query += ' WHERE ' + conditions.join(' AND ');
302
398
  }
399
+ // Use random ordering to prevent multiple workers from contending for the same rows
400
+ // This spreads the load when multiple workers query simultaneously
401
+ query += ' ORDER BY RANDOM()';
303
402
  if (filter?.limit !== undefined) {
304
403
  query += ` LIMIT $${paramIndex++}`;
305
404
  params.push(filter.limit);
@@ -307,7 +406,7 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
307
406
  const rows = await client.query(query, params);
308
407
  const entries = [];
309
408
  for (const row of rows.rows) {
310
- const idempotentRows = await client.query('SELECT idempotent_id FROM queue_idempotent_keys WHERE entry_id = $1 AND path = $2', [row.id, this.pathStr]);
409
+ const idempotentRows = await client.query(`SELECT idempotent_id FROM ${this.tableNameIdempotentKeys} WHERE entry_id = $1 AND path = $2`, [row.id, this.pathStr]);
311
410
  const idempotentKeys = idempotentRows.rows.length > 0
312
411
  ? new Set(idempotentRows.rows.map(function (idempotentRow) {
313
412
  return (ConvertStringToRequestID(idempotentRow.idempotent_id));
@@ -342,7 +441,8 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
342
441
  id: `${this.id}::${path}`,
343
442
  logger: this.logger,
344
443
  pool: this.poolInternal,
345
- path: [...this.path, path]
444
+ path: [...this.path, path],
445
+ tablePrefix: this.tablePrefix
346
446
  });
347
447
  return (retval);
348
448
  }
@@ -366,6 +466,18 @@ export default class KeetaAnchorQueueStorageDriverPostgres {
366
466
  },
367
467
  unsetToctouDelay: () => {
368
468
  this.toctouDelay = undefined;
469
+ },
470
+ getSerializationRetryCount: () => {
471
+ return (this.serializationRetryCount);
472
+ },
473
+ resetSerializationRetryCount: () => {
474
+ this.serializationRetryCount = 0;
475
+ },
476
+ enableDebugForceIndexScan: () => {
477
+ this.debugForceIndexScan = true;
478
+ },
479
+ disableDebugForceIndexScan: () => {
480
+ this.debugForceIndexScan = false;
369
481
  }
370
482
  });
371
483
  }
@@ -1 +1 @@
1
- {"version":3,"file":"queue_postgres.js","sourceRoot":"","sources":["../../../../src/lib/queue/drivers/queue_postgres.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tKeetaAnchorQueueStorageDriver,\n\tKeetaAnchorQueueStorageDriverConstructor,\n\tKeetaAnchorQueueRequest,\n\tKeetaAnchorQueueRequestID,\n\tKeetaAnchorQueueEntry,\n\tKeetaAnchorQueueEntryExtra,\n\tKeetaAnchorQueueEntryAncillaryData,\n\tKeetaAnchorQueueStatus,\n\tKeetaAnchorQueueFilter,\n\tKeetaAnchorQueueWorkerID\n} from '../index.ts';\nimport {\n\tMethodLogger,\n\tManageStatusUpdates,\n\tConvertStringToRequestID\n} from '../internal.js';\nimport { Errors } from '../common.js';\n\nimport { asleep } from '../../utils/asleep.js';\n\nimport type { Logger } from '../../log/index.ts';\nimport type { JSONSerializable } from '../../utils/json.js';\n\nimport type * as pg from 'pg';\n\ntype QueueEntryRow = {\n\tid: string;\n\trequest: string;\n\toutput: string | null;\n\tlast_error: string | null;\n\tstatus: KeetaAnchorQueueStatus;\n\tcreated: Date;\n\tupdated: Date;\n\tworker: number | null;\n\tfailures: number;\n};\n\ntype IdempotentRow = {\n\tidempotent_id: string;\n};\n\nexport default class KeetaAnchorQueueStorageDriverPostgres<QueueRequest extends JSONSerializable = JSONSerializable, QueueResult extends JSONSerializable = JSONSerializable> implements KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult> {\n\tprivate readonly logger: Logger | undefined;\n\tprivate poolInternal: (() => Promise<pg.Pool>) | null = null;\n\tprivate dbInitializationPromise: Promise<boolean> | null = null;\n\n\treadonly name = 'KeetaAnchorQueueStorageDriverPostgres';\n\treadonly id: string;\n\treadonly path: string[] = [];\n\tprivate readonly pathStr: string;\n\tprivate toctouDelay: (() => Promise<void>) | undefined = undefined;\n\n\tconstructor(options: NonNullable<ConstructorParameters<KeetaAnchorQueueStorageDriverConstructor<QueueRequest, QueueResult>>[0]> & { pool: () => Promise<pg.Pool>; }) {\n\t\tthis.id = options?.id ?? crypto.randomUUID();\n\t\tthis.logger = options?.logger\n\t\tthis.poolInternal = options.pool;\n\t\tthis.path = options.path ?? [];\n\t\tthis.pathStr = ['root', ...this.path].join('.');\n\t\tObject.freeze(this.path);\n\n\t\tthis.methodLogger('new')?.debug('Initialized Postgres queue storage driver');\n\t}\n\n\tprivate async initializeDBConnection(pool: pg.Pool): Promise<pg.Pool> {\n\t\tconst logger = this.methodLogger('initializeDBConnection');\n\n\t\tif (this.dbInitializationPromise !== null) {\n\t\t\tlogger?.debug('DB schema initialization already in progress or completed, waiting for it to finish');\n\n\t\t\tawait this.dbInitializationPromise;\n\t\t\treturn(pool);\n\t\t}\n\n\t\tthis.dbInitializationPromise = (async () => {\n\t\t\tlogger?.debug('Initializing DB schema for queue storage driver');\n\n\t\t\tconst client = await pool.connect();\n\t\t\ttry {\n\t\t\t\tawait client.query(`\n\t\t\t\t\tCREATE TABLE IF NOT EXISTS queue_entries (\n\t\t\t\t\t\tid TEXT NOT NULL,\n\t\t\t\t\t\tpath TEXT NOT NULL,\n\t\t\t\t\t\trequest TEXT NOT NULL,\n\t\t\t\t\t\toutput TEXT,\n\t\t\t\t\t\tlast_error TEXT,\n\t\t\t\t\t\tstatus TEXT NOT NULL,\n\t\t\t\t\t\tcreated BIGINT NOT NULL,\n\t\t\t\t\t\tupdated BIGINT NOT NULL,\n\t\t\t\t\t\tworker BIGINT,\n\t\t\t\t\t\tfailures INTEGER NOT NULL DEFAULT 0,\n\t\t\t\t\t\tPRIMARY KEY (id, path)\n\t\t\t\t\t)`);\n\n\t\t\t\tawait client.query(`\n\t\t\t\t\tCREATE TABLE IF NOT EXISTS queue_idempotent_keys (\n\t\t\t\t\t\tentry_id TEXT NOT NULL,\n\t\t\t\t\t\tidempotent_id TEXT NOT NULL,\n\t\t\t\t\t\tpath TEXT NOT NULL,\n\t\t\t\t\t\tUNIQUE (idempotent_id, path),\n\t\t\t\t\t\tPRIMARY KEY (entry_id, idempotent_id, path),\n\t\t\t\t\t\tFOREIGN KEY (entry_id, path) REFERENCES queue_entries(id, path)\n\t\t\t\t\t)`);\n\n\t\t\t\tawait client.query('CREATE INDEX IF NOT EXISTS idx_queue_entries_status ON queue_entries(status)');\n\t\t\t\tawait client.query('CREATE INDEX IF NOT EXISTS idx_queue_entries_updated ON queue_entries(updated)');\n\t\t\t\tawait client.query('CREATE INDEX IF NOT EXISTS idx_queue_idempotent_keys_idempotent_id ON queue_idempotent_keys(idempotent_id)');\n\t\t\t} finally {\n\t\t\t\tclient.release();\n\t\t\t}\n\n\t\t\tlogger?.debug('Completed DB schema initialization for queue storage driver');\n\n\t\t\treturn(true);\n\t\t})();\n\n\t\tawait this.dbInitializationPromise;\n\n\t\treturn(pool);\n\t}\n\n\tprivate methodLogger(method: string): Logger | undefined {\n\t\treturn(MethodLogger(this.logger, {\n\t\t\tclass: 'KeetaAnchorQueueStorageDriverPostgres',\n\t\t\tfile: 'src/lib/queue/drivers/queue_postgres.ts',\n\t\t\tmethod: method,\n\t\t\tinstanceID: this.id\n\t\t}));\n\t}\n\n\tprivate async runWithRetry<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst logger = this.methodLogger('runWithRetry');\n\n\t\tlet lastError: unknown;\n\t\tfor (let retry = 0; retry < 16; retry++) {\n\t\t\tif (this.poolInternal === null) {\n\t\t\t\tthis.methodLogger('runWithRetry')?.debug('Aborting DB operation retries because the instance was destroyed');\n\n\t\t\t\tif (lastError !== undefined) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\t\t\tthrow(lastError);\n\t\t\t\t}\n\t\t\t\tthrow(new Error('Aborting because the instance was destroyed'));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\treturn(await fn());\n\t\t\t} catch (error: unknown) {\n\t\t\t\tlastError = error;\n\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tconst errorCode = 'code' in error ? error.code : null;\n\t\t\t\t\tif (errorCode === '40001' || errorCode === '40P01') {\n\t\t\t\t\t\tlogger?.debug('Serialization failure or deadlock detected');\n\n\t\t\t\t\t\tconst minBackoff = 100;\n\t\t\t\t\t\tconst maxBackoff = 30_000;\n\t\t\t\t\t\tconst backoffIntervalSize = Math.min(maxBackoff - minBackoff, (retry + 50) ** 2);\n\t\t\t\t\t\tconst backoff = Math.round((Math.random() * backoffIntervalSize)) + minBackoff;\n\n\t\t\t\t\t\tthis.methodLogger('runWithRetry')?.debug(`Retrying DB operation in ${backoff}ms (retry #${retry}) from`, new Error().stack);\n\t\t\t\t\t\tawait asleep(backoff);\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthrow(error);\n\t\t\t}\n\t\t}\n\t\tthrow(lastError);\n\t}\n\n\tprivate async newDBConnection(): Promise<pg.Pool> {\n\t\tif (this.poolInternal === null) {\n\t\t\tthrow(new Error('Database connection is not available'));\n\t\t}\n\n\t\treturn(await this.runWithRetry(async () => {\n\t\t\tif (this.poolInternal === null) {\n\t\t\t\tthrow(new Error('Database connection is not available'));\n\t\t\t}\n\n\t\t\tthis.methodLogger('newDBConnection')?.debug('Getting DB pool');\n\t\t\tconst pool = await this.poolInternal();\n\n\t\t\treturn(await this.initializeDBConnection(pool));\n\t\t}));\n\t}\n\n\tprivate async dbTransaction<T>(className: string, fn: (client: pg.PoolClient, logger: Logger | undefined) => Promise<T>): Promise<T> {\n\t\tconst pool = await this.newDBConnection();\n\t\tconst logger = this.methodLogger(className);\n\n\t\tconst result = await this.runWithRetry(async function() {\n\t\t\tconst client = await pool.connect();\n\n\t\t\ttry {\n\t\t\t\tlogger?.debug('Starting DB transaction');\n\t\t\t\tawait client.query('BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE');\n\t\t\t\tlogger?.debug('DB transaction started');\n\n\t\t\t\tconst retval = await fn(client, logger);\n\n\t\t\t\tlogger?.debug('Committing DB transaction');\n\t\t\t\tawait client.query('COMMIT');\n\t\t\t\tlogger?.debug('DB transaction committed');\n\n\t\t\t\treturn(retval);\n\t\t\t} catch (error: unknown) {\n\t\t\t\ttry {\n\t\t\t\t\tlogger?.debug('Rolling back DB transaction due to error:', error);\n\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\tlogger?.debug('DB transaction rolled back');\n\t\t\t\t} catch {\n\t\t\t\t\tlogger?.debug('Error rolling back DB transaction !!');\n\t\t\t\t}\n\t\t\t\tthrow(error);\n\t\t\t} finally {\n\t\t\t\tclient.release();\n\t\t\t}\n\t\t});\n\n\t\treturn(result);\n\t}\n\n\tasync add(request: KeetaAnchorQueueRequest<QueueRequest>, info?: KeetaAnchorQueueEntryExtra): Promise<KeetaAnchorQueueRequestID> {\n\t\treturn(await this.dbTransaction('add', async (client, logger): Promise<KeetaAnchorQueueRequestID> => {\n\t\t\tlet entryID = ConvertStringToRequestID(info?.id);\n\t\t\tif (entryID) {\n\t\t\t\tconst existingEntry = await client.query<{ id: string }>('SELECT id FROM queue_entries WHERE id = $1 AND path = $2', [entryID, this.pathStr]);\n\t\t\t\tif (existingEntry.rows.length > 0) {\n\t\t\t\t\tlogger?.debug(`Request with id ${String(entryID)} already exists, ignoring`);\n\t\t\t\t\treturn(entryID);\n\t\t\t\t}\n\n\t\t\t\tawait this.toctouDelay?.();\n\t\t\t}\n\n\t\t\tconst idempotentIDs = info?.idempotentKeys;\n\t\t\tif (idempotentIDs) {\n\t\t\t\tconst matchingIdempotentEntries = new Set<KeetaAnchorQueueRequestID>();\n\t\t\t\tfor (const idempotentID of idempotentIDs) {\n\t\t\t\t\tconst idempotentEntryExists = await client.query<IdempotentRow>(\n\t\t\t\t\t\t'SELECT idempotent_id FROM queue_idempotent_keys WHERE idempotent_id = $1 AND path = $2',\n\t\t\t\t\t\t[idempotentID, this.pathStr]\n\t\t\t\t\t);\n\t\t\t\t\tif (idempotentEntryExists.rows.length > 0) {\n\t\t\t\t\t\tmatchingIdempotentEntries.add(idempotentID);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (matchingIdempotentEntries.size !== 0) {\n\t\t\t\t\tthrow(new Errors.IdempotentExistsError('One or more idempotent entries already exist in the queue', matchingIdempotentEntries));\n\t\t\t\t}\n\n\t\t\t\tawait this.toctouDelay?.();\n\t\t\t}\n\n\t\t\tentryID ??= ConvertStringToRequestID(crypto.randomUUID());\n\n\t\t\tlogger?.debug(`Enqueuing request with id ${String(entryID)}`);\n\n\t\t\tconst currentTime = Date.now();\n\t\t\tconst requestJSON = JSON.stringify(request);\n\n\t\t\t/**\n\t\t\t * The status to use for the new entry\n\t\t\t */\n\t\t\tconst status = info?.status ?? 'pending';\n\n\t\t\tawait client.query(\n\t\t\t\t`INSERT INTO queue_entries (id, path, request, output, last_error, status, created, updated, worker, failures)\n\t\t\t\t VALUES ($1, $2, $3, NULL, NULL, $4, $5, $6, NULL, 0)`,\n\t\t\t\t[entryID, this.pathStr, requestJSON, status, currentTime, currentTime]\n\t\t\t);\n\n\t\t\tif (idempotentIDs && idempotentIDs.size > 0) {\n\t\t\t\tfor (const idempotentID of idempotentIDs) {\n\t\t\t\t\tawait client.query('INSERT INTO queue_idempotent_keys (entry_id, path, idempotent_id) VALUES ($1, $2, $3)', [entryID, this.pathStr, idempotentID]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn(entryID);\n\t\t}));\n\t}\n\n\tasync setStatus(id: KeetaAnchorQueueRequestID, status: KeetaAnchorQueueStatus, ancillary?: KeetaAnchorQueueEntryAncillaryData<QueueResult>): Promise<void> {\n\t\tconst { oldStatus } = ancillary ?? {};\n\n\t\treturn(await this.dbTransaction('setStatus', async (client, logger): Promise<void> => {\n\t\t\tconst existingEntry = await client.query<{ status: KeetaAnchorQueueStatus; failures: number; last_error: string | null; output: string | null }>('SELECT status, failures, last_error, output FROM queue_entries WHERE id = $1 AND path = $2', [id, this.pathStr]);\n\t\t\tif (existingEntry.rows.length === 0) {\n\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t}\n\n\t\t\tawait this.toctouDelay?.();\n\n\t\t\tconst currentEntry = existingEntry.rows[0];\n\t\t\tif (!currentEntry) {\n\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t}\n\n\t\t\tconst newEntry = ManageStatusUpdates<QueueResult>(id, currentEntry, status, ancillary, logger);\n\t\t\tconst currentTime = newEntry.updated.getTime();\n\t\t\tconst workerValue = newEntry.worker;\n\t\t\tconst newFailures = newEntry.failures ?? currentEntry.failures;\n\t\t\tconst newLastError = newEntry.lastError !== undefined ? newEntry.lastError : currentEntry.last_error;\n\t\t\tconst newOutput = newEntry.output !== undefined ? JSON.stringify(newEntry.output) : currentEntry.output;\n\n\t\t\tlet updateQuery: string;\n\t\t\tlet updateParams: (KeetaAnchorQueueRequestID | string | number | null)[];\n\n\t\t\tif (oldStatus) {\n\t\t\t\tupdateQuery = `UPDATE queue_entries\n\t\t\t\t SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6\n\t\t\t\t WHERE id = $7 AND path = $8 AND status = $9`;\n\t\t\t\tupdateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr, oldStatus];\n\t\t\t} else {\n\t\t\t\tupdateQuery = `UPDATE queue_entries\n\t\t\t\t SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6\n\t\t\t\t WHERE id = $7 AND path = $8`;\n\t\t\t\tupdateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr];\n\t\t\t}\n\n\t\t\tconst result = await client.query(updateQuery, updateParams);\n\n\t\t\tawait this.toctouDelay?.();\n\n\t\t\tif (oldStatus && result.rowCount === 0) {\n\t\t\t\tconst currentEntry = await client.query<{ status: KeetaAnchorQueueStatus }>('SELECT status FROM queue_entries WHERE id = $1 AND path = $2', [id, this.pathStr]);\n\t\t\t\tconst currentStatus = currentEntry.rows[0]?.status;\n\t\t\t\tif (currentEntry.rows.length > 0) {\n\t\t\t\t\tif (currentStatus === undefined) {\n\t\t\t\t\t\tthrow(new Error(`internal error: could not retrieve current status for request with ID ${String(id)}`));\n\t\t\t\t\t}\n\t\t\t\t\tthrow(new Errors.IncorrectStateAssertedError(id, oldStatus, currentStatus));\n\t\t\t\t} else {\n\t\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t\t}\n\t\t\t}\n\t\t}));\n\t}\n\n\tasync get(id: KeetaAnchorQueueRequestID): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult> | null> {\n\t\treturn(await this.dbTransaction('get', async (client): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult> | null> => {\n\t\t\tconst row = await client.query<QueueEntryRow>(\n\t\t\t\t`SELECT id, request, output, last_error, status, created, updated, worker, failures\n\t\t\t\t FROM queue_entries WHERE id = $1 AND path = $2`,\n\t\t\t\t[id, this.pathStr]\n\t\t\t);\n\n\t\t\tif (row.rows.length === 0) {\n\t\t\t\treturn(null);\n\t\t\t}\n\n\t\t\tconst entry = row.rows[0];\n\t\t\tif (!entry) {\n\t\t\t\treturn(null);\n\t\t\t}\n\n\t\t\tconst idempotentRows = await client.query<IdempotentRow>(\n\t\t\t\t'SELECT idempotent_id FROM queue_idempotent_keys WHERE entry_id = $1 AND path = $2',\n\t\t\t\t[id, this.pathStr]\n\t\t\t);\n\n\t\t\tconst idempotentKeys = idempotentRows.rows.length > 0\n\t\t\t\t? new Set(idempotentRows.rows.map(function(idempotentRow: IdempotentRow) {\n\t\t\t\t\treturn(ConvertStringToRequestID(idempotentRow.idempotent_id));\n\t\t\t\t}))\n\t\t\t\t: undefined;\n\n\t\t\treturn({\n\t\t\t\tid: ConvertStringToRequestID(entry.id),\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\trequest: JSON.parse(entry.request) as QueueRequest,\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\toutput: entry.output ? JSON.parse(entry.output) as QueueResult : null,\n\t\t\t\tlastError: entry.last_error,\n\t\t\t\tstatus: entry.status,\n\t\t\t\tcreated: new Date(Number(entry.created)),\n\t\t\t\tupdated: new Date(Number(entry.updated)),\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\tworker: entry.worker as unknown as KeetaAnchorQueueWorkerID | null,\n\t\t\t\tfailures: entry.failures,\n\t\t\t\tidempotentKeys: idempotentKeys\n\t\t\t});\n\t\t}));\n\t}\n\n\tasync query(filter?: KeetaAnchorQueueFilter): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult>[]> {\n\t\treturn(await this.dbTransaction('query', async (client, logger): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult>[]> => {\n\t\t\tlogger?.debug(`Querying queue with id ${this.id} with filter:`, filter);\n\n\t\t\tconst conditions: string[] = [];\n\t\t\tconst params: (string | number)[] = [];\n\t\t\tlet paramIndex = 1;\n\n\t\t\tconditions.push(`path = $${paramIndex++}`);\n\t\t\tparams.push(this.pathStr);\n\n\t\t\tif (filter?.status) {\n\t\t\t\tconditions.push(`status = $${paramIndex++}`);\n\t\t\t\tparams.push(filter.status);\n\t\t\t}\n\n\t\t\tif (filter?.updatedBefore) {\n\t\t\t\tconditions.push(`updated < $${paramIndex++}`);\n\t\t\t\tparams.push(filter.updatedBefore.getTime());\n\t\t\t}\n\n\t\t\tlet query = 'SELECT id, request, output, last_error, status, created, updated, worker, failures FROM queue_entries';\n\n\t\t\tif (conditions.length > 0) {\n\t\t\t\tquery += ' WHERE ' + conditions.join(' AND ');\n\t\t\t}\n\n\t\t\tif (filter?.limit !== undefined) {\n\t\t\t\tquery += ` LIMIT $${paramIndex++}`;\n\t\t\t\tparams.push(filter.limit);\n\t\t\t}\n\n\t\t\tconst rows = await client.query<QueueEntryRow>(query, params);\n\n\t\t\tconst entries: KeetaAnchorQueueEntry<QueueRequest, QueueResult>[] = [];\n\n\t\t\tfor (const row of rows.rows) {\n\t\t\t\tconst idempotentRows = await client.query<IdempotentRow>(\n\t\t\t\t\t'SELECT idempotent_id FROM queue_idempotent_keys WHERE entry_id = $1 AND path = $2',\n\t\t\t\t\t[row.id, this.pathStr]\n\t\t\t\t);\n\n\t\t\t\tconst idempotentKeys = idempotentRows.rows.length > 0\n\t\t\t\t\t? new Set(idempotentRows.rows.map(function(idempotentRow: IdempotentRow) {\n\t\t\t\t\t\treturn(ConvertStringToRequestID(idempotentRow.idempotent_id));\n\t\t\t\t\t}))\n\t\t\t\t\t: undefined;\n\n\t\t\t\tentries.push({\n\t\t\t\t\tid: ConvertStringToRequestID(row.id),\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\trequest: JSON.parse(row.request) as QueueRequest,\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\toutput: row.output ? JSON.parse(row.output) as QueueResult : null,\n\t\t\t\t\tlastError: row.last_error,\n\t\t\t\t\tstatus: row.status,\n\t\t\t\t\tcreated: new Date(Number(row.created)),\n\t\t\t\t\tupdated: new Date(Number(row.updated)),\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\tworker: row.worker as unknown as KeetaAnchorQueueWorkerID | null,\n\t\t\t\t\tfailures: row.failures,\n\t\t\t\t\tidempotentKeys: idempotentKeys\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlogger?.debug(`Queried queue with id ${this.id} with filter:`, filter, '-- found', entries.length, 'entries');\n\n\t\t\treturn(entries);\n\t\t}));\n\t}\n\n\tasync partition(path: string) : Promise<KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult>> {\n\t\tthis.methodLogger('partition')?.debug(`Creating partitioned queue storage driver for path: ${path}`);\n\n\t\tif (this.poolInternal === null) {\n\t\t\tthrow(new Error('Asked to partition but the instance has been destroyed'));\n\t\t}\n\n\t\tconst retval = new KeetaAnchorQueueStorageDriverPostgres<QueueRequest, QueueResult>({\n\t\t\tid: `${this.id}::${path}`,\n\t\t\tlogger: this.logger,\n\t\t\tpool: this.poolInternal,\n\t\t\tpath: [...this.path, path]\n\t\t});\n\n\t\treturn(retval);\n\t}\n\n\tasync destroy(): Promise<void> {\n\t\tthis.methodLogger('destroy')?.debug('Destroying instance');\n\n\t\tthis.poolInternal = null;\n\t}\n\n\tasync [Symbol.asyncDispose](): Promise<void> {\n\t\treturn(await this.destroy());\n\t}\n\n\t/** @internal */\n\t_Testing(key: string): {\n\t\tsetToctouDelay(delay: number): void;\n\t\tunsetToctouDelay(): void;\n\t} {\n\t\tif (key !== 'bc81abf8-e43b-490b-b486-744fb49a5082') {\n\t\t\tthrow(new Error('This is a testing only method'));\n\t\t}\n\n\t\treturn({\n\t\t\tsetToctouDelay: (delay: number): void => {\n\t\t\t\tthis.toctouDelay = async (): Promise<void> => {\n\t\t\t\t\treturn(await asleep(delay));\n\t\t\t\t};\n\t\t\t},\n\t\t\tunsetToctouDelay: (): void => {\n\t\t\t\tthis.toctouDelay = undefined;\n\t\t\t}\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"queue_postgres.js","sourceRoot":"","sources":["../../../../src/lib/queue/drivers/queue_postgres.ts"],"names":[],"mappings":"AAcA,OAAO,EACN,YAAY,EACZ,mBAAmB,EACnB,wBAAwB,EACxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAqC/C,MAAM,CAAC,OAAO,OAAO,qCAAqC;IACxC,MAAM,CAAqB;IACpC,YAAY,GAAoC,IAAI,CAAC;IACrD,WAAW,CAAqB;IAChC,gBAAgB,CAAS;IACzB,sBAAsB,CAAS;IAC/B,uBAAuB,CAAS;IAChC,uBAAuB,GAA4B,IAAI,CAAC;IACxD,uBAAuB,GAAG,CAAC,CAAC;IAC5B,mBAAmB,GAAG,KAAK,CAAC;IAE3B,IAAI,GAAG,uCAAuC,CAAC;IAC/C,EAAE,CAAS;IACX,IAAI,GAAa,EAAE,CAAC;IACZ,OAAO,CAAS;IACzB,WAAW,GAAsC,SAAS,CAAC;IAEnE,YAAY,OAAkK;QAC7K,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,EAAE,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;QAC9B,IAAI,CAAC,WAAW,GAAG,OAAO,EAAE,WAAW,CAAC;QACxC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEzB,IAAI,CAAC,gBAAgB,GAAG,GAAG,IAAI,CAAC,WAAW,IAAI,OAAO,UAAU,CAAC;QACjE,IAAI,CAAC,uBAAuB,GAAG,GAAG,IAAI,CAAC,WAAW,IAAI,OAAO,kBAAkB,CAAC;QAChF,IAAI,CAAC,sBAAsB,GAAG,GAAG,IAAI,CAAC,WAAW,IAAI,OAAO,iBAAiB,CAAC;QAE9E,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC9E,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAAC,IAAa;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,wBAAwB,CAAC,CAAC;QAE3D,IAAI,IAAI,CAAC,uBAAuB,KAAK,IAAI,EAAE,CAAC;YAC3C,MAAM,EAAE,KAAK,CAAC,qFAAqF,CAAC,CAAC;YAErG,MAAM,IAAI,CAAC,uBAAuB,CAAC;YACnC,OAAM,CAAC,IAAI,CAAC,CAAC;QACd,CAAC;QAED,IAAI,CAAC,uBAAuB,GAAG,CAAC,KAAK,IAAI,EAAE;YAC1C,MAAM,EAAE,KAAK,CAAC,2DAA2D,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC;YAElI,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC;gBACJ;;;;;;;;mBAQG;gBACH,MAAM,MAAM,GAAG,UAAU,CAAC;gBAC1B,MAAM,EAAE,KAAK,CAAC,8CAA8C,CAAC,CAAC;gBAC9D,MAAM,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;gBAE5D,IAAI,CAAC;oBACJ,kDAAkD;oBAClD,MAAM,MAAM,CAAC,KAAK,CAAC;mCACW,IAAI,CAAC,sBAAsB;;;;QAItD,CAAC,CAAC;oBAEL,+BAA+B;oBAC/B,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,KAAK,CAAsB,uCAAuC,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC;oBACpI,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC;oBAE3D,MAAM,EAAE,KAAK,CAAC,iCAAiC,cAAc,EAAE,CAAC,CAAC;oBAEjE,4BAA4B;oBAC5B,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;wBACxB,MAAM,EAAE,KAAK,CAAC,uDAAuD,CAAC,CAAC;wBAEvE,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;wBAC5B,IAAI,CAAC;4BACJ,MAAM,MAAM,CAAC,KAAK,CAAC;qCACW,IAAI,CAAC,gBAAgB;;;;;;;;;;;;UAYhD,CAAC,CAAC;4BAEL,MAAM,MAAM,CAAC,KAAK,CAAC;qCACW,IAAI,CAAC,uBAAuB;;;;;;mDAMd,IAAI,CAAC,gBAAgB;UAC9D,CAAC,CAAC;4BAEL,wDAAwD;4BACxD,MAAM,MAAM,CAAC,KAAK,CAAC,kCAAkC,IAAI,CAAC,gBAAgB,cAAc,IAAI,CAAC,gBAAgB,UAAU,CAAC,CAAC;4BACzH,MAAM,MAAM,CAAC,KAAK,CAAC,kCAAkC,IAAI,CAAC,gBAAgB,eAAe,IAAI,CAAC,gBAAgB,WAAW,CAAC,CAAC;4BAC3H,MAAM,MAAM,CAAC,KAAK,CAAC,kCAAkC,IAAI,CAAC,uBAAuB,qBAAqB,IAAI,CAAC,uBAAuB,iBAAiB,CAAC,CAAC;4BAErJ,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,sBAAsB,uCAAuC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;4BACpH,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;4BAC7B,MAAM,EAAE,KAAK,CAAC,0BAA0B,CAAC,CAAC;wBAC3C,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BAChB,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;4BAC/B,MAAK,CAAC,KAAK,CAAC,CAAC;wBACd,CAAC;oBACF,CAAC;oBAED,+CAA+C;oBAC/C,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;wBACxB,MAAM,EAAE,KAAK,CAAC,8DAA8D,CAAC,CAAC;wBAE9E,gFAAgF;wBAChF,MAAM,EAAE,KAAK,CAAC,qCAAqC,CAAC,CAAC;wBACrD,MAAM,MAAM,CAAC,KAAK,CAAC,+CAA+C,IAAI,CAAC,gBAAgB,mBAAmB,IAAI,CAAC,gBAAgB,gBAAgB,CAAC,CAAC;wBACjJ,MAAM,MAAM,CAAC,KAAK,CAAC,+CAA+C,IAAI,CAAC,gBAAgB,oBAAoB,IAAI,CAAC,gBAAgB,iBAAiB,CAAC,CAAC;wBACnJ,MAAM,MAAM,CAAC,KAAK,CAAC,+CAA+C,IAAI,CAAC,gBAAgB,2BAA2B,IAAI,CAAC,gBAAgB,yBAAyB,CAAC,CAAC;wBAClK,MAAM,MAAM,CAAC,KAAK,CAAC,+CAA+C,IAAI,CAAC,uBAAuB,0BAA0B,IAAI,CAAC,uBAAuB,uBAAuB,CAAC,CAAC;wBAE7K,0CAA0C;wBAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;wBAC5B,IAAI,CAAC;4BACJ,8FAA8F;4BAC9F,MAAM,EAAE,KAAK,CAAC,uCAAuC,CAAC,CAAC;4BACvD,MAAM,MAAM,CAAC,KAAK,CAAC,4BAA4B,IAAI,CAAC,gBAAgB,SAAS,CAAC,CAAC;4BAC/E,MAAM,MAAM,CAAC,KAAK,CAAC,4BAA4B,IAAI,CAAC,gBAAgB,UAAU,CAAC,CAAC;4BAChF,MAAM,MAAM,CAAC,KAAK,CAAC,4BAA4B,IAAI,CAAC,uBAAuB,gBAAgB,CAAC,CAAC;4BAE7F,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,sBAAsB,uCAAuC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;4BACpH,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;4BAC7B,MAAM,EAAE,KAAK,CAAC,0BAA0B,CAAC,CAAC;wBAC3C,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BAChB,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;4BAC/B,MAAK,CAAC,KAAK,CAAC,CAAC;wBACd,CAAC;oBACF,CAAC;oBAED,MAAM,EAAE,KAAK,CAAC,sBAAsB,CAAC,CAAC;gBACvC,CAAC;wBAAS,CAAC;oBACV,mCAAmC;oBACnC,yEAAyE;oBACzE,qFAAqF;oBACrF,IAAI,CAAC;wBACJ,MAAM,EAAE,KAAK,CAAC,yBAAyB,CAAC,CAAC;wBACzC,MAAM,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC/D,CAAC;oBAAC,OAAO,WAAW,EAAE,CAAC;wBACtB,8EAA8E;wBAC9E,MAAM,EAAE,KAAK,CAAC,+EAA+E,EAAE,WAAW,CAAC,CAAC;oBAC7G,CAAC;gBACF,CAAC;YACF,CAAC;oBAAS,CAAC;gBACV,MAAM,CAAC,OAAO,EAAE,CAAC;YAClB,CAAC;YAED,MAAM,EAAE,KAAK,CAAC,6DAA6D,CAAC,CAAC;YAE7E,OAAM,CAAC,IAAI,CAAC,CAAC;QACd,CAAC,CAAC,EAAE,CAAC;QAEL,MAAM,IAAI,CAAC,uBAAuB,CAAC;QAEnC,OAAM,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAEO,YAAY,CAAC,MAAc;QAClC,OAAM,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE;YAChC,KAAK,EAAE,uCAAuC;YAC9C,IAAI,EAAE,yCAAyC;YAC/C,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,IAAI,CAAC,EAAE;SACnB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,YAAY,CAAI,EAAoB;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;QAEjD,IAAI,SAAkB,CAAC;QACvB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;gBAChC,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,kEAAkE,CAAC,CAAC;gBAE7G,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC7B,+DAA+D;oBAC/D,MAAK,CAAC,SAAS,CAAC,CAAC;gBAClB,CAAC;gBACD,MAAK,CAAC,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,CAAC;YACjE,CAAC;YAED,IAAI,CAAC;gBACJ,OAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACpB,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACzB,SAAS,GAAG,KAAK,CAAC;gBAElB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;oBAC5B,MAAM,SAAS,GAAG,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;oBACtD,IAAI,SAAS,KAAK,OAAO,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;wBACpD,MAAM,EAAE,KAAK,CAAC,4CAA4C,CAAC,CAAC;wBAE5D,kDAAkD;wBAClD,IAAI,CAAC,uBAAuB,EAAE,CAAC;wBAE/B,MAAM,UAAU,GAAG,GAAG,CAAC;wBACvB,MAAM,UAAU,GAAG,MAAM,CAAC;wBAC1B,MAAM,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,UAAU,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;wBACjF,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,mBAAmB,CAAC,CAAC,GAAG,UAAU,CAAC;wBAE/E,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,4BAA4B,OAAO,cAAc,KAAK,QAAQ,EAAE,IAAI,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;wBAC5H,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;wBAEtB,SAAS;oBACV,CAAC;gBACF,CAAC;gBAED,MAAK,CAAC,KAAK,CAAC,CAAC;YACd,CAAC;QACF,CAAC;QACD,MAAK,CAAC,SAAS,CAAC,CAAC;IAClB,CAAC;IAEO,KAAK,CAAC,eAAe;QAC5B,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAChC,MAAK,CAAC,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAC;QAC1D,CAAC;QAED,OAAM,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE;YACzC,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;gBAChC,MAAK,CAAC,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAC;YAC1D,CAAC;YAED,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC/D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YAEvC,OAAM,CAAC,MAAM,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,aAAa,CAAI,SAAiB,EAAE,EAAqE;QACtH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAE5C,MAAM,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,CAAC;QAErD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK;YAC3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YAEpC,IAAI,CAAC;gBACJ,MAAM,EAAE,KAAK,CAAC,yBAAyB,CAAC,CAAC;gBACzC,MAAM,MAAM,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;gBACrE,MAAM,EAAE,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBAExC,IAAI,mBAAmB,EAAE,CAAC;oBACzB,MAAM,MAAM,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;gBACvD,CAAC;gBAED,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAExC,MAAM,EAAE,KAAK,CAAC,2BAA2B,CAAC,CAAC;gBAC3C,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC7B,MAAM,EAAE,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBAE1C,OAAM,CAAC,MAAM,CAAC,CAAC;YAChB,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACzB,IAAI,CAAC;oBACJ,MAAM,EAAE,KAAK,CAAC,2CAA2C,EAAE,KAAK,CAAC,CAAC;oBAClE,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;oBAC/B,MAAM,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAC;gBAC7C,CAAC;gBAAC,MAAM,CAAC;oBACR,MAAM,EAAE,KAAK,CAAC,sCAAsC,CAAC,CAAC;gBACvD,CAAC;gBACD,MAAK,CAAC,KAAK,CAAC,CAAC;YACd,CAAC;oBAAS,CAAC;gBACV,MAAM,CAAC,OAAO,EAAE,CAAC;YAClB,CAAC;QACF,CAAC,CAAC,CAAC;QAEH,OAAM,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAA8C,EAAE,IAAiC;QAC1F,OAAM,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAsC,EAAE;YACnG,IAAI,OAAO,GAAG,wBAAwB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACjD,IAAI,OAAO,EAAE,CAAC;gBACb,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,KAAK,CAAiB,kBAAkB,IAAI,CAAC,gBAAgB,8BAA8B,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;gBACzJ,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACnC,MAAM,EAAE,KAAK,CAAC,mBAAmB,MAAM,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;oBAC7E,OAAM,CAAC,OAAO,CAAC,CAAC;gBACjB,CAAC;gBAED,MAAM,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC5B,CAAC;YAED,MAAM,aAAa,GAAG,IAAI,EAAE,cAAc,CAAC;YAC3C,IAAI,aAAa,EAAE,CAAC;gBACnB,MAAM,yBAAyB,GAAG,IAAI,GAAG,EAA6B,CAAC;gBACvE,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;oBAC1C,MAAM,qBAAqB,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/C,6BAA6B,IAAI,CAAC,uBAAuB,yCAAyC,EAClG,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,CAC5B,CAAC;oBACF,IAAI,qBAAqB,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC3C,yBAAyB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;oBAC7C,CAAC;gBACF,CAAC;gBAED,IAAI,yBAAyB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;oBAC1C,MAAK,CAAC,IAAI,MAAM,CAAC,qBAAqB,CAAC,2DAA2D,EAAE,yBAAyB,CAAC,CAAC,CAAC;gBACjI,CAAC;gBAED,MAAM,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC5B,CAAC;YAED,OAAO,KAAK,wBAAwB,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;YAE1D,MAAM,EAAE,KAAK,CAAC,6BAA6B,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAE9D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAE5C;;eAEG;YACH,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,SAAS,CAAC;YAEzC,MAAM,MAAM,CAAC,KAAK,CACjB,eAAe,IAAI,CAAC,gBAAgB;0DACkB,EACtD,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,CACtE,CAAC;YAEF,IAAI,aAAa,IAAI,aAAa,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC7C,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;oBAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,uBAAuB,sDAAsD,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;gBAC9J,CAAC;YACF,CAAC;YAED,OAAM,CAAC,OAAO,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAA6B,EAAE,MAA8B,EAAE,SAA2D;QACzI,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,IAAI,EAAE,CAAC;QAEtC,OAAM,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAiB,EAAE;YACpF,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,KAAK,CAAyG,oDAAoD,IAAI,CAAC,gBAAgB,yCAAyC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YACzR,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACrC,MAAK,CAAC,IAAI,KAAK,CAAC,mBAAmB,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;YAC7D,CAAC;YAED,MAAM,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAE3B,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3C,IAAI,CAAC,YAAY,EAAE,CAAC;gBACnB,MAAK,CAAC,IAAI,KAAK,CAAC,mBAAmB,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;YAC7D,CAAC;YAED,MAAM,QAAQ,GAAG,mBAAmB,CAAc,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;YAC/F,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YAC/C,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC;YACpC,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,IAAI,YAAY,CAAC,QAAQ,CAAC;YAC/D,MAAM,YAAY,GAAG,QAAQ,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC;YACrG,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC;YAExG,IAAI,WAAmB,CAAC;YACxB,IAAI,YAAoE,CAAC;YAEzE,IAAI,SAAS,EAAE,CAAC;gBACf,WAAW,GAAG,UAAU,IAAI,CAAC,gBAAgB;;+DAEc,CAAC;gBAC5D,YAAY,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACtH,CAAC;iBAAM,CAAC;gBACP,WAAW,GAAG,UAAU,IAAI,CAAC,gBAAgB;;+CAEF,CAAC;gBAC5C,YAAY,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3G,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAE7D,MAAM,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAE3B,IAAI,SAAS,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACxC,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,KAAK,CAAqC,sBAAsB,IAAI,CAAC,gBAAgB,8BAA8B,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC3K,MAAM,aAAa,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;gBACnD,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClC,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;wBACjC,MAAK,CAAC,IAAI,KAAK,CAAC,yEAAyE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;oBACzG,CAAC;oBACD,MAAK,CAAC,IAAI,MAAM,CAAC,2BAA2B,CAAC,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC;gBAC7E,CAAC;qBAAM,CAAC;oBACP,MAAK,CAAC,IAAI,KAAK,CAAC,mBAAmB,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;gBAC7D,CAAC;YACF,CAAC;QACF,CAAC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAA6B;QACtC,OAAM,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAoE,EAAE;YACzH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC7B;YACQ,IAAI,CAAC,gBAAgB,8BAA8B,EAC3D,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAClB,CAAC;YAEF,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,OAAM,CAAC,IAAI,CAAC,CAAC;YACd,CAAC;YAED,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACZ,OAAM,CAAC,IAAI,CAAC,CAAC;YACd,CAAC;YAED,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,KAAK,CACxC,6BAA6B,IAAI,CAAC,uBAAuB,oCAAoC,EAC7F,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAClB,CAAC;YAEF,MAAM,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;gBACpD,CAAC,CAAC,IAAI,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,UAAS,aAA4B;oBACtE,OAAM,CAAC,wBAAwB,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC;gBAC/D,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,SAAS,CAAC;YAEb,OAAM,CAAC;gBACN,EAAE,EAAE,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtC,yEAAyE;gBACzE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAiB;gBAClD,yEAAyE;gBACzE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAgB,CAAC,CAAC,CAAC,IAAI;gBACrE,SAAS,EAAE,KAAK,CAAC,UAAU;gBAC3B,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,OAAO,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACxC,OAAO,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACxC,yEAAyE;gBACzE,MAAM,EAAE,KAAK,CAAC,MAAoD;gBAClE,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,cAAc,EAAE,cAAc;aAC9B,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,MAA+B;QAC1C,OAAM,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAA+D,EAAE;YAC9H,MAAM,EAAE,KAAK,CAAC,0BAA0B,IAAI,CAAC,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;YAExE,MAAM,UAAU,GAAa,EAAE,CAAC;YAChC,MAAM,MAAM,GAAwB,EAAE,CAAC;YACvC,IAAI,UAAU,GAAG,CAAC,CAAC;YAEnB,UAAU,CAAC,IAAI,CAAC,WAAW,UAAU,EAAE,EAAE,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE1B,IAAI,MAAM,EAAE,MAAM,EAAE,CAAC;gBACpB,UAAU,CAAC,IAAI,CAAC,aAAa,UAAU,EAAE,EAAE,CAAC,CAAC;gBAC7C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;YAED,IAAI,MAAM,EAAE,aAAa,EAAE,CAAC;gBAC3B,UAAU,CAAC,IAAI,CAAC,cAAc,UAAU,EAAE,EAAE,CAAC,CAAC;gBAC9C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7C,CAAC;YAED,IAAI,KAAK,GAAG,2FAA2F,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAE/H,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,KAAK,IAAI,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/C,CAAC;YAED,oFAAoF;YACpF,mEAAmE;YACnE,KAAK,IAAI,oBAAoB,CAAC;YAE9B,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;gBACjC,KAAK,IAAI,WAAW,UAAU,EAAE,EAAE,CAAC;gBACnC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,KAAK,CAAgB,KAAK,EAAE,MAAM,CAAC,CAAC;YAE9D,MAAM,OAAO,GAAuD,EAAE,CAAC;YAEvE,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC7B,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,KAAK,CACxC,6BAA6B,IAAI,CAAC,uBAAuB,oCAAoC,EAC7F,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CACtB,CAAC;gBAEF,MAAM,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;oBACpD,CAAC,CAAC,IAAI,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,UAAS,aAA4B;wBACtE,OAAM,CAAC,wBAAwB,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC;oBAC/D,CAAC,CAAC,CAAC;oBACH,CAAC,CAAC,SAAS,CAAC;gBAEb,OAAO,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,wBAAwB,CAAC,GAAG,CAAC,EAAE,CAAC;oBACpC,yEAAyE;oBACzE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAiB;oBAChD,yEAAyE;oBACzE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAgB,CAAC,CAAC,CAAC,IAAI;oBACjE,SAAS,EAAE,GAAG,CAAC,UAAU;oBACzB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,OAAO,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACtC,OAAO,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACtC,yEAAyE;oBACzE,MAAM,EAAE,GAAG,CAAC,MAAoD;oBAChE,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,cAAc,EAAE,cAAc;iBAC9B,CAAC,CAAC;YACJ,CAAC;YAED,MAAM,EAAE,KAAK,CAAC,yBAAyB,IAAI,CAAC,EAAE,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAE9G,OAAM,CAAC,OAAO,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY;QAC3B,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,uDAAuD,IAAI,EAAE,CAAC,CAAC;QAErG,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAChC,MAAK,CAAC,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,qCAAqC,CAA4B;YACnF,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE;YACzB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,IAAI,EAAE,IAAI,CAAC,YAAY;YACvB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;YAC1B,WAAW,EAAE,IAAI,CAAC,WAAW;SAC7B,CAAC,CAAC;QAEH,OAAM,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAE3D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QAC1B,OAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,gBAAgB;IAChB,QAAQ,CAAC,GAAW;QAQnB,IAAI,GAAG,KAAK,sCAAsC,EAAE,CAAC;YACpD,MAAK,CAAC,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,OAAM,CAAC;YACN,cAAc,EAAE,CAAC,KAAa,EAAQ,EAAE;gBACvC,IAAI,CAAC,WAAW,GAAG,KAAK,IAAmB,EAAE;oBAC5C,OAAM,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC7B,CAAC,CAAC;YACH,CAAC;YACD,gBAAgB,EAAE,GAAS,EAAE;gBAC5B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YAC9B,CAAC;YACD,0BAA0B,EAAE,GAAW,EAAE;gBACxC,OAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;YACtC,CAAC;YACD,4BAA4B,EAAE,GAAS,EAAE;gBACxC,IAAI,CAAC,uBAAuB,GAAG,CAAC,CAAC;YAClC,CAAC;YACD,yBAAyB,EAAE,GAAS,EAAE;gBACrC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;YACjC,CAAC;YACD,0BAA0B,EAAE,GAAS,EAAE;gBACtC,IAAI,CAAC,mBAAmB,GAAG,KAAK,CAAC;YAClC,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;CACD","sourcesContent":["// cspell:ignore seqscan\n\nimport type {\n\tKeetaAnchorQueueStorageDriver,\n\tKeetaAnchorQueueStorageDriverConstructor,\n\tKeetaAnchorQueueRequest,\n\tKeetaAnchorQueueRequestID,\n\tKeetaAnchorQueueEntry,\n\tKeetaAnchorQueueEntryExtra,\n\tKeetaAnchorQueueEntryAncillaryData,\n\tKeetaAnchorQueueStatus,\n\tKeetaAnchorQueueFilter,\n\tKeetaAnchorQueueWorkerID\n} from '../index.ts';\nimport {\n\tMethodLogger,\n\tManageStatusUpdates,\n\tConvertStringToRequestID\n} from '../internal.js';\nimport { Errors } from '../common.js';\n\nimport { asleep } from '../../utils/asleep.js';\n\nimport type { Logger } from '../../log/index.ts';\nimport type { JSONSerializable } from '../../utils/json.js';\n\nimport type * as pg from 'pg';\n\ntype QueueEntryRow = {\n\tid: string;\n\trequest: string;\n\toutput: string | null;\n\tlast_error: string | null;\n\tstatus: KeetaAnchorQueueStatus;\n\tcreated: Date;\n\tupdated: Date;\n\tworker: number | null;\n\tfailures: number;\n};\n\ntype IdempotentRow = {\n\tidempotent_id: string;\n};\n\ntype KeetaAnchorQueueStorageDriverPostgresOptions = {\n\t/**\n\t * Function that returns a Postgres connection pool.\n\t */\n\tpool: () => Promise<pg.Pool>;\n\n\t/**\n\t * The prefix to use for the database tables. Defaults to 'queue',\n\t * resulting in table names 'queue_entries', 'queue_idempotent_keys',\n\t * and `queue_schema_version`.\n\t */\n\ttablePrefix?: string | undefined;\n};\n\nexport default class KeetaAnchorQueueStorageDriverPostgres<QueueRequest extends JSONSerializable = JSONSerializable, QueueResult extends JSONSerializable = JSONSerializable> implements KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult> {\n\tprivate readonly logger: Logger | undefined;\n\tprivate poolInternal: (() => Promise<pg.Pool>) | null = null;\n\tprivate tablePrefix: string | undefined;\n\tprivate tableNameEntries: string;\n\tprivate tableNameSchemaVersion: string;\n\tprivate tableNameIdempotentKeys: string;\n\tprivate dbInitializationPromise: Promise<boolean> | null = null;\n\tprivate serializationRetryCount = 0;\n\tprivate debugForceIndexScan = false;\n\n\treadonly name = 'KeetaAnchorQueueStorageDriverPostgres';\n\treadonly id: string;\n\treadonly path: string[] = [];\n\tprivate readonly pathStr: string;\n\tprivate toctouDelay: (() => Promise<void>) | undefined = undefined;\n\n\tconstructor(options: NonNullable<ConstructorParameters<KeetaAnchorQueueStorageDriverConstructor<QueueRequest, QueueResult>>[0]> & KeetaAnchorQueueStorageDriverPostgresOptions) {\n\t\tthis.id = options?.id ?? crypto.randomUUID();\n\t\tthis.logger = options?.logger;\n\t\tthis.tablePrefix = options?.tablePrefix;\n\t\tthis.poolInternal = options.pool;\n\t\tthis.path = options.path ?? [];\n\t\tthis.pathStr = ['root', ...this.path].join('.');\n\t\tObject.freeze(this.path);\n\n\t\tthis.tableNameEntries = `${this.tablePrefix ?? 'queue'}_entries`;\n\t\tthis.tableNameIdempotentKeys = `${this.tablePrefix ?? 'queue'}_idempotent_keys`;\n\t\tthis.tableNameSchemaVersion = `${this.tablePrefix ?? 'queue'}_schema_version`;\n\n\t\tthis.methodLogger('new')?.debug('Initialized Postgres queue storage driver');\n\t}\n\n\tprivate async initializeDBConnection(pool: pg.Pool): Promise<pg.Pool> {\n\t\tconst logger = this.methodLogger('initializeDBConnection');\n\n\t\tif (this.dbInitializationPromise !== null) {\n\t\t\tlogger?.debug('DB schema initialization already in progress or completed, waiting for it to finish');\n\n\t\t\tawait this.dbInitializationPromise;\n\t\t\treturn(pool);\n\t\t}\n\n\t\tthis.dbInitializationPromise = (async () => {\n\t\t\tlogger?.debug('Initializing DB schema for queue storage driver on tables', [this.tableNameEntries, this.tableNameIdempotentKeys]);\n\n\t\t\tconst client = await pool.connect();\n\t\t\ttry {\n\t\t\t\t/*\n\t\t\t\t * Random lock key (32-bit integer), to ensure\n\t\t\t\t * that multiple instances of this driver\n\t\t\t\t * (potentially across different\n\t\t\t\t * instances/process) will run the migration\n\t\t\t\t * sequentially to avoid multiple concurrent\n\t\t\t\t * migrations from competing with each other\n\t\t\t\t * and causing delays\n\t\t\t\t */\n\t\t\t\tconst lockId = 0x24995E48;\n\t\t\t\tlogger?.debug('Acquiring advisory lock for schema migration');\n\t\t\t\tawait client.query('SELECT pg_advisory_lock($1)', [lockId]);\n\n\t\t\t\ttry {\n\t\t\t\t\t// Create schema version table if it doesn't exist\n\t\t\t\t\tawait client.query(`\n\t\t\t\t\t\tCREATE TABLE IF NOT EXISTS ${this.tableNameSchemaVersion} (\n\t\t\t\t\t\t\tversion INTEGER NOT NULL,\n\t\t\t\t\t\t\tapplied_at BIGINT NOT NULL,\n\t\t\t\t\t\t\tPRIMARY KEY (version)\n\t\t\t\t\t\t)`);\n\n\t\t\t\t\t// Check current schema version\n\t\t\t\t\tconst versionResult = await client.query<{ version: number }>(`SELECT MAX(version) as version FROM ${this.tableNameSchemaVersion}`);\n\t\t\t\t\tconst currentVersion = versionResult.rows[0]?.version ?? 0;\n\n\t\t\t\t\tlogger?.debug(`Current queue schema version: ${currentVersion}`);\n\n\t\t\t\t\t// Version 1: Initial schema\n\t\t\t\t\tif (currentVersion < 1) {\n\t\t\t\t\t\tlogger?.debug('Applying schema version 1: Initial tables and indexes');\n\n\t\t\t\t\t\tawait client.query('BEGIN');\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait client.query(`\n\t\t\t\t\t\t\t\tCREATE TABLE IF NOT EXISTS ${this.tableNameEntries} (\n\t\t\t\t\t\t\t\t\tid TEXT NOT NULL,\n\t\t\t\t\t\t\t\t\tpath TEXT NOT NULL,\n\t\t\t\t\t\t\t\t\trequest TEXT NOT NULL,\n\t\t\t\t\t\t\t\t\toutput TEXT,\n\t\t\t\t\t\t\t\t\tlast_error TEXT,\n\t\t\t\t\t\t\t\t\tstatus TEXT NOT NULL,\n\t\t\t\t\t\t\t\t\tcreated BIGINT NOT NULL,\n\t\t\t\t\t\t\t\t\tupdated BIGINT NOT NULL,\n\t\t\t\t\t\t\t\t\tworker BIGINT,\n\t\t\t\t\t\t\t\t\tfailures INTEGER NOT NULL DEFAULT 0,\n\t\t\t\t\t\t\t\t\tPRIMARY KEY (id, path)\n\t\t\t\t\t\t\t\t)`);\n\n\t\t\t\t\t\t\tawait client.query(`\n\t\t\t\t\t\t\t\tCREATE TABLE IF NOT EXISTS ${this.tableNameIdempotentKeys} (\n\t\t\t\t\t\t\t\t\tentry_id TEXT NOT NULL,\n\t\t\t\t\t\t\t\t\tidempotent_id TEXT NOT NULL,\n\t\t\t\t\t\t\t\t\tpath TEXT NOT NULL,\n\t\t\t\t\t\t\t\t\tUNIQUE (idempotent_id, path),\n\t\t\t\t\t\t\t\t\tPRIMARY KEY (entry_id, idempotent_id, path),\n\t\t\t\t\t\t\t\t\tFOREIGN KEY (entry_id, path) REFERENCES ${this.tableNameEntries}(id, path)\n\t\t\t\t\t\t\t\t)`);\n\n\t\t\t\t\t\t\t// Old single-column indexes (for pre-version-2 schemas)\n\t\t\t\t\t\t\tawait client.query(`CREATE INDEX IF NOT EXISTS idx_${this.tableNameEntries}_status ON ${this.tableNameEntries}(status)`);\n\t\t\t\t\t\t\tawait client.query(`CREATE INDEX IF NOT EXISTS idx_${this.tableNameEntries}_updated ON ${this.tableNameEntries}(updated)`);\n\t\t\t\t\t\t\tawait client.query(`CREATE INDEX IF NOT EXISTS idx_${this.tableNameIdempotentKeys}_idempotent_id ON ${this.tableNameIdempotentKeys}(idempotent_id)`);\n\n\t\t\t\t\t\t\tawait client.query(`INSERT INTO ${this.tableNameSchemaVersion} (version, applied_at) VALUES (1, $1)`, [Date.now()]);\n\t\t\t\t\t\t\tawait client.query('COMMIT');\n\t\t\t\t\t\t\tlogger?.debug('Applied schema version 1');\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\t\t\tthrow(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Version 2: Partition-aware composite indexes\n\t\t\t\t\tif (currentVersion < 2) {\n\t\t\t\t\t\tlogger?.debug('Applying schema version 2: Partition-aware composite indexes');\n\n\t\t\t\t\t\t// Create new partition-aware indexes (CONCURRENTLY must be outside transaction)\n\t\t\t\t\t\tlogger?.debug('Creating partition-aware indexes...');\n\t\t\t\t\t\tawait client.query(`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_${this.tableNameEntries}_path_status ON ${this.tableNameEntries}(path, status)`);\n\t\t\t\t\t\tawait client.query(`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_${this.tableNameEntries}_path_updated ON ${this.tableNameEntries}(path, updated)`);\n\t\t\t\t\t\tawait client.query(`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_${this.tableNameEntries}_path_status_updated ON ${this.tableNameEntries}(path, status, updated)`);\n\t\t\t\t\t\tawait client.query(`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_${this.tableNameIdempotentKeys}_path_idempotent_id ON ${this.tableNameIdempotentKeys}(path, idempotent_id)`);\n\n\t\t\t\t\t\t// Now drop old indexes and record version\n\t\t\t\t\t\tawait client.query('BEGIN');\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Drop old indexes that are now redundant (these will fail gracefully if indexes don't exist)\n\t\t\t\t\t\t\tlogger?.debug('Dropping old single-column indexes...');\n\t\t\t\t\t\t\tawait client.query(`DROP INDEX IF EXISTS idx_${this.tableNameEntries}_status`);\n\t\t\t\t\t\t\tawait client.query(`DROP INDEX IF EXISTS idx_${this.tableNameEntries}_updated`);\n\t\t\t\t\t\t\tawait client.query(`DROP INDEX IF EXISTS idx_${this.tableNameIdempotentKeys}_idempotent_id`);\n\n\t\t\t\t\t\t\tawait client.query(`INSERT INTO ${this.tableNameSchemaVersion} (version, applied_at) VALUES (2, $1)`, [Date.now()]);\n\t\t\t\t\t\t\tawait client.query('COMMIT');\n\t\t\t\t\t\t\tlogger?.debug('Applied schema version 2');\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\t\t\tthrow(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tlogger?.debug('Schema is up to date');\n\t\t\t\t} finally {\n\t\t\t\t\t// Always release the advisory lock\n\t\t\t\t\t// Note: Advisory locks are session-based and auto-release on disconnect,\n\t\t\t\t\t// but we explicitly unlock for clarity and to avoid holding locks longer than needed\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlogger?.debug('Releasing advisory lock');\n\t\t\t\t\t\tawait client.query('SELECT pg_advisory_unlock($1)', [lockId]);\n\t\t\t\t\t} catch (unlockError) {\n\t\t\t\t\t\t// Log but don't throw - the lock will be auto-released when connection closes\n\t\t\t\t\t\tlogger?.debug('Failed to explicitly release advisory lock (will auto-release on disconnect):', unlockError);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tclient.release();\n\t\t\t}\n\n\t\t\tlogger?.debug('Completed DB schema initialization for queue storage driver');\n\n\t\t\treturn(true);\n\t\t})();\n\n\t\tawait this.dbInitializationPromise;\n\n\t\treturn(pool);\n\t}\n\n\tprivate methodLogger(method: string): Logger | undefined {\n\t\treturn(MethodLogger(this.logger, {\n\t\t\tclass: 'KeetaAnchorQueueStorageDriverPostgres',\n\t\t\tfile: 'src/lib/queue/drivers/queue_postgres.ts',\n\t\t\tmethod: method,\n\t\t\tinstanceID: this.id\n\t\t}));\n\t}\n\n\tprivate async runWithRetry<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst logger = this.methodLogger('runWithRetry');\n\n\t\tlet lastError: unknown;\n\t\tfor (let retry = 0; retry < 16; retry++) {\n\t\t\tif (this.poolInternal === null) {\n\t\t\t\tthis.methodLogger('runWithRetry')?.debug('Aborting DB operation retries because the instance was destroyed');\n\n\t\t\t\tif (lastError !== undefined) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\t\t\tthrow(lastError);\n\t\t\t\t}\n\t\t\t\tthrow(new Error('Aborting because the instance was destroyed'));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\treturn(await fn());\n\t\t\t} catch (error: unknown) {\n\t\t\t\tlastError = error;\n\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tconst errorCode = 'code' in error ? error.code : null;\n\t\t\t\t\tif (errorCode === '40001' || errorCode === '40P01') {\n\t\t\t\t\t\tlogger?.debug('Serialization failure or deadlock detected');\n\n\t\t\t\t\t\t// Track serialization retries for instrumentation\n\t\t\t\t\t\tthis.serializationRetryCount++;\n\n\t\t\t\t\t\tconst minBackoff = 100;\n\t\t\t\t\t\tconst maxBackoff = 30_000;\n\t\t\t\t\t\tconst backoffIntervalSize = Math.min(maxBackoff - minBackoff, (retry + 50) ** 2);\n\t\t\t\t\t\tconst backoff = Math.round((Math.random() * backoffIntervalSize)) + minBackoff;\n\n\t\t\t\t\t\tthis.methodLogger('runWithRetry')?.debug(`Retrying DB operation in ${backoff}ms (retry #${retry}) from`, new Error().stack);\n\t\t\t\t\t\tawait asleep(backoff);\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthrow(error);\n\t\t\t}\n\t\t}\n\t\tthrow(lastError);\n\t}\n\n\tprivate async newDBConnection(): Promise<pg.Pool> {\n\t\tif (this.poolInternal === null) {\n\t\t\tthrow(new Error('Database connection is not available'));\n\t\t}\n\n\t\treturn(await this.runWithRetry(async () => {\n\t\t\tif (this.poolInternal === null) {\n\t\t\t\tthrow(new Error('Database connection is not available'));\n\t\t\t}\n\n\t\t\tthis.methodLogger('newDBConnection')?.debug('Getting DB pool');\n\t\t\tconst pool = await this.poolInternal();\n\n\t\t\treturn(await this.initializeDBConnection(pool));\n\t\t}));\n\t}\n\n\tprivate async dbTransaction<T>(className: string, fn: (client: pg.PoolClient, logger: Logger | undefined) => Promise<T>): Promise<T> {\n\t\tconst pool = await this.newDBConnection();\n\t\tconst logger = this.methodLogger(className);\n\n\t\tconst debugForceIndexScan = this.debugForceIndexScan;\n\n\t\tconst result = await this.runWithRetry(async function() {\n\t\t\tconst client = await pool.connect();\n\n\t\t\ttry {\n\t\t\t\tlogger?.debug('Starting DB transaction');\n\t\t\t\tawait client.query('BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE');\n\t\t\t\tlogger?.debug('DB transaction started');\n\n\t\t\t\tif (debugForceIndexScan) {\n\t\t\t\t\tawait client.query('SET LOCAL enable_seqscan TO off');\n\t\t\t\t}\n\n\t\t\t\tconst retval = await fn(client, logger);\n\n\t\t\t\tlogger?.debug('Committing DB transaction');\n\t\t\t\tawait client.query('COMMIT');\n\t\t\t\tlogger?.debug('DB transaction committed');\n\n\t\t\t\treturn(retval);\n\t\t\t} catch (error: unknown) {\n\t\t\t\ttry {\n\t\t\t\t\tlogger?.debug('Rolling back DB transaction due to error:', error);\n\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\tlogger?.debug('DB transaction rolled back');\n\t\t\t\t} catch {\n\t\t\t\t\tlogger?.debug('Error rolling back DB transaction !!');\n\t\t\t\t}\n\t\t\t\tthrow(error);\n\t\t\t} finally {\n\t\t\t\tclient.release();\n\t\t\t}\n\t\t});\n\n\t\treturn(result);\n\t}\n\n\tasync add(request: KeetaAnchorQueueRequest<QueueRequest>, info?: KeetaAnchorQueueEntryExtra): Promise<KeetaAnchorQueueRequestID> {\n\t\treturn(await this.dbTransaction('add', async (client, logger): Promise<KeetaAnchorQueueRequestID> => {\n\t\t\tlet entryID = ConvertStringToRequestID(info?.id);\n\t\t\tif (entryID) {\n\t\t\t\tconst existingEntry = await client.query<{ id: string }>(`SELECT id FROM ${this.tableNameEntries} WHERE id = $1 AND path = $2`, [entryID, this.pathStr]);\n\t\t\t\tif (existingEntry.rows.length > 0) {\n\t\t\t\t\tlogger?.debug(`Request with id ${String(entryID)} already exists, ignoring`);\n\t\t\t\t\treturn(entryID);\n\t\t\t\t}\n\n\t\t\t\tawait this.toctouDelay?.();\n\t\t\t}\n\n\t\t\tconst idempotentIDs = info?.idempotentKeys;\n\t\t\tif (idempotentIDs) {\n\t\t\t\tconst matchingIdempotentEntries = new Set<KeetaAnchorQueueRequestID>();\n\t\t\t\tfor (const idempotentID of idempotentIDs) {\n\t\t\t\t\tconst idempotentEntryExists = await client.query<IdempotentRow>(\n\t\t\t\t\t\t`SELECT idempotent_id FROM ${this.tableNameIdempotentKeys} WHERE idempotent_id = $1 AND path = $2`,\n\t\t\t\t\t\t[idempotentID, this.pathStr]\n\t\t\t\t\t);\n\t\t\t\t\tif (idempotentEntryExists.rows.length > 0) {\n\t\t\t\t\t\tmatchingIdempotentEntries.add(idempotentID);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (matchingIdempotentEntries.size !== 0) {\n\t\t\t\t\tthrow(new Errors.IdempotentExistsError('One or more idempotent entries already exist in the queue', matchingIdempotentEntries));\n\t\t\t\t}\n\n\t\t\t\tawait this.toctouDelay?.();\n\t\t\t}\n\n\t\t\tentryID ??= ConvertStringToRequestID(crypto.randomUUID());\n\n\t\t\tlogger?.debug(`Enqueuing request with id ${String(entryID)}`);\n\n\t\t\tconst currentTime = Date.now();\n\t\t\tconst requestJSON = JSON.stringify(request);\n\n\t\t\t/**\n\t\t\t * The status to use for the new entry\n\t\t\t */\n\t\t\tconst status = info?.status ?? 'pending';\n\n\t\t\tawait client.query(\n\t\t\t\t`INSERT INTO ${this.tableNameEntries} (id, path, request, output, last_error, status, created, updated, worker, failures)\n\t\t\t\t VALUES ($1, $2, $3, NULL, NULL, $4, $5, $6, NULL, 0)`,\n\t\t\t\t[entryID, this.pathStr, requestJSON, status, currentTime, currentTime]\n\t\t\t);\n\n\t\t\tif (idempotentIDs && idempotentIDs.size > 0) {\n\t\t\t\tfor (const idempotentID of idempotentIDs) {\n\t\t\t\t\tawait client.query(`INSERT INTO ${this.tableNameIdempotentKeys} (entry_id, path, idempotent_id) VALUES ($1, $2, $3)`, [entryID, this.pathStr, idempotentID]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn(entryID);\n\t\t}));\n\t}\n\n\tasync setStatus(id: KeetaAnchorQueueRequestID, status: KeetaAnchorQueueStatus, ancillary?: KeetaAnchorQueueEntryAncillaryData<QueueResult>): Promise<void> {\n\t\tconst { oldStatus } = ancillary ?? {};\n\n\t\treturn(await this.dbTransaction('setStatus', async (client, logger): Promise<void> => {\n\t\t\tconst existingEntry = await client.query<{ status: KeetaAnchorQueueStatus; failures: number; last_error: string | null; output: string | null }>(`SELECT status, failures, last_error, output FROM ${this.tableNameEntries} WHERE id = $1 AND path = $2 FOR UPDATE`, [id, this.pathStr]);\n\t\t\tif (existingEntry.rows.length === 0) {\n\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t}\n\n\t\t\tawait this.toctouDelay?.();\n\n\t\t\tconst currentEntry = existingEntry.rows[0];\n\t\t\tif (!currentEntry) {\n\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t}\n\n\t\t\tconst newEntry = ManageStatusUpdates<QueueResult>(id, currentEntry, status, ancillary, logger);\n\t\t\tconst currentTime = newEntry.updated.getTime();\n\t\t\tconst workerValue = newEntry.worker;\n\t\t\tconst newFailures = newEntry.failures ?? currentEntry.failures;\n\t\t\tconst newLastError = newEntry.lastError !== undefined ? newEntry.lastError : currentEntry.last_error;\n\t\t\tconst newOutput = newEntry.output !== undefined ? JSON.stringify(newEntry.output) : currentEntry.output;\n\n\t\t\tlet updateQuery: string;\n\t\t\tlet updateParams: (KeetaAnchorQueueRequestID | string | number | null)[];\n\n\t\t\tif (oldStatus) {\n\t\t\t\tupdateQuery = `UPDATE ${this.tableNameEntries}\n\t\t\t\t SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6\n\t\t\t\t WHERE id = $7 AND path = $8 AND status = $9`;\n\t\t\t\tupdateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr, oldStatus];\n\t\t\t} else {\n\t\t\t\tupdateQuery = `UPDATE ${this.tableNameEntries}\n\t\t\t\t SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6\n\t\t\t\t WHERE id = $7 AND path = $8`;\n\t\t\t\tupdateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr];\n\t\t\t}\n\n\t\t\tconst result = await client.query(updateQuery, updateParams);\n\n\t\t\tawait this.toctouDelay?.();\n\n\t\t\tif (oldStatus && result.rowCount === 0) {\n\t\t\t\tconst currentEntry = await client.query<{ status: KeetaAnchorQueueStatus }>(`SELECT status FROM ${this.tableNameEntries} WHERE id = $1 AND path = $2`, [id, this.pathStr]);\n\t\t\t\tconst currentStatus = currentEntry.rows[0]?.status;\n\t\t\t\tif (currentEntry.rows.length > 0) {\n\t\t\t\t\tif (currentStatus === undefined) {\n\t\t\t\t\t\tthrow(new Error(`internal error: could not retrieve current status for request with ID ${String(id)}`));\n\t\t\t\t\t}\n\t\t\t\t\tthrow(new Errors.IncorrectStateAssertedError(id, oldStatus, currentStatus));\n\t\t\t\t} else {\n\t\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t\t}\n\t\t\t}\n\t\t}));\n\t}\n\n\tasync get(id: KeetaAnchorQueueRequestID): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult> | null> {\n\t\treturn(await this.dbTransaction('get', async (client): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult> | null> => {\n\t\t\tconst row = await client.query<QueueEntryRow>(\n\t\t\t\t`SELECT id, request, output, last_error, status, created, updated, worker, failures\n\t\t\t\t FROM ${this.tableNameEntries} WHERE id = $1 AND path = $2`,\n\t\t\t\t[id, this.pathStr]\n\t\t\t);\n\n\t\t\tif (row.rows.length === 0) {\n\t\t\t\treturn(null);\n\t\t\t}\n\n\t\t\tconst entry = row.rows[0];\n\t\t\tif (!entry) {\n\t\t\t\treturn(null);\n\t\t\t}\n\n\t\t\tconst idempotentRows = await client.query<IdempotentRow>(\n\t\t\t\t`SELECT idempotent_id FROM ${this.tableNameIdempotentKeys} WHERE entry_id = $1 AND path = $2`,\n\t\t\t\t[id, this.pathStr]\n\t\t\t);\n\n\t\t\tconst idempotentKeys = idempotentRows.rows.length > 0\n\t\t\t\t? new Set(idempotentRows.rows.map(function(idempotentRow: IdempotentRow) {\n\t\t\t\t\treturn(ConvertStringToRequestID(idempotentRow.idempotent_id));\n\t\t\t\t}))\n\t\t\t\t: undefined;\n\n\t\t\treturn({\n\t\t\t\tid: ConvertStringToRequestID(entry.id),\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\trequest: JSON.parse(entry.request) as QueueRequest,\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\toutput: entry.output ? JSON.parse(entry.output) as QueueResult : null,\n\t\t\t\tlastError: entry.last_error,\n\t\t\t\tstatus: entry.status,\n\t\t\t\tcreated: new Date(Number(entry.created)),\n\t\t\t\tupdated: new Date(Number(entry.updated)),\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\tworker: entry.worker as unknown as KeetaAnchorQueueWorkerID | null,\n\t\t\t\tfailures: entry.failures,\n\t\t\t\tidempotentKeys: idempotentKeys\n\t\t\t});\n\t\t}));\n\t}\n\n\tasync query(filter?: KeetaAnchorQueueFilter): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult>[]> {\n\t\treturn(await this.dbTransaction('query', async (client, logger): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult>[]> => {\n\t\t\tlogger?.debug(`Querying queue with id ${this.id} with filter:`, filter);\n\n\t\t\tconst conditions: string[] = [];\n\t\t\tconst params: (string | number)[] = [];\n\t\t\tlet paramIndex = 1;\n\n\t\t\tconditions.push(`path = $${paramIndex++}`);\n\t\t\tparams.push(this.pathStr);\n\n\t\t\tif (filter?.status) {\n\t\t\t\tconditions.push(`status = $${paramIndex++}`);\n\t\t\t\tparams.push(filter.status);\n\t\t\t}\n\n\t\t\tif (filter?.updatedBefore) {\n\t\t\t\tconditions.push(`updated < $${paramIndex++}`);\n\t\t\t\tparams.push(filter.updatedBefore.getTime());\n\t\t\t}\n\n\t\t\tlet query = `SELECT id, request, output, last_error, status, created, updated, worker, failures FROM ${this.tableNameEntries}`;\n\n\t\t\tif (conditions.length > 0) {\n\t\t\t\tquery += ' WHERE ' + conditions.join(' AND ');\n\t\t\t}\n\n\t\t\t// Use random ordering to prevent multiple workers from contending for the same rows\n\t\t\t// This spreads the load when multiple workers query simultaneously\n\t\t\tquery += ' ORDER BY RANDOM()';\n\n\t\t\tif (filter?.limit !== undefined) {\n\t\t\t\tquery += ` LIMIT $${paramIndex++}`;\n\t\t\t\tparams.push(filter.limit);\n\t\t\t}\n\n\t\t\tconst rows = await client.query<QueueEntryRow>(query, params);\n\n\t\t\tconst entries: KeetaAnchorQueueEntry<QueueRequest, QueueResult>[] = [];\n\n\t\t\tfor (const row of rows.rows) {\n\t\t\t\tconst idempotentRows = await client.query<IdempotentRow>(\n\t\t\t\t\t`SELECT idempotent_id FROM ${this.tableNameIdempotentKeys} WHERE entry_id = $1 AND path = $2`,\n\t\t\t\t\t[row.id, this.pathStr]\n\t\t\t\t);\n\n\t\t\t\tconst idempotentKeys = idempotentRows.rows.length > 0\n\t\t\t\t\t? new Set(idempotentRows.rows.map(function(idempotentRow: IdempotentRow) {\n\t\t\t\t\t\treturn(ConvertStringToRequestID(idempotentRow.idempotent_id));\n\t\t\t\t\t}))\n\t\t\t\t\t: undefined;\n\n\t\t\t\tentries.push({\n\t\t\t\t\tid: ConvertStringToRequestID(row.id),\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\trequest: JSON.parse(row.request) as QueueRequest,\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\toutput: row.output ? JSON.parse(row.output) as QueueResult : null,\n\t\t\t\t\tlastError: row.last_error,\n\t\t\t\t\tstatus: row.status,\n\t\t\t\t\tcreated: new Date(Number(row.created)),\n\t\t\t\t\tupdated: new Date(Number(row.updated)),\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\tworker: row.worker as unknown as KeetaAnchorQueueWorkerID | null,\n\t\t\t\t\tfailures: row.failures,\n\t\t\t\t\tidempotentKeys: idempotentKeys\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlogger?.debug(`Queried queue with id ${this.id} with filter:`, filter, '-- found', entries.length, 'entries');\n\n\t\t\treturn(entries);\n\t\t}));\n\t}\n\n\tasync partition(path: string) : Promise<KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult>> {\n\t\tthis.methodLogger('partition')?.debug(`Creating partitioned queue storage driver for path: ${path}`);\n\n\t\tif (this.poolInternal === null) {\n\t\t\tthrow(new Error('Asked to partition but the instance has been destroyed'));\n\t\t}\n\n\t\tconst retval = new KeetaAnchorQueueStorageDriverPostgres<QueueRequest, QueueResult>({\n\t\t\tid: `${this.id}::${path}`,\n\t\t\tlogger: this.logger,\n\t\t\tpool: this.poolInternal,\n\t\t\tpath: [...this.path, path],\n\t\t\ttablePrefix: this.tablePrefix\n\t\t});\n\n\t\treturn(retval);\n\t}\n\n\tasync destroy(): Promise<void> {\n\t\tthis.methodLogger('destroy')?.debug('Destroying instance');\n\n\t\tthis.poolInternal = null;\n\t}\n\n\tasync [Symbol.asyncDispose](): Promise<void> {\n\t\treturn(await this.destroy());\n\t}\n\n\t/** @internal */\n\t_Testing(key: string): {\n\t\tsetToctouDelay(delay: number): void;\n\t\tunsetToctouDelay(): void;\n\t\tgetSerializationRetryCount(): number;\n\t\tresetSerializationRetryCount(): void;\n\t\tenableDebugForceIndexScan(): void;\n\t\tdisableDebugForceIndexScan(): void;\n\t} {\n\t\tif (key !== 'bc81abf8-e43b-490b-b486-744fb49a5082') {\n\t\t\tthrow(new Error('This is a testing only method'));\n\t\t}\n\n\t\treturn({\n\t\t\tsetToctouDelay: (delay: number): void => {\n\t\t\t\tthis.toctouDelay = async (): Promise<void> => {\n\t\t\t\t\treturn(await asleep(delay));\n\t\t\t\t};\n\t\t\t},\n\t\t\tunsetToctouDelay: (): void => {\n\t\t\t\tthis.toctouDelay = undefined;\n\t\t\t},\n\t\t\tgetSerializationRetryCount: (): number => {\n\t\t\t\treturn(this.serializationRetryCount);\n\t\t\t},\n\t\t\tresetSerializationRetryCount: (): void => {\n\t\t\t\tthis.serializationRetryCount = 0;\n\t\t\t},\n\t\t\tenableDebugForceIndexScan: (): void => {\n\t\t\t\tthis.debugForceIndexScan = true;\n\t\t\t},\n\t\t\tdisableDebugForceIndexScan: (): void => {\n\t\t\t\tthis.debugForceIndexScan = false;\n\t\t\t}\n\t\t});\n\t}\n}\n"]}
package/lib/resolver.d.ts CHANGED
@@ -192,7 +192,7 @@ type ServiceMetadata = {
192
192
  assetMovement?: {
193
193
  [id: string]: SharedAnchorMetadataLegalExtension & {
194
194
  operations: {
195
- [Operation in ('initiateTransfer' | 'executeTransfer' | 'getTransferStatus' | 'initiatePersistentForwardingTemplate' | 'createPersistentForwardingTemplate' | 'listPersistentForwardingTemplate' | 'createPersistentForwarding' | 'listPersistentForwarding' | 'listTransactions' | 'shareKYC')]?: ServiceMetadataEndpoint;
195
+ [Operation in ('initiateTransfer' | 'simulateTransfer' | 'executeTransfer' | 'getTransferStatus' | 'initiatePersistentForwardingTemplate' | 'createPersistentForwardingTemplate' | 'listPersistentForwardingTemplate' | 'createPersistentForwarding' | 'listPersistentForwarding' | 'listTransactions' | 'shareKYC')]?: ServiceMetadataEndpoint;
196
196
  };
197
197
  supportedAssets: SupportedAssetsMetadata[];
198
198
  locationMetadata?: AnchorCustomLocationMetadata | undefined;