Skip to content

Latest commit

 

History

History
68 lines (47 loc) · 2.7 KB

File metadata and controls

68 lines (47 loc) · 2.7 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Build & Development Commands

This project uses mise for tooling. All commands should be run via mise run:

mise run all       # Format, lint, and test (full CI pipeline)
mise run test      # Run tests with gotestsum (auto-reruns failures)
mise run lint      # Run golangci-lint with --fix
mise run format    # Run go fmt
mise run tidy      # Update dependencies and run go mod tidy

To run a single test:

go test -run TestIsTransient -v

Architecture

retrypool is a drop-in wrapper around pgxpool.Pool that adds automatic retry with exponential backoff for transient PostgreSQL errors.

Core Design

  • Pool (retrypool.go) - Wraps *pgxpool.Pool and implements the DBTX interface (Exec, Query, QueryRow) expected by sqlc-generated code. Each method retries on transient errors.

  • retryRow (retry.go) - Wraps pgx.Row to handle retry in QueryRow. This is needed because pgx.Row.Scan() has no context parameter, so the context must be stored in the struct (hence the //nolint:containedctx directive).

  • IsTransient() (retry.go) - Determines if an error is safe to retry. Checks:

    • pgconn.SafeToRetry() - query never sent to server
    • Network errors (timeouts, connection refused/reset, broken pipe)
    • PostgreSQL error codes: 08xxx (connection exceptions), 57P01/57P02/57P03 (admin/crash shutdown)
    • Error message patterns as fallback

Retry Strategy

  • Exponential backoff: baseDelay * 2^attempt
  • 25% jitter to prevent thundering herd
  • Maximum delay capped at 5 seconds
  • Context cancellation respected during waits
  • Defaults: 3 retries, 100ms base delay

Write Safety Limitation

This library is designed primarily for read operations. While pgconn.SafeToRetry() prevents retrying queries already sent to the server, edge cases exist where a write succeeds but the connection fails before client confirmation.

Safe: SELECT queries, idempotent writes, writes with unique constraints Caution: Non-idempotent INSERTs, incremental updates (balance = balance + 100)

For critical non-idempotent writes, use rp.Pool() directly or wrap in a transaction.

Usage Pattern

pool, _ := pgxpool.New(ctx, connString)
rp := retrypool.New(pool, retrypool.WithMaxRetries(5))
// rp is now usable as a DBTX for sqlc

Linting

Uses golangci-lint with a strict configuration (.golangci.yml). Notable enabled linters:

  • containedctx - warns about storing context in structs (explicitly allowed in retryRow)
  • wsl_v5 - enforces whitespace style
  • exhaustive - requires exhaustive switch statements