@stonyx/orm 0.3.2-beta.89 → 0.3.2-beta.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dynamodb/dynamodb-db.d.ts +10 -0
- package/dist/dynamodb/dynamodb-db.js +10 -0
- package/dist/mysql/mysql-db.d.ts +8 -0
- package/dist/mysql/mysql-db.js +22 -8
- package/dist/postgres/postgres-db.d.ts +8 -0
- package/dist/postgres/postgres-db.js +22 -8
- package/package.json +1 -1
- package/src/dynamodb/dynamodb-db.ts +10 -0
- package/src/mysql/mysql-db.ts +24 -8
- package/src/postgres/postgres-db.ts +24 -8
|
@@ -101,6 +101,16 @@ export default class DynamoDBDB {
|
|
|
101
101
|
*/
|
|
102
102
|
startup(): Promise<void>;
|
|
103
103
|
shutdown(): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* DynamoDB does NOT use write serialization (#156).
|
|
106
|
+
*
|
|
107
|
+
* Unlike MySQL/PostgreSQL, DynamoDB has no server-side foreign key
|
|
108
|
+
* constraints and no multi-row transactions in standard single-item
|
|
109
|
+
* operations (PutItem, UpdateItem, DeleteItem). Each operation is
|
|
110
|
+
* atomic at the item level and cannot deadlock against other items.
|
|
111
|
+
* Concurrent fire-and-forget writes therefore cannot produce the
|
|
112
|
+
* cross-row lock contention that causes InnoDB/PG deadlocks.
|
|
113
|
+
*/
|
|
104
114
|
persist(operation: string, modelName: string, context: PersistContext, response: PersistResponse): Promise<void>;
|
|
105
115
|
findRecord(modelName: string, id: unknown): Promise<OrmRecord | undefined>;
|
|
106
116
|
findAll(modelName: string, conditions?: Record<string, unknown>): Promise<OrmRecord[]>;
|
|
@@ -191,6 +191,16 @@ export default class DynamoDBDB {
|
|
|
191
191
|
// -------------------------------------------------------------------------
|
|
192
192
|
// SqlDb contract — persist
|
|
193
193
|
// -------------------------------------------------------------------------
|
|
194
|
+
/**
|
|
195
|
+
* DynamoDB does NOT use write serialization (#156).
|
|
196
|
+
*
|
|
197
|
+
* Unlike MySQL/PostgreSQL, DynamoDB has no server-side foreign key
|
|
198
|
+
* constraints and no multi-row transactions in standard single-item
|
|
199
|
+
* operations (PutItem, UpdateItem, DeleteItem). Each operation is
|
|
200
|
+
* atomic at the item level and cannot deadlock against other items.
|
|
201
|
+
* Concurrent fire-and-forget writes therefore cannot produce the
|
|
202
|
+
* cross-row lock contention that causes InnoDB/PG deadlocks.
|
|
203
|
+
*/
|
|
194
204
|
async persist(operation, modelName, context, response) {
|
|
195
205
|
const OrmModule = await this._getOrm();
|
|
196
206
|
if (OrmModule.default?.instance?.isView?.(modelName))
|
package/dist/mysql/mysql-db.d.ts
CHANGED
|
@@ -61,6 +61,14 @@ export default class MysqlDB {
|
|
|
61
61
|
deps: MysqlDBDeps;
|
|
62
62
|
pool: Pool | null;
|
|
63
63
|
mysqlConfig: MysqlConfig;
|
|
64
|
+
/**
|
|
65
|
+
* Promise-chain mutex for write serialization (#156).
|
|
66
|
+
* All persist() calls chain through this single queue so concurrent
|
|
67
|
+
* fire-and-forget writes never produce parallel InnoDB transactions
|
|
68
|
+
* on FK-linked rows (which cause deadlocks).
|
|
69
|
+
* Reads are NOT affected — only persist() serializes.
|
|
70
|
+
*/
|
|
71
|
+
private _writeQueue;
|
|
64
72
|
constructor(deps?: Partial<MysqlDBDeps>);
|
|
65
73
|
private requirePool;
|
|
66
74
|
init(): Promise<void>;
|
package/dist/mysql/mysql-db.js
CHANGED
|
@@ -26,6 +26,14 @@ export default class MysqlDB {
|
|
|
26
26
|
deps;
|
|
27
27
|
pool;
|
|
28
28
|
mysqlConfig;
|
|
29
|
+
/**
|
|
30
|
+
* Promise-chain mutex for write serialization (#156).
|
|
31
|
+
* All persist() calls chain through this single queue so concurrent
|
|
32
|
+
* fire-and-forget writes never produce parallel InnoDB transactions
|
|
33
|
+
* on FK-linked rows (which cause deadlocks).
|
|
34
|
+
* Reads are NOT affected — only persist() serializes.
|
|
35
|
+
*/
|
|
36
|
+
_writeQueue = Promise.resolve();
|
|
29
37
|
constructor(deps = {}) {
|
|
30
38
|
if (MysqlDB.instance)
|
|
31
39
|
return MysqlDB.instance;
|
|
@@ -302,14 +310,20 @@ export default class MysqlDB {
|
|
|
302
310
|
const Orm = (await import('@stonyx/orm')).default;
|
|
303
311
|
if (Orm.instance?.isView?.(modelName))
|
|
304
312
|
return;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
+
const work = async () => {
|
|
314
|
+
switch (operation) {
|
|
315
|
+
case 'create':
|
|
316
|
+
return this._persistCreate(modelName, context, response);
|
|
317
|
+
case 'update':
|
|
318
|
+
return this._persistUpdate(modelName, context, response);
|
|
319
|
+
case 'delete':
|
|
320
|
+
return this._persistDelete(modelName, context);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
// Chain through the write queue — .then(work, work) ensures the queue
|
|
324
|
+
// advances even when a previous persist rejects (#156).
|
|
325
|
+
this._writeQueue = this._writeQueue.then(work, work);
|
|
326
|
+
return this._writeQueue;
|
|
313
327
|
}
|
|
314
328
|
async _persistCreate(modelName, context, response) {
|
|
315
329
|
const schemas = this.deps.introspectModels();
|
|
@@ -72,6 +72,14 @@ export default class PostgresDB {
|
|
|
72
72
|
deps: PostgresDeps;
|
|
73
73
|
pool: Pool | null;
|
|
74
74
|
pgConfig: Record<string, unknown>;
|
|
75
|
+
/**
|
|
76
|
+
* Promise-chain mutex for write serialization (#156).
|
|
77
|
+
* All persist() calls chain through this single queue so concurrent
|
|
78
|
+
* fire-and-forget writes never produce parallel transactions
|
|
79
|
+
* on FK-linked rows (which cause deadlocks).
|
|
80
|
+
* Reads are NOT affected — only persist() serializes.
|
|
81
|
+
*/
|
|
82
|
+
private _writeQueue;
|
|
75
83
|
constructor(deps?: Partial<PostgresDeps>);
|
|
76
84
|
protected requirePool(): Pool;
|
|
77
85
|
init(): Promise<void>;
|
|
@@ -30,6 +30,14 @@ export default class PostgresDB {
|
|
|
30
30
|
deps;
|
|
31
31
|
pool;
|
|
32
32
|
pgConfig;
|
|
33
|
+
/**
|
|
34
|
+
* Promise-chain mutex for write serialization (#156).
|
|
35
|
+
* All persist() calls chain through this single queue so concurrent
|
|
36
|
+
* fire-and-forget writes never produce parallel transactions
|
|
37
|
+
* on FK-linked rows (which cause deadlocks).
|
|
38
|
+
* Reads are NOT affected — only persist() serializes.
|
|
39
|
+
*/
|
|
40
|
+
_writeQueue = Promise.resolve();
|
|
33
41
|
constructor(deps = {}) {
|
|
34
42
|
const Ctor = this.constructor;
|
|
35
43
|
if (Ctor.instance)
|
|
@@ -356,14 +364,20 @@ export default class PostgresDB {
|
|
|
356
364
|
const Orm = (await import('@stonyx/orm')).default;
|
|
357
365
|
if (Orm.instance?.isView?.(modelName))
|
|
358
366
|
return;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
+
const work = async () => {
|
|
368
|
+
switch (operation) {
|
|
369
|
+
case 'create':
|
|
370
|
+
return this._persistCreate(modelName, context, response);
|
|
371
|
+
case 'update':
|
|
372
|
+
return this._persistUpdate(modelName, context, response);
|
|
373
|
+
case 'delete':
|
|
374
|
+
return this._persistDelete(modelName, context);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
// Chain through the write queue — .then(work, work) ensures the queue
|
|
378
|
+
// advances even when a previous persist rejects (#156).
|
|
379
|
+
this._writeQueue = this._writeQueue.then(work, work);
|
|
380
|
+
return this._writeQueue;
|
|
367
381
|
}
|
|
368
382
|
async _persistCreate(modelName, context, response) {
|
|
369
383
|
const schemas = this.deps.introspectModels();
|
package/package.json
CHANGED
|
@@ -319,6 +319,16 @@ export default class DynamoDBDB {
|
|
|
319
319
|
// SqlDb contract — persist
|
|
320
320
|
// -------------------------------------------------------------------------
|
|
321
321
|
|
|
322
|
+
/**
|
|
323
|
+
* DynamoDB does NOT use write serialization (#156).
|
|
324
|
+
*
|
|
325
|
+
* Unlike MySQL/PostgreSQL, DynamoDB has no server-side foreign key
|
|
326
|
+
* constraints and no multi-row transactions in standard single-item
|
|
327
|
+
* operations (PutItem, UpdateItem, DeleteItem). Each operation is
|
|
328
|
+
* atomic at the item level and cannot deadlock against other items.
|
|
329
|
+
* Concurrent fire-and-forget writes therefore cannot produce the
|
|
330
|
+
* cross-row lock contention that causes InnoDB/PG deadlocks.
|
|
331
|
+
*/
|
|
322
332
|
async persist(operation: string, modelName: string, context: PersistContext, response: PersistResponse): Promise<void> {
|
|
323
333
|
const OrmModule = await this._getOrm();
|
|
324
334
|
if (OrmModule.default?.instance?.isView?.(modelName)) return;
|
package/src/mysql/mysql-db.ts
CHANGED
|
@@ -84,6 +84,15 @@ export default class MysqlDB {
|
|
|
84
84
|
pool!: Pool | null;
|
|
85
85
|
mysqlConfig!: MysqlConfig;
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Promise-chain mutex for write serialization (#156).
|
|
89
|
+
* All persist() calls chain through this single queue so concurrent
|
|
90
|
+
* fire-and-forget writes never produce parallel InnoDB transactions
|
|
91
|
+
* on FK-linked rows (which cause deadlocks).
|
|
92
|
+
* Reads are NOT affected — only persist() serializes.
|
|
93
|
+
*/
|
|
94
|
+
private _writeQueue: Promise<void> = Promise.resolve();
|
|
95
|
+
|
|
87
96
|
constructor(deps: Partial<MysqlDBDeps> = {}) {
|
|
88
97
|
if (MysqlDB.instance) return MysqlDB.instance;
|
|
89
98
|
MysqlDB.instance = this;
|
|
@@ -398,14 +407,21 @@ export default class MysqlDB {
|
|
|
398
407
|
const Orm = (await import('@stonyx/orm')).default;
|
|
399
408
|
if ((Orm as unknown as { instance?: { isView?: (name: string) => boolean } }).instance?.isView?.(modelName)) return;
|
|
400
409
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
410
|
+
const work = async () => {
|
|
411
|
+
switch (operation) {
|
|
412
|
+
case 'create':
|
|
413
|
+
return this._persistCreate(modelName, context, response);
|
|
414
|
+
case 'update':
|
|
415
|
+
return this._persistUpdate(modelName, context, response);
|
|
416
|
+
case 'delete':
|
|
417
|
+
return this._persistDelete(modelName, context);
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// Chain through the write queue — .then(work, work) ensures the queue
|
|
422
|
+
// advances even when a previous persist rejects (#156).
|
|
423
|
+
this._writeQueue = this._writeQueue.then(work, work);
|
|
424
|
+
return this._writeQueue;
|
|
409
425
|
}
|
|
410
426
|
|
|
411
427
|
private async _persistCreate(modelName: string, context: PersistContext, response: PersistResponse): Promise<void> {
|
|
@@ -90,6 +90,15 @@ export default class PostgresDB {
|
|
|
90
90
|
pool!: Pool | null;
|
|
91
91
|
pgConfig!: Record<string, unknown>;
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Promise-chain mutex for write serialization (#156).
|
|
95
|
+
* All persist() calls chain through this single queue so concurrent
|
|
96
|
+
* fire-and-forget writes never produce parallel transactions
|
|
97
|
+
* on FK-linked rows (which cause deadlocks).
|
|
98
|
+
* Reads are NOT affected — only persist() serializes.
|
|
99
|
+
*/
|
|
100
|
+
private _writeQueue: Promise<void> = Promise.resolve();
|
|
101
|
+
|
|
93
102
|
constructor(deps: Partial<PostgresDeps> = {}) {
|
|
94
103
|
const Ctor = this.constructor as typeof PostgresDB;
|
|
95
104
|
if (Ctor.instance) return Ctor.instance;
|
|
@@ -468,14 +477,21 @@ export default class PostgresDB {
|
|
|
468
477
|
const Orm = (await import('@stonyx/orm')).default;
|
|
469
478
|
if ((Orm.instance as { isView?: (name: string) => boolean })?.isView?.(modelName)) return;
|
|
470
479
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
480
|
+
const work = async () => {
|
|
481
|
+
switch (operation) {
|
|
482
|
+
case 'create':
|
|
483
|
+
return this._persistCreate(modelName, context, response);
|
|
484
|
+
case 'update':
|
|
485
|
+
return this._persistUpdate(modelName, context, response);
|
|
486
|
+
case 'delete':
|
|
487
|
+
return this._persistDelete(modelName, context);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// Chain through the write queue — .then(work, work) ensures the queue
|
|
492
|
+
// advances even when a previous persist rejects (#156).
|
|
493
|
+
this._writeQueue = this._writeQueue.then(work, work);
|
|
494
|
+
return this._writeQueue;
|
|
479
495
|
}
|
|
480
496
|
|
|
481
497
|
private async _persistCreate(modelName: string, context: PersistContext, response: PersistResponse): Promise<void> {
|