{"record":{"id":"11b1a1d28b61e433","repo":"gastownhall/beads","slug":"failed-to-acquire-connection-w-11b1a1","errorCode":null,"errorMessage":"failed to acquire connection: %w","messagePattern":"failed to acquire connection: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/transaction.go","lineNumber":185,"sourceCode":"\tacquireStart := time.Now()\n\n\tconn, err := s.db.Conn(ctx)\n\tacquireMs := float64(time.Since(acquireStart).Microseconds()) / 1000.0\n\tdoltMetrics.connAcquireMs.Record(ctx, acquireMs)\n\n\t// Detect pool-wait: if WaitCount increased, the pool was exhausted and\n\t// this caller had to wait for a connection to become available.\n\tif err == nil {\n\t\tstatsAfter := s.db.Stats()\n\t\tif statsAfter.WaitCount > statsBefore.WaitCount {\n\t\t\tdoltMetrics.poolWaitCount.Add(ctx, statsAfter.WaitCount-statsBefore.WaitCount)\n\t\t\twaitMs := float64(statsAfter.WaitDuration-statsBefore.WaitDuration) / float64(time.Millisecond)\n\t\t\tdoltMetrics.poolWaitMs.Record(ctx, waitMs)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to acquire connection: %w\", err)\n\t}\n\tdefer conn.Close()\n\n\tvar currentBranch string\n\tif err := conn.QueryRowContext(ctx, \"SELECT active_branch()\").Scan(&currentBranch); err != nil {\n\t\treturn fmt.Errorf(\"failed to read active branch: %w\", err)\n\t}\n\n\tregularTx, err := conn.BeginTx(ctx, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to begin regular tx: %w\", err)\n\t}\n\n\t// The journal counter and rows must commit in the SAME SQL transaction as\n\t// every mutation they describe. bd_events_journal and bd_events_seq are\n\t// dolt_ignored, so on the default split-transaction shape they would land in\n\t// the ignored transaction while the mutation lands in the regular one: a\n\t// mixed durable+wisp callback would then make the two transactions contend","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/transaction.go#L167-L203","documentation":"runDoltTransaction pins a single pooled connection for the whole logical transaction (SQL tx + DOLT_COMMIT must share one Dolt session). This error wraps the failure of s.db.Conn(ctx) — the pool could not hand out a connection, usually because the pool is exhausted and the context deadline expired, the Dolt SQL server refused/dropped the dial, or the context was canceled. It is the entry gate of every RunInTransaction write in the Dolt storage backend.","triggerScenarios":"Calling DoltStore.RunInTransaction (or any write API) when: the connection pool is exhausted and ctx expires before a connection frees up; the Dolt server is down or the TCP dial fails; the context is canceled mid-wait; or the connection was closed at dial time due to bad DSN/auth.","commonSituations":"Long-running callbacks holding connections while MaxOpenConns is small; many concurrent `bd` processes against one embedded/hosted Dolt server; server restart or network blip; a leaked connection elsewhere starving the pool.","solutions":["Check Dolt server reachability (host/port, auth) and that the DSN in s.connStr is valid","Increase MaxOpenConns on the pool or reduce concurrent writers holding connections","Check ctx deadlines — a too-short timeout makes pool waits fail; raise it or scope it wider","Look for connection leaks (conns not closed) and long transactions that pin pool connections; retry the operation — runDoltTransaction setup failures are retried by withTransactionSetupRetry"],"exampleFix":"// before\ndb.SetMaxOpenConns(1) // serializes all writers; pool waits expire\n// after\ndb.SetMaxOpenConns(10) // allow concurrent pinned-connection transactions","handlingStrategy":"retry","validationCode":"if err := db.PingContext(ctx); err != nil { return fmt.Errorf(\"dolt unreachable before write: %w\", err) }\nst := db.Stats()\nif st.MaxOpenConnections > 0 && st.InUse >= st.MaxOpenConnections { return errors.New(\"pool exhausted; defer or widen pool\") }","typeGuard":null,"tryCatchPattern":"err := store.RunInTransaction(ctx, msg, fn)\nvar netErr net.Error\nif errors.As(err, &netErr) || errors.Is(err, context.DeadlineExceeded) {\n    // setup-phase failure: safe to retry\n    err = store.RunInTransaction(ctx, msg, fn)\n}","preventionTips":["Size MaxOpenConns above your worst-case concurrent writers","Keep per-operation context deadlines generous enough for pool waits","Ping or health-check the store before long write batches","Watch db.Stats() WaitCount to detect pool exhaustion early"],"tags":["go","database","connection-pool","dolt"],"backgroundTag":"db-connection-acquisition-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}