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:
parent
e0f6e7722f
commit
6ef9fcb7a2
7 changed files with 175 additions and 15 deletions
|
|
@ -71,6 +71,7 @@ import type {
|
|||
} from './plugin.js'
|
||||
import { ConnectionsCodec } from './hnsw/connectionsCodec.js'
|
||||
import { TransactionManager } from './transaction/TransactionManager.js'
|
||||
import { transactTimeoutBudget } from './transaction/Transaction.js'
|
||||
import { RevisionConflictError } from './transaction/RevisionConflictError.js'
|
||||
import { EntityNotFoundError, RelationNotFoundError } from './errors/notFound.js'
|
||||
import {
|
||||
|
|
@ -1752,7 +1753,11 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
captureAndCheck({ nouns, verbs } as CommitBeforeImages)
|
||||
}
|
||||
await this.generationStore.runWithoutGeneration(() =>
|
||||
this.transactionManager.executeTransaction(run)
|
||||
this.transactionManager.executeTransaction(run, {
|
||||
timeout: transactTimeoutBudget(
|
||||
(touched.nouns?.length ?? 0) + (touched.verbs?.length ?? 0)
|
||||
)
|
||||
})
|
||||
)
|
||||
const timestamp = Date.now()
|
||||
// Bootstrap writes are not generation-stamped; emit without one.
|
||||
|
|
@ -1764,7 +1769,12 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
receipt = await this.generationStore.commitSingleOp({
|
||||
touched,
|
||||
precommit: captureAndCheck,
|
||||
execute: () => this.transactionManager.executeTransaction(run)
|
||||
execute: () =>
|
||||
this.transactionManager.executeTransaction(run, {
|
||||
timeout: transactTimeoutBudget(
|
||||
(touched.nouns?.length ?? 0) + (touched.verbs?.length ?? 0)
|
||||
)
|
||||
})
|
||||
})
|
||||
} catch (err) {
|
||||
// A failed rollback that left the store inconsistent (a remove/update
|
||||
|
|
@ -6655,10 +6665,13 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
/**
|
||||
* Add multiple entities in a single batch operation
|
||||
*
|
||||
* Uses batch embedding (embedBatch) to pre-compute all vectors in a single
|
||||
* WASM forward pass instead of N individual embed() calls, providing 5-10x
|
||||
* speedup on bulk inserts. Automatically adapts batch size and parallelism
|
||||
* to the storage adapter (e.g., smaller batches for cloud storage).
|
||||
* Uses batch embedding (embedBatch) to pre-compute all vectors before any
|
||||
* storage write, keeping model inference out of the per-item write path.
|
||||
* (On the default WASM engine, batch throughput is comparable to sequential
|
||||
* embed() calls — measured ~160 ms/text either way; a native embedding
|
||||
* provider may batch faster.) Automatically adapts batch size and
|
||||
* parallelism to the storage adapter (e.g., smaller batches for cloud
|
||||
* storage).
|
||||
*
|
||||
* @param params - Batch add parameters
|
||||
* @param params.items - Array of AddParams (same shape as brain.add())
|
||||
|
|
@ -7797,11 +7810,17 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
ifAtGeneration: options?.ifAtGeneration,
|
||||
precommit: casPrecommit,
|
||||
execute: async () => {
|
||||
await this.transactionManager.executeTransaction(async (tx) => {
|
||||
for (const operation of plan.operations) {
|
||||
tx.addOperation(operation)
|
||||
}
|
||||
})
|
||||
await this.transactionManager.executeTransaction(
|
||||
async (tx) => {
|
||||
for (const operation of plan.operations) {
|
||||
tx.addOperation(operation)
|
||||
}
|
||||
},
|
||||
// Budget scales with the batch (or the caller's explicit
|
||||
// timeoutMs): a flat 30s cap silently limited honest bulk work
|
||||
// to ~15 ops on network disks (~2s/op measured in the field).
|
||||
{ timeout: transactTimeoutBudget(plan.operations.length, options?.timeoutMs) }
|
||||
)
|
||||
}
|
||||
}))
|
||||
} catch (err) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue