@naturalcycles/db-lib 8.60.0 → 9.0.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.
Files changed (52) hide show
  1. package/dist/adapter/cachedb/cache.db.d.ts +3 -4
  2. package/dist/adapter/cachedb/cache.db.js +5 -4
  3. package/dist/adapter/cachedb/cache.db.model.d.ts +2 -2
  4. package/dist/adapter/file/file.db.d.ts +15 -7
  5. package/dist/adapter/file/file.db.js +93 -57
  6. package/dist/adapter/file/localFile.persistence.plugin.js +3 -3
  7. package/dist/adapter/inmemory/inMemory.db.d.ts +30 -4
  8. package/dist/adapter/inmemory/inMemory.db.js +89 -33
  9. package/dist/base.common.db.d.ts +7 -10
  10. package/dist/base.common.db.js +11 -7
  11. package/dist/common.db.d.ts +56 -4
  12. package/dist/common.db.js +23 -0
  13. package/dist/commondao/common.dao.d.ts +17 -9
  14. package/dist/commondao/common.dao.js +82 -69
  15. package/dist/commondao/common.dao.model.d.ts +0 -10
  16. package/dist/db.model.d.ts +12 -1
  17. package/dist/index.d.ts +0 -1
  18. package/dist/index.js +0 -1
  19. package/dist/pipeline/dbPipelineBackup.js +4 -4
  20. package/dist/pipeline/dbPipelineRestore.js +2 -2
  21. package/dist/testing/daoTest.d.ts +2 -2
  22. package/dist/testing/daoTest.js +29 -39
  23. package/dist/testing/dbTest.d.ts +1 -39
  24. package/dist/testing/dbTest.js +40 -49
  25. package/dist/testing/index.d.ts +2 -2
  26. package/dist/timeseries/commonTimeSeriesDao.js +5 -6
  27. package/dist/transaction/dbTransaction.util.d.ts +17 -4
  28. package/dist/transaction/dbTransaction.util.js +46 -22
  29. package/dist/validation/index.js +2 -2
  30. package/package.json +1 -1
  31. package/src/adapter/cachedb/cache.db.model.ts +7 -2
  32. package/src/adapter/cachedb/cache.db.ts +7 -8
  33. package/src/adapter/file/file.db.ts +121 -69
  34. package/src/adapter/file/localFile.persistence.plugin.ts +4 -5
  35. package/src/adapter/inmemory/inMemory.db.ts +106 -33
  36. package/src/base.common.db.ts +20 -11
  37. package/src/common.db.ts +80 -3
  38. package/src/commondao/common.dao.model.ts +0 -11
  39. package/src/commondao/common.dao.ts +103 -89
  40. package/src/db.model.ts +15 -2
  41. package/src/index.ts +0 -1
  42. package/src/pipeline/dbPipelineBackup.ts +5 -8
  43. package/src/pipeline/dbPipelineRestore.ts +3 -4
  44. package/src/testing/daoTest.ts +32 -52
  45. package/src/testing/dbTest.ts +42 -119
  46. package/src/testing/index.ts +2 -12
  47. package/src/timeseries/commonTimeSeriesDao.ts +5 -6
  48. package/src/transaction/dbTransaction.util.ts +61 -22
  49. package/src/validation/index.ts +2 -2
  50. package/dist/transaction/dbTransaction.d.ts +0 -27
  51. package/dist/transaction/dbTransaction.js +0 -64
  52. package/src/transaction/dbTransaction.ts +0 -67
@@ -2,10 +2,9 @@
2
2
  import { Readable } from 'node:stream';
3
3
  import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
4
4
  import { BaseCommonDB } from '../../base.common.db';
5
- import { CommonDB } from '../../common.db';
6
- import { CommonDBOptions, DBPatch, RunQueryResult } from '../../db.model';
5
+ import { CommonDB, CommonDBSupport } from '../../common.db';
6
+ import { DBPatch, RunQueryResult } from '../../db.model';
7
7
  import { DBQuery } from '../../query/dbQuery';
