@abtnode/db-cache 1.16.47-beta-20250730-070104-87a128a5 → 1.16.47-beta-20250731-014139-e860268f
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/index.cjs +120 -155
- package/dist/index.d.cts +15 -1
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.mjs +120 -156
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -166,48 +166,94 @@ __publicField$4(_RedisAdapter, "clients", /* @__PURE__ */ new Map());
|
|
|
166
166
|
__publicField$4(_RedisAdapter, "initPromises", /* @__PURE__ */ new Map());
|
|
167
167
|
let RedisAdapter = _RedisAdapter;
|
|
168
168
|
|
|
169
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
170
|
+
async function withRetry(fn, {
|
|
171
|
+
max = 15,
|
|
172
|
+
backoffBase = 200,
|
|
173
|
+
backoffExponent = 1.1,
|
|
174
|
+
backoffJitter = 100,
|
|
175
|
+
needRetry = () => true
|
|
176
|
+
// eslint-disable-next-line @typescript-eslint/comma-dangle
|
|
177
|
+
} = {}) {
|
|
178
|
+
let attempt = 0;
|
|
179
|
+
while (true) {
|
|
180
|
+
try {
|
|
181
|
+
return await fn();
|
|
182
|
+
} catch (err) {
|
|
183
|
+
if (!needRetry(err) || ++attempt > max) {
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
const exp = backoffExponent ** (attempt - 1);
|
|
187
|
+
const expDelay = backoffBase * exp;
|
|
188
|
+
const jitter = Math.random() * backoffJitter;
|
|
189
|
+
const waitTime = Math.floor(expDelay + jitter);
|
|
190
|
+
await sleep(waitTime);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
169
195
|
var __defProp$3 = Object.defineProperty;
|
|
170
196
|
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
171
197
|
var __publicField$3 = (obj, key, value) => {
|
|
172
198
|
__defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
173
199
|
return value;
|
|
174
200
|
};
|
|
175
|
-
const
|
|
201
|
+
const retryOptions = {
|
|
202
|
+
needRetry: (err) => err.message?.includes("SQLITE_BUSY")
|
|
203
|
+
};
|
|
204
|
+
const dbRun = (db, sql, params) => {
|
|
205
|
+
return withRetry(() => {
|
|
206
|
+
return new Promise((res, rej) => {
|
|
207
|
+
db.run(sql, params, (err) => err ? rej(err) : res());
|
|
208
|
+
});
|
|
209
|
+
}, retryOptions);
|
|
210
|
+
};
|
|
211
|
+
const dbExec = (db, sql) => {
|
|
212
|
+
return withRetry(() => {
|
|
213
|
+
return new Promise((res, rej) => {
|
|
214
|
+
db.exec(sql, (err) => err ? rej(err) : res());
|
|
215
|
+
});
|
|
216
|
+
}, retryOptions);
|
|
217
|
+
};
|
|
218
|
+
const dbGet = (db, sql, params) => {
|
|
219
|
+
return withRetry(() => {
|
|
220
|
+
return new Promise((res, rej) => {
|
|
221
|
+
db.get(sql, params, (err, result) => err ? rej(err) : res(result));
|
|
222
|
+
});
|
|
223
|
+
}, retryOptions);
|
|
224
|
+
};
|
|
176
225
|
async function initSqliteWithRetry(db) {
|
|
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
|
-
}
|
|
226
|
+
db.configure("busyTimeout", 5e3);
|
|
227
|
+
try {
|
|
228
|
+
await dbExec(
|
|
229
|
+
db,
|
|
230
|
+
`
|
|
231
|
+
PRAGMA journal_mode = WAL;
|
|
232
|
+
PRAGMA synchronous = normal;
|
|
233
|
+
PRAGMA wal_autocheckpoint = 5000;
|
|
234
|
+
PRAGMA busy_timeout = 5000;
|
|
235
|
+
`
|
|
236
|
+
);
|
|
237
|
+
await dbRun(
|
|
238
|
+
db,
|
|
239
|
+
`
|
|
240
|
+
CREATE TABLE IF NOT EXISTS kvcache (
|
|
241
|
+
key TEXT NOT NULL,
|
|
242
|
+
subKey TEXT NOT NULL,
|
|
243
|
+
value TEXT NOT NULL,
|
|
244
|
+
expiresAt INTEGER,
|
|
245
|
+
PRIMARY KEY (key, subKey)
|
|
246
|
+
)
|
|
247
|
+
`
|
|
248
|
+
);
|
|
249
|
+
} catch (err) {
|
|
250
|
+
throw new Error(`SQLite init failed: ${err}`);
|
|
203
251
|
}
|
|
204
252
|
}
|
|
205
253
|
const _SqliteAdapter = class _SqliteAdapter {
|
|
206
254
|
constructor() {
|
|
207
255
|
__publicField$3(this, "opts", null);
|
|
208
256
|
__publicField$3(this, "prefix", "");
|
|
209
|
-
__publicField$3(this, "sqliteBusyRetryCount", 0);
|
|
210
|
-
__publicField$3(this, "sqliteBusyRetryGroupCount", 0);
|
|
211
257
|
__publicField$3(this, "defaultTtl", 1e3 * 60 * 60);
|
|
212
258
|
__publicField$3(this, "cleanupInterval", 5 * 60 * 1e3);
|
|
213
259
|
__publicField$3(this, "dbPath", "");
|
|
@@ -235,28 +281,6 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
235
281
|
}
|
|
236
282
|
const db = new sqlite3__default.Database(this.dbPath);
|
|
237
283
|
await initSqliteWithRetry(db);
|
|
238
|
-
await new Promise(
|
|
239
|
-
(res, rej) => db.run(
|
|
240
|
-
`
|
|
241
|
-
CREATE TABLE IF NOT EXISTS kvcache (
|
|
242
|
-
key TEXT NOT NULL,
|
|
243
|
-
subKey TEXT NOT NULL,
|
|
244
|
-
value TEXT NOT NULL,
|
|
245
|
-
expiresAt INTEGER,
|
|
246
|
-
PRIMARY KEY (key, subKey)
|
|
247
|
-
)
|
|
248
|
-
`,
|
|
249
|
-
(err) => err ? rej(err) : res()
|
|
250
|
-
)
|
|
251
|
-
);
|
|
252
|
-
await new Promise(
|
|
253
|
-
(res, rej) => db.run(
|
|
254
|
-
`
|
|
255
|
-
PRAGMA shrink_memory;
|
|
256
|
-
`,
|
|
257
|
-
(err) => err ? rej(err) : res()
|
|
258
|
-
)
|
|
259
|
-
);
|
|
260
284
|
_SqliteAdapter.clients.set(this.dbPath, db);
|
|
261
285
|
_SqliteAdapter.cleanupTimers.set(
|
|
262
286
|
this.dbPath,
|
|
@@ -315,51 +339,28 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
315
339
|
if (exists)
|
|
316
340
|
return false;
|
|
317
341
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
(err) => err ? rej(err) : res()
|
|
330
|
-
);
|
|
331
|
-
});
|
|
332
|
-
} catch (error) {
|
|
333
|
-
if (error.message?.includes("SQLITE_BUSY")) {
|
|
334
|
-
if (this.sqliteBusyRetryCount > 10) {
|
|
335
|
-
this.sqliteBusyRetryCount = 0;
|
|
336
|
-
throw error;
|
|
337
|
-
}
|
|
338
|
-
this.sqliteBusyRetryCount++;
|
|
339
|
-
await sleep(2e3);
|
|
340
|
-
const out = await this.set(key, value, opts);
|
|
341
|
-
this.sqliteBusyRetryCount = 0;
|
|
342
|
-
return out;
|
|
343
|
-
}
|
|
344
|
-
throw error;
|
|
345
|
-
}
|
|
342
|
+
await dbRun(
|
|
343
|
+
client,
|
|
344
|
+
`
|
|
345
|
+
INSERT INTO kvcache (key, subKey, value, expiresAt)
|
|
346
|
+
VALUES (?, ?, ?, ?)
|
|
347
|
+
ON CONFLICT(key, subKey) DO UPDATE SET
|
|
348
|
+
value = excluded.value,
|
|
349
|
+
expiresAt = excluded.expiresAt
|
|
350
|
+
`,
|
|
351
|
+
[storageKey, subKey, this.serialize(value), expiresAt]
|
|
352
|
+
);
|
|
346
353
|
return true;
|
|
347
354
|
}
|
|
348
355
|
async get(key) {
|
|
349
356
|
const client = this.getClient();
|
|
350
357
|
const storageKey = this.prefixKey(key);
|
|
351
358
|
const subKey = "";
|
|
352
|
-
const row = await
|
|
353
|
-
client
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if (err)
|
|
358
|
-
return rej(err);
|
|
359
|
-
return res(result);
|
|
360
|
-
}
|
|
361
|
-
);
|
|
362
|
-
});
|
|
359
|
+
const row = await dbGet(
|
|
360
|
+
client,
|
|
361
|
+
'SELECT value, "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
362
|
+
[storageKey, subKey]
|
|
363
|
+
);
|
|
363
364
|
if (!row)
|
|
364
365
|
return null;
|
|
365
366
|
if (row.expiresAt !== null && Date.now() > row.expiresAt) {
|
|
@@ -371,22 +372,17 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
371
372
|
async del(key) {
|
|
372
373
|
const client = this.getClient();
|
|
373
374
|
const storageKey = this.prefixKey(key);
|
|
374
|
-
await
|
|
375
|
-
client.run("DELETE FROM kvcache WHERE key = ?", [storageKey], (err) => err ? rej(err) : res());
|
|
376
|
-
});
|
|
375
|
+
await dbRun(client, "DELETE FROM kvcache WHERE key = ?", [storageKey]);
|
|
377
376
|
}
|
|
378
377
|
async has(key) {
|
|
379
378
|
const client = this.getClient();
|
|
380
379
|
const storageKey = this.prefixKey(key);
|
|
381
380
|
const subKey = "";
|
|
382
|
-
const row = await
|
|
383
|
-
client
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
(err, result) => err ? rej(err) : res(result)
|
|
388
|
-
);
|
|
389
|
-
});
|
|
381
|
+
const row = await dbGet(
|
|
382
|
+
client,
|
|
383
|
+
'SELECT "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
384
|
+
[storageKey, subKey]
|
|
385
|
+
);
|
|
390
386
|
if (!row)
|
|
391
387
|
return false;
|
|
392
388
|
if (row.expiresAt !== null && Date.now() > row.expiresAt) {
|
|
@@ -405,49 +401,26 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
405
401
|
if (exists)
|
|
406
402
|
return;
|
|
407
403
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
(err) => err ? rej(err) : res()
|
|
420
|
-
);
|
|
421
|
-
});
|
|
422
|
-
} catch (error) {
|
|
423
|
-
if (error.message?.includes("SQLITE_BUSY")) {
|
|
424
|
-
if (this.sqliteBusyRetryGroupCount > 10) {
|
|
425
|
-
this.sqliteBusyRetryGroupCount = 0;
|
|
426
|
-
throw error;
|
|
427
|
-
}
|
|
428
|
-
this.sqliteBusyRetryGroupCount++;
|
|
429
|
-
await sleep(2e3);
|
|
430
|
-
await this.groupSet(key, subKey, value, opts);
|
|
431
|
-
this.sqliteBusyRetryGroupCount = 0;
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
throw error;
|
|
435
|
-
}
|
|
404
|
+
await dbRun(
|
|
405
|
+
client,
|
|
406
|
+
`
|
|
407
|
+
INSERT INTO kvcache (key, subKey, value, expiresAt)
|
|
408
|
+
VALUES (?, ?, ?, ?)
|
|
409
|
+
ON CONFLICT(key, subKey) DO UPDATE SET
|
|
410
|
+
value = excluded.value,
|
|
411
|
+
expiresAt = excluded.expiresAt
|
|
412
|
+
`,
|
|
413
|
+
[storageKey, subKey, this.serialize(value), expiresAt]
|
|
414
|
+
);
|
|
436
415
|
}
|
|
437
416
|
async groupGet(key, subKey) {
|
|
438
417
|
const client = this.getClient();
|
|
439
418
|
const storageKey = this.prefixKey(key);
|
|
440
|
-
const row = await
|
|
441
|
-
client
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (err)
|
|
446
|
-
return rej(err);
|
|
447
|
-
return res(result);
|
|
448
|
-
}
|
|
449
|
-
);
|
|
450
|
-
});
|
|
419
|
+
const row = await dbGet(
|
|
420
|
+
client,
|
|
421
|
+
'SELECT value, "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
422
|
+
[storageKey, subKey]
|
|
423
|
+
);
|
|
451
424
|
if (!row)
|
|
452
425
|
return null;
|
|
453
426
|
if (row.expiresAt !== null && Date.now() > row.expiresAt) {
|
|
@@ -459,14 +432,11 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
459
432
|
async groupHas(key, subKey) {
|
|
460
433
|
const client = this.getClient();
|
|
461
434
|
const storageKey = this.prefixKey(key);
|
|
462
|
-
const row = await
|
|
463
|
-
client
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
(err, result) => err ? rej(err) : res(result)
|
|
468
|
-
);
|
|
469
|
-
});
|
|
435
|
+
const row = await dbGet(
|
|
436
|
+
client,
|
|
437
|
+
'SELECT "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
438
|
+
[storageKey, subKey]
|
|
439
|
+
);
|
|
470
440
|
if (!row)
|
|
471
441
|
return false;
|
|
472
442
|
if (row.expiresAt !== null && Date.now() > row.expiresAt) {
|
|
@@ -478,13 +448,7 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
478
448
|
async groupDel(key, subKey) {
|
|
479
449
|
const client = this.getClient();
|
|
480
450
|
const storageKey = this.prefixKey(key);
|
|
481
|
-
|
|
482
|
-
client.run(
|
|
483
|
-
"DELETE FROM kvcache WHERE key = ? AND subKey = ?",
|
|
484
|
-
[storageKey, subKey],
|
|
485
|
-
(err) => err ? rej(err) : res()
|
|
486
|
-
);
|
|
487
|
-
});
|
|
451
|
+
return dbRun(client, "DELETE FROM kvcache WHERE key = ? AND subKey = ?", [storageKey, subKey]);
|
|
488
452
|
}
|
|
489
453
|
async close() {
|
|
490
454
|
if (this.dbPath) {
|
|
@@ -876,3 +840,4 @@ const getAbtNodeRedisAndSQLiteUrl = () => {
|
|
|
876
840
|
|
|
877
841
|
exports.DBCache = LockDBCache;
|
|
878
842
|
exports.getAbtNodeRedisAndSQLiteUrl = getAbtNodeRedisAndSQLiteUrl;
|
|
843
|
+
exports.withRetry = withRetry;
|
package/dist/index.d.cts
CHANGED
|
@@ -120,9 +120,23 @@ declare class LockDBCache extends SingleFlightDBCache {
|
|
|
120
120
|
private _formatLockKey;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
interface WithRetryOptions {
|
|
124
|
+
/** 最大重试次数 */
|
|
125
|
+
max?: number;
|
|
126
|
+
/** 初始退避时长,单位 ms */
|
|
127
|
+
backoffBase?: number;
|
|
128
|
+
/** 每次重试时的指数倍数 */
|
|
129
|
+
backoffExponent?: number;
|
|
130
|
+
/** 抖动范围,0~backoffJitter 毫秒 */
|
|
131
|
+
backoffJitter?: number;
|
|
132
|
+
/** 判断是否需要重试的函数 */
|
|
133
|
+
needRetry?: (err: any) => boolean;
|
|
134
|
+
}
|
|
135
|
+
declare function withRetry<T>(fn: () => Promise<T>, { max, backoffBase, backoffExponent, backoffJitter, needRetry, }?: WithRetryOptions): Promise<T>;
|
|
136
|
+
|
|
123
137
|
declare const getAbtNodeRedisAndSQLiteUrl: () => {
|
|
124
138
|
redisUrl: string | undefined;
|
|
125
139
|
sqlitePath: string | undefined;
|
|
126
140
|
};
|
|
127
141
|
|
|
128
|
-
export { type BaseDBCacheOptions, LockDBCache as DBCache, type IDBAdapter, getAbtNodeRedisAndSQLiteUrl };
|
|
142
|
+
export { type BaseDBCacheOptions, LockDBCache as DBCache, type IDBAdapter, getAbtNodeRedisAndSQLiteUrl, withRetry };
|
package/dist/index.d.mts
CHANGED
|
@@ -120,9 +120,23 @@ declare class LockDBCache extends SingleFlightDBCache {
|
|
|
120
120
|
private _formatLockKey;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
interface WithRetryOptions {
|
|
124
|
+
/** 最大重试次数 */
|
|
125
|
+
max?: number;
|
|
126
|
+
/** 初始退避时长,单位 ms */
|
|
127
|
+
backoffBase?: number;
|
|
128
|
+
/** 每次重试时的指数倍数 */
|
|
129
|
+
backoffExponent?: number;
|
|
130
|
+
/** 抖动范围,0~backoffJitter 毫秒 */
|
|
131
|
+
backoffJitter?: number;
|
|
132
|
+
/** 判断是否需要重试的函数 */
|
|
133
|
+
needRetry?: (err: any) => boolean;
|
|
134
|
+
}
|
|
135
|
+
declare function withRetry<T>(fn: () => Promise<T>, { max, backoffBase, backoffExponent, backoffJitter, needRetry, }?: WithRetryOptions): Promise<T>;
|
|
136
|
+
|
|
123
137
|
declare const getAbtNodeRedisAndSQLiteUrl: () => {
|
|
124
138
|
redisUrl: string | undefined;
|
|
125
139
|
sqlitePath: string | undefined;
|
|
126
140
|
};
|
|
127
141
|
|
|
128
|
-
export { type BaseDBCacheOptions, LockDBCache as DBCache, type IDBAdapter, getAbtNodeRedisAndSQLiteUrl };
|
|
142
|
+
export { type BaseDBCacheOptions, LockDBCache as DBCache, type IDBAdapter, getAbtNodeRedisAndSQLiteUrl, withRetry };
|
package/dist/index.d.ts
CHANGED
|
@@ -120,9 +120,23 @@ declare class LockDBCache extends SingleFlightDBCache {
|
|
|
120
120
|
private _formatLockKey;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
interface WithRetryOptions {
|
|
124
|
+
/** 最大重试次数 */
|
|
125
|
+
max?: number;
|
|
126
|
+
/** 初始退避时长,单位 ms */
|
|
127
|
+
backoffBase?: number;
|
|
128
|
+
/** 每次重试时的指数倍数 */
|
|
129
|
+
backoffExponent?: number;
|
|
130
|
+
/** 抖动范围,0~backoffJitter 毫秒 */
|
|
131
|
+
backoffJitter?: number;
|
|
132
|
+
/** 判断是否需要重试的函数 */
|
|
133
|
+
needRetry?: (err: any) => boolean;
|
|
134
|
+
}
|
|
135
|
+
declare function withRetry<T>(fn: () => Promise<T>, { max, backoffBase, backoffExponent, backoffJitter, needRetry, }?: WithRetryOptions): Promise<T>;
|
|
136
|
+
|
|
123
137
|
declare const getAbtNodeRedisAndSQLiteUrl: () => {
|
|
124
138
|
redisUrl: string | undefined;
|
|
125
139
|
sqlitePath: string | undefined;
|
|
126
140
|
};
|
|
127
141
|
|
|
128
|
-
export { type BaseDBCacheOptions, LockDBCache as DBCache, type IDBAdapter, getAbtNodeRedisAndSQLiteUrl };
|
|
142
|
+
export { type BaseDBCacheOptions, LockDBCache as DBCache, type IDBAdapter, getAbtNodeRedisAndSQLiteUrl, withRetry };
|
package/dist/index.mjs
CHANGED
|
@@ -146,48 +146,94 @@ __publicField$4(_RedisAdapter, "clients", /* @__PURE__ */ new Map());
|
|
|
146
146
|
__publicField$4(_RedisAdapter, "initPromises", /* @__PURE__ */ new Map());
|
|
147
147
|
let RedisAdapter = _RedisAdapter;
|
|
148
148
|
|
|
149
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
150
|
+
async function withRetry(fn, {
|
|
151
|
+
max = 15,
|
|
152
|
+
backoffBase = 200,
|
|
153
|
+
backoffExponent = 1.1,
|
|
154
|
+
backoffJitter = 100,
|
|
155
|
+
needRetry = () => true
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/comma-dangle
|
|
157
|
+
} = {}) {
|
|
158
|
+
let attempt = 0;
|
|
159
|
+
while (true) {
|
|
160
|
+
try {
|
|
161
|
+
return await fn();
|
|
162
|
+
} catch (err) {
|
|
163
|
+
if (!needRetry(err) || ++attempt > max) {
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
166
|
+
const exp = backoffExponent ** (attempt - 1);
|
|
167
|
+
const expDelay = backoffBase * exp;
|
|
168
|
+
const jitter = Math.random() * backoffJitter;
|
|
169
|
+
const waitTime = Math.floor(expDelay + jitter);
|
|
170
|
+
await sleep(waitTime);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
149
175
|
var __defProp$3 = Object.defineProperty;
|
|
150
176
|
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
151
177
|
var __publicField$3 = (obj, key, value) => {
|
|
152
178
|
__defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
153
179
|
return value;
|
|
154
180
|
};
|
|
155
|
-
const
|
|
181
|
+
const retryOptions = {
|
|
182
|
+
needRetry: (err) => err.message?.includes("SQLITE_BUSY")
|
|
183
|
+
};
|
|
184
|
+
const dbRun = (db, sql, params) => {
|
|
185
|
+
return withRetry(() => {
|
|
186
|
+
return new Promise((res, rej) => {
|
|
187
|
+
db.run(sql, params, (err) => err ? rej(err) : res());
|
|
188
|
+
});
|
|
189
|
+
}, retryOptions);
|
|
190
|
+
};
|
|
191
|
+
const dbExec = (db, sql) => {
|
|
192
|
+
return withRetry(() => {
|
|
193
|
+
return new Promise((res, rej) => {
|
|
194
|
+
db.exec(sql, (err) => err ? rej(err) : res());
|
|
195
|
+
});
|
|
196
|
+
}, retryOptions);
|
|
197
|
+
};
|
|
198
|
+
const dbGet = (db, sql, params) => {
|
|
199
|
+
return withRetry(() => {
|
|
200
|
+
return new Promise((res, rej) => {
|
|
201
|
+
db.get(sql, params, (err, result) => err ? rej(err) : res(result));
|
|
202
|
+
});
|
|
203
|
+
}, retryOptions);
|
|
204
|
+
};
|
|
156
205
|
async function initSqliteWithRetry(db) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
206
|
+
db.configure("busyTimeout", 5e3);
|
|
207
|
+
try {
|
|
208
|
+
await dbExec(
|
|
209
|
+
db,
|
|
210
|
+
`
|
|
211
|
+
PRAGMA journal_mode = WAL;
|
|
212
|
+
PRAGMA synchronous = normal;
|
|
213
|
+
PRAGMA wal_autocheckpoint = 5000;
|
|
214
|
+
PRAGMA busy_timeout = 5000;
|
|
215
|
+
`
|
|
216
|
+
);
|
|
217
|
+
await dbRun(
|
|
218
|
+
db,
|
|
219
|
+
`
|
|
220
|
+
CREATE TABLE IF NOT EXISTS kvcache (
|
|
221
|
+
key TEXT NOT NULL,
|
|
222
|
+
subKey TEXT NOT NULL,
|
|
223
|
+
value TEXT NOT NULL,
|
|
224
|
+
expiresAt INTEGER,
|
|
225
|
+
PRIMARY KEY (key, subKey)
|
|
226
|
+
)
|
|
227
|
+
`
|
|
228
|
+
);
|
|
229
|
+
} catch (err) {
|
|
230
|
+
throw new Error(`SQLite init failed: ${err}`);
|
|
183
231
|
}
|
|
184
232
|
}
|
|
185
233
|
const _SqliteAdapter = class _SqliteAdapter {
|
|
186
234
|
constructor() {
|
|
187
235
|
__publicField$3(this, "opts", null);
|
|
188
236
|
__publicField$3(this, "prefix", "");
|
|
189
|
-
__publicField$3(this, "sqliteBusyRetryCount", 0);
|
|
190
|
-
__publicField$3(this, "sqliteBusyRetryGroupCount", 0);
|
|
191
237
|
__publicField$3(this, "defaultTtl", 1e3 * 60 * 60);
|
|
192
238
|
__publicField$3(this, "cleanupInterval", 5 * 60 * 1e3);
|
|
193
239
|
__publicField$3(this, "dbPath", "");
|
|
@@ -215,28 +261,6 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
215
261
|
}
|
|
216
262
|
const db = new sqlite3.Database(this.dbPath);
|
|
217
263
|
await initSqliteWithRetry(db);
|
|
218
|
-
await new Promise(
|
|
219
|
-
(res, rej) => db.run(
|
|
220
|
-
`
|
|
221
|
-
CREATE TABLE IF NOT EXISTS kvcache (
|
|
222
|
-
key TEXT NOT NULL,
|
|
223
|
-
subKey TEXT NOT NULL,
|
|
224
|
-
value TEXT NOT NULL,
|
|
225
|
-
expiresAt INTEGER,
|
|
226
|
-
PRIMARY KEY (key, subKey)
|
|
227
|
-
)
|
|
228
|
-
`,
|
|
229
|
-
(err) => err ? rej(err) : res()
|
|
230
|
-
)
|
|
231
|
-
);
|
|
232
|
-
await new Promise(
|
|
233
|
-
(res, rej) => db.run(
|
|
234
|
-
`
|
|
235
|
-
PRAGMA shrink_memory;
|
|
236
|
-
`,
|
|
237
|
-
(err) => err ? rej(err) : res()
|
|
238
|
-
)
|
|
239
|
-
);
|
|
240
264
|
_SqliteAdapter.clients.set(this.dbPath, db);
|
|
241
265
|
_SqliteAdapter.cleanupTimers.set(
|
|
242
266
|
this.dbPath,
|
|
@@ -295,51 +319,28 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
295
319
|
if (exists)
|
|
296
320
|
return false;
|
|
297
321
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
(err) => err ? rej(err) : res()
|
|
310
|
-
);
|
|
311
|
-
});
|
|
312
|
-
} catch (error) {
|
|
313
|
-
if (error.message?.includes("SQLITE_BUSY")) {
|
|
314
|
-
if (this.sqliteBusyRetryCount > 10) {
|
|
315
|
-
this.sqliteBusyRetryCount = 0;
|
|
316
|
-
throw error;
|
|
317
|
-
}
|
|
318
|
-
this.sqliteBusyRetryCount++;
|
|
319
|
-
await sleep(2e3);
|
|
320
|
-
const out = await this.set(key, value, opts);
|
|
321
|
-
this.sqliteBusyRetryCount = 0;
|
|
322
|
-
return out;
|
|
323
|
-
}
|
|
324
|
-
throw error;
|
|
325
|
-
}
|
|
322
|
+
await dbRun(
|
|
323
|
+
client,
|
|
324
|
+
`
|
|
325
|
+
INSERT INTO kvcache (key, subKey, value, expiresAt)
|
|
326
|
+
VALUES (?, ?, ?, ?)
|
|
327
|
+
ON CONFLICT(key, subKey) DO UPDATE SET
|
|
328
|
+
value = excluded.value,
|
|
329
|
+
expiresAt = excluded.expiresAt
|
|
330
|
+
`,
|
|
331
|
+
[storageKey, subKey, this.serialize(value), expiresAt]
|
|
332
|
+
);
|
|
326
333
|
return true;
|
|
327
334
|
}
|
|
328
335
|
async get(key) {
|
|
329
336
|
const client = this.getClient();
|
|
330
337
|
const storageKey = this.prefixKey(key);
|
|
331
338
|
const subKey = "";
|
|
332
|
-
const row = await
|
|
333
|
-
client
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (err)
|
|
338
|
-
return rej(err);
|
|
339
|
-
return res(result);
|
|
340
|
-
}
|
|
341
|
-
);
|
|
342
|
-
});
|
|
339
|
+
const row = await dbGet(
|
|
340
|
+
client,
|
|
341
|
+
'SELECT value, "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
342
|
+
[storageKey, subKey]
|
|
343
|
+
);
|
|
343
344
|
if (!row)
|
|
344
345
|
return null;
|
|
345
346
|
if (row.expiresAt !== null && Date.now() > row.expiresAt) {
|
|
@@ -351,22 +352,17 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
351
352
|
async del(key) {
|
|
352
353
|
const client = this.getClient();
|
|
353
354
|
const storageKey = this.prefixKey(key);
|
|
354
|
-
await
|
|
355
|
-
client.run("DELETE FROM kvcache WHERE key = ?", [storageKey], (err) => err ? rej(err) : res());
|
|
356
|
-
});
|
|
355
|
+
await dbRun(client, "DELETE FROM kvcache WHERE key = ?", [storageKey]);
|
|
357
356
|
}
|
|
358
357
|
async has(key) {
|
|
359
358
|
const client = this.getClient();
|
|
360
359
|
const storageKey = this.prefixKey(key);
|
|
361
360
|
const subKey = "";
|
|
362
|
-
const row = await
|
|
363
|
-
client
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
(err, result) => err ? rej(err) : res(result)
|
|
368
|
-
);
|
|
369
|
-
});
|
|
361
|
+
const row = await dbGet(
|
|
362
|
+
client,
|
|
363
|
+
'SELECT "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
364
|
+
[storageKey, subKey]
|
|
365
|
+
);
|
|
370
366
|
if (!row)
|
|
371
367
|
return false;
|
|
372
368
|
if (row.expiresAt !== null && Date.now() > row.expiresAt) {
|
|
@@ -385,49 +381,26 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
385
381
|
if (exists)
|
|
386
382
|
return;
|
|
387
383
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
(err) => err ? rej(err) : res()
|
|
400
|
-
);
|
|
401
|
-
});
|
|
402
|
-
} catch (error) {
|
|
403
|
-
if (error.message?.includes("SQLITE_BUSY")) {
|
|
404
|
-
if (this.sqliteBusyRetryGroupCount > 10) {
|
|
405
|
-
this.sqliteBusyRetryGroupCount = 0;
|
|
406
|
-
throw error;
|
|
407
|
-
}
|
|
408
|
-
this.sqliteBusyRetryGroupCount++;
|
|
409
|
-
await sleep(2e3);
|
|
410
|
-
await this.groupSet(key, subKey, value, opts);
|
|
411
|
-
this.sqliteBusyRetryGroupCount = 0;
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
throw error;
|
|
415
|
-
}
|
|
384
|
+
await dbRun(
|
|
385
|
+
client,
|
|
386
|
+
`
|
|
387
|
+
INSERT INTO kvcache (key, subKey, value, expiresAt)
|
|
388
|
+
VALUES (?, ?, ?, ?)
|
|
389
|
+
ON CONFLICT(key, subKey) DO UPDATE SET
|
|
390
|
+
value = excluded.value,
|
|
391
|
+
expiresAt = excluded.expiresAt
|
|
392
|
+
`,
|
|
393
|
+
[storageKey, subKey, this.serialize(value), expiresAt]
|
|
394
|
+
);
|
|
416
395
|
}
|
|
417
396
|
async groupGet(key, subKey) {
|
|
418
397
|
const client = this.getClient();
|
|
419
398
|
const storageKey = this.prefixKey(key);
|
|
420
|
-
const row = await
|
|
421
|
-
client
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if (err)
|
|
426
|
-
return rej(err);
|
|
427
|
-
return res(result);
|
|
428
|
-
}
|
|
429
|
-
);
|
|
430
|
-
});
|
|
399
|
+
const row = await dbGet(
|
|
400
|
+
client,
|
|
401
|
+
'SELECT value, "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
402
|
+
[storageKey, subKey]
|
|
403
|
+
);
|
|
431
404
|
if (!row)
|
|
432
405
|
return null;
|
|
433
406
|
if (row.expiresAt !== null && Date.now() > row.expiresAt) {
|
|
@@ -439,14 +412,11 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
439
412
|
async groupHas(key, subKey) {
|
|
440
413
|
const client = this.getClient();
|
|
441
414
|
const storageKey = this.prefixKey(key);
|
|
442
|
-
const row = await
|
|
443
|
-
client
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
(err, result) => err ? rej(err) : res(result)
|
|
448
|
-
);
|
|
449
|
-
});
|
|
415
|
+
const row = await dbGet(
|
|
416
|
+
client,
|
|
417
|
+
'SELECT "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
418
|
+
[storageKey, subKey]
|
|
419
|
+
);
|
|
450
420
|
if (!row)
|
|
451
421
|
return false;
|
|
452
422
|
if (row.expiresAt !== null && Date.now() > row.expiresAt) {
|
|
@@ -458,13 +428,7 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
458
428
|
async groupDel(key, subKey) {
|
|
459
429
|
const client = this.getClient();
|
|
460
430
|
const storageKey = this.prefixKey(key);
|
|
461
|
-
|
|
462
|
-
client.run(
|
|
463
|
-
"DELETE FROM kvcache WHERE key = ? AND subKey = ?",
|
|
464
|
-
[storageKey, subKey],
|
|
465
|
-
(err) => err ? rej(err) : res()
|
|
466
|
-
);
|
|
467
|
-
});
|
|
431
|
+
return dbRun(client, "DELETE FROM kvcache WHERE key = ? AND subKey = ?", [storageKey, subKey]);
|
|
468
432
|
}
|
|
469
433
|
async close() {
|
|
470
434
|
if (this.dbPath) {
|
|
@@ -854,4 +818,4 @@ const getAbtNodeRedisAndSQLiteUrl = () => {
|
|
|
854
818
|
return params;
|
|
855
819
|
};
|
|
856
820
|
|
|
857
|
-
export { LockDBCache as DBCache, getAbtNodeRedisAndSQLiteUrl };
|
|
821
|
+
export { LockDBCache as DBCache, getAbtNodeRedisAndSQLiteUrl, withRetry };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abtnode/db-cache",
|
|
3
|
-
"version": "1.16.47-beta-
|
|
3
|
+
"version": "1.16.47-beta-20250731-014139-e860268f",
|
|
4
4
|
"description": "Db cache use redis or sqlite as backend",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"typescript": "^5.6.3",
|
|
46
46
|
"unbuild": "^2.0.0"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "85b5870be6b7a531cd7a86bd132112d68547fde1"
|
|
49
49
|
}
|