sql: `withTransaction` turns a failed COMMIT into a defect, not a SqlError
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 67/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- postgresql, typescript
- Ambito
- databases
Direzione di ricerca
Start at packages/effect/src/sql/SqlClient.ts:432 (Effect.orDie(options.commit(conn))) and read the surrounding withTransaction plus the onCommitFailure logic added in #8529. The linked history matters: #8558's tests in packages/pg assert the defect for a ROLLBACK-answered COMMIT and will need updating, and #7236 shows the precedent for turning BEGIN failures into typed SqlError. Done means the reproduction in the issue fails (not dies) with UniqueViolation so catchTag("SqlError") catches it, ROLLBACK/RELEASE SAVEPOINT failures stay defects, and the maintainer answers the two open questions (cleanup-failure cause, patch vs v4/next-minor) before merging.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
When PostgreSQL rejects COMMIT, withTransaction dies instead of failing with a SqlError. PostgreSQL reports deferred constraint violations and serialization failures (40001) at COMMIT. The driver classifies them (UniqueViolation, SerializationError), but catchTag("SqlError") and typed retry policies never see them. withTransaction already returns Effect<A, E | SqlError, R> (packages/effect/src/sql/SqlClient.ts:63).
Is the defect intended? If not, I have a PR ready (2 source lines).
Versions: [email protected] and @effect/[email protected]. Same code on main at 2131d44bd and on the head of #8735. PostgreSQL 18, Node 26.8.2.
Reproduction
import { Cause, Effect, Exit, Redacted } from "effect"
import { PgClient } from "@effect/sql-pg"
import * as Reactivity from "effect/reactivity/Reactivity"
const url = Redacted.make("postgres://postgres:[email protected]:5432/postgres")
const show = (label, exit) =>
console.log(label, Exit.isSuccess(exit) ? exit.value : {
hasFails: Cause.hasFails(exit.cause),
hasDies: Cause.hasDies(exit.cause),
reason: Cause.squash(exit.cause).reason._tag
})
const program = Effect.gen(function*() {
const a = yield* PgClient.make({ url, maxConnections: 1 })
yield* a`drop table if exists dfr`
yield* a`create table dfr (id int unique deferrable initially deferred)`
show("deferred unique", yield* Effect.exit(a.withTransaction(a`insert into dfr values (1), (1)`)))
show("catchTag", yield* Effect.exit(a.withTransaction(a`insert into dfr values (2), (2)`).pipe(
Effect.catchTag("SqlError", (e) => Effect.succeed(`caught ${e.reason._tag}`))
)))
})
await Effect.runPromise(Effect.scoped(program).pipe(Effect.provide(Reactivity.layer)))
Actual:
deferred unique { hasFails: false, hasDies: true, reason: 'UniqueViolation' }
catchTag { hasFails: false, hasDies: true, reason: 'UniqueViolation' }
A write skew under SERIALIZABLE dies the same way with SerializationError. The server log shows each error on STATEMENT: COMMIT.
Expected: each exit fails with the SqlError, and catchTag returns caught UniqueViolation.
Cause
packages/effect/src/sql/SqlClient.ts:432: effect = Effect.orDie(options.commit(conn))
History
- The
orDiecame with the sqlfx import (#2104) with no stated reason. - #7236 made a failed
BEGINa typedSqlError. It leftCOMMITunchanged. - #8257 made
@effect/sql-sqlite-dofail a rejected native commit with a typedSqlError. - #8529 (merged 2026-09-25) added
onCommitFailure. Its body says: "Preserve the original COMMIT defect and include cleanup failure in its cause." - #8558 (2026-09-27) fails a pg
COMMITthat PostgreSQL answers withROLLBACK. It turns that failure into a defect "the same way it handles any failedCOMMIT", and its tests assert the defect.
Proposal
Drop the orDie, so withTransaction fails with the SqlError from commit. The public type does not change. ROLLBACK and RELEASE SAVEPOINT failures stay defects.
One gap: when SQLite onCommitFailure cleanup fails, the cause becomes Fail(SqlError) plus Die(cleanup). catchTag("SqlError") and Effect.catch recover and drop the Die, so the caller never sees the cleanup failure. The connection stays poisoned, so the next query still fails; no data is lost. Should a cleanup failure keep the whole exit a defect?
This changes behavior: Effect.catch now also sees COMMIT failures. Should it go to main as a patch, like #7236, or to v4/next-minor?
- Lingua principale
- TypeScript
- Stelle
- 16.7k
- Fork
- 808
- Merge medio
- 10h 36m
- PR unite (30g)
- 449
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di Effect-TS/effect
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
Effect-TS/effect#8881 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 1-3 ore Idoneità per principianti 86/100
Effect-TS/effect#8863 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
BrowserWorkerRunner: port finalizer throws when the worker global has no close() (Bun)Forse già presa @santiago-ramos-02 l’ha presa 7 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
Effect-TS/effect#8635 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Support {self: this} for fnUntracedForse di nuovo libera @ArjunCodess l’ha presa 19 giorni fa e non c’è nessuna pull request aperta. Apertaenhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
Effect-TS/effect#8101 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 48/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di Effect-TS/effect
Issue simili
-
DB-plane provider_chat_options.* is accepted by config set but never merged into the loaded configAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
Bump Firebase JS SDK (12.19.0 → 13.0.0)Forse già presa @SelaseKay l’ha presa oggi. ApertaNeeds Attention type: enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
invertase/react-native-firebase#9364 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
clouflaure de fernandoApertaenhancement
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
cloudflare/mcp#271 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 4 giorni
-
[fullsend] E2E: rhdh-version-override — run-e2e.sh overrides RHDH_VERSION to non-existent 2.1Apertae2e-failure ready-to-code
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
redhat-developer/rhdh-plugin-export-overlays#4261 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno