feat: scaled transact budgets + labeled timeout diagnostics + envelope docs

- The transact apply budget scales with the batch — max(30s, opCount x 2s) —
  or is exactly the caller's new TransactOptions.timeoutMs. A flat 30s cap
  silently limited honest bulk work to ~15 operations on network-attached
  storage (~2s/op measured in a production import) while looking generous for
  small batches. Internal batch paths (removeMany chunks) get the same
  scaling via the shared transactTimeoutBudget helper.
- TransactionTimeoutError now reports the operation it stopped at as i/N with
  the operation's name, elapsed vs budgeted time, and states the batch rolled
  back atomically and is retryable; context carries the fields
  programmatically.
- The transact operational envelope is documented (optimistic-concurrency
  guide): budget math, chunking with ifAbsent idempotency, when to reach for
  addMany vs transact, and the precompute pattern (embedBatch + per-op
  vector) that keeps inference out of the commit path.
- Embedding claims made honest per an end-to-end probe of the built dist:
  batch and single embedding paths produce bit-identical vectors and a
  vector-supplied add is fully searchable, but batch throughput on the
  default WASM engine measures comparable to sequential (~160ms/text) — the
  5-10x speedup claim in the addMany JSDoc is replaced with the measured
  reality and the actual win (inference outside the budgeted write path).
This commit is contained in:
David Snelling 2026-07-17 16:49:38 -07:00
parent e0f6e7722f
commit 6ef9fcb7a2
7 changed files with 175 additions and 15 deletions

View file

@ -77,11 +77,24 @@ export class InvalidTransactionStateError extends TransactionError {
export class TransactionTimeoutError extends TransactionError {
constructor(
timeoutMs: number,
operationIndex: number
operationIndex: number,
telemetry?: {
elapsedMs?: number
totalOperations?: number
operationName?: string
}
) {
const progress =
telemetry?.totalOperations !== undefined
? `${operationIndex}/${telemetry.totalOperations}`
: String(operationIndex)
const name = telemetry?.operationName ? ` ('${telemetry.operationName}')` : ''
const elapsed =
telemetry?.elapsedMs !== undefined ? `${telemetry.elapsedMs}ms elapsed, ` : ''
super(
`Transaction timed out after ${timeoutMs}ms at operation ${operationIndex}`,
{ timeoutMs, operationIndex }
`Transaction timed out at operation ${progress}${name}${elapsed}budget ${timeoutMs}ms. ` +
`The batch rolled back atomically; retry with a higher timeoutMs or a smaller batch.`,
{ timeoutMs, operationIndex, ...telemetry }
)
this.name = 'TransactionTimeoutError'
}