@naturalcycles/db-lib 9.0.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/file/file.db.d.ts +1 -12
- package/dist/adapter/file/file.db.js +79 -69
- package/dist/adapter/inmemory/inMemory.db.d.ts +7 -4
- package/dist/adapter/inmemory/inMemory.db.js +33 -38
- package/dist/base.common.db.d.ts +2 -2
- package/dist/base.common.db.js +4 -2
- package/dist/common.db.d.ts +7 -3
- package/dist/commondao/common.dao.d.ts +17 -6
- package/dist/commondao/common.dao.js +42 -54
- package/dist/db.model.d.ts +18 -1
- package/dist/testing/daoTest.js +4 -4
- package/dist/testing/dbTest.js +10 -10
- package/dist/timeseries/commonTimeSeriesDao.js +10 -10
- package/dist/transaction/dbTransaction.util.d.ts +1 -4
- package/dist/transaction/dbTransaction.util.js +8 -5
- package/package.json +1 -1
- package/src/adapter/file/file.db.ts +8 -14
- package/src/adapter/inmemory/inMemory.db.ts +44 -41
- package/src/base.common.db.ts +5 -3
- package/src/common.db.ts +7 -3
- package/src/commondao/common.dao.ts +52 -55
- package/src/db.model.ts +20 -1
- package/src/testing/daoTest.ts +4 -4
- package/src/testing/dbTest.ts +10 -10
- package/src/timeseries/commonTimeSeriesDao.ts +11 -13
- package/src/transaction/dbTransaction.util.ts +9 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
|
|
2
2
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { BaseCommonDB, CommonDBSupport,
|
|
3
|
+
import { BaseCommonDB, CommonDBSupport, DBSaveBatchOperation } from '../..';
|
|
4
4
|
import { CommonDB } from '../../common.db';
|
|
5
5
|
import { CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, RunQueryResult } from '../../db.model';
|
|
6
6
|
import { DBQuery } from '../../query/dbQuery';
|
|
@@ -32,18 +32,7 @@ export declare class FileDB extends BaseCommonDB implements CommonDB {
|
|
|
32
32
|
loadFile<ROW extends ObjectWithId>(table: string): Promise<ROW[]>;
|
|
33
33
|
saveFile<ROW extends ObjectWithId>(table: string, _rows: ROW[]): Promise<void>;
|
|
34
34
|
saveFiles<ROW extends ObjectWithId>(ops: DBSaveBatchOperation<ROW>[]): Promise<void>;
|
|
35
|
-
createTransaction(): Promise<FileDBTransaction>;
|
|
36
35
|
sortRows<ROW extends ObjectWithId>(rows: ROW[]): ROW[];
|
|
37
36
|
private logStarted;
|
|
38
37
|
private logFinished;
|
|
39
38
|
}
|
|
40
|
-
export declare class FileDBTransaction implements DBTransaction {
|
|
41
|
-
private db;
|
|
42
|
-
constructor(db: FileDB);
|
|
43
|
-
ops: DBOperation[];
|
|
44
|
-
/**
|
|
45
|
-
* Implementation is optimized for loading/saving _whole files_.
|
|
46
|
-
*/
|
|
47
|
-
commit(): Promise<void>;
|
|
48
|
-
rollback(): Promise<void>;
|
|
49
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.FileDB = void 0;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
6
6
|
const __1 = require("../..");
|
|
@@ -24,7 +24,7 @@ class FileDB extends __1.BaseCommonDB {
|
|
|
24
24
|
updateSaveMethod: false,
|
|
25
25
|
updateByQuery: false,
|
|
26
26
|
createTable: false,
|
|
27
|
-
transactions: false,
|
|
27
|
+
transactions: false, // todo
|
|
28
28
|
};
|
|
29
29
|
this.cfg = {
|
|
30
30
|
sortObjects: true,
|
|
@@ -140,9 +140,9 @@ class FileDB extends __1.BaseCommonDB {
|
|
|
140
140
|
await this.cfg.plugin.saveFiles(ops);
|
|
141
141
|
this.logFinished(started, op);
|
|
142
142
|
}
|
|
143
|
-
async createTransaction() {
|
|
144
|
-
|
|
145
|
-
}
|
|
143
|
+
// override async createTransaction(): Promise<FileDBTransaction> {
|
|
144
|
+
// return new FileDBTransaction(this)
|
|
145
|
+
// }
|
|
146
146
|
sortRows(rows) {
|
|
147
147
|
rows = rows.map(r => (0, js_lib_1._filterUndefinedValues)(r));
|
|
148
148
|
if (this.cfg.sortOnSave) {
|
|
@@ -168,69 +168,79 @@ class FileDB extends __1.BaseCommonDB {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
exports.FileDB = FileDB;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
171
|
+
// todo: get back and fix it
|
|
172
|
+
// Implementation is optimized for loading/saving _whole files_.
|
|
173
|
+
/*
|
|
174
|
+
export class FileDBTransaction implements DBTransaction {
|
|
175
|
+
constructor(private db: FileDB) {}
|
|
176
|
+
|
|
177
|
+
ops: DBOperation[] = []
|
|
178
|
+
|
|
179
|
+
async commit(): Promise<void> {
|
|
180
|
+
// data[table][id] => row
|
|
181
|
+
const data: StringMap<StringMap<ObjectWithId>> = {}
|
|
182
|
+
|
|
183
|
+
// 1. Load all tables data (concurrently)
|
|
184
|
+
const tables = _uniq(this.ops.map(o => o.table))
|
|
185
|
+
|
|
186
|
+
await pMap(
|
|
187
|
+
tables,
|
|
188
|
+
async table => {
|
|
189
|
+
const rows = await this.db.loadFile(table)
|
|
190
|
+
data[table] = _by(rows, r => r.id)
|
|
191
|
+
},
|
|
192
|
+
{ concurrency: 16 },
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
const backup = _deepCopy(data)
|
|
196
|
+
|
|
197
|
+
// 2. Apply ops one by one (in order)
|
|
198
|
+
this.ops.forEach(op => {
|
|
199
|
+
if (op.type === 'deleteByIds') {
|
|
200
|
+
op.ids.forEach(id => delete data[op.table]![id])
|
|
201
|
+
} else if (op.type === 'saveBatch') {
|
|
202
|
+
op.rows.forEach(r => {
|
|
203
|
+
if (!r.id) {
|
|
204
|
+
throw new Error('FileDB: row has an empty id')
|
|
205
|
+
}
|
|
206
|
+
data[op.table]![r.id] = r
|
|
207
|
+
})
|
|
208
|
+
} else {
|
|
209
|
+
throw new Error(`DBOperation not supported: ${(op as any).type}`)
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
// 3. Sort, turn it into ops
|
|
214
|
+
// Not filtering empty arrays, cause it's already filtered in this.saveFiles()
|
|
215
|
+
const ops: DBSaveBatchOperation[] = _stringMapEntries(data).map(([table, map]) => {
|
|
216
|
+
return {
|
|
217
|
+
type: 'saveBatch',
|
|
218
|
+
table,
|
|
219
|
+
rows: this.db.sortRows(_stringMapValues(map)),
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// 4. Save all files
|
|
224
|
+
try {
|
|
225
|
+
await this.db.saveFiles(ops)
|
|
226
|
+
} catch (err) {
|
|
227
|
+
const ops: DBSaveBatchOperation[] = _stringMapEntries(backup).map(([table, map]) => {
|
|
228
|
+
return {
|
|
229
|
+
type: 'saveBatch',
|
|
230
|
+
table,
|
|
231
|
+
rows: this.db.sortRows(_stringMapValues(map)),
|
|
230
232
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// Rollback, ignore rollback error (if any)
|
|
236
|
+
await this.db.saveFiles(ops).catch(_ => {})
|
|
237
|
+
|
|
238
|
+
throw err
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async rollback(): Promise<void> {
|
|
243
|
+
this.ops = []
|
|
244
|
+
}
|
|
235
245
|
}
|
|
236
|
-
|
|
246
|
+
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JsonSchemaObject, StringMap, JsonSchemaRootObject, ObjectWithId, CommonLogger } from '@naturalcycles/js-lib';
|
|
2
2
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { CommonDB, CommonDBType, DBOperation, DBPatch } from '../..';
|
|
3
|
+
import { CommonDB, CommonDBType, DBOperation, DBPatch, DBTransactionFn } from '../..';
|
|
4
4
|
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, DBTransaction, RunQueryResult } from '../../db.model';
|
|
5
5
|
import { DBQuery } from '../../query/dbQuery';
|
|
6
6
|
export interface InMemoryDBCfg {
|
|
@@ -72,13 +72,13 @@ export declare class InMemoryDB implements CommonDB {
|
|
|
72
72
|
createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>, opt?: CommonDBCreateOptions): Promise<void>;
|
|
73
73
|
getByIds<ROW extends ObjectWithId>(_table: string, ids: ROW['id'][], _opt?: CommonDBOptions): Promise<ROW[]>;
|
|
74
74
|
saveBatch<ROW extends Partial<ObjectWithId>>(_table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
75
|
-
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>,
|
|
76
|
-
deleteByIds(_table: string, ids: string[],
|
|
75
|
+
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
|
|
76
|
+
deleteByIds(_table: string, ids: string[], _opt?: CommonDBOptions): Promise<number>;
|
|
77
77
|
updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>): Promise<number>;
|
|
78
78
|
runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<RunQueryResult<ROW>>;
|
|
79
79
|
runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
|
|
80
80
|
streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): ReadableTyped<ROW>;
|
|
81
|
-
|
|
81
|
+
runInTransaction(fn: DBTransactionFn): Promise<void>;
|
|
82
82
|
/**
|
|
83
83
|
* Flushes all tables (all namespaces) at once.
|
|
84
84
|
*/
|
|
@@ -92,6 +92,9 @@ export declare class InMemoryDBTransaction implements DBTransaction {
|
|
|
92
92
|
private db;
|
|
93
93
|
constructor(db: InMemoryDB);
|
|
94
94
|
ops: DBOperation[];
|
|
95
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: CommonDBOptions): Promise<ROW[]>;
|
|
96
|
+
saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
97
|
+
deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
|
|
95
98
|
commit(): Promise<void>;
|
|
96
99
|
rollback(): Promise<void>;
|
|
97
100
|
}
|
|
@@ -77,17 +77,6 @@ class InMemoryDB {
|
|
|
77
77
|
return ids.map(id => this.data[table][id]).filter(Boolean);
|
|
78
78
|
}
|
|
79
79
|
async saveBatch(_table, rows, opt = {}) {
|
|
80
|
-
const { tx } = opt;
|
|
81
|
-
if (tx) {
|
|
82
|
-
;
|
|
83
|
-
tx.ops.push({
|
|
84
|
-
type: 'saveBatch',
|
|
85
|
-
table: _table,
|
|
86
|
-
rows,
|
|
87
|
-
opt: (0, js_lib_1._omit)(opt, ['tx']),
|
|
88
|
-
});
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
80
|
const table = this.cfg.tablesPrefix + _table;
|
|
92
81
|
this.data[table] ||= {};
|
|
93
82
|
rows.forEach(r => {
|
|
@@ -107,39 +96,17 @@ class InMemoryDB {
|
|
|
107
96
|
this.data[table][r.id] = JSON.parse(JSON.stringify(r), nodejs_lib_1.bufferReviver);
|
|
108
97
|
});
|
|
109
98
|
}
|
|
110
|
-
async deleteByQuery(q,
|
|
99
|
+
async deleteByQuery(q, _opt) {
|
|
111
100
|
const table = this.cfg.tablesPrefix + q.table;
|
|
112
101
|
if (!this.data[table])
|
|
113
102
|
return 0;
|
|
114
103
|
const ids = (0, __1.queryInMemory)(q, Object.values(this.data[table])).map(r => r.id);
|
|
115
|
-
const { tx } = opt;
|
|
116
|
-
if (tx) {
|
|
117
|
-
;
|
|
118
|
-
tx.ops.push({
|
|
119
|
-
type: 'deleteByIds',
|
|
120
|
-
table: q.table,
|
|
121
|
-
ids,
|
|
122
|
-
opt: (0, js_lib_1._omit)(opt, ['tx']),
|
|
123
|
-
});
|
|
124
|
-
return ids.length;
|
|
125
|
-
}
|
|
126
104
|
return await this.deleteByIds(q.table, ids);
|
|
127
105
|
}
|
|
128
|
-
async deleteByIds(_table, ids,
|
|
106
|
+
async deleteByIds(_table, ids, _opt) {
|
|
129
107
|
const table = this.cfg.tablesPrefix + _table;
|
|
130
108
|
if (!this.data[table])
|
|
131
109
|
return 0;
|
|
132
|
-
const { tx } = opt;
|
|
133
|
-
if (tx) {
|
|
134
|
-
;
|
|
135
|
-
tx.ops.push({
|
|
136
|
-
type: 'deleteByIds',
|
|
137
|
-
table: _table,
|
|
138
|
-
ids,
|
|
139
|
-
opt: (0, js_lib_1._omit)(opt, ['tx']),
|
|
140
|
-
});
|
|
141
|
-
return ids.length;
|
|
142
|
-
}
|
|
143
110
|
let count = 0;
|
|
144
111
|
ids.forEach(id => {
|
|
145
112
|
if (!this.data[table][id])
|
|
@@ -153,7 +120,6 @@ class InMemoryDB {
|
|
|
153
120
|
const patchEntries = Object.entries(patch);
|
|
154
121
|
if (!patchEntries.length)
|
|
155
122
|
return 0;
|
|
156
|
-
// todo: can we support tx here? :thinking:
|
|
157
123
|
const table = this.cfg.tablesPrefix + q.table;
|
|
158
124
|
const rows = (0, __1.queryInMemory)(q, Object.values(this.data[table] || {}));
|
|
159
125
|
rows.forEach((row) => {
|
|
@@ -180,8 +146,16 @@ class InMemoryDB {
|
|
|
180
146
|
const table = this.cfg.tablesPrefix + q.table;
|
|
181
147
|
return node_stream_1.Readable.from((0, __1.queryInMemory)(q, Object.values(this.data[table] || {})));
|
|
182
148
|
}
|
|
183
|
-
async
|
|
184
|
-
|
|
149
|
+
async runInTransaction(fn) {
|
|
150
|
+
const tx = new InMemoryDBTransaction(this);
|
|
151
|
+
try {
|
|
152
|
+
await fn(tx);
|
|
153
|
+
await tx.commit();
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
await tx.rollback();
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
185
159
|
}
|
|
186
160
|
/**
|
|
187
161
|
* Flushes all tables (all namespaces) at once.
|
|
@@ -243,6 +217,26 @@ class InMemoryDBTransaction {
|
|
|
243
217
|
this.db = db;
|
|
244
218
|
this.ops = [];
|
|
245
219
|
}
|
|
220
|
+
async getByIds(table, ids, opt) {
|
|
221
|
+
return await this.db.getByIds(table, ids, opt);
|
|
222
|
+
}
|
|
223
|
+
async saveBatch(table, rows, opt) {
|
|
224
|
+
this.ops.push({
|
|
225
|
+
type: 'saveBatch',
|
|
226
|
+
table,
|
|
227
|
+
rows,
|
|
228
|
+
opt,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
async deleteByIds(table, ids, opt) {
|
|
232
|
+
this.ops.push({
|
|
233
|
+
type: 'deleteByIds',
|
|
234
|
+
table,
|
|
235
|
+
ids,
|
|
236
|
+
opt,
|
|
237
|
+
});
|
|
238
|
+
return ids.length;
|
|
239
|
+
}
|
|
246
240
|
async commit() {
|
|
247
241
|
const backup = (0, js_lib_1._deepCopy)(this.db.data);
|
|
248
242
|
try {
|
|
@@ -261,6 +255,7 @@ class InMemoryDBTransaction {
|
|
|
261
255
|
}
|
|
262
256
|
catch (err) {
|
|
263
257
|
// rollback
|
|
258
|
+
this.ops = [];
|
|
264
259
|
this.db.data = backup;
|
|
265
260
|
this.db.cfg.logger.log('InMemoryDB transaction rolled back');
|
|
266
261
|
throw err;
|
package/dist/base.common.db.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
|
|
2
2
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
3
|
import { CommonDB, CommonDBSupport, CommonDBType } from './common.db';
|
|
4
|
-
import { CommonDBOptions, CommonDBSaveOptions, DBPatch,
|
|
4
|
+
import { CommonDBOptions, CommonDBSaveOptions, DBPatch, DBTransactionFn, RunQueryResult } from './db.model';
|
|
5
5
|
import { DBQuery } from './query/dbQuery';
|
|
6
6
|
/**
|
|
7
7
|
* No-op implementation of CommonDB interface.
|
|
@@ -22,5 +22,5 @@ export declare class BaseCommonDB implements CommonDB {
|
|
|
22
22
|
saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
23
23
|
streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): ReadableTyped<ROW>;
|
|
24
24
|
deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
|
|
25
|
-
|
|
25
|
+
runInTransaction(fn: DBTransactionFn): Promise<void>;
|
|
26
26
|
}
|
package/dist/base.common.db.js
CHANGED
|
@@ -49,8 +49,10 @@ class BaseCommonDB {
|
|
|
49
49
|
async deleteByIds(table, ids, opt) {
|
|
50
50
|
throw new Error('deleteByIds is not implemented');
|
|
51
51
|
}
|
|
52
|
-
async
|
|
53
|
-
|
|
52
|
+
async runInTransaction(fn) {
|
|
53
|
+
const tx = new dbTransaction_util_1.FakeDBTransaction(this);
|
|
54
|
+
await fn(tx);
|
|
55
|
+
// there's no try/catch and rollback, as there's nothing to rollback
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
exports.BaseCommonDB = BaseCommonDB;
|
package/dist/common.db.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
|
|
2
|
-
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, DBPatch,
|
|
2
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
+
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, DBPatch, DBTransactionFn, RunQueryResult } from './db.model';
|
|
4
4
|
import { DBQuery } from './query/dbQuery';
|
|
5
5
|
export declare enum CommonDBType {
|
|
6
6
|
'document' = "document",
|
|
@@ -100,8 +100,12 @@ export interface CommonDB {
|
|
|
100
100
|
/**
|
|
101
101
|
* Should be implemented as a Transaction (best effort), which means that
|
|
102
102
|
* either ALL or NONE of the operations should be applied.
|
|
103
|
+
*
|
|
104
|
+
* Transaction is automatically committed if fn resolves normally.
|
|
105
|
+
* Transaction is rolled back if fn throws, the error is re-thrown in that case.
|
|
106
|
+
* Graceful rollback is allowed on tx.rollback()
|
|
103
107
|
*/
|
|
104
|
-
|
|
108
|
+
runInTransaction: (fn: DBTransactionFn) => Promise<void>;
|
|
105
109
|
}
|
|
106
110
|
/**
|
|
107
111
|
* Manifest of supported features.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Transform } from 'node:stream';
|
|
3
|
-
import { AnyObject, AsyncMapper, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, Saved, UnixTimestampMillisNumber, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
|
|
3
|
+
import { AnyObject, AsyncMapper, CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, Saved, UnixTimestampMillisNumber, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
|
|
4
4
|
import { AjvSchema, ObjectSchema, ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
5
5
|
import { DBModelType, DBPatch, DBTransaction, RunQueryResult } from '../db.model';
|
|
6
6
|
import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
|
|
@@ -169,21 +169,32 @@ export declare class CommonDao<BM extends Partial<ObjectWithId>, DBM extends Obj
|
|
|
169
169
|
* Proxy to this.cfg.db.ping
|
|
170
170
|
*/
|
|
171
171
|
ping(): Promise<void>;
|
|
172
|
-
|
|
173
|
-
createTransaction(): Promise<CommonDaoTransaction>;
|
|
172
|
+
runInTransaction(fn: CommonDaoTransactionFn): Promise<void>;
|
|
174
173
|
protected logResult(started: number, op: string, res: any, table: string): void;
|
|
175
174
|
protected logSaveResult(started: number, op: string, table: string): void;
|
|
176
175
|
protected logStarted(op: string, table: string, force?: boolean): UnixTimestampMillisNumber;
|
|
177
176
|
protected logSaveStarted(op: string, items: any, table: string): UnixTimestampMillisNumber;
|
|
178
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Transaction is committed when the function returns resolved Promise (aka "returns normally").
|
|
180
|
+
*
|
|
181
|
+
* Transaction is rolled back when the function returns rejected Promise (aka "throws").
|
|
182
|
+
*/
|
|
183
|
+
export type CommonDaoTransactionFn = (tx: CommonDaoTransaction) => Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Transaction context.
|
|
186
|
+
* Has similar API than CommonDao, but all operations are performed in the context of the transaction.
|
|
187
|
+
*/
|
|
179
188
|
export declare class CommonDaoTransaction {
|
|
180
189
|
private tx;
|
|
181
|
-
|
|
182
|
-
|
|
190
|
+
private logger;
|
|
191
|
+
constructor(tx: DBTransaction, logger: CommonLogger);
|
|
192
|
+
/**
|
|
193
|
+
* Perform a graceful rollback without throwing/re-throwing any error.
|
|
194
|
+
*/
|
|
183
195
|
rollback(): Promise<void>;
|
|
184
196
|
getById<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, id: string, opt?: CommonDaoOptions): Promise<Saved<BM> | null>;
|
|
185
197
|
getByIds<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, ids: string[], opt?: CommonDaoOptions): Promise<Saved<BM>[]>;
|
|
186
|
-
runQuery<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<Saved<BM>[]>;
|
|
187
198
|
save<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, bm: Unsaved<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>>;
|
|
188
199
|
saveBatch<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>[]>;
|
|
189
200
|
deleteById(dao: CommonDao<any>, id: string, opt?: CommonDaoOptions): Promise<number>;
|
|
@@ -124,7 +124,7 @@ class CommonDao {
|
|
|
124
124
|
const op = `getByIds ${ids.length} id(s) (${(0, js_lib_1._truncate)(ids.slice(0, 10).join(', '), 50)})`;
|
|
125
125
|
const table = opt.table || this.cfg.table;
|
|
126
126
|
const started = this.logStarted(op, table);
|
|
127
|
-
let dbms = await this.cfg.db.getByIds(table, ids);
|
|
127
|
+
let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids);
|
|
128
128
|
if (!opt.raw && this.cfg.hooks.afterLoad && dbms.length) {
|
|
129
129
|
dbms = (await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
|
|
130
130
|
}
|
|
@@ -662,7 +662,7 @@ class CommonDao {
|
|
|
662
662
|
const started = this.logSaveStarted(op, bms, table);
|
|
663
663
|
const { excludeFromIndexes } = this.cfg;
|
|
664
664
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
665
|
-
await this.cfg.db.saveBatch(table, dbms, {
|
|
665
|
+
await (opt.tx || this.cfg.db).saveBatch(table, dbms, {
|
|
666
666
|
excludeFromIndexes,
|
|
667
667
|
assignGeneratedIds,
|
|
668
668
|
...opt,
|
|
@@ -781,7 +781,7 @@ class CommonDao {
|
|
|
781
781
|
const op = `deleteByIds(${ids.join(', ')})`;
|
|
782
782
|
const table = opt.table || this.cfg.table;
|
|
783
783
|
const started = this.logStarted(op, table);
|
|
784
|
-
const count = await this.cfg.db.deleteByIds(table, ids, opt);
|
|
784
|
+
const count = await (opt.tx || this.cfg.db).deleteByIds(table, ids, opt);
|
|
785
785
|
this.logSaveResult(started, op, table);
|
|
786
786
|
return count;
|
|
787
787
|
}
|
|
@@ -991,21 +991,17 @@ class CommonDao {
|
|
|
991
991
|
async ping() {
|
|
992
992
|
await this.cfg.db.ping();
|
|
993
993
|
}
|
|
994
|
-
async
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
async createTransaction() {
|
|
1007
|
-
const tx = await this.cfg.db.createTransaction();
|
|
1008
|
-
return new CommonDaoTransaction(tx);
|
|
994
|
+
async runInTransaction(fn) {
|
|
995
|
+
await this.cfg.db.runInTransaction(async (tx) => {
|
|
996
|
+
const daoTx = new CommonDaoTransaction(tx, this.cfg.logger);
|
|
997
|
+
try {
|
|
998
|
+
await fn(daoTx);
|
|
999
|
+
}
|
|
1000
|
+
catch (err) {
|
|
1001
|
+
await daoTx.rollback();
|
|
1002
|
+
throw err;
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1009
1005
|
}
|
|
1010
1006
|
logResult(started, op, res, table) {
|
|
1011
1007
|
if (!this.cfg.logLevel)
|
|
@@ -1062,65 +1058,57 @@ class CommonDao {
|
|
|
1062
1058
|
}
|
|
1063
1059
|
}
|
|
1064
1060
|
exports.CommonDao = CommonDao;
|
|
1061
|
+
/**
|
|
1062
|
+
* Transaction context.
|
|
1063
|
+
* Has similar API than CommonDao, but all operations are performed in the context of the transaction.
|
|
1064
|
+
*/
|
|
1065
1065
|
class CommonDaoTransaction {
|
|
1066
|
-
constructor(tx) {
|
|
1066
|
+
constructor(tx, logger) {
|
|
1067
1067
|
this.tx = tx;
|
|
1068
|
+
this.logger = logger;
|
|
1068
1069
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1070
|
+
/**
|
|
1071
|
+
* Perform a graceful rollback without throwing/re-throwing any error.
|
|
1072
|
+
*/
|
|
1072
1073
|
async rollback() {
|
|
1073
1074
|
try {
|
|
1074
1075
|
await this.tx.rollback();
|
|
1075
1076
|
}
|
|
1076
1077
|
catch (err) {
|
|
1077
|
-
|
|
1078
|
+
// graceful rollback without re-throw
|
|
1079
|
+
this.logger.error(err);
|
|
1078
1080
|
}
|
|
1079
1081
|
}
|
|
1080
1082
|
async getById(dao, id, opt) {
|
|
1081
1083
|
return (await this.getByIds(dao, [id], opt))[0] || null;
|
|
1082
1084
|
}
|
|
1083
1085
|
async getByIds(dao, ids, opt) {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1086
|
+
return await dao.getByIds(ids, { ...opt, tx: this.tx });
|
|
1087
|
+
}
|
|
1088
|
+
// todo: Queries inside Transaction are not supported yet
|
|
1089
|
+
// async runQuery<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(
|
|
1090
|
+
// dao: CommonDao<BM, DBM, any>,
|
|
1091
|
+
// q: DBQuery<DBM>,
|
|
1092
|
+
// opt?: CommonDaoOptions,
|
|
1093
|
+
// ): Promise<Saved<BM>[]> {
|
|
1094
|
+
// try {
|
|
1095
|
+
// return await dao.runQuery(q, { ...opt, tx: this.tx })
|
|
1096
|
+
// } catch (err) {
|
|
1097
|
+
// await this.rollback()
|
|
1098
|
+
// throw err
|
|
1099
|
+
// }
|
|
1100
|
+
// }
|
|
1101
1101
|
async save(dao, bm, opt) {
|
|
1102
1102
|
return (await this.saveBatch(dao, [bm], opt))[0];
|
|
1103
1103
|
}
|
|
1104
1104
|
async saveBatch(dao, bms, opt) {
|
|
1105
|
-
|
|
1106
|
-
return await dao.saveBatch(bms, { ...opt, tx: this.tx });
|
|
1107
|
-
}
|
|
1108
|
-
catch (err) {
|
|
1109
|
-
await this.rollback();
|
|
1110
|
-
throw err;
|
|
1111
|
-
}
|
|
1105
|
+
return await dao.saveBatch(bms, { ...opt, tx: this.tx });
|
|
1112
1106
|
}
|
|
1113
1107
|
async deleteById(dao, id, opt) {
|
|
1114
1108
|
return await this.deleteByIds(dao, [id], opt);
|
|
1115
1109
|
}
|
|
1116
1110
|
async deleteByIds(dao, ids, opt) {
|
|
1117
|
-
|
|
1118
|
-
return await dao.deleteByIds(ids, { ...opt, tx: this.tx });
|
|
1119
|
-
}
|
|
1120
|
-
catch (err) {
|
|
1121
|
-
await this.rollback();
|
|
1122
|
-
throw err;
|
|
1123
|
-
}
|
|
1111
|
+
return await dao.deleteByIds(ids, { ...opt, tx: this.tx });
|
|
1124
1112
|
}
|
|
1125
1113
|
}
|
|
1126
1114
|
exports.CommonDaoTransaction = CommonDaoTransaction;
|
package/dist/db.model.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ObjectWithId } from '@naturalcycles/js-lib';
|
|
2
|
+
import { CommonDB } from './common.db';
|
|
2
3
|
/**
|
|
3
4
|
* Similar to SQL INSERT, UPDATE.
|
|
4
5
|
* Insert will fail if row already exists.
|
|
@@ -8,8 +9,24 @@ import type { ObjectWithId } from '@naturalcycles/js-lib';
|
|
|
8
9
|
* Default is Upsert.
|
|
9
10
|
*/
|
|
10
11
|
export type CommonDBSaveMethod = 'upsert' | 'insert' | 'update';
|
|
12
|
+
/**
|
|
13
|
+
* Transaction is committed when the function returns resolved Promise (aka "returns normally").
|
|
14
|
+
*
|
|
15
|
+
* Transaction is rolled back when the function returns rejected Promise (aka "throws").
|
|
16
|
+
*/
|
|
17
|
+
export type DBTransactionFn = (tx: DBTransaction) => Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Transaction context.
|
|
20
|
+
* Has similar API than CommonDB, but all operations are performed in the context of the transaction.
|
|
21
|
+
*/
|
|
11
22
|
export interface DBTransaction {
|
|
12
|
-
|
|
23
|
+
getByIds: CommonDB['getByIds'];
|
|
24
|
+
saveBatch: CommonDB['saveBatch'];
|
|
25
|
+
deleteByIds: CommonDB['deleteByIds'];
|
|
26
|
+
/**
|
|
27
|
+
* Perform a graceful rollback.
|
|
28
|
+
* It'll rollback the transaction and won't throw/re-throw any errors.
|
|
29
|
+
*/
|
|
13
30
|
rollback: () => Promise<void>;
|
|
14
31
|
}
|
|
15
32
|
export interface CommonDBOptions {
|