dbx-tools-postgres
Python Lakebase/Postgres connection setup, advisory locks, and topic fan-out for
services that already hold a Databricks WorkspaceClient. This package is
the Python counterpart to @dbx-tools/postgres plus @dbx-tools/appkit's
address parsing.
Install from PyPI:
pip install dbx-tools-postgres
To install the current main branch directly from the repository instead:
pip install "dbx-tools-postgres @ git+https://github.com/reggie-db/dbx-tools.git@main#subdirectory=packages/py/postgres"
Key features:
- accepts the same Postgres URI, Lakebase resource path, hostname, and project-id
address shapes as
@dbx-tools/appkit; - resolves missing autoscaling endpoint fields through
WorkspaceClient.api_client; - resolves provisioned Lakebase instance DNS through
WorkspaceClient.database; - injects a cached database credential on SQLAlchemy's
do_connectevent rather than storing an expiring password in the engine URL, using the SDK's provisioned-instance API or the Autoscaling/postgres/credentialsendpoint; - refreshes built-in credential providers ahead of expiry with a process-local check-lock-check load, so concurrent pool connections share one mint;
- supports sync psycopg and asyncpg SQLAlchemy engines;
- derives advisory-lock ids from the same stable structured keys as the Node package and holds one checked-out connection for the full critical section;
- provides blocking and try-lock context managers for session and transaction locks, with sync and async SQLAlchemy variants;
- fans messages out to every process on a channel with
PostgresTopicBus, using the same lifecycle and wire envelope as the Node package.
from databricks.sdk import WorkspaceClient
from dbx_tools.postgres import PostgresEngineConfig, create_async_engine
engine = create_async_engine(
WorkspaceClient(),
PostgresEngineConfig(instance_name="my-lakebase", database="databricks_postgres"),
pool_pre_ping=True,
pool_recycle=1800,
)
Pass credential_provider= to either engine factory to inject credentials from
another source. A custom provider owns its own cache, expiry, and refresh
serialization policy.
from dbx_tools.postgres import advisory_transaction_lock
with advisory_transaction_lock(engine, ["schema-install", "v2"]) as connection:
connection.exec_driver_sql("CREATE TABLE IF NOT EXISTS ...")
Topic bus
PostgresTopicBus is async Postgres topic fan-out built on LISTEN/NOTIFY. Its
public lifecycle and wire shape match @dbx-tools/postgres's PostgresTopicBus,
so Node and Python services can share a channel:
PostgresTopicBus(engine, options);channelName;await start();await broadcast(topic, TopicPublishInput(...));await listen(topic, listener)returning an async unsubscribe function;await close();- envelope fields
id,topic,type,metadata,body, andpublishedAt.
Channel derivation ports the Node stable-key and FNV rules, so equivalent channel parts resolve to the same PostgreSQL identifier in Python and Node.
from dbx_tools.postgres import PostgresTopicBus, TopicPublishInput
bus = PostgresTopicBus(engine, channel=["billing", "production"])
unsubscribe = await bus.listen("invoice.updated", handle_invoice)
await bus.broadcast(
"invoice.updated",
TopicPublishInput(type="invoice.updated", body={"invoice_id": "inv-7"}),
)
Delivery is live and unstored, like PostgreSQL LISTEN/NOTIFY itself. Use a
table or queue when consumers need replay or acknowledgements.
Databricks notebooks and Spark
Verified end to end against a Lakebase endpoint on serverless notebook compute.
packages/example/notebooks/bus-lakebase.py
is the runnable version of everything below.
Two things about the Databricks Python runtime change how the bus is called, and neither is a limitation of the bus itself:
- A notebook kernel already runs an event loop, so
asyncio.runin a cell raisesRuntimeError: asyncio.run() cannot be called from a running event loop. Drive the coroutine on a short-lived thread with its own loop rather than reaching fornest_asyncio— the bus holds a dedicatedLISTENconnection bound to whichever loop started it, so one loop per bus lifetime is the invariant to preserve. - Install with
%pip install, notpip install --target. A--targetinstall leaves the runtime's preloadedtyping_extensionsahead of the new one onsys.path, and importingdbx_tools.postgresthen fails withImportError: cannot import name 'TypeAliasType'.%piprestarts the Python process, which resolves it.
Publishing from a Spark UDF
Publishing from executors works. Listening from them does not, and should not be
attempted: a UDF invocation is short-lived, while listen keeps a connection
open until close.
Executors have no Databricks credentials, so they cannot build a
WorkspaceClient. This is where connect-time credential injection pays off — the
driver mints the Lakebase token once and the UDF closes over it, so the executor
builds a plain SQLAlchemy engine and installs the token as its provider:
from sqlalchemy import URL
from sqlalchemy.ext.asyncio import create_async_engine
from dbx_tools.postgres import (
PostgresTopicBus,
TopicPublishInput,
install_credential_injection,
)
# driver: resolve once, capture in the closure
resolved = resolve_postgres_connection(workspace_client, config)
token = workspace_client.api_client.do(
"POST",
"/api/2.0/postgres/credentials",
body={"endpoint": resolved.endpoint},
)["token"]
@udf(returnType=StringType())
def publish(key: str) -> str:
async def run() -> str:
engine = create_async_engine(
URL.create(
"postgresql+asyncpg",
username=resolved.user,
host=resolved.host,
port=resolved.port,
database=resolved.database,
query={"ssl": resolved.ssl_mode},
)
)
install_credential_injection(engine.sync_engine, lambda: token)
bus = PostgresTopicBus(engine, channel="app-events")
try:
message = await bus.broadcast(
"row.processed", TopicPublishInput(type="row.processed", body={"key": key})
)
return message.id
finally:
await bus.close()
await engine.dispose()
return asyncio.run(run())
Constraints worth knowing before this reaches production:
- A captured token EXPIRES (about an hour). Re-mint per job run; a long-running streaming query needs a provider that refreshes instead of a captured string.
- Each UDF call opens and closes its own connection, so batch the publish at
partition scope (
mapInPandas,foreachPartition) rather than per row. spark.sparkContext.broadcastis unavailable on serverless (Spark Connect). A plain closure over driver-side values serializes with the UDF and is enough.- Delivery stays live and unstored: if no listener is connected when the UDF publishes, the message is gone. Write to a table when executors produce results a consumer must not miss.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dbx_tools_postgres-0.6.104.tar.gz.
File metadata
- Download URL: dbx_tools_postgres-0.6.104.tar.gz
- Upload date:
- Size: 13.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b284359845d0adb3c94df0e2ba89f8dca24331ff6eb7b5b082683a2aa89aafc4
|
|
| MD5 |
9937d221a0ef8d7ed3dfe5cda6811529
|
|
| BLAKE2b-256 |
a97b1d55c5868095307e24829d11549f839be5e15ec88ad8e24af19d04a7d4d6
|
Provenance
The following attestation bundles were made for dbx_tools_postgres-0.6.104.tar.gz:
Publisher:
python-release.yml on reggie-db/dbx-tools
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dbx_tools_postgres-0.6.104.tar.gz -
Subject digest:
b284359845d0adb3c94df0e2ba89f8dca24331ff6eb7b5b082683a2aa89aafc4 - Sigstore transparency entry: 2395427516
- Sigstore integration time:
-
Permalink:
reggie-db/dbx-tools@de146e55d1199585aed4291f3e3ba7ee25acc9f3 -
Branch / Tag:
refs/tags/v0.6.104 - Owner: https://github.com/reggie-db
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-release.yml@de146e55d1199585aed4291f3e3ba7ee25acc9f3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dbx_tools_postgres-0.6.104-py3-none-any.whl.
File metadata
- Download URL: dbx_tools_postgres-0.6.104-py3-none-any.whl
- Upload date:
- Size: 16.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97082600602a2835ed58846ddb30d5eb15e18323aedf8d3262d4a3f50970cd5e
|
|
| MD5 |
a6fcf1bd8f1cf32e66cd3d9ad2ec1354
|
|
| BLAKE2b-256 |
3eb0d84bc4d20b70fa1d69dcc786457a735276db3b2c5e15e9b8241f3421a1a1
|
Provenance
The following attestation bundles were made for dbx_tools_postgres-0.6.104-py3-none-any.whl:
Publisher:
python-release.yml on reggie-db/dbx-tools
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dbx_tools_postgres-0.6.104-py3-none-any.whl -
Subject digest:
97082600602a2835ed58846ddb30d5eb15e18323aedf8d3262d4a3f50970cd5e - Sigstore transparency entry: 2395427698
- Sigstore integration time:
-
Permalink:
reggie-db/dbx-tools@de146e55d1199585aed4291f3e3ba7ee25acc9f3 -
Branch / Tag:
refs/tags/v0.6.104 - Owner: https://github.com/reggie-db
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-release.yml@de146e55d1199585aed4291f3e3ba7ee25acc9f3 -
Trigger Event:
push
-
Statement type: