@naturalcycles/db-lib 8.41.1 → 8.42.1
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/cachedb/cache.db.d.ts +3 -1
- package/dist/adapter/cachedb/cache.db.js +4 -0
- package/dist/adapter/file/file.db.js +24 -4
- package/dist/adapter/file/file.db.model.d.ts +1 -1
- package/dist/adapter/inmemory/inMemory.db.js +23 -14
- package/dist/base.common.db.d.ts +1 -0
- package/dist/base.common.db.js +1 -0
- package/dist/commondao/common.dao.d.ts +8 -1
- package/dist/commondao/common.dao.js +55 -3
- package/dist/commondao/common.dao.model.d.ts +10 -0
- package/dist/db.model.d.ts +2 -0
- package/dist/index.d.ts +18 -20
- package/dist/index.js +18 -41
- package/dist/testing/daoTest.js +42 -1
- package/dist/testing/dbTest.d.ts +1 -0
- package/dist/testing/dbTest.js +43 -11
- package/dist/timeseries/commonTimeSeriesDao.js +1 -1
- package/dist/transaction/dbTransaction.d.ts +7 -0
- package/dist/transaction/dbTransaction.js +24 -2
- package/dist/transaction/dbTransaction.util.d.ts +2 -1
- package/dist/transaction/dbTransaction.util.js +56 -52
- package/package.json +1 -1
- package/src/adapter/cachedb/cache.db.ts +7 -1
- package/src/adapter/file/file.db.model.ts +1 -1
- package/src/adapter/file/file.db.ts +28 -5
- package/src/adapter/inmemory/inMemory.db.ts +23 -12
- package/src/base.common.db.ts +1 -0
- package/src/commondao/common.dao.model.ts +11 -0
- package/src/commondao/common.dao.ts +76 -5
- package/src/db.model.ts +2 -0
- package/src/index.ts +18 -109
- package/src/testing/daoTest.ts +54 -1
- package/src/testing/dbTest.ts +53 -10
- package/src/timeseries/commonTimeSeriesDao.ts +1 -1
- package/src/transaction/dbTransaction.ts +26 -1
- package/src/transaction/dbTransaction.util.ts +32 -32
|
@@ -3,8 +3,9 @@ import { Readable } from 'stream';
|
|
|
3
3
|
import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
|
|
4
4
|
import { BaseCommonDB } from '../../base.common.db';
|
|
5
5
|
import { CommonDB } from '../../common.db';
|
|
6
|
-
import { RunQueryResult } from '../../db.model';
|
|
6
|
+
import { CommonDBOptions, RunQueryResult } from '../../db.model';
|
|
7
7
|
import { DBQuery } from '../../query/dbQuery';
|
|
8
|
+
import { DBTransaction } from '../../transaction/dbTransaction';
|
|
8
9
|
import { CacheDBCfg, CacheDBCreateOptions, CacheDBOptions, CacheDBSaveOptions, CacheDBStreamOptions } from './cache.db.model';
|
|
9
10
|
/**
|
|
10
11
|
* CommonDB implementation that proxies requests to downstream CommonDB
|
|
@@ -29,4 +30,5 @@ export declare class CacheDB extends BaseCommonDB implements CommonDB {
|
|
|
29
30
|
runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBOptions): Promise<number>;
|
|
30
31
|
streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBStreamOptions): Readable;
|
|
31
32
|
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBOptions): Promise<number>;
|
|
33
|
+
commitTransaction(tx: DBTransaction, opt?: CommonDBOptions): Promise<void>;
|
|
32
34
|
}
|
|
@@ -195,5 +195,9 @@ class CacheDB extends base_common_db_1.BaseCommonDB {
|
|
|
195
195
|
}
|
|
196
196
|
return deletedIds;
|
|
197
197
|
}
|
|
198
|
+
async commitTransaction(tx, opt) {
|
|
199
|
+
await this.cfg.downstreamDB.commitTransaction(tx, opt);
|
|
200
|
+
await this.cfg.cacheDB.commitTransaction(tx, opt);
|
|
201
|
+
}
|
|
198
202
|
}
|
|
199
203
|
exports.CacheDB = CacheDB;
|
|
@@ -70,13 +70,19 @@ class FileDB extends __1.BaseCommonDB {
|
|
|
70
70
|
const rows = await this.loadFile(table);
|
|
71
71
|
data[table] = (0, js_lib_1._by)(rows, r => r.id);
|
|
72
72
|
}, { concurrency: 16 });
|
|
73
|
+
const backup = (0, js_lib_1._deepCopy)(data);
|
|
73
74
|
// 2. Apply ops one by one (in order)
|
|
74
75
|
tx.ops.forEach(op => {
|
|
75
76
|
if (op.type === 'deleteByIds') {
|
|
76
77
|
op.ids.forEach(id => delete data[op.table][id]);
|
|
77
78
|
}
|
|
78
79
|
else if (op.type === 'saveBatch') {
|
|
79
|
-
op.rows.forEach(r =>
|
|
80
|
+
op.rows.forEach(r => {
|
|
81
|
+
if (!r.id) {
|
|
82
|
+
throw new Error('FileDB: row has an empty id');
|
|
83
|
+
}
|
|
84
|
+
data[op.table][r.id] = r;
|
|
85
|
+
});
|
|
80
86
|
}
|
|
81
87
|
else {
|
|
82
88
|
throw new Error(`DBOperation not supported: ${op.type}`);
|
|
@@ -84,15 +90,29 @@ class FileDB extends __1.BaseCommonDB {
|
|
|
84
90
|
});
|
|
85
91
|
// 3. Sort, turn it into ops
|
|
86
92
|
// Not filtering empty arrays, cause it's already filtered in this.saveFiles()
|
|
87
|
-
const ops =
|
|
93
|
+
const ops = (0, js_lib_1._stringMapEntries)(data).map(([table, map]) => {
|
|
88
94
|
return {
|
|
89
95
|
type: 'saveBatch',
|
|
90
96
|
table,
|
|
91
|
-
rows: this.sortRows(
|
|
97
|
+
rows: this.sortRows((0, js_lib_1._stringMapValues)(map)),
|
|
92
98
|
};
|
|
93
99
|
});
|
|
94
100
|
// 4. Save all files
|
|
95
|
-
|
|
101
|
+
try {
|
|
102
|
+
await this.saveFiles(ops);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const ops = (0, js_lib_1._stringMapEntries)(backup).map(([table, map]) => {
|
|
106
|
+
return {
|
|
107
|
+
type: 'saveBatch',
|
|
108
|
+
table,
|
|
109
|
+
rows: this.sortRows((0, js_lib_1._stringMapValues)(map)),
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
// Rollback, ignore rollback error (if any)
|
|
113
|
+
await this.saveFiles(ops).catch(_ => { });
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
96
116
|
}
|
|
97
117
|
async runQuery(q, _opt) {
|
|
98
118
|
return {
|
|
@@ -5,7 +5,7 @@ export interface FileDBPersistencePlugin {
|
|
|
5
5
|
ping(): Promise<void>;
|
|
6
6
|
getTables(): Promise<string[]>;
|
|
7
7
|
loadFile<ROW extends ObjectWithId>(table: string): Promise<ROW[]>;
|
|
8
|
-
saveFiles(ops: DBSaveBatchOperation[]): Promise<void>;
|
|
8
|
+
saveFiles(ops: DBSaveBatchOperation<any>[]): Promise<void>;
|
|
9
9
|
}
|
|
10
10
|
export interface FileDBCfg {
|
|
11
11
|
plugin: FileDBPersistencePlugin;
|
|
@@ -36,7 +36,7 @@ class InMemoryDB {
|
|
|
36
36
|
async resetCache(_table) {
|
|
37
37
|
if (_table) {
|
|
38
38
|
const table = this.cfg.tablesPrefix + _table;
|
|
39
|
-
this.cfg.logger
|
|
39
|
+
this.cfg.logger.log(`reset ${table}`);
|
|
40
40
|
this.data[table] = {};
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
@@ -44,7 +44,7 @@ class InMemoryDB {
|
|
|
44
44
|
(await this.getTables()).forEach(table => {
|
|
45
45
|
this.data[table] = {};
|
|
46
46
|
});
|
|
47
|
-
this.cfg.logger
|
|
47
|
+
this.cfg.logger.log('reset');
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
async getTables() {
|
|
@@ -79,7 +79,7 @@ class InMemoryDB {
|
|
|
79
79
|
(_a = this.data)[table] || (_a[table] = {});
|
|
80
80
|
rows.forEach(r => {
|
|
81
81
|
if (!r.id) {
|
|
82
|
-
this.cfg.logger
|
|
82
|
+
this.cfg.logger.warn({ rows });
|
|
83
83
|
throw new Error(`InMemoryDB doesn't support id auto-generation in saveBatch, row without id was given`);
|
|
84
84
|
}
|
|
85
85
|
if (opt.saveMethod === 'insert' && this.data[table][r.id]) {
|
|
@@ -125,17 +125,26 @@ class InMemoryDB {
|
|
|
125
125
|
return stream_1.Readable.from((0, __1.queryInMemory)(q, Object.values(this.data[table] || {})));
|
|
126
126
|
}
|
|
127
127
|
async commitTransaction(tx, opt) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
128
|
+
const backup = (0, js_lib_1._deepCopy)(this.data);
|
|
129
|
+
try {
|
|
130
|
+
for await (const op of tx.ops) {
|
|
131
|
+
if (op.type === 'saveBatch') {
|
|
132
|
+
await this.saveBatch(op.table, op.rows, { ...op.opt, ...opt });
|
|
133
|
+
}
|
|
134
|
+
else if (op.type === 'deleteByIds') {
|
|
135
|
+
await this.deleteByIds(op.table, op.ids, { ...op.opt, ...opt });
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
throw new Error(`DBOperation not supported: ${op.type}`);
|
|
139
|
+
}
|
|
137
140
|
}
|
|
138
141
|
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
// rollback
|
|
144
|
+
this.data = backup;
|
|
145
|
+
this.cfg.logger.log('InMemoryDB transaction rolled back');
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
139
148
|
}
|
|
140
149
|
/**
|
|
141
150
|
* Flushes all tables (all namespaces) at once.
|
|
@@ -163,7 +172,7 @@ class InMemoryDB {
|
|
|
163
172
|
fs.createWriteStream(fname),
|
|
164
173
|
]);
|
|
165
174
|
});
|
|
166
|
-
this.cfg.logger
|
|
175
|
+
this.cfg.logger.log(`flushToDisk took ${(0, colors_1.dimGrey)((0, js_lib_1._since)(started))} to save ${(0, colors_1.yellow)(tables)} tables`);
|
|
167
176
|
}
|
|
168
177
|
/**
|
|
169
178
|
* Restores all tables (all namespaces) at once.
|
|
@@ -192,7 +201,7 @@ class InMemoryDB {
|
|
|
192
201
|
]);
|
|
193
202
|
this.data[table] = (0, js_lib_1._by)(rows, r => r.id);
|
|
194
203
|
});
|
|
195
|
-
this.cfg.logger
|
|
204
|
+
this.cfg.logger.log(`restoreFromDisk took ${(0, colors_1.dimGrey)((0, js_lib_1._since)(started))} to read ${(0, colors_1.yellow)(files.length)} tables`);
|
|
196
205
|
}
|
|
197
206
|
}
|
|
198
207
|
exports.InMemoryDB = InMemoryDB;
|
package/dist/base.common.db.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export declare class BaseCommonDB implements CommonDB {
|
|
|
22
22
|
streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW>;
|
|
23
23
|
/**
|
|
24
24
|
* Naive implementation.
|
|
25
|
+
* Doesn't support rollback on error, hence doesn't pass dbTest.
|
|
25
26
|
* To be extended.
|
|
26
27
|
*/
|
|
27
28
|
commitTransaction(tx: DBTransaction, opt?: CommonDBOptions): Promise<void>;
|
package/dist/base.common.db.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AsyncMapper, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, Saved, Unsaved } from '@naturalcycles/js-lib';
|
|
2
2
|
import { AjvSchema, ObjectSchemaTyped, ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { DBModelType, RunQueryResult } from '../db.model';
|
|
3
|
+
import { DBDeleteByIdsOperation, DBModelType, DBOperation, DBSaveBatchOperation, RunQueryResult } from '../db.model';
|
|
4
4
|
import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
|
|
5
5
|
import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoSaveOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions } from './common.dao.model';
|
|
6
6
|
/**
|
|
@@ -83,6 +83,12 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
|
|
|
83
83
|
assignIdCreatedUpdated(obj: DBM, opt?: CommonDaoOptions): DBM;
|
|
84
84
|
assignIdCreatedUpdated(obj: BM, opt?: CommonDaoOptions): Saved<BM>;
|
|
85
85
|
assignIdCreatedUpdated(obj: Unsaved<BM>, opt?: CommonDaoOptions): Saved<BM>;
|
|
86
|
+
tx: {
|
|
87
|
+
save: (bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<DBM>) => Promise<DBSaveBatchOperation>;
|
|
88
|
+
saveBatch: (bms: Unsaved<BM>[], opt?: CommonDaoSaveOptions<DBM>) => Promise<DBSaveBatchOperation>;
|
|
89
|
+
deleteByIds: (ids: ID[], opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation>;
|
|
90
|
+
deleteById: (id: ID, opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation>;
|
|
91
|
+
};
|
|
86
92
|
/**
|
|
87
93
|
* Mutates with id, created, updated
|
|
88
94
|
*/
|
|
@@ -146,6 +152,7 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
|
|
|
146
152
|
* Proxy to this.cfg.db.ping
|
|
147
153
|
*/
|
|
148
154
|
ping(): Promise<void>;
|
|
155
|
+
runInTransaction(ops: Promise<DBOperation>[]): Promise<void>;
|
|
149
156
|
protected logResult(started: number, op: string, res: any, table: string): void;
|
|
150
157
|
protected logSaveResult(started: number, op: string, table: string): void;
|
|
151
158
|
protected logStarted(op: string, table: string, force?: boolean): number;
|
|
@@ -6,6 +6,7 @@ const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
|
6
6
|
const cnst_1 = require("../cnst");
|
|
7
7
|
const db_model_1 = require("../db.model");
|
|
8
8
|
const dbQuery_1 = require("../query/dbQuery");
|
|
9
|
+
const dbTransaction_1 = require("../transaction/dbTransaction");
|
|
9
10
|
const common_dao_model_1 = require("./common.dao.model");
|
|
10
11
|
/* eslint-disable no-dupe-class-members */
|
|
11
12
|
const isGAE = !!process.env['GAE_INSTANCE'];
|
|
@@ -21,6 +22,48 @@ class CommonDao {
|
|
|
21
22
|
constructor(cfg) {
|
|
22
23
|
var _a;
|
|
23
24
|
this.cfg = cfg;
|
|
25
|
+
this.tx = {
|
|
26
|
+
save: async (bm, opt = {}) => {
|
|
27
|
+
const row = (await this.save(bm, { ...opt, tx: true }));
|
|
28
|
+
return {
|
|
29
|
+
type: 'saveBatch',
|
|
30
|
+
table: this.cfg.table,
|
|
31
|
+
rows: [row],
|
|
32
|
+
opt: {
|
|
33
|
+
excludeFromIndexes: this.cfg.excludeFromIndexes,
|
|
34
|
+
...opt,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
saveBatch: async (bms, opt = {}) => {
|
|
39
|
+
const rows = (await this.saveBatch(bms, { ...opt, tx: true }));
|
|
40
|
+
return {
|
|
41
|
+
type: 'saveBatch',
|
|
42
|
+
table: this.cfg.table,
|
|
43
|
+
rows,
|
|
44
|
+
opt: {
|
|
45
|
+
excludeFromIndexes: this.cfg.excludeFromIndexes,
|
|
46
|
+
...opt,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
deleteByIds: async (ids, opt = {}) => {
|
|
51
|
+
return {
|
|
52
|
+
type: 'deleteByIds',
|
|
53
|
+
table: this.cfg.table,
|
|
54
|
+
ids: ids,
|
|
55
|
+
opt,
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
deleteById: async (id, opt = {}) => {
|
|
59
|
+
return {
|
|
60
|
+
type: 'deleteByIds',
|
|
61
|
+
table: this.cfg.table,
|
|
62
|
+
ids: [id],
|
|
63
|
+
opt,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
};
|
|
24
67
|
this.cfg = {
|
|
25
68
|
// Default is to NOT log in AppEngine and in CI,
|
|
26
69
|
// otherwise to log Operations
|
|
@@ -459,6 +502,9 @@ class CommonDao {
|
|
|
459
502
|
const idWasGenerated = !bm.id && this.cfg.createId;
|
|
460
503
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
461
504
|
const dbm = await this.bmToDBM(bm, opt);
|
|
505
|
+
if (opt.tx) {
|
|
506
|
+
return dbm;
|
|
507
|
+
}
|
|
462
508
|
const table = opt.table || this.cfg.table;
|
|
463
509
|
if (opt.ensureUniqueId && idWasGenerated)
|
|
464
510
|
await this.ensureUniqueId(table, dbm);
|
|
@@ -538,6 +584,9 @@ class CommonDao {
|
|
|
538
584
|
const table = opt.table || this.cfg.table;
|
|
539
585
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt));
|
|
540
586
|
const dbms = await this.bmsToDBM(bms, opt);
|
|
587
|
+
if (opt.tx) {
|
|
588
|
+
return dbms;
|
|
589
|
+
}
|
|
541
590
|
if (opt.ensureUniqueId)
|
|
542
591
|
throw new js_lib_1.AppError('ensureUniqueId is not supported in saveBatch');
|
|
543
592
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
@@ -811,9 +860,12 @@ class CommonDao {
|
|
|
811
860
|
async ping() {
|
|
812
861
|
await this.cfg.db.ping();
|
|
813
862
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
863
|
+
async runInTransaction(ops) {
|
|
864
|
+
if (!ops.length)
|
|
865
|
+
return;
|
|
866
|
+
const resolvedOps = await Promise.all(ops);
|
|
867
|
+
await this.cfg.db.commitTransaction(dbTransaction_1.DBTransaction.create(resolvedOps));
|
|
868
|
+
}
|
|
817
869
|
logResult(started, op, res, table) {
|
|
818
870
|
if (!this.cfg.logLevel)
|
|
819
871
|
return;
|
|
@@ -167,6 +167,16 @@ export interface CommonDaoOptions extends CommonDBOptions {
|
|
|
167
167
|
* @experimental
|
|
168
168
|
*/
|
|
169
169
|
timeout?: number;
|
|
170
|
+
/**
|
|
171
|
+
* If passed - operation will not be performed immediately, but instead "added" to the transaction.
|
|
172
|
+
* In the end - transaction needs to be committed (by calling `commit`).
|
|
173
|
+
* This API is inspired by Datastore API.
|
|
174
|
+
*
|
|
175
|
+
* Only applicable to save* and delete* operations
|
|
176
|
+
*
|
|
177
|
+
* @experimental
|
|
178
|
+
*/
|
|
179
|
+
tx?: boolean;
|
|
170
180
|
}
|
|
171
181
|
/**
|
|
172
182
|
* All properties default to undefined.
|
package/dist/db.model.d.ts
CHANGED
|
@@ -45,11 +45,13 @@ export interface DBSaveBatchOperation<ROW extends ObjectWithId = AnyObjectWithId
|
|
|
45
45
|
type: 'saveBatch';
|
|
46
46
|
table: string;
|
|
47
47
|
rows: ROW[];
|
|
48
|
+
opt?: CommonDBSaveOptions<ROW>;
|
|
48
49
|
}
|
|
49
50
|
export interface DBDeleteByIdsOperation {
|
|
50
51
|
type: 'deleteByIds';
|
|
51
52
|
table: string;
|
|
52
53
|
ids: string[];
|
|
54
|
+
opt?: CommonDBOptions;
|
|
53
55
|
}
|
|
54
56
|
export declare enum DBRelation {
|
|
55
57
|
ONE_TO_ONE = "ONE_TO_ONE",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
export * from './adapter/inmemory/inMemory.db';
|
|
2
|
+
export * from './adapter/inmemory/inMemoryKeyValueDB';
|
|
3
|
+
export * from './adapter/inmemory/queryInMemory';
|
|
4
|
+
export * from './base.common.db';
|
|
5
|
+
export * from './cnst';
|
|
6
|
+
export * from './common.db';
|
|
7
|
+
export * from './commondao/common.dao';
|
|
8
|
+
export * from './commondao/common.dao.model';
|
|
9
|
+
export * from './db.model';
|
|
10
|
+
export * from './kv/commonKeyValueDao';
|
|
11
|
+
export * from './kv/commonKeyValueDB';
|
|
12
|
+
export * from './model.util';
|
|
13
|
+
export * from './pipeline/dbPipelineBackup';
|
|
14
|
+
export * from './pipeline/dbPipelineCopy';
|
|
15
|
+
export * from './pipeline/dbPipelineRestore';
|
|
16
|
+
export * from './query/dbQuery';
|
|
17
|
+
export * from './transaction/dbTransaction';
|
|
18
|
+
export * from './transaction/dbTransaction.util';
|
|
19
19
|
export * from './kv/commonKeyValueDaoMemoCache';
|
|
20
|
-
export type { DBQueryFilterOperator, DBQueryFilter, DBQueryOrder, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoSaveOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoHooks, CommonDBOptions, CommonDBSaveOptions, CommonDBSaveMethod, CommonDBStreamOptions, CommonDBCreateOptions, CommonDB, RunQueryResult, CommonDaoCfg, InMemoryDBCfg, InMemoryKeyValueDBCfg, DBPipelineBackupOptions, DBPipelineRestoreOptions, DBPipelineCopyOptions, DBOperation, DBSaveBatchOperation, DBDeleteByIdsOperation, CommonKeyValueDB, CommonKeyValueDaoCfg, KeyValueDBTuple, };
|
|
21
|
-
export { DBQuery, dbQueryFilterOperatorValues, RunnableDBQuery, CommonDaoLogLevel, DBRelation, DBModelType, CommonDao, createdUpdatedFields, createdUpdatedIdFields, InMemoryDB, InMemoryKeyValueDB, queryInMemory, serializeJsonField, deserializeJsonField, dbPipelineBackup, dbPipelineRestore, dbPipelineCopy, DBLibError, BaseCommonDB, DBTransaction, RunnableDBTransaction, mergeDBOperations, commitDBTransactionSimple, CommonKeyValueDao, };
|
package/dist/index.js
CHANGED
|
@@ -1,45 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CommonKeyValueDao = exports.commitDBTransactionSimple = exports.mergeDBOperations = exports.RunnableDBTransaction = exports.DBTransaction = exports.BaseCommonDB = exports.DBLibError = exports.dbPipelineCopy = exports.dbPipelineRestore = exports.dbPipelineBackup = exports.deserializeJsonField = exports.serializeJsonField = exports.queryInMemory = exports.InMemoryKeyValueDB = exports.InMemoryDB = exports.createdUpdatedIdFields = exports.createdUpdatedFields = exports.CommonDao = exports.DBModelType = exports.DBRelation = exports.CommonDaoLogLevel = exports.RunnableDBQuery = exports.dbQueryFilterOperatorValues = exports.DBQuery = void 0;
|
|
4
3
|
const tslib_1 = require("tslib");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Object.defineProperty(exports, "CommonKeyValueDao", { enumerable: true, get: function () { return commonKeyValueDao_1.CommonKeyValueDao; } });
|
|
24
|
-
const model_util_1 = require("./model.util");
|
|
25
|
-
Object.defineProperty(exports, "createdUpdatedFields", { enumerable: true, get: function () { return model_util_1.createdUpdatedFields; } });
|
|
26
|
-
Object.defineProperty(exports, "createdUpdatedIdFields", { enumerable: true, get: function () { return model_util_1.createdUpdatedIdFields; } });
|
|
27
|
-
Object.defineProperty(exports, "deserializeJsonField", { enumerable: true, get: function () { return model_util_1.deserializeJsonField; } });
|
|
28
|
-
Object.defineProperty(exports, "serializeJsonField", { enumerable: true, get: function () { return model_util_1.serializeJsonField; } });
|
|
29
|
-
const dbPipelineBackup_1 = require("./pipeline/dbPipelineBackup");
|
|
30
|
-
Object.defineProperty(exports, "dbPipelineBackup", { enumerable: true, get: function () { return dbPipelineBackup_1.dbPipelineBackup; } });
|
|
31
|
-
const dbPipelineCopy_1 = require("./pipeline/dbPipelineCopy");
|
|
32
|
-
Object.defineProperty(exports, "dbPipelineCopy", { enumerable: true, get: function () { return dbPipelineCopy_1.dbPipelineCopy; } });
|
|
33
|
-
const dbPipelineRestore_1 = require("./pipeline/dbPipelineRestore");
|
|
34
|
-
Object.defineProperty(exports, "dbPipelineRestore", { enumerable: true, get: function () { return dbPipelineRestore_1.dbPipelineRestore; } });
|
|
35
|
-
const dbQuery_1 = require("./query/dbQuery");
|
|
36
|
-
Object.defineProperty(exports, "DBQuery", { enumerable: true, get: function () { return dbQuery_1.DBQuery; } });
|
|
37
|
-
Object.defineProperty(exports, "dbQueryFilterOperatorValues", { enumerable: true, get: function () { return dbQuery_1.dbQueryFilterOperatorValues; } });
|
|
38
|
-
Object.defineProperty(exports, "RunnableDBQuery", { enumerable: true, get: function () { return dbQuery_1.RunnableDBQuery; } });
|
|
39
|
-
const dbTransaction_1 = require("./transaction/dbTransaction");
|
|
40
|
-
Object.defineProperty(exports, "DBTransaction", { enumerable: true, get: function () { return dbTransaction_1.DBTransaction; } });
|
|
41
|
-
Object.defineProperty(exports, "RunnableDBTransaction", { enumerable: true, get: function () { return dbTransaction_1.RunnableDBTransaction; } });
|
|
42
|
-
const dbTransaction_util_1 = require("./transaction/dbTransaction.util");
|
|
43
|
-
Object.defineProperty(exports, "commitDBTransactionSimple", { enumerable: true, get: function () { return dbTransaction_util_1.commitDBTransactionSimple; } });
|
|
44
|
-
Object.defineProperty(exports, "mergeDBOperations", { enumerable: true, get: function () { return dbTransaction_util_1.mergeDBOperations; } });
|
|
4
|
+
tslib_1.__exportStar(require("./adapter/inmemory/inMemory.db"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./adapter/inmemory/inMemoryKeyValueDB"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./adapter/inmemory/queryInMemory"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./base.common.db"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./cnst"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./common.db"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./commondao/common.dao"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./commondao/common.dao.model"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./db.model"), exports);
|
|
13
|
+
tslib_1.__exportStar(require("./kv/commonKeyValueDao"), exports);
|
|
14
|
+
tslib_1.__exportStar(require("./kv/commonKeyValueDB"), exports);
|
|
15
|
+
tslib_1.__exportStar(require("./model.util"), exports);
|
|
16
|
+
tslib_1.__exportStar(require("./pipeline/dbPipelineBackup"), exports);
|
|
17
|
+
tslib_1.__exportStar(require("./pipeline/dbPipelineCopy"), exports);
|
|
18
|
+
tslib_1.__exportStar(require("./pipeline/dbPipelineRestore"), exports);
|
|
19
|
+
tslib_1.__exportStar(require("./query/dbQuery"), exports);
|
|
20
|
+
tslib_1.__exportStar(require("./transaction/dbTransaction"), exports);
|
|
21
|
+
tslib_1.__exportStar(require("./transaction/dbTransaction.util"), exports);
|
|
45
22
|
tslib_1.__exportStar(require("./kv/commonKeyValueDaoMemoCache"), exports);
|
package/dist/testing/daoTest.js
CHANGED
|
@@ -21,7 +21,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
21
21
|
// tableSchemas = true,
|
|
22
22
|
createTable = true, dbQueryFilter = true,
|
|
23
23
|
// dbQueryFilterIn = true,
|
|
24
|
-
dbQueryOrder = true, dbQuerySelectFields = true, streaming = true, strongConsistency = true, nullValues = true, } = features;
|
|
24
|
+
dbQueryOrder = true, dbQuerySelectFields = true, streaming = true, strongConsistency = true, nullValues = true, transactions = true, } = features;
|
|
25
25
|
// const {
|
|
26
26
|
// allowExtraPropertiesInResponse,
|
|
27
27
|
// allowBooleansAsUndefined,
|
|
@@ -191,5 +191,46 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
191
191
|
await db.deleteByIds(test_model_1.TEST_TABLE, rows.map(i => i.id));
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
|
+
if (transactions) {
|
|
195
|
+
test('transaction happy path', async () => {
|
|
196
|
+
// cleanup
|
|
197
|
+
await dao.query().deleteByQuery();
|
|
198
|
+
// Test that id, created, updated are created
|
|
199
|
+
const now = (0, js_lib_1.localTime)().unix();
|
|
200
|
+
await dao.runInTransaction([dao.tx.save((0, js_lib_1._omit)(item1, ['id', 'created', 'updated']))]);
|
|
201
|
+
const loaded = await dao.query().runQuery();
|
|
202
|
+
expect(loaded.length).toBe(1);
|
|
203
|
+
expect(loaded[0].id).toBeDefined();
|
|
204
|
+
expect(loaded[0].created).toBeGreaterThanOrEqual(now);
|
|
205
|
+
expect(loaded[0].updated).toBe(loaded[0].created);
|
|
206
|
+
await dao.runInTransaction([dao.tx.deleteById(loaded[0].id)]);
|
|
207
|
+
// saveBatch [item1, 2, 3]
|
|
208
|
+
// save item3 with k1: k1_mod
|
|
209
|
+
// delete item2
|
|
210
|
+
// remaining: item1, item3_with_k1_mod
|
|
211
|
+
await dao.runInTransaction([
|
|
212
|
+
dao.tx.saveBatch(items),
|
|
213
|
+
dao.tx.save({ ...items[2], k1: 'k1_mod' }),
|
|
214
|
+
dao.tx.deleteById(items[1].id),
|
|
215
|
+
]);
|
|
216
|
+
const rows = await dao.query().runQuery();
|
|
217
|
+
const expected = [items[0], { ...items[2], k1: 'k1_mod' }];
|
|
218
|
+
(0, dbTest_1.expectMatch)(expected, rows, quirks);
|
|
219
|
+
});
|
|
220
|
+
test('transaction rollback', async () => {
|
|
221
|
+
await expect(dao.runInTransaction([
|
|
222
|
+
dao.tx.deleteById(items[2].id),
|
|
223
|
+
dao.tx.save({ ...items[0], k1: 5 }), // it should fail here
|
|
224
|
+
])).rejects.toThrow();
|
|
225
|
+
const rows = await dao.query().runQuery();
|
|
226
|
+
const expected = [items[0], { ...items[2], k1: 'k1_mod' }];
|
|
227
|
+
(0, dbTest_1.expectMatch)(expected, rows, quirks);
|
|
228
|
+
});
|
|
229
|
+
if (querying) {
|
|
230
|
+
test('transaction cleanup', async () => {
|
|
231
|
+
await dao.query().deleteByQuery();
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
194
235
|
}
|
|
195
236
|
exports.runCommonDaoTest = runCommonDaoTest;
|
package/dist/testing/dbTest.d.ts
CHANGED
package/dist/testing/dbTest.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.expectMatch = exports.runCommonDBTest = 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 dbQuery_1 = require("../query/dbQuery");
|
|
7
|
+
const dbTransaction_1 = require("../transaction/dbTransaction");
|
|
7
8
|
const test_model_1 = require("./test.model");
|
|
8
9
|
const test_util_1 = require("./test.util");
|
|
9
10
|
/**
|
|
@@ -12,7 +13,7 @@ const test_util_1 = require("./test.util");
|
|
|
12
13
|
function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
13
14
|
const { querying = true, tableSchemas = true, createTable = true, dbQueryFilter = true,
|
|
14
15
|
// dbQueryFilterIn = true,
|
|
15
|
-
dbQueryOrder = true, dbQuerySelectFields = true, insert = true, update = true, streaming = true, strongConsistency = true, bufferSupport = true, nullValues = true, documentDB = true, } = features;
|
|
16
|
+
dbQueryOrder = true, dbQuerySelectFields = true, insert = true, update = true, streaming = true, strongConsistency = true, bufferSupport = true, nullValues = true, documentDB = true, transactions = true, } = features;
|
|
16
17
|
// const {
|
|
17
18
|
// allowExtraPropertiesInResponse,
|
|
18
19
|
// allowBooleansAsUndefined,
|
|
@@ -95,6 +96,9 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
95
96
|
test('saveBatch test items', async () => {
|
|
96
97
|
await db.saveBatch(test_model_1.TEST_TABLE, items);
|
|
97
98
|
});
|
|
99
|
+
test('saveBatch should throw on null id', async () => {
|
|
100
|
+
await expect(db.saveBatch(test_model_1.TEST_TABLE, [{ ...item1, id: null }])).rejects.toThrow();
|
|
101
|
+
});
|
|
98
102
|
if (insert) {
|
|
99
103
|
test('saveBatch INSERT method should throw', async () => {
|
|
100
104
|
await expect(db.saveBatch(test_model_1.TEST_TABLE, items, { saveMethod: 'insert' })).rejects.toThrow();
|
|
@@ -169,11 +173,11 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
169
173
|
// getTables
|
|
170
174
|
test('getTables, getTableSchema (if supported)', async () => {
|
|
171
175
|
const tables = await db.getTables();
|
|
172
|
-
console.log({ tables })
|
|
176
|
+
// console.log({ tables })
|
|
173
177
|
if (tableSchemas) {
|
|
174
178
|
await (0, js_lib_1.pMap)(tables, async (table) => {
|
|
175
179
|
const schema = await db.getTableSchema(table);
|
|
176
|
-
console.log(schema)
|
|
180
|
+
// console.log(schema)
|
|
177
181
|
expect(schema.$id).toBe(`${table}.schema.json`);
|
|
178
182
|
});
|
|
179
183
|
}
|
|
@@ -201,18 +205,46 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
201
205
|
await db.saveBatch(test_model_1.TEST_TABLE, [item]);
|
|
202
206
|
const [loaded] = await db.getByIds(test_model_1.TEST_TABLE, [item.id]);
|
|
203
207
|
const b1Loaded = loaded.b1;
|
|
204
|
-
console.log({
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
})
|
|
208
|
+
// console.log({
|
|
209
|
+
// b11: typeof b1,
|
|
210
|
+
// b12: typeof b1Loaded,
|
|
211
|
+
// l1: b1.length,
|
|
212
|
+
// l2: b1Loaded.length,
|
|
213
|
+
// b1,
|
|
214
|
+
// b1Loaded,
|
|
215
|
+
// })
|
|
212
216
|
expect(b1Loaded).toEqual(b1);
|
|
213
217
|
expect(b1Loaded.toString()).toBe(s);
|
|
214
218
|
});
|
|
215
219
|
}
|
|
220
|
+
if (transactions) {
|
|
221
|
+
test('transaction happy path', async () => {
|
|
222
|
+
// cleanup
|
|
223
|
+
await db.deleteByQuery(queryAll());
|
|
224
|
+
// saveBatch [item1, 2, 3]
|
|
225
|
+
// save item3 with k1: k1_mod
|
|
226
|
+
// delete item2
|
|
227
|
+
// remaining: item1, item3_with_k1_mod
|
|
228
|
+
const tx = dbTransaction_1.DBTransaction.create()
|
|
229
|
+
.saveBatch(test_model_1.TEST_TABLE, items)
|
|
230
|
+
.save(test_model_1.TEST_TABLE, { ...items[2], k1: 'k1_mod' })
|
|
231
|
+
.deleteById(test_model_1.TEST_TABLE, items[1].id);
|
|
232
|
+
await db.commitTransaction(tx);
|
|
233
|
+
const { rows } = await db.runQuery(queryAll());
|
|
234
|
+
const expected = [items[0], { ...items[2], k1: 'k1_mod' }];
|
|
235
|
+
expectMatch(expected, rows, quirks);
|
|
236
|
+
});
|
|
237
|
+
test('transaction rollback', async () => {
|
|
238
|
+
// It should fail on id == null
|
|
239
|
+
const tx = dbTransaction_1.DBTransaction.create()
|
|
240
|
+
.deleteById(test_model_1.TEST_TABLE, items[2].id)
|
|
241
|
+
.save(test_model_1.TEST_TABLE, { ...items[0], k1: 5, id: null });
|
|
242
|
+
await expect(db.commitTransaction(tx)).rejects.toThrow();
|
|
243
|
+
const { rows } = await db.runQuery(queryAll());
|
|
244
|
+
const expected = [items[0], { ...items[2], k1: 'k1_mod' }];
|
|
245
|
+
expectMatch(expected, rows, quirks);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
216
248
|
if (querying) {
|
|
217
249
|
test('cleanup', async () => {
|
|
218
250
|
// CLEAN UP
|
|
@@ -42,7 +42,7 @@ class CommonTimeSeriesDao {
|
|
|
42
42
|
async commitTransaction(ops) {
|
|
43
43
|
if (!ops.length)
|
|
44
44
|
return;
|
|
45
|
-
const tx =
|
|
45
|
+
const tx = __1.DBTransaction.create();
|
|
46
46
|
ops.forEach(op => {
|
|
47
47
|
const rows = op.dataPoints.map(([ts, v]) => ({
|
|
48
48
|
id: String(ts),
|
|
@@ -6,7 +6,14 @@ import type { CommonDBSaveOptions, DBOperation } from '../db.model';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare class DBTransaction {
|
|
8
8
|
ops: DBOperation[];
|
|
9
|
+
protected constructor(ops?: DBOperation[]);
|
|
10
|
+
/**
|
|
11
|
+
* Convenience method.
|
|
12
|
+
*/
|
|
13
|
+
static create(ops?: DBOperation[]): DBTransaction;
|
|
14
|
+
save<ROW extends ObjectWithId = AnyObjectWithId>(table: string, row: ROW): this;
|
|
9
15
|
saveBatch<ROW extends ObjectWithId = AnyObjectWithId>(table: string, rows: ROW[]): this;
|
|
16
|
+
deleteById(table: string, id: string): this;
|
|
10
17
|
deleteByIds(table: string, ids: string[]): this;
|
|
11
18
|
}
|
|
12
19
|
/**
|