ueberdb2 5.0.42 → 5.0.44
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/{lib/AbstractDatabase.js → AbstractDatabase-a4jdIEuT.js} +7 -2
- package/dist/{databases/cassandra_db.js → cassandra_db-C5K2zZew.js} +3 -3
- package/dist/{_virtual/_rolldown/runtime.js → chunk-8l464Juk.js} +6 -1
- package/dist/{databases/couch_db.js → couch_db-BVd81sS7.js} +5 -5
- package/dist/{databases/dirty_db.js → dirty_db-C9zUhXIG.js} +4 -4
- package/dist/{databases/dirty_git_db.js → dirty_git_db-Cj3oEEyZ.js} +4 -4
- package/dist/{databases/elasticsearch_db.js → elasticsearch_db-DjnZ1DHG.js} +4 -4
- package/dist/index.js +502 -22
- package/dist/{lib/logging.js → logging-DmqnZRde.js} +6 -1
- package/dist/{databases/memory_db.js → memory_db-SlQsh4tk.js} +2 -2
- package/dist/{databases/mock_db.js → mock_db-BBijMErd.js} +2 -2
- package/dist/{databases/mongodb_db.js → mongodb_db-ByZscnbs.js} +3 -3
- package/dist/{databases/mssql_db.js → mssql_db-C9-gmVgP.js} +5 -5
- package/dist/{databases/mysql_db.js → mysql_db-CRYtP-Mk.js} +4 -4
- package/dist/{databases/postgres_db.js → postgres_db-CVDQiNwN.js} +5 -5
- package/dist/{databases/postgrespool_db.js → postgrespool_db-CynysVC1.js} +1 -1
- package/dist/{databases/redis_db.js → redis_db-CDma40om.js} +3 -3
- package/dist/{databases/rethink_db.js → rethink_db-D7NvPe_4.js} +5 -5
- package/dist/{databases/rusty_db.js → rusty_db-CaVGu0z7.js} +3 -3
- package/dist/{databases/sqlite_db.js → sqlite_db-B-nmfVje.js} +3 -4
- package/dist/{databases/surrealdb_db.js → surrealdb_db-JmcyCabz.js} +3 -3
- package/package.json +7 -8
- package/dist/lib/CacheAndBufferLayer.js +0 -484
|
@@ -1,484 +0,0 @@
|
|
|
1
|
-
const require_runtime = require("../_virtual/_rolldown/runtime.js");
|
|
2
|
-
let util = require("util");
|
|
3
|
-
util = require_runtime.__toESM(util);
|
|
4
|
-
//#region lib/CacheAndBufferLayer.ts
|
|
5
|
-
/**
|
|
6
|
-
* 2011 Peter 'Pita' Martischka
|
|
7
|
-
*
|
|
8
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
-
* you may not use this file except in compliance with the License.
|
|
10
|
-
* You may obtain a copy of the License at
|
|
11
|
-
*
|
|
12
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
-
*
|
|
14
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
15
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
16
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
-
* See the License for the specific language governing permissions and
|
|
18
|
-
* limitations under the License.
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* This module is made for the case, you want to use a SQL-Based Databse or a KeyValue Database that
|
|
22
|
-
* can only save strings(and no objects), as a JSON KeyValue Store.
|
|
23
|
-
*
|
|
24
|
-
* The idea of the dbWrapper is to provide following features:
|
|
25
|
-
*
|
|
26
|
-
* * automatic JSON serialize/deserialize to abstract that away from the database driver and the
|
|
27
|
-
* module user.
|
|
28
|
-
* * cache reads. A amount of KeyValues are hold in the memory, so that reading is faster.
|
|
29
|
-
* * Buffer DB Writings. Sets and deletes should be buffered to make them in a setted interval
|
|
30
|
-
* with a bulk. This reduces the overhead of database transactions and makes the database
|
|
31
|
-
* faster. But there is also a danger to loose data integrity, to keep that, we should provide a
|
|
32
|
-
* flush function.
|
|
33
|
-
*
|
|
34
|
-
* All Features can be disabled or configured. The Wrapper provides default settings that can be
|
|
35
|
-
* overwriden by the driver and by the module user.
|
|
36
|
-
*/
|
|
37
|
-
/**
|
|
38
|
-
* Cache with Least Recently Used eviction policy.
|
|
39
|
-
*/
|
|
40
|
-
var LRU = class {
|
|
41
|
-
/**
|
|
42
|
-
* @param evictable Optional predicate that dictates whether it is permissable to evict the entry
|
|
43
|
-
* if it is old and the cache is over capacity. The predicate is passed two arguments (key,
|
|
44
|
-
* value). If no predicate is provided, all entries are evictable. Warning: Non-evictable
|
|
45
|
-
* entries can cause the cache to go over capacity. If the number of non-evictable entries is
|
|
46
|
-
* greater than or equal to the capacity, all new evictable entries will be evicted
|
|
47
|
-
* immediately.
|
|
48
|
-
*/
|
|
49
|
-
constructor(capacity, evictable = (k, v) => true) {
|
|
50
|
-
this._capacity = capacity;
|
|
51
|
-
this._evictable = evictable;
|
|
52
|
-
this._cache = /* @__PURE__ */ new Map();
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* The entries accessed via this iterator are not considered to have been "used" (for purposes of
|
|
56
|
-
* determining least recently used).
|
|
57
|
-
*/
|
|
58
|
-
[Symbol.iterator]() {
|
|
59
|
-
return this._cache.entries();
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* @param isUse Optional boolean indicating whether this get() should be considered a "use" of the
|
|
63
|
-
* entry (for determining least recently used). Defaults to true.
|
|
64
|
-
* @returns undefined if there is no entry matching the given key.
|
|
65
|
-
*/
|
|
66
|
-
get(k, isUse = true) {
|
|
67
|
-
if (!this._cache.has(k)) return;
|
|
68
|
-
const v = this._cache.get(k);
|
|
69
|
-
if (isUse) {
|
|
70
|
-
this._cache.delete(k);
|
|
71
|
-
this._cache.set(k, v);
|
|
72
|
-
}
|
|
73
|
-
return v;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Adds or updates an entry in the cache. This marks the entry as the most recently used entry.
|
|
77
|
-
*/
|
|
78
|
-
set(k, v) {
|
|
79
|
-
this._cache.delete(k);
|
|
80
|
-
this._cache.set(k, v);
|
|
81
|
-
this.evictOld();
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Evicts the oldest evictable entries until the number of entries is equal to or less than the
|
|
85
|
-
* cache's capacity. This method is automatically called by set(). Call this if you need to evict
|
|
86
|
-
* newly evictable entries before the next call to set().
|
|
87
|
-
*/
|
|
88
|
-
evictOld() {
|
|
89
|
-
for (const [k, v] of this._cache.entries()) {
|
|
90
|
-
if (this._cache.size <= this._capacity) break;
|
|
91
|
-
if (!this._evictable(k, v)) continue;
|
|
92
|
-
this._cache.delete(k);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
var SelfContainedPromise = class extends Promise {
|
|
97
|
-
constructor(executor = null) {
|
|
98
|
-
let done;
|
|
99
|
-
super((resolve, reject) => {
|
|
100
|
-
done = (err, val) => err != null ? reject(err) : resolve(val);
|
|
101
|
-
if (executor != null) executor(resolve, reject);
|
|
102
|
-
});
|
|
103
|
-
this.done = done;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
const defaultSettings = {
|
|
107
|
-
bulkLimit: 0,
|
|
108
|
-
cache: 1e4,
|
|
109
|
-
writeInterval: 100,
|
|
110
|
-
json: true,
|
|
111
|
-
charset: "utf8mb4"
|
|
112
|
-
};
|
|
113
|
-
const Database = class {
|
|
114
|
-
/**
|
|
115
|
-
* @param wrappedDB The Database that should be wrapped
|
|
116
|
-
* @param settings (optional) The settings that should be applied to the wrapper
|
|
117
|
-
*/
|
|
118
|
-
constructor(wrappedDB, settings, logger) {
|
|
119
|
-
if (wrappedDB.isAsync) this.wrappedDB = wrappedDB;
|
|
120
|
-
else {
|
|
121
|
-
this.wrappedDB = {};
|
|
122
|
-
for (const fn of [
|
|
123
|
-
"close",
|
|
124
|
-
"doBulk",
|
|
125
|
-
"findKeys",
|
|
126
|
-
"get",
|
|
127
|
-
"init",
|
|
128
|
-
"remove",
|
|
129
|
-
"set"
|
|
130
|
-
]) {
|
|
131
|
-
const f = wrappedDB[fn];
|
|
132
|
-
if (typeof f !== "function") continue;
|
|
133
|
-
this.wrappedDB[fn] = util.default.promisify(f.bind(wrappedDB));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
this.logger = logger;
|
|
137
|
-
this.settings = Object.freeze({
|
|
138
|
-
...defaultSettings,
|
|
139
|
-
...wrappedDB.settings || {},
|
|
140
|
-
...settings || {}
|
|
141
|
-
});
|
|
142
|
-
this.buffer = new LRU(this.settings.cache, (k, v) => !v.dirty && !v.writingInProgress);
|
|
143
|
-
this._flushPaused = null;
|
|
144
|
-
this._locks = /* @__PURE__ */ new Map();
|
|
145
|
-
this.metrics = {
|
|
146
|
-
lockAwaits: 0,
|
|
147
|
-
lockAcquires: 0,
|
|
148
|
-
lockReleases: 0,
|
|
149
|
-
reads: 0,
|
|
150
|
-
readsFailed: 0,
|
|
151
|
-
readsFinished: 0,
|
|
152
|
-
readsFromCache: 0,
|
|
153
|
-
readsFromDb: 0,
|
|
154
|
-
readsFromDbFailed: 0,
|
|
155
|
-
readsFromDbFinished: 0,
|
|
156
|
-
writes: 0,
|
|
157
|
-
writesFailed: 0,
|
|
158
|
-
writesFinished: 0,
|
|
159
|
-
writesObsoleted: 0,
|
|
160
|
-
writesToDb: 0,
|
|
161
|
-
writesToDbFailed: 0,
|
|
162
|
-
writesToDbFinished: 0,
|
|
163
|
-
writesToDbRetried: 0
|
|
164
|
-
};
|
|
165
|
-
this.flushInterval = this.settings.writeInterval > 0 ? setInterval(() => this.flush(), this.settings.writeInterval) : null;
|
|
166
|
-
}
|
|
167
|
-
async _lock(key) {
|
|
168
|
-
while (true) {
|
|
169
|
-
const l = this._locks.get(key);
|
|
170
|
-
if (l == null) break;
|
|
171
|
-
++this.metrics.lockAwaits;
|
|
172
|
-
await l;
|
|
173
|
-
}
|
|
174
|
-
++this.metrics.lockAcquires;
|
|
175
|
-
this._locks.set(key, new SelfContainedPromise());
|
|
176
|
-
}
|
|
177
|
-
async _unlock(key) {
|
|
178
|
-
++this.metrics.lockReleases;
|
|
179
|
-
this._locks.get(key).done();
|
|
180
|
-
this._locks.delete(key);
|
|
181
|
-
}
|
|
182
|
-
_pauseFlush() {
|
|
183
|
-
if (this._flushPaused == null) {
|
|
184
|
-
this._flushPaused = new SelfContainedPromise();
|
|
185
|
-
this._flushPaused.count = 0;
|
|
186
|
-
}
|
|
187
|
-
++this._flushPaused.count;
|
|
188
|
-
}
|
|
189
|
-
_resumeFlush() {
|
|
190
|
-
if (--this._flushPaused.count > 0) return;
|
|
191
|
-
this._flushPaused.done();
|
|
192
|
-
this._flushPaused = null;
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* wraps the init function of the original DB
|
|
196
|
-
*/
|
|
197
|
-
async init() {
|
|
198
|
-
await this.wrappedDB.init();
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* wraps the close function of the original DB
|
|
202
|
-
*/
|
|
203
|
-
async close() {
|
|
204
|
-
clearInterval(this.flushInterval);
|
|
205
|
-
await this.flush();
|
|
206
|
-
await this.wrappedDB.close();
|
|
207
|
-
this.wrappedDB = null;
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Gets the value trough the wrapper.
|
|
211
|
-
*/
|
|
212
|
-
async get(key) {
|
|
213
|
-
let v;
|
|
214
|
-
await this._lock(key);
|
|
215
|
-
try {
|
|
216
|
-
v = await this._getLocked(key);
|
|
217
|
-
} finally {
|
|
218
|
-
this._unlock(key);
|
|
219
|
-
}
|
|
220
|
-
return clone(v);
|
|
221
|
-
}
|
|
222
|
-
async _getLocked(key) {
|
|
223
|
-
++this.metrics.reads;
|
|
224
|
-
try {
|
|
225
|
-
const entry = this.buffer.get(key);
|
|
226
|
-
if (entry != null) {
|
|
227
|
-
++this.metrics.readsFromCache;
|
|
228
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key} - ${JSON.stringify(entry.value)} - from ${entry.dirty ? "dirty buffer" : "cache"}`);
|
|
229
|
-
return entry.value;
|
|
230
|
-
}
|
|
231
|
-
let value;
|
|
232
|
-
++this.metrics.readsFromDb;
|
|
233
|
-
try {
|
|
234
|
-
value = await this.wrappedDB.get(key);
|
|
235
|
-
} catch (err) {
|
|
236
|
-
++this.metrics.readsFromDbFailed;
|
|
237
|
-
throw err;
|
|
238
|
-
} finally {
|
|
239
|
-
++this.metrics.readsFromDbFinished;
|
|
240
|
-
}
|
|
241
|
-
if (this.settings.json) try {
|
|
242
|
-
value = JSON.parse(value);
|
|
243
|
-
} catch (err) {
|
|
244
|
-
this.logger.error(`JSON-PROBLEM:${value}`);
|
|
245
|
-
throw err;
|
|
246
|
-
}
|
|
247
|
-
if (this.settings.cache > 0) this.buffer.set(key, {
|
|
248
|
-
value,
|
|
249
|
-
dirty: null,
|
|
250
|
-
writingInProgress: false
|
|
251
|
-
});
|
|
252
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key} - ${JSON.stringify(value)} - from database `);
|
|
253
|
-
return value;
|
|
254
|
-
} catch (err) {
|
|
255
|
-
++this.metrics.readsFailed;
|
|
256
|
-
throw err;
|
|
257
|
-
} finally {
|
|
258
|
-
++this.metrics.readsFinished;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Find keys function searches the db sets for matching entries and
|
|
263
|
-
* returns the key entries via callback.
|
|
264
|
-
*/
|
|
265
|
-
async findKeys(key, notKey) {
|
|
266
|
-
await this.flush();
|
|
267
|
-
const keyValues = await this.wrappedDB.findKeys(key, notKey);
|
|
268
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key}-${notKey} - ${JSON.stringify(keyValues)} - from database `);
|
|
269
|
-
return clone(keyValues);
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Remove a record from the database
|
|
273
|
-
*/
|
|
274
|
-
async remove(key) {
|
|
275
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`DELETE - ${key} - from database `);
|
|
276
|
-
await this.set(key, null);
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Sets the value trough the wrapper
|
|
280
|
-
*/
|
|
281
|
-
async set(key, value) {
|
|
282
|
-
value = clone(value);
|
|
283
|
-
let p;
|
|
284
|
-
this._pauseFlush();
|
|
285
|
-
try {
|
|
286
|
-
await this._lock(key);
|
|
287
|
-
try {
|
|
288
|
-
p = this._setLocked(key, value);
|
|
289
|
-
} finally {
|
|
290
|
-
this._unlock(key);
|
|
291
|
-
}
|
|
292
|
-
} finally {
|
|
293
|
-
this._resumeFlush();
|
|
294
|
-
}
|
|
295
|
-
await p;
|
|
296
|
-
}
|
|
297
|
-
async _setLocked(key, value) {
|
|
298
|
-
++this.metrics.writes;
|
|
299
|
-
try {
|
|
300
|
-
let entry = this.buffer.get(key);
|
|
301
|
-
if (!entry || entry.writingInProgress) entry = {};
|
|
302
|
-
else if (entry.dirty) ++this.metrics.writesObsoleted;
|
|
303
|
-
entry.value = value;
|
|
304
|
-
if (!entry.dirty) entry.dirty = new SelfContainedPromise();
|
|
305
|
-
this.buffer.set(key, entry);
|
|
306
|
-
const buffered = this.settings.writeInterval > 0;
|
|
307
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`SET - ${key} - ${JSON.stringify(value)} - to ${buffered ? "buffer" : "database"}`);
|
|
308
|
-
if (!buffered) this._write([[key, entry]]);
|
|
309
|
-
await entry.dirty;
|
|
310
|
-
} catch (err) {
|
|
311
|
-
++this.metrics.writesFailed;
|
|
312
|
-
throw err;
|
|
313
|
-
} finally {
|
|
314
|
-
++this.metrics.writesFinished;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Sets a subvalue
|
|
319
|
-
*/
|
|
320
|
-
async setSub(key, sub, value) {
|
|
321
|
-
value = clone(value);
|
|
322
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`SETSUB - ${key}${JSON.stringify(sub)} - ${JSON.stringify(value)}`);
|
|
323
|
-
let p;
|
|
324
|
-
this._pauseFlush();
|
|
325
|
-
try {
|
|
326
|
-
await this._lock(key);
|
|
327
|
-
try {
|
|
328
|
-
let base;
|
|
329
|
-
try {
|
|
330
|
-
const fullValue = await this._getLocked(key);
|
|
331
|
-
base = { fullValue };
|
|
332
|
-
const ptr = {
|
|
333
|
-
obj: base,
|
|
334
|
-
prop: "fullValue"
|
|
335
|
-
};
|
|
336
|
-
for (let i = 0; i < sub.length; i++) {
|
|
337
|
-
if (sub[i] === "__proto__") throw new Error("Modifying object prototype is not supported for security reasons");
|
|
338
|
-
let o = ptr.obj[ptr.prop];
|
|
339
|
-
if (o == null) ptr.obj[ptr.prop] = o = {};
|
|
340
|
-
if (typeof o !== "object") throw new TypeError(`Cannot set property ${JSON.stringify(sub[i])} on non-object ${JSON.stringify(o)} (key: ${JSON.stringify(key)} value in db: ${JSON.stringify(fullValue)} sub: ${JSON.stringify(sub.slice(0, i + 1))})`);
|
|
341
|
-
ptr.obj = ptr.obj[ptr.prop];
|
|
342
|
-
ptr.prop = sub[i];
|
|
343
|
-
}
|
|
344
|
-
if (value == null) delete ptr.obj[ptr.prop];
|
|
345
|
-
else ptr.obj[ptr.prop] = value;
|
|
346
|
-
} catch (err) {
|
|
347
|
-
++this.metrics.writes;
|
|
348
|
-
++this.metrics.writesFailed;
|
|
349
|
-
++this.metrics.writesFinished;
|
|
350
|
-
throw err;
|
|
351
|
-
}
|
|
352
|
-
p = this._setLocked(key, base.fullValue);
|
|
353
|
-
} finally {
|
|
354
|
-
await this._unlock(key);
|
|
355
|
-
}
|
|
356
|
-
} finally {
|
|
357
|
-
this._resumeFlush();
|
|
358
|
-
}
|
|
359
|
-
await p;
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Returns a sub value of the object
|
|
363
|
-
* @param sub is a array, for example if you want to access object.test.bla, the array is ["test",
|
|
364
|
-
* "bla"]
|
|
365
|
-
*/
|
|
366
|
-
async getSub(key, sub) {
|
|
367
|
-
await this._lock(key);
|
|
368
|
-
try {
|
|
369
|
-
let v = await this._getLocked(key);
|
|
370
|
-
for (const k of sub) {
|
|
371
|
-
if (typeof v !== "object" || v != null && !Object.prototype.hasOwnProperty.call(v, k) || k === "__proto__") v = null;
|
|
372
|
-
if (v == null) break;
|
|
373
|
-
v = v[k];
|
|
374
|
-
}
|
|
375
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`GETSUB - ${key}${JSON.stringify(sub)} - ${JSON.stringify(v)}`);
|
|
376
|
-
return clone(v);
|
|
377
|
-
} finally {
|
|
378
|
-
this._unlock(key);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Writes all dirty values to the database
|
|
383
|
-
*/
|
|
384
|
-
async flush() {
|
|
385
|
-
if (this._flushDone == null) this._flushDone = (async () => {
|
|
386
|
-
while (true) {
|
|
387
|
-
while (this._flushPaused != null) await this._flushPaused;
|
|
388
|
-
const dirtyEntries = [];
|
|
389
|
-
for (const entry of this.buffer) if (entry[1].dirty && !entry[1].writingInProgress) {
|
|
390
|
-
dirtyEntries.push(entry);
|
|
391
|
-
if (this.settings.bulkLimit && dirtyEntries.length >= this.settings.bulkLimit) break;
|
|
392
|
-
}
|
|
393
|
-
if (dirtyEntries.length === 0) return;
|
|
394
|
-
await this._write(dirtyEntries);
|
|
395
|
-
}
|
|
396
|
-
})();
|
|
397
|
-
await this._flushDone;
|
|
398
|
-
this._flushDone = null;
|
|
399
|
-
}
|
|
400
|
-
async _write(dirtyEntries) {
|
|
401
|
-
const markDone = (entry, err) => {
|
|
402
|
-
if (entry.writingInProgress) {
|
|
403
|
-
entry.writingInProgress = false;
|
|
404
|
-
if (err != null) ++this.metrics.writesToDbFailed;
|
|
405
|
-
++this.metrics.writesToDbFinished;
|
|
406
|
-
}
|
|
407
|
-
entry.dirty.done(err);
|
|
408
|
-
entry.dirty = null;
|
|
409
|
-
};
|
|
410
|
-
const ops = [];
|
|
411
|
-
const entries = [];
|
|
412
|
-
for (const [key, entry] of dirtyEntries) {
|
|
413
|
-
let value = entry.value;
|
|
414
|
-
try {
|
|
415
|
-
value = this.settings.json && value != null ? JSON.stringify(value) : clone(value);
|
|
416
|
-
} catch (err) {
|
|
417
|
-
markDone(entry, err);
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
entry.writingInProgress = true;
|
|
421
|
-
ops.push({
|
|
422
|
-
type: value == null ? "remove" : "set",
|
|
423
|
-
key,
|
|
424
|
-
value
|
|
425
|
-
});
|
|
426
|
-
entries.push(entry);
|
|
427
|
-
}
|
|
428
|
-
if (ops.length === 0) return;
|
|
429
|
-
this.metrics.writesToDb += ops.length;
|
|
430
|
-
const writeOneOp = async (op, entry) => {
|
|
431
|
-
let writeErr = null;
|
|
432
|
-
try {
|
|
433
|
-
switch (op.type) {
|
|
434
|
-
case "remove":
|
|
435
|
-
await this.wrappedDB.remove(op.key);
|
|
436
|
-
break;
|
|
437
|
-
case "set":
|
|
438
|
-
await this.wrappedDB.set(op.key, op.value);
|
|
439
|
-
break;
|
|
440
|
-
default: throw new Error(`unsupported operation type: ${op.type}`);
|
|
441
|
-
}
|
|
442
|
-
} catch (err) {
|
|
443
|
-
writeErr = err || new Error(err);
|
|
444
|
-
}
|
|
445
|
-
markDone(entry, writeErr);
|
|
446
|
-
};
|
|
447
|
-
if (ops.length === 1) await writeOneOp(ops[0], entries[0]);
|
|
448
|
-
else {
|
|
449
|
-
let success = false;
|
|
450
|
-
try {
|
|
451
|
-
await this.wrappedDB.doBulk(ops);
|
|
452
|
-
success = true;
|
|
453
|
-
} catch (err) {
|
|
454
|
-
this.logger.error(`Bulk write of ${ops.length} ops failed, retrying individually: ${err.stack || err}`);
|
|
455
|
-
this.metrics.writesToDbRetried += ops.length;
|
|
456
|
-
await Promise.all(ops.map(async (op, i) => await writeOneOp(op, entries[i])));
|
|
457
|
-
}
|
|
458
|
-
if (success) entries.forEach((entry) => markDone(entry, null));
|
|
459
|
-
}
|
|
460
|
-
this.buffer.evictOld();
|
|
461
|
-
}
|
|
462
|
-
};
|
|
463
|
-
const clone = (obj, key = "") => {
|
|
464
|
-
if (null == obj || "object" !== typeof obj) return obj;
|
|
465
|
-
if (typeof obj.toJSON === "function") return clone(obj.toJSON(key));
|
|
466
|
-
if (obj instanceof Date) {
|
|
467
|
-
const copy = /* @__PURE__ */ new Date();
|
|
468
|
-
copy.setTime(obj.getTime());
|
|
469
|
-
return copy;
|
|
470
|
-
}
|
|
471
|
-
if (obj instanceof Array) {
|
|
472
|
-
const copy = [];
|
|
473
|
-
for (let i = 0, len = obj.length; i < len; ++i) copy[i] = clone(obj[i], String(i));
|
|
474
|
-
return copy;
|
|
475
|
-
}
|
|
476
|
-
if (obj instanceof Object) {
|
|
477
|
-
const copy = {};
|
|
478
|
-
for (const attr in obj) if (Object.prototype.hasOwnProperty.call(obj, attr)) copy[attr] = clone(obj[attr], attr);
|
|
479
|
-
return copy;
|
|
480
|
-
}
|
|
481
|
-
throw new Error("Unable to copy obj! Its type isn't supported.");
|
|
482
|
-
};
|
|
483
|
-
//#endregion
|
|
484
|
-
exports.Database = Database;
|