8
- import { DBTransaction } from '../../transaction/dbTransaction';
9
8
  import { CacheDBCfg, CacheDBCreateOptions, CacheDBOptions, CacheDBSaveOptions, CacheDBStreamOptions } from './cache.db.model';
10
9
  /**
11
10
  * CommonDB implementation that proxies requests to downstream CommonDB
@@ -14,6 +13,7 @@ import { CacheDBCfg, CacheDBCreateOptions, CacheDBOptions, CacheDBSaveOptions, C
14
13
  * Queries always hit downstream (unless `onlyCache` is passed)
15
14
  */
16
15
  export declare class CacheDB extends BaseCommonDB implements CommonDB {
16
+ support: CommonDBSupport;
17
17
  constructor(cfg: CacheDBCfg);
18
18
  cfg: CacheDBCfg;
19
19
  ping(): Promise<void>;
@@ -30,5 +30,4 @@ export declare class CacheDB extends BaseCommonDB implements CommonDB {
30
30
  streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBStreamOptions): Readable;
31
31
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBOptions): Promise<number>;
32
32
  updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>, opt?: CacheDBOptions): Promise<number>;
33
- commitTransaction(tx: DBTransaction, opt?: CommonDBOptions): Promise<void>;
34
33
  }
@@ -4,6 +4,7 @@ exports.CacheDB = void 0;
4
4
  const node_stream_1 = require("node:stream");
5
5
  const js_lib_1 = require("@naturalcycles/js-lib");
6
6
  const base_common_db_1 = require("../../base.common.db");
7
+ const common_db_1 = require("../../common.db");
7
8
  /**
8
9
  * CommonDB implementation that proxies requests to downstream CommonDB
9
10
  * and does in-memory caching.
@@ -13,6 +14,10 @@ const base_common_db_1 = require("../../base.common.db");
13
14
  class CacheDB extends base_common_db_1.BaseCommonDB {
14
15
  constructor(cfg) {
15
16
  super();
17
+ this.support = {
18
+ ...common_db_1.commonDBFullSupport,
19
+ transactions: false,
20
+ };
16
21
  this.cfg = {
17
22
  logger: console,
18
23
  ...cfg,
@@ -187,9 +192,5 @@ class CacheDB extends base_common_db_1.BaseCommonDB {
187
192
  }
188
193
  return updated || 0;
189
194
  }
190
- async commitTransaction(tx, opt) {
191
- await this.cfg.downstreamDB.commitTransaction(tx, opt);
192
- await this.cfg.cacheDB.commitTransaction(tx, opt);
193
- }
194
195
  }
195
196
  exports.CacheDB = CacheDB;
@@ -1,6 +1,6 @@
1
1
  import { CommonLogger, ObjectWithId } from '@naturalcycles/js-lib';
2
2
  import { CommonDB } from '../../common.db';
3
- import { CommonDBCreateOptions, CommonDBSaveOptions, CommonDBStreamOptions } from '../../db.model';
3
+ import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions } from '../../db.model';
4
4
  export interface CacheDBCfg {
5
5
  name: string;
6
6
  cacheDB: CommonDB;
@@ -37,7 +37,7 @@ export interface CacheDBCfg {
37
37
  */
38
38
  logger?: CommonLogger;
39
39
  }
40
- export interface CacheDBOptions {
40
+ export interface CacheDBOptions extends CommonDBOptions {
41
41
  /**
42
42
  * @default false
43
43
  */
@@ -1,10 +1,9 @@
1
1
  import { JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
- import { BaseCommonDB, DBSaveBatchOperation } from '../..';
3
+ import { BaseCommonDB, CommonDBSupport, DBOperation, DBSaveBatchOperation, DBTransaction } 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';
7
- import { DBTransaction } from '../../transaction/dbTransaction';
8
7
  import { FileDBCfg } from './file.db.model';
9
8
  /**
10
9
  * Provides barebone implementation for "whole file" based CommonDB.
@@ -17,25 +16,34 @@ import { FileDBCfg } from './file.db.model';
17
16
  * Each save operation saves *whole* file to the persistence layer.
18
17
  */
19
18
  export declare class FileDB extends BaseCommonDB implements CommonDB {
19
+ support: CommonDBSupport;
20
20
  constructor(cfg: FileDBCfg);
21
21
  cfg: FileDBCfg;
22
22
  ping(): Promise<void>;
23
23
  getTables(): Promise<string[]>;
24
24
  getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], _opt?: CommonDBOptions): Promise<ROW[]>;
25
25
  saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
26
- /**
27
- * Implementation is optimized for loading/saving _whole files_.
28
- */
29
- commitTransaction(tx: DBTransaction, _opt?: CommonDBOptions): Promise<void>;
30
26
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<RunQueryResult<ROW>>;
31
27
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
32
28
  streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CommonDBStreamOptions): ReadableTyped<ROW>;
33
29
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
30
+ deleteByIds(table: string, ids: string[], _opt?: CommonDBOptions): Promise<number>;
34
31
  getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
35
32
  loadFile<ROW extends ObjectWithId>(table: string): Promise<ROW[]>;
36
33
  saveFile<ROW extends ObjectWithId>(table: string, _rows: ROW[]): Promise<void>;
37
34
  saveFiles<ROW extends ObjectWithId>(ops: DBSaveBatchOperation<ROW>[]): Promise<void>;
38
- private sortRows;
35
+ createTransaction(): Promise<FileDBTransaction>;
36
+ sortRows<ROW extends ObjectWithId>(rows: ROW[]): ROW[];
39
37
  private logStarted;
40
38
  private logFinished;
41
39
  }
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.FileDB = void 0;
3
+ exports.FileDBTransaction = 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("../..");
@@ -17,6 +17,15 @@ const __1 = require("../..");
17
17
  class FileDB extends __1.BaseCommonDB {
18
18
  constructor(cfg) {
19
19
  super();
20
+ this.support = {
21
+ ...__1.commonDBFullSupport,
22
+ bufferValues: false, // todo: implement
23
+ insertSaveMethod: false,
24
+ updateSaveMethod: false,
25
+ updateByQuery: false,
26
+ createTable: false,
27
+ transactions: false,
28
+ };
20
29
  this.cfg = {
21
30
  sortObjects: true,
22
31
  logFinished: true,
@@ -57,62 +66,6 @@ class FileDB extends __1.BaseCommonDB {
57
66
  await this.saveFile(table, (0, js_lib_1._stringMapValues)(byId));
58
67
  }
59
68
  }
60
- /**
61
- * Implementation is optimized for loading/saving _whole files_.
62
- */
63
- async commitTransaction(tx, _opt) {
64
- // data[table][id] => row
65
- const data = {};
66
- // 1. Load all tables data (concurrently)
67
- const tables = (0, js_lib_1._uniq)(tx.ops.map(o => o.table));
68
- await (0, js_lib_1.pMap)(tables, async (table) => {
69
- const rows = await this.loadFile(table);
70
- data[table] = (0, js_lib_1._by)(rows, r => r.id);
71
- }, { concurrency: 16 });
72
- const backup = (0, js_lib_1._deepCopy)(data);
73
- // 2. Apply ops one by one (in order)
74
- tx.ops.forEach(op => {
75
- if (op.type === 'deleteByIds') {
76
- op.ids.forEach(id => delete data[op.table][id]);
77
- }
78
- else if (op.type === 'saveBatch') {
79
- op.rows.forEach(r => {
80
- if (!r.id) {
81
- throw new Error('FileDB: row has an empty id');
82
- }
83
- data[op.table][r.id] = r;
84
- });
85
- }
86
- else {
87
- throw new Error(`DBOperation not supported: ${op.type}`);
88
- }
89
- });
90
- // 3. Sort, turn it into ops
91
- // Not filtering empty arrays, cause it's already filtered in this.saveFiles()
92
- const ops = (0, js_lib_1._stringMapEntries)(data).map(([table, map]) => {
93
- return {
94
- type: 'saveBatch',
95
- table,
96
- rows: this.sortRows((0, js_lib_1._stringMapValues)(map)),
97
- };
98
- });
99
- // 4. Save all files
100
- try {
101
- await this.saveFiles(ops);
102
- }
103
- catch (err) {
104
- const ops = (0, js_lib_1._stringMapEntries)(backup).map(([table, map]) => {
105
- return {
106
- type: 'saveBatch',
107
- table,
108
- rows: this.sortRows((0, js_lib_1._stringMapValues)(map)),
109
- };
110
- });
111
- // Rollback, ignore rollback error (if any)
112
- await this.saveFiles(ops).catch(_ => { });
113
- throw err;
114
- }
115
- }
116
69
  async runQuery(q, _opt) {
117
70
  return {
118
71
  rows: (0, __1.queryInMemory)(q, await this.loadFile(q.table)),
@@ -141,6 +94,20 @@ class FileDB extends __1.BaseCommonDB {
141
94
  }
142
95
  return deleted;
143
96
  }
97
+ async deleteByIds(table, ids, _opt) {
98
+ const byId = (0, js_lib_1._by)(await this.loadFile(table), r => r.id);
99
+ let deleted = 0;
100
+ ids.forEach(id => {
101
+ if (!byId[id])
102
+ return;
103
+ delete byId[id];
104
+ deleted++;
105
+ });
106
+ if (deleted > 0) {
107
+ await this.saveFile(table, (0, js_lib_1._stringMapValues)(byId));
108
+ }
109
+ return deleted;
110
+ }
144
111
  async getTableSchema(table) {
145
112
  const rows = await this.loadFile(table);
146
113
  return {
@@ -173,6 +140,9 @@ class FileDB extends __1.BaseCommonDB {
173
140
  await this.cfg.plugin.saveFiles(ops);
174
141
  this.logFinished(started, op);
175
142
  }
143
+ async createTransaction() {
144
+ return new FileDBTransaction(this);
145
+ }
176
146
  sortRows(rows) {
177
147
  rows = rows.map(r => (0, js_lib_1._filterUndefinedValues)(r));
178
148
  if (this.cfg.sortOnSave) {
@@ -198,3 +168,69 @@ class FileDB extends __1.BaseCommonDB {
198
168
  }
199
169
  }
200
170
  exports.FileDB = FileDB;
171
+ class FileDBTransaction {
172
+ constructor(db) {
173
+ this.db = db;
174
+ this.ops = [];
175
+ }
176
+ /**
177
+ * Implementation is optimized for loading/saving _whole files_.
178
+ */
179
+ async commit() {
180
+ // data[table][id] => row
181
+ const data = {};
182
+ // 1. Load all tables data (concurrently)
183
+ const tables = (0, js_lib_1._uniq)(this.ops.map(o => o.table));
184
+ await (0, js_lib_1.pMap)(tables, async (table) => {
185
+ const rows = await this.db.loadFile(table);
186
+ data[table] = (0, js_lib_1._by)(rows, r => r.id);
187
+ }, { concurrency: 16 });
188
+ const backup = (0, js_lib_1._deepCopy)(data);
189
+ // 2. Apply ops one by one (in order)
190
+ this.ops.forEach(op => {
191
+ if (op.type === 'deleteByIds') {
192
+ op.ids.forEach(id => delete data[op.table][id]);
193
+ }
194
+ else if (op.type === 'saveBatch') {
195
+ op.rows.forEach(r => {
196
+ if (!r.id) {
197
+ throw new Error('FileDB: row has an empty id');
198
+ }
199
+ data[op.table][r.id] = r;
200
+ });
201
+ }
202
+ else {
203
+ throw new Error(`DBOperation not supported: ${op.type}`);
204
+ }
205
+ });
206
+ // 3. Sort, turn it into ops
207
+ // Not filtering empty arrays, cause it's already filtered in this.saveFiles()
208
+ const ops = (0, js_lib_1._stringMapEntries)(data).map(([table, map]) => {
209
+ return {
210
+ type: 'saveBatch',
211
+ table,
212
+ rows: this.db.sortRows((0, js_lib_1._stringMapValues)(map)),
213
+ };
214
+ });
215
+ // 4. Save all files
216
+ try {
217
+ await this.db.saveFiles(ops);
218
+ }
219
+ catch (err) {
220
+ const ops = (0, js_lib_1._stringMapEntries)(backup).map(([table, map]) => {
221
+ return {
222
+ type: 'saveBatch',
223
+ table,
224
+ rows: this.db.sortRows((0, js_lib_1._stringMapValues)(map)),
225
+ };
226
+ });
227
+ // Rollback, ignore rollback error (if any)
228
+ await this.db.saveFiles(ops).catch(_ => { });
229
+ throw err;
230
+ }
231
+ }
232
+ async rollback() {
233
+ this.ops = [];
234
+ }
235
+ }
236
+ exports.FileDBTransaction = FileDBTransaction;
@@ -26,10 +26,10 @@ class LocalFilePersistencePlugin {
26
26
  .map(f => f.split('.ndjson')[0]);
27
27
  }
28
28
  async loadFile(table) {
29
- await (0, nodejs_lib_1._ensureDir)(this.cfg.storagePath);
29
+ await nodejs_lib_1.fs2.ensureDirAsync(this.cfg.storagePath);
30
30
  const ext = `ndjson${this.cfg.gzip ? '.gz' : ''}`;
31
31
  const filePath = `${this.cfg.storagePath}/${table}.${ext}`;
32
- if (!(await (0, nodejs_lib_1._pathExists)(filePath)))
32
+ if (!(await nodejs_lib_1.fs2.pathExistsAsync(filePath)))
33
33
  return [];
34
34
  const transformUnzip = this.cfg.gzip ? [(0, node_zlib_1.createUnzip)()] : [];
35
35
  const rows = [];
@@ -46,7 +46,7 @@ class LocalFilePersistencePlugin {
46
46
  await (0, js_lib_1.pMap)(ops, async (op) => await this.saveFile(op.table, op.rows), { concurrency: 16 });
47
47
  }
48
48
  async saveFile(table, rows) {
49
- await (0, nodejs_lib_1._ensureDir)(this.cfg.storagePath);
49
+ await nodejs_lib_1.fs2.ensureDirAsync(this.cfg.storagePath);
50
50
  const ext = `ndjson${this.cfg.gzip ? '.gz' : ''}`;
51
51
  const filePath = `${this.cfg.storagePath}/${table}.${ext}`;
52
52
  const transformZip = this.cfg.gzip ? [(0, node_zlib_1.createGzip)()] : [];
@@ -1,7 +1,7 @@
1
1
  import { JsonSchemaObject, StringMap, JsonSchemaRootObject, ObjectWithId, CommonLogger } from '@naturalcycles/js-lib';
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
- import { CommonDB, DBPatch, DBTransaction } from '../..';
4
- import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, RunQueryResult } from '../../db.model';
3
+ import { CommonDB, CommonDBType, DBOperation, DBPatch } from '../..';
4
+ import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, DBTransaction, RunQueryResult } from '../../db.model';
5
5
  import { DBQuery } from '../../query/dbQuery';
6
6
  export interface InMemoryDBCfg {
7
7
  /**
@@ -36,6 +36,24 @@ export interface InMemoryDBCfg {
36
36
  logger?: CommonLogger;
37
37
  }
38
38
  export declare class InMemoryDB implements CommonDB {
39
+ dbType: CommonDBType;
40
+ support: {
41
+ queries?: boolean | undefined;
42
+ dbQueryFilter?: boolean | undefined;
43
+ dbQueryFilterIn?: boolean | undefined;
44
+ dbQueryOrder?: boolean | undefined;
45
+ dbQuerySelectFields?: boolean | undefined;
46
+ insertSaveMethod?: boolean | undefined;
47
+ updateSaveMethod?: boolean | undefined;
48
+ updateByQuery?: boolean | undefined;
49
+ dbIncrement?: boolean | undefined;
50
+ createTable?: boolean | undefined;
51
+ tableSchemas?: boolean | undefined;
52
+ streaming?: boolean | undefined;
53
+ bufferValues?: boolean | undefined;
54
+ nullValues?: boolean | undefined;
55
+ transactions?: boolean | undefined;
56
+ };
39
57
  constructor(cfg?: Partial<InMemoryDBCfg>);
40
58
  cfg: InMemoryDBCfg;
41
59
  data: StringMap<StringMap<ObjectWithId>>;
@@ -54,12 +72,13 @@ export declare class InMemoryDB implements CommonDB {
54
72
  createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>, opt?: CommonDBCreateOptions): Promise<void>;
55
73
  getByIds<ROW extends ObjectWithId>(_table: string, ids: ROW['id'][], _opt?: CommonDBOptions): Promise<ROW[]>;
56
74
  saveBatch<ROW extends Partial<ObjectWithId>>(_table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
57
- deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
75
+ deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CommonDBOptions): Promise<number>;
76
+ deleteByIds(_table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
58
77
  updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>): Promise<number>;
59
78
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<RunQueryResult<ROW>>;
60
79
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
61
80
  streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): ReadableTyped<ROW>;
62
- commitTransaction(tx: DBTransaction, opt?: CommonDBOptions): Promise<void>;
81
+ createTransaction(): Promise<DBTransaction>;
63
82
  /**
64
83
  * Flushes all tables (all namespaces) at once.
65
84
  */
@@ -69,3 +88,10 @@ export declare class InMemoryDB implements CommonDB {
69
88
  */
70
89
  restoreFromDisk(): Promise<void>;
71
90
  }
91
+ export declare class InMemoryDBTransaction implements DBTransaction {
92
+ private db;
93
+ constructor(db: InMemoryDB);
94
+ ops: DBOperation[];
95
+ commit(): Promise<void>;
96
+ rollback(): Promise<void>;
97
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InMemoryDB = void 0;
3
+ exports.InMemoryDBTransaction = exports.InMemoryDB = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
6
6
  const promises_1 = tslib_1.__importDefault(require("node:fs/promises"));
@@ -9,9 +9,12 @@ const node_zlib_1 = require("node:zlib");
9
9
  const js_lib_1 = require("@naturalcycles/js-lib");
10
10
  const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
11
11
  const __1 = require("../..");
12
- const dbQuery_1 = require("../../query/dbQuery");
13
12
  class InMemoryDB {
14
13
  constructor(cfg) {
14
+ this.dbType = __1.CommonDBType.document;
15
+ this.support = {
16
+ ...__1.commonDBFullSupport,
17
+ };
15
18
  // data[table][id] > {id: 'a', created: ... }
16
19
  this.data = {};
17
20
  this.cfg = {
@@ -74,6 +77,17 @@ class InMemoryDB {
74
77
  return ids.map(id => this.data[table][id]).filter(Boolean);
75
78
  }
76
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
+ }
77
91
  const table = this.cfg.tablesPrefix + _table;
78
92
  this.data[table] ||= {};
79
93
  rows.forEach(r => {
@@ -93,14 +107,44 @@ class InMemoryDB {
93
107
  this.data[table][r.id] = JSON.parse(JSON.stringify(r), nodejs_lib_1.bufferReviver);
94
108
  });
95
109
  }
96
- async deleteByQuery(q, _opt) {
110
+ async deleteByQuery(q, opt = {}) {
97
111
  const table = this.cfg.tablesPrefix + q.table;
98
- this.data[table] ||= {};
112
+ if (!this.data[table])
113
+ return 0;
114
+ 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
+ return await this.deleteByIds(q.table, ids);
127
+ }
128
+ async deleteByIds(_table, ids, opt = {}) {
129
+ const table = this.cfg.tablesPrefix + _table;
130
+ if (!this.data[table])
131
+ 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
+ }
99
143
  let count = 0;
100
- (0, __1.queryInMemory)(q, Object.values(this.data[table] || {})).forEach(r => {
101
- if (!this.data[table][r.id])
144
+ ids.forEach(id => {
145
+ if (!this.data[table][id])
102
146
  return;
103
- delete this.data[table][r.id];
147
+ delete this.data[table][id];
104
148
  count++;
105
149
  });
106
150
  return count;
@@ -109,6 +153,7 @@ class InMemoryDB {
109
153
  const patchEntries = Object.entries(patch);
110
154
  if (!patchEntries.length)
111
155
  return 0;
156
+ // todo: can we support tx here? :thinking:
112
157
  const table = this.cfg.tablesPrefix + q.table;
113
158
  const rows = (0, __1.queryInMemory)(q, Object.values(this.data[table] || {}));
114
159
  rows.forEach((row) => {
@@ -135,30 +180,8 @@ class InMemoryDB {
135
180
  const table = this.cfg.tablesPrefix + q.table;
136
181
  return node_stream_1.Readable.from((0, __1.queryInMemory)(q, Object.values(this.data[table] || {})));
137
182
  }
138
- async commitTransaction(tx, opt) {
139
- const backup = (0, js_lib_1._deepCopy)(this.data);
140
- try {
141
- for await (const op of tx.ops) {
142
- if (op.type === 'saveBatch') {
143
- await this.saveBatch(op.table, op.rows, { ...op.opt, ...opt });
144
- }
145
- else if (op.type === 'deleteByIds') {
146
- await this.deleteByQuery(dbQuery_1.DBQuery.create(op.table).filter('id', 'in', op.ids), {
147
- ...op.opt,
148
- ...opt,
149
- });
150
- }
151
- else {
152
- throw new Error(`DBOperation not supported: ${op.type}`);
153
- }
154
- }
155
- }
156
- catch (err) {
157
- // rollback
158
- this.data = backup;
159
- this.cfg.logger.log('InMemoryDB transaction rolled back');
160
- throw err;
161
- }
183
+ async createTransaction() {
184
+ return new InMemoryDBTransaction(this);
162
185
  }
163
186
  /**
164
187
  * Flushes all tables (all namespaces) at once.
@@ -167,7 +190,7 @@ class InMemoryDB {
167
190
  (0, js_lib_1._assert)(this.cfg.persistenceEnabled, 'flushToDisk() called but persistenceEnabled=false');
168
191
  const { persistentStoragePath, persistZip } = this.cfg;
169
192
  const started = Date.now();
170
- await (0, nodejs_lib_1._emptyDir)(persistentStoragePath);
193
+ await nodejs_lib_1.fs2.emptyDirAsync(persistentStoragePath);
171
194
  const transformZip = persistZip ? [(0, node_zlib_1.createGzip)()] : [];
172
195
  let tables = 0;
173
196
  // infinite concurrency for now
@@ -193,7 +216,7 @@ class InMemoryDB {
193
216
  (0, js_lib_1._assert)(this.cfg.persistenceEnabled, 'restoreFromDisk() called but persistenceEnabled=false');
194
217
  const { persistentStoragePath } = this.cfg;
195
218
  const started = Date.now();
196
- await (0, nodejs_lib_1._ensureDir)(persistentStoragePath);
219
+ await nodejs_lib_1.fs2.ensureDirAsync(persistentStoragePath);
197
220
  this.data = {}; // empty it in the beginning!
198
221
  const files = (await promises_1.default.readdir(persistentStoragePath)).filter(f => f.includes('.ndjson'));
199
222
  // infinite concurrency for now
@@ -215,3 +238,36 @@ class InMemoryDB {
215
238
  }
216
239
  }
217
240
  exports.InMemoryDB = InMemoryDB;
241
+ class InMemoryDBTransaction {
242
+ constructor(db) {
243
+ this.db = db;
244
+ this.ops = [];
245
+ }
246
+ async commit() {
247
+ const backup = (0, js_lib_1._deepCopy)(this.db.data);
248
+ try {
249
+ for (const op of this.ops) {
250
+ if (op.type === 'saveBatch') {
251
+ await this.db.saveBatch(op.table, op.rows, op.opt);
252
+ }
253
+ else if (op.type === 'deleteByIds') {
254
+ await this.db.deleteByIds(op.table, op.ids, op.opt);
255
+ }
256
+ else {
257
+ throw new Error(`DBOperation not supported: ${op.type}`);
258
+ }
259
+ }
260
+ this.ops = [];
261
+ }
262
+ catch (err) {
263
+ // rollback
264
+ this.db.data = backup;
265
+ this.db.cfg.logger.log('InMemoryDB transaction rolled back');
266
+ throw err;
267
+ }
268
+ }
269
+ async rollback() {
270
+ this.ops = [];
271
+ }
272
+ }
273
+ exports.InMemoryDBTransaction = InMemoryDBTransaction;
@@ -1,29 +1,26 @@
1
1
  import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
- import { CommonDB } from './common.db';
4
- import { CommonDBOptions, CommonDBSaveOptions, DBPatch, RunQueryResult } from './db.model';
3
+ import { CommonDB, CommonDBSupport, CommonDBType } from './common.db';
4
+ import { CommonDBOptions, CommonDBSaveOptions, DBPatch, DBTransaction, RunQueryResult } from './db.model';
5
5
  import { DBQuery } from './query/dbQuery';
6
- import { DBTransaction } from './transaction/dbTransaction';
7
6
  /**
8
7
  * No-op implementation of CommonDB interface.
9
8
  * To be extended by actual implementations.
10
9
  */
11
10
  export declare class BaseCommonDB implements CommonDB {
11
+ dbType: CommonDBType;
12
+ support: CommonDBSupport;
12
13
  ping(): Promise<void>;
13
14
  getTables(): Promise<string[]>;
14
15
  getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
15
16
  createTable<ROW extends ObjectWithId>(table: string, schema: JsonSchemaObject<ROW>): Promise<void>;
16
- getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][]): Promise<ROW[]>;
17
+ getByIds<ROW extends ObjectWithId>(table: string, ids: string[]): Promise<ROW[]>;
17
18
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number>;
18
19
  updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>, opt?: CommonDBOptions): Promise<number>;
19
20
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<RunQueryResult<ROW>>;
20
21
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number>;
21
22
  saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
22
23
  streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): ReadableTyped<ROW>;
23
- /**
24
- * Naive implementation.
25
- * Doesn't support rollback on error, hence doesn't pass dbTest.
26
- * To be extended.
27
- */
28
- commitTransaction(tx: DBTransaction, opt?: CommonDBOptions): Promise<void>;
24
+ deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
25
+ createTransaction(): Promise<DBTransaction>;
29
26
  }
@@ -1,12 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseCommonDB = void 0;
4
+ const common_db_1 = require("./common.db");
5
+ const dbTransaction_util_1 = require("./transaction/dbTransaction.util");
4
6
  /* eslint-disable unused-imports/no-unused-vars */
5
7
  /**
6
8
  * No-op implementation of CommonDB interface.
7
9
  * To be extended by actual implementations.
8
10
  */
9
11
  class BaseCommonDB {
12
+ constructor() {
13
+ this.dbType = common_db_1.CommonDBType.document;
14
+ this.support = {};
15
+ }
10
16
  async ping() {
11
17
  throw new Error('ping is not implemented');
12
18
  }
@@ -40,13 +46,11 @@ class BaseCommonDB {
40
46
  streamQuery(q) {
41
47
  throw new Error('streamQuery is not implemented');
42
48
  }
43
- /**
44
- * Naive implementation.
45
- * Doesn't support rollback on error, hence doesn't pass dbTest.
46
- * To be extended.
47
- */
48
- async commitTransaction(tx, opt) {
49
- throw new Error('commitTransaction is not implemented');
49
+ async deleteByIds(table, ids, opt) {
50
+ throw new Error('deleteByIds is not implemented');
51
+ }
52
+ async createTransaction() {
53
+ return new dbTransaction_util_1.FakeDBTransaction(this);
50
54
  }
51
55
  }
52
56
  exports.BaseCommonDB = BaseCommonDB;