mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-01 04:21:58 +00:00
2.9 KiB
2.9 KiB
Request Transactions (cheatsheet)
Every DB statement and every DB-touching hook of one request runs on one transaction. Hooks never get the pool.
Rules
hookCtx.Txis always the open transaction (neverh.db), exceptBeforeHandle, which runs before any tx and must not touch the DB.OnTxBeginfires once, first, in every tx the handler opens (incl. the second short tx).OnTxBeginerror or abort: rollback, client gets a generic error, nothing leaked.- Begin or commit failure: generic error (
transaction_error/ "Transaction failed" in websocketspec, mqttspec, funcspec). - Transaction-local state (
set_config(..., true), RLS GUCs) is only visible on that tx. Set it inOnTxBegin.
Transactions per operation
| Operation | Tx 1 | Tx 2 (short, after commit) |
|---|---|---|
| read | BeforeRead, count, scan, AfterRead* |
restheadspec: AfterRead |
| create | Before*, insert |
re-fetch, BeforeScan, AfterCreate |
| update | Before*, select, update, AfterUpdate† |
re-fetch (+ AfterUpdate where noted) |
| delete (single/batch) | BeforeDelete, select, delete, AfterDelete |
none |
| funcspec query | BeforeQuery*, BeforeSQLExec, SQL, After* |
BeforeResponse |
* resolvespec, websocketspec, mqttspec, resolvemcp. restheadspec runs AfterRead in tx 2.
† restheadspec, websocketspec, mqttspec run AfterUpdate in tx 2. resolvespec, resolvemcp run it in tx 1.
resolvespec has no tx 2 for create: AfterCreate runs in tx 1, once per record (per item in a batch), after the insert and re-fetch. Its AfterRead gets the scanned slice (single and list reads) and may mask in place; a failing AfterRead fails the read (fail closed).
Tx 2 exists so the re-fetch sees trigger changes from the committed write.
Per spec
| Spec | OnTxBegin |
Helper |
|---|---|---|
| resolvespec, restheadspec, websocketspec, resolvemcp, funcspec | own HookType = common.TxHookName |
Handler.runInTx |
| mqttspec | re-exports websocketspec.OnTxBegin |
Handler.runInTx |
common.RunRequestTx(ctx, db, TxContext, onBegin, body): open tx,SetTx,onBegin,body.common.TxContext:SetTx(tx); implemented by each spec'sHookContext.
RLS / transaction settings (pkg/security)
SecurityList.SetTxSettings(fn):fn(SecurityContext) (map[string]string, error); nil disables.- Every spec's
RegisterSecurityHooksregistersOnTxBegin→security.StampTxSettings.fnis read per call, so set order does not matter. - Stamps via
set_config(name, value, true)in name order, before any other SQL. - Fail closed:
fnerror, invalid name, or non-Postgres driver with a non-empty map aborts the tx. - Name: dotted identifier (
ns.name). Value is hex-encoded in SQL, never inlined. - Low level:
security.ApplyTxSettings(secCtx, tx, map).
Test notes
- sqlmock +
SetMaxOpenConns(1): any pool use inside an open tx blocks and fails. - restheadspec model-based updates/reads need the bun adapter.