ueberdb2 6.1.8 → 6.1.10

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/README.md CHANGED
@@ -334,6 +334,65 @@ write to the database.
334
334
 
335
335
  You should create your database as utf8mb4_bin.
336
336
 
337
+ ## PostgreSQL Advice
338
+
339
+ The `postgres` driver uses a [`pg`](https://node-postgres.com/) connection
340
+ pool. The pool-related settings below are part of the `Settings` type; other
341
+ [`pg` pool options](https://node-postgres.com/apis/pool) are forwarded to the
342
+ pool at runtime as well, but aren't in the type yet (so TypeScript callers may
343
+ need a cast for those). The defaults applied by ueberDB2 are:
344
+
345
+ | Setting | Default | Notes |
346
+ | ----------------------------- | ------- | ------------------------------------------------------------- |
347
+ | `max` | `20` | Maximum connections in the pool. |
348
+ | `min` | `4` | Minimum warm connections kept open (honored by `pg` >= 8.16). |
349
+ | `idleTimeoutMillis` | `1000` | Idle reaping only applies to connections **above** `min`. |
350
+ | `keepAlive` | `true` | Enables TCP keep-alive on pooled sockets. |
351
+ | `keepAliveInitialDelayMillis` | `10000` | Delay before the first keep-alive probe (ms). |
352
+
353
+ ### Idle connections behind a proxy / load balancer / firewall
354
+
355
+ Because `min` connections are kept warm, those sockets can sit idle
356
+ indefinitely. Anything between your application and PostgreSQL that drops idle
357
+ connections will eventually close them, and the next use then fails with
358
+ `Connection terminated unexpectedly`. There are two distinct flavours of
359
+ "idle drop", and they need different handling:
360
+
361
+ - **Kernel / NAT / firewall / conntrack state expiry** — these expire idle
362
+ TCP flows when _no packets at all_ are seen. `keepAlive` (enabled by default,
363
+ 10s initial delay) fixes this: the OS emits periodic keep-alive probes so the
364
+ flow never looks dead. Lower `keepAliveInitialDelayMillis` if the idle window
365
+ is shorter than 10s.
366
+
367
+ - **Application-layer proxy idle timeouts** — e.g. HAProxy `timeout server` /
368
+ `timeout client`, pgbouncer, many cloud LBs. These count _data_ inactivity,
369
+ and TCP keep-alive probes are empty kernel-level segments that the proxy does
370
+ **not** see as activity. **`keepAlive` does not help here** — the proxy will
371
+ still close the connection on schedule. For these you must either raise the
372
+ proxy's idle timeout (for HAProxy in `mode tcp`, `timeout tunnel` is the knob
373
+ for long-lived connections), or rely on the pool recovering from the drop.
374
+
375
+ That recovery is the important part, and is always on regardless of proxy
376
+ config: **a pool `error` handler is attached**, so a dropped idle connection is
377
+ logged and discarded (the pool transparently reconnects on next use) instead of
378
+ being re-thrown as an uncaught EventEmitter `'error'` that crashes the host
379
+ process. The drop itself is harmless; what used to be fatal was the missing
380
+ handler.
381
+
382
+ ```javascript
383
+ const db = new ueberdb.Database("postgres", {
384
+ host: "127.0.0.1",
385
+ user: "ueberdb",
386
+ password: "ueberdb",
387
+ database: "ueberdb",
388
+ // Helps against kernel/NAT/firewall idle expiry. Does NOT defeat an
389
+ // application-layer proxy idle timeout (e.g. HAProxy timeout server) —
390
+ // raise the proxy timeout for that; the pool reconnects either way.
391
+ keepAlive: true,
392
+ keepAliveInitialDelayMillis: 5000,
393
+ });
394
+ ```
395
+
337
396
  ## Redis TLS communication
338
397
 
339
398
  If you enabled TLS on your Redis database (available since Redis 6.0) you will
@@ -17,14 +17,15 @@ import AbstractDatabase, { type Settings } from "../lib/AbstractDatabase";
17
17
  import type { BulkObject } from "./cassandra_db";
18
18
  import { MongoClient } from "mongodb";
19
19
  import type { Collection, Db } from "mongodb";
