Skip to content

Releases: Mayuradlak123/pgmesh

release-v0.2.0.md

Choose a tag to compare

@Mayuradlak123 Mayuradlak123 released this 13 Aug 16:54

With it on, every statement's plan is logged to the pgmesh logger at INFO before the statement runs — on the same connection, so the plan describes the same session.

Three deliberate choices here:

Plain EXPLAIN, not EXPLAIN ANALYZE. ANALYZE executes the statement it measures, so using it for automatic capture would double-apply every write. The flag plans without executing; your INSERT reaches the server exactly once. There's an integration test asserting precisely that against a real server.
Failures are swallowed. Not everything is explainable — VACUUM, SET. A statement PostgreSQL rejects for EXPLAIN is logged at DEBUG and skipped. A diagnostic never becomes the reason a query fails.
executemany is skipped. EXPLAIN takes one parameter set; a batch has many.
It costs one extra round trip per query, so treat it as a debugging aid rather than a production default. For a single plan, prefer explain().

Notes
No breaking changes. explain defaults to False, so upgrading changes nothing until you opt in.

Test suite is now 143 unit tests, up from 121.

release-v1.0.0

Choose a tag to compare

@Mayuradlak123 Mayuradlak123 released this 12 Aug 16:09

Here’s a cleaned-up, GitHub-ready version with the Markdown formatting fixed and the wording polished while preserving your technical content:

v0.1.0 — Multi-database PostgreSQL orchestration for asyncio

First release of pgmesh — an async orchestration layer for applications that communicate with multiple PostgreSQL databases. Register your databases once, address them by index or label, and let pgmesh manage connection pools, concurrency, timeouts, and failure isolation.

pip install pgmesh
from pgmesh import PGCluster

async with PGCluster(
    {
        1: "postgresql://user:pass@db1/app",
        2: "postgresql://user:pass@db2/app",
        "analytics": "postgresql://user:pass@db3/analytics",
    },
    max_concurrency=10,
    query_timeout=5,
) as db:

    users = await db.connection(1).execute(
        "SELECT * FROM users LIMIT 10"
    )

    results = await db.parallel(
        [
            (1, "SELECT count(*) FROM users"),
            (2, "SELECT count(*) FROM orders"),
            ("analytics", "SELECT count(*) FROM events"),
        ]
    )

What's Included

  • One pool per database. Each registered database gets its own asyncpg pool, created on first use or upfront via startup(). Connections are reused rather than opened for every query.

  • Routing by index or label. Use db.connection(1) or db.connection("analytics") to access a database. Numeric IDs and their string equivalents resolve to the same database, while an unknown ID raises DatabaseNotFoundError.

  • Bounded parallel execution. max_concurrency limits the number of operations that can run simultaneously. For example, if you submit 100 operations with a concurrency limit of 20, only 20 run at a time while the remaining operations wait without creating an unbounded number of tasks.

  • Timeouts that release connections. Configure cluster-wide or per-query timeouts. When a query exceeds its timeout, QueryTimeoutError is raised and the connection is safely returned to the pool.

  • Failure isolation. A slow or unreachable database does not cancel operations against other databases. parallel() returns a Success or Failure result for each operation, allowing you to identify exactly which operations succeeded or failed. Nothing is raised unless you explicitly request it.

  • A real error model. Driver exceptions are mapped to PGMeshError and its subclasses, with the original exception preserved as __cause__. DatabaseNotFoundError also behaves as a KeyError, while QueryTimeoutError also behaves as a TimeoutError.

  • No secrets in logs. Passwords are masked wherever connection strings could appear, including log messages, representations, and exception messages.

  • Fully typed. pgmesh ships with py.typed and is checked with mypy --strict.

Also included:

  • Transactions
  • Raw connection escape hatch via acquire()
  • parallel_map() fan-out execution
  • health() and ping() utilities

Requirements

  • Python 3.10+
  • One runtime dependency: asyncpg

Scope

Included: Registration, routing, connection pooling, query execution, bounded parallelism, timeouts, failure isolation, and a clean error model.

Intentionally out of scope: Proxying, SQL parsing, distributed transactions, cross-database joins, automatic sharding, query rewriting, and replication management.

Retries, circuit breakers, structured logging, and metrics are planned for future releases.

Testing

The project includes 121 unit tests running against a fake database driver, so the unit test suite does not require PostgreSQL.

Integration tests run in CI against a real postgres:16 instance.

CI currently covers:

  • Python 3.10–3.13
  • Linux and Windows
  • Ruff
  • Mypy

📦 [pgmesh on PyPI](https://pypi.org/project/pgmesh/) · 📖 [README](https://github.com/Mayuradlak123/pgmesh#readme)