@retaw106/mmc-pi 1.2.3 → 2.1.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.
@@ -4,12 +4,13 @@ import {
4
4
 
5
5
  // ../plugin/src/shared/write-transaction-timing.ts
6
6
  var SLOW_WRITE_TRANSACTION_THRESHOLD_MS = 1000;
7
- function logSlowWriteTransaction(site, startedAt, thresholdMs = SLOW_WRITE_TRANSACTION_THRESHOLD_MS, completedAtMs = performance.now()) {
7
+ function logSlowWriteTransaction(site, startedAt, thresholdMs = SLOW_WRITE_TRANSACTION_THRESHOLD_MS, completedAtMs = performance.now(), steps) {
8
8
  try {
9
9
  const durationMs = completedAtMs - startedAt;
10
10
  if (durationMs < thresholdMs)
11
11
  return;
12
- log(`[magic-context] slow write transaction: site=${site} held=${durationMs.toFixed(1)}ms`);
12
+ const detail = steps ? Object.entries(steps).map(([name, ms]) => `${name}=${ms.toFixed(1)}ms`).join(" ") : "";
13
+ log(`[magic-context] slow write transaction: site=${site} held=${durationMs.toFixed(1)}ms${detail ? ` ${detail}` : ""}`);
13
14
  } catch {}
14
15
  }
15
16
 
@@ -69,6 +69,8 @@ function installTransactionRouting(db, readonly) {
69
69
  configurable: true,
70
70
  writable: true,
71
71
  value: (sql) => {
72
+ if (/^\s*(?:COMMIT|END)\b/i.test(sql))
73
+ assertTransformWrite();
72
74
  if (/^\s*BEGIN\s+(?:IMMEDIATE|EXCLUSIVE)(?:\s+TRANSACTION)?\s*;?\s*$/i.test(sql)) {
73
75
  openWriterTransactions.delete(db);
74
76
  if (transformPassScope.getStore()?.active || backgroundWriterScope.getStore())
@@ -221,6 +223,15 @@ function pragmaNumber(db, name) {
221
223
  }
222
224
  var statementOwners = new WeakMap;
223
225
  var FIRST_PRUNE_AT = 256;
226
+ function closeReleasingStatements(close, args) {
227
+ if (args[0] === true)
228
+ return close(true);
229
+ try {
230
+ return close(true);
231
+ } catch {
232
+ return close(...args);
233
+ }
234
+ }
224
235
  function ownPreparedStatement(db, statement) {
225
236
  const finalizable = statement;
226
237
  if (typeof finalizable.finalize !== "function")
@@ -244,7 +255,7 @@ function ownPreparedStatement(db, statement) {
244
255
  }
245
256
  }
246
257
  references.clear();
247
- const result = close(...args);
258
+ const result = closeReleasingStatements(close, args);
248
259
  if (failure)
249
260
  throw failure;
250
261
  return result;
@@ -298,6 +309,15 @@ function withSqliteTransformPass(operation) {
298
309
  function withoutSqliteTransformPass(operation) {
299
310
  return transformPassScope.run(undefined, operation);
300
311
  }
312
+ function guardSqliteTransformPass(guard) {
313
+ const lease = transformPassScope.getStore();
314
+ if (lease)
315
+ lease.guard = guard;
316
+ }
317
+ function assertTransformWrite() {
318
+ if (!backgroundWriterScope.getStore())
319
+ transformPassScope.getStore()?.guard?.assert();
320
+ }
301
321
  function isTransientSqliteError(error) {
302
322
  if (!error || typeof error !== "object")
303
323
  return false;
@@ -316,6 +336,7 @@ class SqliteAcquisitionBusyError extends Error {
316
336
  }
317
337
  var SHORT_BUSY_TIMEOUT_MS = 25;
318
338
  var FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS = 250;
339
+ var FOREGROUND_ACQUISITION_BUDGET_MS = 16500;
319
340
  var SLOW_WRITER_LOG_MS = 250;
320
341
  var WRITER_TRANSACTION_END = /^\s*(?:COMMIT|END|ROLLBACK)(?:\s+TRANSACTION)?\s*;?\s*$/i;
321
342
  var writerAcquisitionScope = new AsyncLocalStorage;
@@ -325,6 +346,7 @@ function foregroundWaitLease() {
325
346
  return lease?.active && !admissionScope.getStore() && !backgroundWriterScope.getStore() ? lease : undefined;
326
347
  }
327
348
  function withAutocommitTimeout(db, operation) {
349
+ assertTransformWrite();
328
350
  const lease = foregroundWaitLease();
329
351
  if (isInTransaction(db) || !lease && !backgroundWriterScope.getStore())
330
352
  return operation();
@@ -345,6 +367,7 @@ function withAutocommitTimeout(db, operation) {
345
367
  }
346
368
  }
347
369
  function acquireShort(db, acquire, site) {
370
+ assertTransformWrite();
348
371
  const started = performance.now();
349
372
  const previous = pragmaNumber(db, "busy_timeout");
350
373
  const outer = writerAcquisitionScope.getStore();
@@ -356,7 +379,7 @@ function acquireShort(db, acquire, site) {
356
379
  let acquiredAt;
357
380
  try {
358
381
  const lease = foregroundWaitLease();
359
- const timeout = lease ? Math.floor(lease.remainingWaitMs) : SHORT_BUSY_TIMEOUT_MS;
382
+ const timeout = Math.floor(Math.min(lease ? lease.remainingWaitMs : SHORT_BUSY_TIMEOUT_MS, owner?.remainingMs?.() ?? lease?.guard?.remainingMs() ?? Infinity));
360
383
  db.exec(`PRAGMA busy_timeout=${timeout}`);
361
384
  acquire();
362
385
  acquiredAt = performance.now();
@@ -402,6 +425,56 @@ function endWriterTransaction(db, end, sql) {
402
425
  }
403
426
  }
404
427
  }
428
+ async function withAsyncPrivilegedWriter(db, operation, options = {}) {
429
+ const started = performance.now();
430
+ const budgetMs = Math.max(0, Math.min(FOREGROUND_ACQUISITION_BUDGET_MS, options.budgetMs ?? Infinity));
431
+ let attempts = 0;
432
+ for (;; ) {
433
+ options.signal?.throwIfAborted();
434
+ options.assertCurrentPass?.();
435
+ attempts++;
436
+ let entered = false;
437
+ try {
438
+ const acquisition = {
439
+ site: "privileged_writer",
440
+ lane: "foreground",
441
+ startedAt: started,
442
+ attempts,
443
+ claimed: false,
444
+ remainingMs: () => Math.max(0, budgetMs - (performance.now() - started))
445
+ };
446
+ return admissionScope.run(true, () => withSqliteTransformPass(() => writerAcquisitionScope.run(acquisition, () => withPrivilegedWriter(db, () => {
447
+ entered = true;
448
+ return operation();
449
+ }))));
450
+ } catch (error) {
451
+ if (entered || !(error instanceof SqliteAcquisitionBusyError))
452
+ throw error;
453
+ options.beforeRetry?.(error);
454
+ const elapsed = performance.now() - started;
455
+ if (elapsed >= budgetMs) {
456
+ reportSqliteDiagnostic(`sqlite writer site=privileged_writer lane=foreground acquire_ms=${Math.round(elapsed)} attempts=${attempts} outcome=busy`);
457
+ throw error;
458
+ }
459
+ const backoff = attempts === 1 ? 500 : 1000;
460
+ const delay = Math.min(options.jitter ? backoff * (0.8 + Math.random() * 0.4) : backoff, budgetMs - elapsed);
461
+ await new Promise((resolve, reject) => {
462
+ const abort = () => {
463
+ clearTimeout(timer);
464
+ options.signal?.removeEventListener("abort", abort);
465
+ reject(options.signal?.reason ?? new Error("Writer wait aborted"));
466
+ };
467
+ const timer = setTimeout(() => {
468
+ options.signal?.removeEventListener("abort", abort);
469
+ resolve();
470
+ }, delay);
471
+ options.signal?.addEventListener("abort", abort, { once: true });
472
+ if (options.signal?.aborted)
473
+ abort();
474
+ });
475
+ }
476
+ }
477
+ }
405
478
  function isInTransaction(db) {
406
479
  const candidate = db;
407
480
  return candidate.inTransaction === true || candidate.isTransaction === true;
@@ -461,4 +534,4 @@ function withPrivilegedWriter(db, operation) {
461
534
  }
462
535
  }
463
536
 
464
- export { registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, Database, prepareCachedStatement, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, isTransientSqliteError, withPrivilegedWriter };
537
+ export { registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, Database, prepareCachedStatement, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, guardSqliteTransformPass, isTransientSqliteError, withAsyncPrivilegedWriter, withPrivilegedWriter };