20
+ type UeberDoc = {
21
+ _id: string;
22
+ value: string;
23
+ };
20
24
  export default class extends AbstractDatabase {
21
- interval: NodeJS.Timer | undefined;
22
25
  database: Db | undefined;
23
26
  client: MongoClient | undefined;
24
- collection: Collection | undefined;
27
+ collection: Collection<UeberDoc> | undefined;
25
28
  constructor(settings: Settings);
26
- clearPing(): void;
27
- schedulePing(): void;
28
29
  init(callback: Function): void;
29
30
  get(key: string, callback: Function): void;
30
31
  findKeys(key: string, notKey: string, callback: Function): void;
@@ -33,4 +34,5 @@ export default class extends AbstractDatabase {
33
34
  doBulk(bulk: BulkObject[], callback: Function): void;
34
35
  close(callback: any): void;
35
36
  }
37
+ export {};
36
38
  //# sourceMappingURL=mongodb_db.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mongodb_db.d.ts","sourceRoot":"","sources":["../../databases/mongodb_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,gBAAgB,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE9C,MAAM,CAAC,OAAO,MAAO,SAAQ,gBAAgB;IACpC,QAAQ,EAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC;IACnC,QAAQ,EAAE,EAAE,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;gBAC9B,QAAQ,EAAE,QAAQ;IAW9B,SAAS;IAMT,YAAY;IASZ,IAAI,CAAC,QAAQ,EAAE,QAAQ;IAcvB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAcnC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAsBxD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAalD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAStC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,QAAQ;IA0B7C,KAAK,CAAC,QAAQ,EAAE,GAAG;CAIpB"}
1
+ {"version":3,"file":"mongodb_db.d.ts","sourceRoot":"","sources":["../../databases/mongodb_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,gBAAgB,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,EAAE,EAAU,MAAM,SAAS,CAAC;AAItD,KAAK,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,MAAM,CAAC,OAAO,MAAO,SAAQ,gBAAgB;IACpC,QAAQ,EAAE,EAAE,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;gBACxC,QAAQ,EAAE,QAAQ;IAW9B,IAAI,CAAC,QAAQ,EAAE,QAAQ;IAavB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAWnC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAiBxD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAUlD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAMtC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,QAAQ;IAwB7C,KAAK,CAAC,QAAQ,EAAE,GAAG;CAGpB"}
@@ -29,7 +29,7 @@ export default class extends AbstractDatabase {
29
29
  }, callback: (err: Error | null, value: string[]) => void): void;
30
30
  set(key: string, value: string, callback: (err: Error, result: pg.QueryResult) => void): void;
31
31
  remove(key: string, callback: () => {}): void;
32
- doBulk(bulk: BulkObject[], callback: () => {}): void;
32
+ doBulk(bulk: BulkObject[], callback: (err?: Error | null) => void): void;
33
33
  close(callback: () => {}): void;
34
34
  }
35
35
  //# sourceMappingURL=postgres_db.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"postgres_db.d.ts","sourceRoot":"","sources":["../../databases/postgres_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,gBAAgB,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,CAAC,OAAO,MAAO,SAAQ,gBAAgB;IACpC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;IACZ,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;gBACtC,QAAQ,EAAE,QAAQ,GAAG,MAAM;IAgBvC,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,EAAE;IA4EjC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE;IAYhE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE;IA0BrF,aAAa,CACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAC1C,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI;IA2BxD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,WAAW,KAAK,IAAI;IAStF,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAItC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;IAyC7C,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE;CAGzB"}
