@bobfrankston/mailx-store 0.1.48 → 0.1.49

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 (3) hide show
  1. package/db.d.ts +1 -0
  2. package/db.js +19 -0
  3. package/package.json +1 -1
package/db.d.ts CHANGED
@@ -15,6 +15,7 @@ export declare class MailxDB {
15
15
  readonly readOnly: boolean;
16
16
  constructor(dbDir: string, opts?: {
17
17
  readOnly?: boolean;
18
+ skipMigrations?: boolean;
18
19
  });
19
20
  /** Fail loud + early if expected columns are missing. Cheap (PRAGMA only
20
21
  * runs at startup). The user-facing message names the recovery command. */
package/db.js CHANGED
@@ -412,6 +412,16 @@ export class MailxDB {
412
412
  const dbPath = path.join(dbDir, "mailx.db");
413
413
  this.readOnly = !!opts.readOnly;
414
414
  this.db = new DatabaseSync(dbPath);
415
+ if (opts.skipMigrations && !opts.readOnly) {
416
+ // Write-capable connection that does NOT run schema/migrations —
417
+ // used by the sync worker, which opens its own writer AFTER the
418
+ // main thread has already built the schema. Two threads running the
419
+ // DDL/backfills concurrently would race on the WAL write lock.
420
+ this.db.exec("PRAGMA journal_mode = WAL");
421
+ this.db.exec("PRAGMA foreign_keys = ON");
422
+ this.db.exec("PRAGMA busy_timeout = 5000");
423
+ return;
424
+ }
415
425
  if (this.readOnly) {
416
426
  // Read-worker connection (Phase 0 read isolation). The main-thread
417
427
  // MailxDB has ALREADY created the schema and run every migration /
@@ -430,10 +440,19 @@ export class MailxDB {
430
440
  // so readers see committed snapshots with no journal_mode call.
431
441
  this.db.exec("PRAGMA foreign_keys = ON");
432
442
  this.db.exec("PRAGMA query_only = ON");
443
+ // A reader can briefly collide with a WAL checkpoint; wait, don't throw.
444
+ this.db.exec("PRAGMA busy_timeout = 5000");
433
445
  return;
434
446
  }
435
447
  this.db.exec("PRAGMA journal_mode = WAL");
436
448
  this.db.exec("PRAGMA foreign_keys = ON");
449
+ // Multi-writer foundation for the sync-worker migration: once sync runs
450
+ // on its own thread with its own write connection, it and the main
451
+ // thread are two writers on one WAL file. WAL serializes writers, so a
452
+ // collision returns SQLITE_BUSY *immediately* by default — busy_timeout
453
+ // makes the loser WAIT for the lock (up to 5s) instead of failing. Safe
454
+ // and inert today (single writer); required before the worker lands.
455
+ this.db.exec("PRAGMA busy_timeout = 5000");
437
456
  this.db.exec(SCHEMA);
438
457
  this.migrateFtsSchema();
439
458
  // Purge phantom uid=0 rows (invalid IMAP UID — empty stub letters, the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-store",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",