Metadata-Version: 2.4
Name: parallax-postgres
Version: 0.1.0
Summary: Parallax Postgres database adapter: the concrete database runtime over psycopg and its companion pool.
Author: Parallax
License-Expression: Apache-2.0
Project-URL: Changelog, https://github.com/davidsunglee/parallax/blob/main/languages/python/docs/releases.md
Project-URL: Documentation, https://github.com/davidsunglee/parallax/blob/main/languages/python/docs/postgresql-lifecycle.md
Project-URL: Repository, https://github.com/davidsunglee/parallax
Requires-Python: >=3.14
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: parallax-core==0.1.0
Requires-Dist: psycopg[binary]<3.4,>=3.3.6
Requires-Dist: psycopg-pool>=3.3.1
Dynamic: license-file

# parallax-postgres

Parallax's Postgres database adapter over `psycopg` and its companion
`psycopg-pool`. Install `parallax-postgres==0.1.0` with the application packages
shown in the [getting-started guide](https://github.com/davidsunglee/parallax/blob/main/languages/python/docs/guide/01-getting-started.md#install).
That guide links the authoritative Python, platform and PostgreSQL support
declarations and the C-driver and source-build installation paths. The normal
installation uses the binary driver and a prebuilt Parallax wheel. All installed
first-party packages must have matching release versions. See the
[release notes](https://github.com/davidsunglee/parallax/blob/main/languages/python/docs/releases.md)
for publication status. The distribution includes Apache-2.0 `LICENSE` and `NOTICE`.

## Connecting

```python
from parallax.core.db_port import Password
from parallax.postgres import OnDemandOptions, PoolOptions, PostgresAdapter, PostgresEndpoint
from parallax.snapshot import connect

writer = PostgresEndpoint("postgresql://app@localhost/app", credentials=Password("s3cret"))
adapter = PostgresAdapter(writer=writer)
with connect(adapter, model) as root:
    db = root.using_database_login()
    ...
```

`PostgresEndpoint` and `PostgresAdapter` are **configuration**. Constructing
either opens no connection, no pool, and no thread: an endpoint parses its
connection string and validates its credential declaration and retention
policy, and the adapter validates the acquisition and startup budgets its
runtime spends. That is what makes them safe to build at import time, hold as
module constants, share between threads, and — for a forking server — build
before the fork and open after it. The adapter's `writer` is the endpoint that
transactions and every read of a single-endpoint deployment use; nothing about
its role is inferred from its connection string.

## Where the password lives

The connection string says **where**, and `credentials` says **how**. A string
carrying a password is refused at construction, whatever `credentials` is, and
the refusal never quotes the string back:

```text
PostgresEndpoint("postgresql://app:<password>@db.internal/app", credentials=Password("<password>"))
ValueError: connection_string must not carry a password; supply it through credentials.
```

There are three spellings, all exported from `parallax.core.db_port`:

```python
from parallax.core.db_port import DRIVER_MANAGED, Password

# a constant secret; `Password` is its own source and keeps it out of every repr
PostgresEndpoint("postgresql://app@db.internal:5432/app", credentials=Password("s3cret"))

# any object with `resolve() -> Password`, asked as connections are established
PostgresEndpoint("postgresql://app@db.internal/app", credentials=my_source)

# Parallax supplies none: peer, trust, a client certificate, Kerberos, or
# libpq's own PGPASSWORD, .pgpass and service files
PostgresEndpoint("host=/var/run/postgresql dbname=app", credentials=DRIVER_MANAGED)
```

A source is asked where the driver establishes a **physical** connection, and
never when an acquisition reuses a retained one, which is what lets a
short-lived cloud token authenticate a pool that outlives it. It may block on a
network and must bound its own I/O. Parallax cannot see a password reachable
through a `service` file or `PGPASSWORD`, so neither is refused above; under an
explicit source the resolved password wins over both, by libpq's own
precedence.

`connect` is what opens a runtime, and the Database Root it returns is what owns
that runtime. Modeled work requires an explicitly selected `ScopedDatabase`, as
shown above. **Close the root**: `root.close()` and the context manager above are
equivalent, and both are idempotent. Every `connect` over one configuration
opens an independent runtime, so closing one root leaves another working.

Each operation acquires a connection and gives it back — an eager read for its
whole execution, each standalone stream page through its materialization, and a
transaction attempt for the attempt. A standalone page gives the connection back
before publishing roots, so caller work between pages can use even a one-slot
pool. A participating stream instead inherits its transaction attempt's
connection for every page.

A `reader=PostgresEndpoint(...)` beside the `writer` declares a reader route
with its own credentials and retention. New reads then default to it, writable
transactions stay on the writer, and both routes must authenticate the same
login; `reader_startup="required"` makes an unreachable reader fail `connect`
instead of leaving the reader pending. `with_routing(read_policy="reader_preferred")`
reads from the reader and moves to the writer, within the same
`acquire_timeout`, only where the reader cannot be reached or loses its
connection before any of your work runs; each route keeps its own `max_size`,
so a two-route root can hold both pools' connections in every process.

An endpoint's `pool=PoolOptions(...)` keeps connections between operations;
`pool=OnDemandOptions(...)` keeps none, closing each on release unless a caller
is already waiting for one. Omitting `pool` takes the retaining defaults. The
adapter's `acquire_timeout` and `startup_timeout` budget the whole runtime
rather than one pool.

## Everything else

[PostgreSQL connection lifecycle](https://github.com/davidsunglee/parallax/blob/main/languages/python/docs/postgresql-lifecycle.md)
is this adapter's operational guide, and the one place these facts are written
down:

- the full retention settings and their defaults, and what the driver's own
  maintenance settings mean;
- what each operation holds, standalone per-page leases, and participating
  connection affinity;
- the five distinct timeouts, and the one driver precedence that surprises
  people;
- process and application-server lifetime — post-fork construction, the ASGI
  lifespan, offloading complete operations, and why pool capacity is not HTTP
  concurrency;
- writer and reader routes, deferred reader readiness, preferred reads and
  their fallback, and sizing two routes' pools;
- connection budgeting across workers, replicas and runtimes;
- observing the pool, and what its measurements do and do not mean;
- which session settings are required, which are preserved;
- how fixed login identity, scoped role replacement/restoration, and required
  session affinity behave, including unsupported transaction/statement pooling
  proxies and the absence of runtime proxy detection;
- the three logging paths and their different disclosure policies;
- migrating from the pre-pooling surface.

Every application-shaped Python block in it is the source of an executable story
that runs against a real PostgreSQL server, so nothing there is a snippet that
has never executed.