1
+ {"version":3,"file":"postgres_db.d.ts","sourceRoot":"","sources":["../../databases/postgres_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,gBAAgB,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,CAAC,OAAO,MAAO,SAAQ,gBAAgB;IACpC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;IACZ,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;gBACtC,QAAQ,EAAE,QAAQ,GAAG,MAAM;IA0CvC,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,EAAE;IA4EjC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE;IAehE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE;IA0BrF,aAAa,CACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAC1C,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI;IA2BxD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,WAAW,KAAK,IAAI;IAiBtF,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAOtC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI;IA2DjE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE;CAGzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"redis_db.d.ts","sourceRoot":"","sources":["../../databases/redis_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,gBAAgB,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAG1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,gBAAgB;IAC5C,OAAO,EAAE,GAAG,CAAC;gBACR,QAAQ,EAAE,QAAQ;IAM9B,IAAI,OAAO,YAEV;IAEK,IAAI;IAwBJ,GAAG,CAAC,GAAG,EAAE,MAAM;IAKf,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAepC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAS9B,MAAM,CAAC,GAAG,EAAE,MAAM;IASlB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBzC,KAAK;CAKZ"}
1
+ {"version":3,"file":"redis_db.d.ts","sourceRoot":"","sources":["../../databases/redis_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,gBAAgB,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAG1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,gBAAgB;IAC5C,OAAO,EAAE,GAAG,CAAC;gBACR,QAAQ,EAAE,QAAQ;IAM9B,IAAI,OAAO,YAEV;IAEK,IAAI;IAqCJ,GAAG,CAAC,GAAG,EAAE,MAAM;IAKf,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAepC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAS9B,MAAM,CAAC,GAAG,EAAE,MAAM;IASlB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBzC,KAAK;CAKZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"rethink_db.d.ts","sourceRoot":"","sources":["../../databases/rethink_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,gBAAgB,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,CAAC,MAAM,WAAW,CAAC;AAE1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,gBAAgB;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC;gBAC3B,QAAQ,EAAE,QAAQ;IAuB9B,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE;IAkB1C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE;IAUrD,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAWxD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAMlD,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;IA2B7C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAKtC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE;CAKzB"}
1
+ {"version":3,"file":"rethink_db.d.ts","sourceRoot":"","sources":["../../databases/rethink_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,gBAAgB,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,CAAC,MAAM,WAAW,CAAC;AAE1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,gBAAgB;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC;gBAC3B,QAAQ,EAAE,QAAQ;IAuB9B,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE;IA8B1C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE;IAUrD,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAWxD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAMlD,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;IA2B7C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAKtC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE;CAKzB"}
package/dist/index.js CHANGED
@@ -81,9 +81,10 @@ var Database$1 = class {
81
81
  _flushPaused = null;
82
82
  _flushPausedCount = 0;
83
83
  _locks = /* @__PURE__ */ new Map();
84
+ _dirtyKeys = /* @__PURE__ */ new Set();
84
85
  _flushDone = null;
85
86
  metrics;
86
- flushInterval;
87
+ _flushTimer = null;
87
88
  constructor(wrappedDB, settings, logger) {
88
89
  if (wrappedDB.isAsync) this.wrappedDB = wrappedDB;
89
90
  else {
@@ -131,9 +132,6 @@ var Database$1 = class {
131
132
  writesToDbFinished: 0,
132
133
  writesToDbRetried: 0
133
134
  };
134
- this.flushInterval = this.settings.writeInterval > 0 ? setInterval(() => {
135
- this.flush();
136
- }, this.settings.writeInterval) : null;
137
135
  }
138
136
  async _lock(key) {
139
137
  while (true) {
@@ -150,6 +148,23 @@ var Database$1 = class {
150
148
  this._locks.get(key).done();
151
149
  this._locks.delete(key);
152
150
  }
151
+ _scheduleFlush() {
152
+ if (this._flushTimer != null) return;
153
+ if (this._flushDone != null) return;
154
+ if (this.settings.writeInterval <= 0) return;
155
+ if (this._dirtyKeys.size === 0) return;
156
+ this._flushTimer = setTimeout(() => {
157
+ this._flushTimer = null;
158
+ this.flush();
159
+ }, this.settings.writeInterval);
160
+ this._flushTimer.unref?.();
161
+ }
162
+ _cancelFlushTimer() {
163
+ if (this._flushTimer != null) {
164
+ clearTimeout(this._flushTimer);
165
+ this._flushTimer = null;
166
+ }
167
+ }
153
168
  _pauseFlush() {
154
169
  if (this._flushPaused == null) {
155
170
  this._flushPaused = new SelfContainedPromise();
@@ -166,12 +181,22 @@ var Database$1 = class {
166
181
  await this.wrappedDB.init();
167
182
  }
168
183
  async close() {
169
- clearInterval(this.flushInterval ?? void 0);
184
+ this._cancelFlushTimer();
170
185
  await this.flush();
171
186
  await this.wrappedDB.close();
172
187
  this.wrappedDB = null;
173
188
  }
174
189
  async get(key) {
190
+ if (!this._locks.has(key)) {
191
+ const entry = this.buffer.get(key);
192
+ if (entry != null) {
193
+ ++this.metrics.reads;
194
+ ++this.metrics.readsFromCache;
195
+ ++this.metrics.readsFinished;
196
+ if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key} - ${JSON.stringify(entry.value)} - from ${entry.dirty ? "dirty buffer" : "cache"}`);
197
+ return cloneOut(entry.value);
198
+ }
199
+ }
175
200
  let v;
176
201
  await this._lock(key);
177
202
  try {
@@ -179,7 +204,7 @@ var Database$1 = class {
179
204
  } finally {
180
205
  this._unlock(key);
181
206
  }
182
- return clone(v);
207
+ return cloneOut(v);
183
208
  }
184
209
  async _getLocked(key) {
185
210
  ++this.metrics.reads;
@@ -224,7 +249,7 @@ var Database$1 = class {
224
249
  await this.flush();
225
250
  const keyValues = await this.wrappedDB.findKeys(key, notKey);
226
251
  if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key}-${notKey} - ${JSON.stringify(keyValues)} - from database `);
227
- return clone(keyValues);
252
+ return cloneOut(keyValues);
228
253
  }
229
254
  async findKeysPaged(key, notKey, options) {
230
255
  if (!options || !Number.isInteger(options.limit) || options.limit <= 0) throw new Error("findKeysPaged requires a positive integer limit");
@@ -241,18 +266,18 @@ var Database$1 = class {
241
266
  }
242
267
  return lo;
243
268
  })();
244
- return clone(all.slice(start, start + options.limit));
269
+ return cloneOut(all.slice(start, start + options.limit));
245
270
  }
246
271
  const keys = await this.wrappedDB.findKeysPaged(key, notKey, options);
247
272
  if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key}-${notKey} (paged limit=${options.limit} after=${options.after ?? ""}) - ${JSON.stringify(keys)} - from database `);
248
- return clone(keys);
273
+ return cloneOut(keys);
249
274
  }
250
275
  async remove(key) {
251
276
  if (this.logger.isDebugEnabled()) this.logger.debug(`DELETE - ${key} - from database `);
252
277
  await this.set(key, null);
253
278
  }
254
279
  async set(key, value) {
255
- value = clone(value);
280
+ value = cloneIn(value);
256
281
  let p;
257
282
  this._pauseFlush();
258
283
  try {
@@ -280,6 +305,8 @@ var Database$1 = class {
280
305
  entry.value = value;
281
306
  if (!entry.dirty) entry.dirty = new SelfContainedPromise();
282
307
  this.buffer.set(key, entry);
308
+ this._dirtyKeys.add(key);
309
+ this._scheduleFlush();
283
310
  const buffered = this.settings.writeInterval > 0;
284
311
  if (this.logger.isDebugEnabled()) this.logger.debug(`SET - ${key} - ${JSON.stringify(value)} - to ${buffered ? "buffer" : "database"}`);
285
312
  if (!buffered) this._write([[key, entry]]);
@@ -292,7 +319,7 @@ var Database$1 = class {
292
319
  }
293
320
  }
294
321
  async setSub(key, sub, value) {
295
- value = clone(value);
322
+ value = cloneIn(value);
296
323
  if (this.logger.isDebugEnabled()) this.logger.debug(`SETSUB - ${key}${JSON.stringify(sub)} - ${JSON.stringify(value)}`);
297
324
  let p;
298
325
  this._pauseFlush();
@@ -342,18 +369,21 @@ var Database$1 = class {
342
369
  v = v[k];
343
370
  }
344
371
  if (this.logger.isDebugEnabled()) this.logger.debug(`GETSUB - ${key}${JSON.stringify(sub)} - ${JSON.stringify(v)}`);
345
- return clone(v);
372
+ return cloneOut(v);
346
373
  } finally {
347
374
  this._unlock(key);
348
375
  }
349
376
  }
350
377
  async flush() {
378
+ this._cancelFlushTimer();
351
379
  if (this._flushDone == null) this._flushDone = (async () => {
352
380
  while (true) {
353
381
  while (this._flushPaused != null) await this._flushPaused;
354
382
  const dirtyEntries = [];
355
- for (const entry of this.buffer) if (entry[1].dirty && !entry[1].writingInProgress) {
356
- dirtyEntries.push(entry);
383
+ for (const key of this._dirtyKeys) {
384
+ const entry = this.buffer.get(key, false);
385
+ if (!entry || !entry.dirty || entry.writingInProgress) continue;
386
+ dirtyEntries.push([key, entry]);
357
387
  if (this.settings.bulkLimit && dirtyEntries.length >= this.settings.bulkLimit) break;
358
388
  }
359
389
  if (dirtyEntries.length === 0) return;
@@ -362,14 +392,16 @@ var Database$1 = class {
362
392
  })();
363
393
  await this._flushDone;
364
394
  this._flushDone = null;
395
+ if (this._dirtyKeys.size > 0) this._scheduleFlush();
365
396
  }
366
397
  async _write(dirtyEntries) {
367
- const markDone = (entry, err) => {
398
+ const markDone = (key, entry, err) => {
368
399
  if (entry.writingInProgress) {
369
400
  entry.writingInProgress = false;
370
401
  if (err != null) ++this.metrics.writesToDbFailed;
371
402
  ++this.metrics.writesToDbFinished;
372
403
  }
404
+ if (this.buffer.get(key, false) === entry) this._dirtyKeys.delete(key);
373
405
  entry.dirty.done(err);
374
406
  entry.dirty = null;
375
407
  };
@@ -379,9 +411,9 @@ var Database$1 = class {
379
411
  let serialized;
380
412
  try {
381
413
  if (this.settings.json && entry.value != null) serialized = JSON.stringify(entry.value);
382
- else serialized = clone(entry.value);
414
+ else serialized = cloneOut(entry.value);
383
415
  } catch (err) {
384
- markDone(entry, err);
416
+ markDone(key, entry, err);
385
417
  continue;
386
418
  }
387
419
  entry.writingInProgress = true;
@@ -402,7 +434,7 @@ var Database$1 = class {
402
434
  } catch (err) {
403
435
  writeErr = err instanceof Error ? err : new Error(String(err));
404
436
  }
405
- markDone(entry, writeErr);
437
+ markDone(op.key, entry, writeErr);
406
438
  };
407
439
  if (ops.length === 1) await writeOneOp(ops[0], entries[0]);
408
440
  else if (typeof this.wrappedDB.doBulk !== "function") await Promise.all(ops.map(async (op, i) => writeOneOp(op, entries[i])));
@@ -416,27 +448,35 @@ var Database$1 = class {
416
448
  this.metrics.writesToDbRetried += ops.length;
417
449
  await Promise.all(ops.map(async (op, i) => writeOneOp(op, entries[i])));
418
450
  }
419
- if (success) entries.forEach((entry) => markDone(entry, null));
451
+ if (success) for (let i = 0; i < entries.length; i++) markDone(ops[i].key, entries[i], null);
420
452
  }
421
453
  this.buffer.evictOld();
422
454
  }
423
455
  };
424
- const clone = (obj, key = "") => {
456
+ const cloneIn = (obj, key = "") => {
425
457
  if (obj == null || typeof obj !== "object") return obj;
426
- if (typeof obj.toJSON === "function") return clone(obj.toJSON(key));
458
+ if (typeof obj.toJSON === "function") return cloneIn(obj.toJSON(key));
427
459
  if (obj instanceof Date) {
428
460
  const copy = /* @__PURE__ */ new Date();
429
461
  copy.setTime(obj.getTime());
430
462
  return copy;
431
463
  }
432
- if (Array.isArray(obj)) return obj.map((item, i) => clone(item, String(i)));
464
+ if (Array.isArray(obj)) return obj.map((item, i) => cloneIn(item, String(i)));
433
465
  if (obj instanceof Object) {
434
466
  const copy = {};
435
- for (const attr of Object.keys(obj)) copy[attr] = clone(obj[attr], attr);
467
+ for (const attr of Object.keys(obj)) copy[attr] = cloneIn(obj[attr], attr);
436
468
  return copy;
437
469
  }
438
470
  throw new Error("Unable to copy obj! Its type isn't supported.");
439
471
  };
472
+ const cloneOut = (v) => {
473
+ if (v == null || typeof v !== "object") return v;
474
+ try {
475
+ return structuredClone(v);
476
+ } catch {
477
+ return cloneIn(v);
478
+ }
479
+ };
440
480
  //#endregion
441
481
  //#region index.ts
442
482
  /**
@@ -489,11 +529,11 @@ var Database = class {
489
529
  async initDB() {
490
530
  switch (this.type) {
491
531
  case "mysql": return new (await (import("./mysql_db-DIXEbML4.js"))).default(this.dbSettings);
492
- case "postgres": return new (await (import("./postgres_db-BzDKMRC5.js"))).default(this.dbSettings);
532
+ case "postgres": return new (await (import("./postgres_db-BlGpklXj.js"))).default(this.dbSettings);
493
533
  case "sqlite": return new (await (import("./sqlite_db-Bp4nZn-c.js"))).default(this.dbSettings);
494
534
  case "rustydb": return new (await (import("./rusty_db-90_S-z-w.js"))).default(this.dbSettings);
495
- case "mongodb": return new (await (import("./mongodb_db-DJVvvNTo.js"))).default(this.dbSettings);
496
- case "redis": return new (await (import("./redis_db-C8LRkRGe.js"))).default(this.dbSettings);
535
+ case "mongodb": return new (await (import("./mongodb_db-DfViPvX4.js"))).default(this.dbSettings);
536
+ case "redis": return new (await (import("./redis_db-501GYkb7.js"))).default(this.dbSettings);
497
537
  case "cassandra": return new (await (import("./cassandra_db-B96_l9V7.js"))).default(this.dbSettings);
498
538
  case "dirty": return new (await (import("./dirty_db-BGvHB8ud.js"))).default(this.dbSettings);
499
539
  case "dirtygit": return new (await (import("./dirty_git_db-LJWW2YT6.js"))).default(this.dbSettings);
@@ -501,8 +541,8 @@ var Database = class {
501
541
  case "memory": return new (await (import("./memory_db-BQaHRpkv.js"))).default(this.dbSettings);
502
542
  case "mock": return new (await (import("./mock_db-CgEDPTpF.js"))).default(this.dbSettings);
503
543
  case "mssql": return new (await (import("./mssql_db-BqtTYXk9.js"))).default(this.dbSettings);
504
- case "postgrespool": return new (await (import("./postgrespool_db-B6FOhTY3.js"))).default(this.dbSettings);
505
- case "rethink": return new (await (import("./rethink_db-qv884MUM.js"))).default(this.dbSettings);
544
+ case "postgrespool": return new (await (import("./postgrespool_db-DuODGBuR.js"))).default(this.dbSettings);
545
+ case "rethink": return new (await (import("./rethink_db-BklcFMyp.js"))).default(this.dbSettings);
506
546
  case "couch": return new (await (import("./couch_db-DZ6MLt5j.js"))).default(this.dbSettings);
507
547
  case "surrealdb": return new (await (import("./surrealdb_db-K8QAySpG.js"))).default(this.dbSettings);
508
548
  default: throw new Error(`Invalid database type: ${this.type}`);
@@ -4,6 +4,8 @@ export type Settings = {
4
4
  table?: string;
5
5
  db?: string;
6
6
  idleTimeoutMillis?: number;
7
+ keepAlive?: boolean;
8
+ keepAliveInitialDelayMillis?: number;
7
9
  min?: number;
8
10
  max?: number;
9
11
  engine?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractDatabase.d.ts","sourceRoot":"","sources":["../../lib/AbstractDatabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAOzD,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,cAAM,gBAAgB;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;gBAEd,QAAQ,EAAE,QAAQ;IAa9B,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM;IAarD,MAAM,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,IAAI,OAAO,IAAI,OAAO,CAErB;CACF;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"AbstractDatabase.d.ts","sourceRoot":"","sources":["../../lib/AbstractDatabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAOzD,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,cAAM,gBAAgB;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;gBAEd,QAAQ,EAAE,QAAQ;IAa9B,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM;IAarD,MAAM,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,IAAI,OAAO,IAAI,OAAO,CAErB;CACF;AAED,eAAe,gBAAgB,CAAC"}
@@ -79,12 +79,15 @@ export declare class Database {
79
79
  private _flushPaused;
80
80
  private _flushPausedCount;
81
81
  private readonly _locks;
82
+ private readonly _dirtyKeys;
82
83
  private _flushDone;
83
84
  metrics: Metrics;
84
- private readonly flushInterval;
85
+ private _flushTimer;
85
86
  constructor(wrappedDB: LegacyWrappedDB, settings: Partial<CacheSettings> | null | undefined, logger: Logger);
86
87
  private _lock;
87
88
  private _unlock;
89
+ private _scheduleFlush;
90
+ private _cancelFlushTimer;
88
91
  private _pauseFlush;
89
92
  private _resumeFlush;
90
93
  init(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"CacheAndBufferLayer.d.ts","sourceRoot":"","sources":["../../lib/CacheAndBufferLayer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CacheAndBufferLayer.d.ts","sourceRoot":"","sources":["../../lib/CacheAndBufferLayer.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,6 @@ import { MongoClient } from "mongodb";
17
17
  * limitations under the License.
18
18
  */
19
19
  var mongodb_db_default = class extends AbstractDatabase {
20
- interval;
21
20
  database;
22
21
  client;
23
22
  collection;
@@ -28,20 +27,10 @@ var mongodb_db_default = class extends AbstractDatabase {
28
27
  if (this.settings.database == null) this.settings.database = this.settings.dbName;
29
28
  if (!this.settings.collection) this.settings.collection = "ueberdb";
30
29
  }
31
- clearPing() {
32
- if (this.interval) clearInterval(this.interval[Symbol.toPrimitive]());
33
- }
34
- schedulePing() {
35
- this.clearPing();
36
- this.interval = setInterval(() => {
37
- this.database.command({ ping: 1 });
38
- }, 1e4);
39
- }
40
30
  init(callback) {
41
31
  MongoClient.connect(this.settings.url).then((v) => {
42
32
  this.client = v;
43
33
  this.database = v.db(this.settings.database);
44
- this.schedulePing();
45
34
  this.collection = this.database.collection(this.settings.collection);
46
35
  callback(null);
47
36
  }).catch((v) => {
@@ -55,27 +44,22 @@ var mongodb_db_default = class extends AbstractDatabase {
55
44
  console.log(v);
56
45
  callback(v);
57
46
  });
58
- this.schedulePing();
59
47
  }
60
48
  findKeys(key, notKey, callback) {
61
- const selector = { $and: [{ _id: { $regex: `${key.replace(/\*/g, "")}` } }] };
62
- if (notKey) selector.$and.push({ _id: { $not: { $regex: `${notKey.replace(/\*/g, "")}` } } });
63
- this.collection.find(selector).map((i) => i._id).toArray().then((r) => {
64
- callback(null, r);
65
- }).catch((v) => callback(v));
66
- this.schedulePing();
49
+ const escape = (s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
50
+ const selector = { $and: [{ _id: { $regex: `^${escape(key)}$` } }] };
51
+ if (notKey) selector.$and.push({ _id: { $not: { $regex: `^${escape(notKey)}$` } } });
52
+ this.collection.find(selector).map((i) => i._id).toArray().then((r) => callback(null, r)).catch((v) => callback(v));
67
53
  }
68
54
  set(key, value, callback) {
69
55
  if (key.length > 100) callback("Your Key can only be 100 chars");
70
56
  else this.collection.updateMany({ _id: key }, { $set: { value } }, { upsert: true }).then(() => callback(null)).catch((v) => callback(v));
71
- this.schedulePing();
72
57
  }
73
58
  remove(key, callback) {
74
59
  this.collection.deleteOne({ _id: key }).then((r) => callback(null, r)).catch((v) => callback(v));
75
- this.schedulePing();
76
60
  }
77
61
  doBulk(bulk, callback) {
78
- const bulkMongo = this.collection.initializeOrderedBulkOp();
62
+ const bulkMongo = this.collection.initializeUnorderedBulkOp();
79
63
  for (const i in bulk) if (bulk[i].type === "set") bulkMongo.find({ _id: bulk[i].key }).upsert().updateOne({ $set: { value: bulk[i].value } });
80
64
  else if (bulk[i].type === "remove") bulkMongo.find({ _id: bulk[i].key }).deleteOne();
81
65
  bulkMongo.execute().then((res) => {
@@ -83,10 +67,8 @@ var mongodb_db_default = class extends AbstractDatabase {
83
67
  }).catch((error) => {
84
68
  callback(error);
85
69
  });
86
- this.schedulePing();
87
70
  }
88
71
  close(callback) {
89
- this.clearPing();
90
72
  this.client.close().then((r) => callback(r));
91
73
  }
92
74
  };
@@ -27,10 +27,15 @@ var postgres_db_default = class extends AbstractDatabase {
27
27
  this.settings.cache = settings.cache || 1e3;
28
28
  this.settings.writeInterval = 100;
29
29
  this.settings.json = true;
30
- this.settings.max = this.settings.max || 20;
31
- this.settings.min = this.settings.min || 4;
32
- this.settings.idleTimeoutMillis = this.settings.idleTimeoutMillis || 1e3;
30
+ this.settings.max = this.settings.max ?? 20;
31
+ this.settings.min = this.settings.min ?? 4;
32
+ this.settings.idleTimeoutMillis = this.settings.idleTimeoutMillis ?? 1e3;
33
+ this.settings.keepAlive = this.settings.keepAlive ?? true;
34
+ this.settings.keepAliveInitialDelayMillis = this.settings.keepAliveInitialDelayMillis ?? 1e4;
33
35
  this.db = new pg.Pool(this.settings);
36
+ this.db.on("error", (err) => {
37
+ this.logger.error(`Postgres idle client error (connection discarded): ${err.stack || err}`);
38
+ });
34
39
  }
35
40
  init(callback) {
36
41
  const testTableExists = "SELECT 1 as exists FROM pg_tables WHERE tablename = 'store'";
@@ -61,7 +66,11 @@ var postgres_db_default = class extends AbstractDatabase {
61
66
  });
62
67
  }
63
68
  get(key, callback) {
64
- this.db.query("SELECT value FROM store WHERE key=$1", [key], (err, results) => {
69
+ this.db.query({
70
+ name: "ueberdb_get",
71
+ text: "SELECT value FROM store WHERE key=$1",
72
+ values: [key]
73
+ }, (err, results) => {
65
74
  let value = null;
66
75
  if (!err && results.rows.length === 1) value = results.rows[0].value;
67
76
  callback(err, value);
@@ -108,32 +117,55 @@ var postgres_db_default = class extends AbstractDatabase {
108
117
  }
109
118
  set(key, value, callback) {
110
119
  if (key.length > 100) callback(Error("Your Key can only be 100 chars"), "");
111
- else if (this.upsertStatement != null) this.db.query(this.upsertStatement, [key, value], callback);
120
+ else if (this.upsertStatement != null) {
121
+ const name = this.upsertStatement.startsWith("INSERT INTO store(key, value) VALUES") ? "ueberdb_set_native" : "ueberdb_set_function";
122
+ this.db.query({
123
+ name,
124
+ text: this.upsertStatement,
125
+ values: [key, value]
126
+ }, callback);
127
+ } else callback(Error("PostgreSQL driver not initialised: call init() before set()"), null);
112
128
  }
113
129
  remove(key, callback) {
114
- this.db.query("DELETE FROM store WHERE key=$1", [key], callback);
130
+ this.db.query({
131
+ name: "ueberdb_remove",
132
+ text: "DELETE FROM store WHERE key=$1",
133
+ values: [key]
134
+ }, callback);
115
135
  }
116
136
  doBulk(bulk, callback) {
117
- const replaceVALs = [];
118
- let removeSQL = "DELETE FROM store WHERE key IN (";
119
- const removeVALs = [];
120
- let removeCount = 0;
121
- for (const i in bulk) if (bulk[i].type === "set") replaceVALs.push([bulk[i].key, bulk[i].value]);
122
- else if (bulk[i].type === "remove") {
123
- if (removeCount !== 0) removeSQL += ",";
124
- removeCount += 1;
125
- removeSQL += `$${removeCount}`;
126
- removeVALs.push(bulk[i].key);
137
+ if (!this.upsertStatement) {
138
+ callback(Error("PostgreSQL driver not initialised: call init() before doBulk()"));
139
+ return;
127
140
  }
128
- removeSQL += ");";
129
- if (!this.upsertStatement) return;
130
- const functions = replaceVALs.map((v) => (cb) => this.db.query(this.upsertStatement, v, cb));
131
- const removeFunction = (callback) => {
132
- if (!(removeVALs.length < 1)) this.db.query(removeSQL, removeVALs, callback);
133
- else callback();
134
- };
135
- functions.push(removeFunction);
136
- async.parallel(functions, callback);
141
+ const setOps = [];
142
+ const removeKeys = [];
143
+ for (const op of bulk) if (op.type === "set") setOps.push([op.key, op.value]);
144
+ else if (op.type === "remove") removeKeys.push(op.key);
145
+ const isNativeUpsert = this.upsertStatement.startsWith("INSERT INTO store(key, value) VALUES");
146
+ const tasks = [];
147
+ if (setOps.length > 0) if (isNativeUpsert && setOps.length > 1) {
148
+ const valuesSql = [];
149
+ const params = [];
150
+ let i = 1;
151
+ for (const [k, v] of setOps) {
152
+ valuesSql.push(`($${i++},$${i++})`);
153
+ params.push(k, v);
154
+ }
155
+ const sql = `INSERT INTO store(key, value) VALUES ${valuesSql.join(",")} ON CONFLICT (key) DO UPDATE SET value = excluded.value`;
156
+ tasks.push((cb) => {
157
+ this.db.query(sql, params, (err) => cb(err));
158
+ });
159
+ } else for (const [k, v] of setOps) tasks.push((cb) => {
160
+ this.db.query(this.upsertStatement, [k, v], (err) => cb(err));
161
+ });
162
+ if (removeKeys.length > 0) {
163
+ const sql = `DELETE FROM store WHERE key IN (${removeKeys.map((_, idx) => `$${idx + 1}`).join(",")})`;
164
+ tasks.push((cb) => {
165
+ this.db.query(sql, removeKeys, (err) => cb(err));
166
+ });
167
+ }
168
+ async.parallel(tasks, callback);
137
169
  }
138
170
  close(callback) {
139
171
  this.db.end(callback);
@@ -1,4 +1,4 @@
1
- import postgres_db_default from "./postgres_db-BzDKMRC5.js";
1
+ import postgres_db_default from "./postgres_db-BlGpklXj.js";
2
2
  //#region databases/postgrespool_db.ts
3
3
  var PostgresDB = class extends postgres_db_default {
4
4
  constructor(settings) {
@@ -38,6 +38,9 @@ var RedisDB = class extends AbstractDatabase {
38
38
  this._client = createClient(options);
39
39
  }
40
40
  if (this._client) {
41
+ this._client.on("error", (err) => {
42
+ this.logger.error(`Redis client error: ${err?.stack || err}`);
43
+ });
41
44
  await this._client.connect();
42
45
  await this._client.ping();
43
46
  }
@@ -40,6 +40,9 @@ var Rethink_db = class extends AbstractDatabase {
40
40
  r.connect(this, (err, conn) => {
41
41
  if (err) throw err;
42
42
  this.connection = conn;
43
+ this.connection.on("error", (connErr) => {
44
+ this.logger.error(`RethinkDB connection error: ${connErr.stack || connErr}`);
45
+ });
43
46
  r.table(this.table).run(this.connection, (err, cursor) => {
44
47
  if (err) r.tableCreate(this.table).run(this.connection, callback);
45
48
  else if (callback) callback(null, cursor);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ueberdb2",
3
- "version": "6.1.8",
3
+ "version": "6.1.10",
4
4
  "description": "Transform every database into a object key value store",
5
5
  "keywords": [
6
6
  "database",
@@ -53,28 +53,28 @@
53
53
  "@elastic/elasticsearch": "^9.4.2",
54
54
  "@types/async": "^3.2.25",
55
55
  "@types/mssql": "^12.3.0",
56
- "@types/node": "^25.9.1",
56
+ "@types/node": "^25.9.3",
57
57
  "@types/pg": "^8.20.0",
58
58
  "@types/rethinkdb": "^2.3.21",
59
59
  "@types/wtfnode": "^0.10.0",
60
60
  "cassandra-driver": "^4.9.0",
61
61
  "cli-table3": "^0.6.5",
62
- "mongodb": "^7.2.0",
63
- "mssql": "^12.5.4",
64
- "mysql2": "^3.22.4",
62
+ "mongodb": "^7.3.0",
63
+ "mssql": "^12.5.5",
64
+ "mysql2": "^3.22.5",
65
65
  "nano": "^11.0.5",
66
- "oxfmt": "^0.52.0",
67
- "oxlint": "^1.67.0",
66
+ "oxfmt": "^0.54.0",
67
+ "oxlint": "^1.69.0",
68
68
  "pg": "^8.21.0",
69
69
  "randexp-ts": "^1.0.5",
70
70
  "redis": "^6.0.0",
71
71
  "rethinkdb": "^2.4.2",
72
- "rolldown": "^1.0.3",
73
- "semver": "^7.8.1",
72
+ "rolldown": "^1.1.1",
73
+ "semver": "^7.8.4",
74
74
  "surrealdb": "^2.0.3",
75
75
  "testcontainers": "^12.0.1",
76
76
  "typescript": "^6.0.3",
77
- "vitest": "^4.1.7",
77
+ "vitest": "^4.1.8",
78
78
  "wtfnode": "^0.10.1"
79
79
  },
80
80
  "peerDependencies": {