{"id":25741501,"url":"https://github.com/mkbeh/xpg","last_synced_at":"2026-04-19T20:01:20.257Z","repository":{"id":271118219,"uuid":"891998787","full_name":"mkbeh/xpg","owner":"mkbeh","description":"library for working with Postgres, using pgx and integration with OpenTelemetry for tracing and metrics","archived":false,"fork":false,"pushed_at":"2025-11-19T19:30:38.000Z","size":42,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-19T21:12:52.015Z","etag":null,"topics":["client","go","golang","pgx","postgres","postgres-client"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mkbeh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-11-21T10:36:13.000Z","updated_at":"2025-11-19T19:30:14.000Z","dependencies_parsed_at":"2025-01-05T16:36:51.978Z","dependency_job_id":"54ae9b1b-9bf4-47b7-be5a-a8c76080d39c","html_url":"https://github.com/mkbeh/xpg","commit_stats":null,"previous_names":["mkbeh/postgres","mkbeh/xpg"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/mkbeh/xpg","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkbeh%2Fxpg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkbeh%2Fxpg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkbeh%2Fxpg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkbeh%2Fxpg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mkbeh","download_url":"https://codeload.github.com/mkbeh/xpg/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkbeh%2Fxpg/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32020702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","go","golang","pgx","postgres","postgres-client"],"created_at":"2025-02-26T09:46:02.612Z","updated_at":"2026-04-19T20:01:20.251Z","avatar_url":"https://github.com/mkbeh.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xpg\n\nPostgreSQL client for Go built on top of [pgx](https://github.com/jackc/pgx), with connection pooling, separate\nmaster/replica pools, built-in migrations, query builder, and OpenTelemetry observability out of the box.\n\n## Features\n\n- Separate connection pools for master (writer) and replica (reader)\n- Query builder via [squirrel](https://github.com/Masterminds/squirrel)\n- Built-in migrations via [golang-migrate](https://github.com/golang-migrate/migrate)\n- Transaction helpers with panic recovery\n- OpenTelemetry tracing and Prometheus metrics\n\n## Installation\n\n```bash\ngo get github.com/mkbeh/xpg\n```\n\n## Getting started\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/mkbeh/xpg\"\n)\n\nfunc main() {\n\tcfg := \u0026postgres.Config{\n\t\tClusterHost:        \"127.0.0.1\",\n\t\tClusterPort:        \"5432\",\n\t\tClusterReplicaPort: \"5432\",\n\t\tUser:               \"user\",\n\t\tPassword:           \"pass\",\n\t\tDB:                 \"postgres\",\n\t}\n\n\twriter, err := postgres.NewWriter(\n\t\tpostgres.WithConfig(cfg),\n\t\tpostgres.WithClientID(\"my-service\"),\n\t)\n\tif err != nil {\n\t\tlog.Fatalln(\"failed to init writer pool:\", err)\n\t}\n\tdefer writer.Close()\n\n\treader, err := postgres.NewReader(postgres.WithConfig(cfg))\n\tif err != nil {\n\t\tlog.Fatalln(\"failed to init reader pool:\", err)\n\t}\n\tdefer reader.Close()\n\n\tvar greeting string\n\tif err = writer.QueryRow(context.Background(), \"select 'Hello, world!'\").Scan(\u0026greeting); err != nil {\n\t\tlog.Fatalln(\"QueryRow failed:\", err)\n\t}\n\tfmt.Println(greeting)\n}\n```\n\nMore examples: [examples/](https://github.com/mkbeh/xpg/tree/main/examples)\n\n## Transactions\n\n```go\nerr := writer.RunInTxx(ctx, func (ctx context.Context) error {\n_, err := writer.Exec(ctx, \"insert into orders (id) values ($1)\", id)\nreturn err\n})\n```\n\n`RunInTxx` uses default transaction options. For custom isolation level or access mode, use `RunInTx`:\n\n```go\nerr := writer.RunInTx(ctx, func (ctx context.Context) error {\n// ...\n}, pgx.TxOptions{IsoLevel: pgx.Serializable})\n```\n\nRollback is automatic. Commit happens only if the function returns nil. Panics are recovered and returned as errors.\n\n## Migrations\n\nCreate `embed.go` in your migrations directory:\n\n```go\npackage migrations\n\nimport \"embed\"\n\n//go:embed *.sql\nvar FS embed.FS\n```\n\nPass it via `WithMigrations`. Migrations run automatically on `NewWriter` when `MigrateEnabled` is true:\n\n```go\nwriter, err := postgres.NewWriter(\npostgres.WithConfig(cfg),\npostgres.WithMigrations(migrations.FS),\n)\n```\n\nMigration files follow standard golang-migrate naming: `000001_create_users.up.sql` / `000001_create_users.down.sql`.\n\n## Configuration\n\nThe DSN is constructed from `Config` fields in the format:\n\n```\npostgres://user:pass@host:port/db?sslmode=disable\u0026application_name=\u003cid\u003e\u0026\u003cargs\u003e\n```\n\n### Config struct\n\n```go\ncfg := \u0026postgres.Config{\nClusterHost:        \"127.0.0.1\", // required\nClusterPort:        \"5432\",      // required, master port\nClusterReplicaPort: \"5433\", // required, replica port\nUser:               \"user\", // required\nPassword:           \"pass\",        // required\nDB:                 \"mydb\",        // required\n\nMaxRWConn:       16,\nMaxROConn:       16,\nMaxConnLifetime: 5 * time.Minute,\nMaxConnIdleTime: 30 * time.Second,\n\nMigrateEnabled: true,\n}\n```\n\n### Environment variables\n\n| Variable                              | Required | Default                 | Description                                            |\n|---------------------------------------|----------|-------------------------|--------------------------------------------------------|\n| `POSTGRES_CLUSTER_HOST`               | ✓        | —                       | Database host                                          |\n| `POSTGRES_CLUSTER_PORT`               | ✓        | —                       | Master port                                            |\n| `POSTGRES_CLUSTER_REPLICA_PORT`       | ✓        | —                       | Replica port                                           |\n| `POSTGRES_USER`                       | ✓        | —                       | Database user                                          |\n| `POSTGRES_PASSWORD`                   | ✓        | —                       | Database password                                      |\n| `POSTGRES_DB`                         | ✓        | —                       | Database name                                          |\n| `POSTGRES_SHARD_ID`                   |          | `0`                     | Shard ID (exposed in metrics)                          |\n| `POSTGRES_MIN_RW_CONN`                |          | `1`                     | Min connections in writer pool                         |\n| `POSTGRES_MIN_RO_CONN`                |          | `1`                     | Min connections in reader pool                         |\n| `POSTGRES_MAX_RW_CONN`                |          | `max(4, NumCPU)`        | Max connections in writer pool                         |\n| `POSTGRES_MAX_RO_CONN`                |          | `max(4, NumCPU)`        | Max connections in reader pool                         |\n| `POSTGRES_MAX_CONN_LIFETIME`          |          | `1m`                    | Max connection lifetime                                |\n| `POSTGRES_MAX_CONN_IDLE_TIME`         |          | `30s`                   | Max idle connection lifetime                           |\n| `POSTGRES_QUERY_EXEC_MODE`            |          | `cache_statement`       | Query execution mode (see below)                       |\n| `POSTGRES_STATEMENT_CACHE_CAPACITY`   |          | `128`                   | Statement cache size                                   |\n| `POSTGRES_DESCRIPTION_CACHE_CAPACITY` |          | `512`                   | Description cache size                                 |\n| `POSTGRES_MASTER_ARGS`                |          | —                       | Extra DSN args for master, e.g. `search_path=myschema` |\n| `POSTGRES_REPLICA_ARGS`               |          | —                       | Extra DSN args for replica                             |\n| `POSTGRES_MIGRATE_ENABLED`            |          | `false`                 | Run migrations on startup                              |\n| `POSTGRES_MIGRATE_PORT`               |          | `POSTGRES_CLUSTER_PORT` | Port used for migrations                               |\n| `POSTGRES_MIGRATE_ARGS`               |          | —                       | Extra DSN args for migration connection                |\n\n### Query execution modes\n\n| Value             | Protocol | Round trips             | Description                                                                                                                                                                        |\n|-------------------|----------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `cache_statement` | Extended | 1 (after cache warm-up) | Automatically prepares and caches statements. **Default.** May fail on first execution after schema changes.                                                                       |\n| `cache_describe`  | Extended | 1 (after cache warm-up) | Caches argument and result type descriptions instead of full statements. Same schema-change caveat.                                                                                |\n| `describe_exec`   | Extended | 2                       | Fetches description on every execution, then executes. No caching — safe with concurrent schema changes. May break connection poolers that switch connections between round trips. |\n| `exec`            | Extended | 1                       | No prepare, no describe. Infers PostgreSQL types from Go types using text format. Register custom types with `pgtype.Map.RegisterDefaultPgType`.                                   |\n| `simple_protocol` | Simple   | 1                       | Client-side parameter interpolation. Compatible with PgBouncer and proxies that don't support the extended protocol. Prefer `exec` when possible.                                  |\n\n\u003e For PgBouncer in transaction pooling mode use `simple_protocol`. For session pooling mode `cache_statement` works\n\u003e fine.\n\n## Client options\n\n| Option                     | Description                                                             |\n|----------------------------|-------------------------------------------------------------------------|\n| `WithConfig(cfg)`          | Set connection config                                                   |\n| `WithClientID(id)`         | Set a human-readable client identifier (appended to `application_name`) |\n| `WithLogger(l)`            | Set a custom `slog.Logger`                                              |\n| `WithTraceProvider(tp)`    | Set a custom OpenTelemetry `TracerProvider`                             |\n| `WithMigrations(fs...)`    | Provide one or more `embed.FS` with migration files                     |\n| `WithMetricsNamespace(ns)` | Set Prometheus metrics namespace                                        |\n\n## License\n\n[MIT](LICENSE)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkbeh%2Fxpg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmkbeh%2Fxpg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkbeh%2Fxpg/lists"}