Parallel UniVerse reader using uopy
Project description
universe-reads
Reads large volumes of records from a UniVerse DB using 5 concurrent sessions by default (configurable) via uopy.
Quick start
1) Install
python -m venv .venv
source .venv/bin/activate
pip install -e .
If uopy is installable via pip in your environment:
pip install -e ".[uopy]"
If uopy is not available on PyPI in your environment, install it using your internal method and keep the import name uopy.
For a quick run without installing the package, you can also do:
PYTHONPATH=src python -m universe_reads --dry-run 10000 --count-only
2) Configure
Set environment variables as you like for your own factory (optional).
universe-reads itself only needs:
UV_FILE(optional if you pass--file)
You provide the UniVerse session creation via --session-factory.
Session factory (important)
This project uses multiprocessing with the spawn start method, which means worker processes must be able to import whatever they need.
Because of that, the recommended way to specify a session factory is a string import spec:
"module.submodule:callable_name"
This is robust across processes and lets you reuse the same factory reference in many modules.
A) Minimal factory module
Example factory module my_uv.py:
import uopy
def make_session():
return uopy.connect(host="...", account="...", user="...", password="...")
Then use it from the CLI:
UV_FILE=READS universe-reads --session-factory my_uv:make_session --count-only
Or programmatically:
from universe_reads import run
results = run(
file_name="READS",
session_factory="my_uv:make_session",
sessions=5,
count_only=True,
)
B) Why not pass a lambda/closure?
Avoid nested functions / lambdas for session_factory when using multiprocessing spawn.
Do NOT do:
from universe_reads import run
def make_factory():
def factory():
...
return factory
run(file_name="READS", session_factory=make_factory())
Prefer a top-level function in an importable module and pass it as a string spec.
C) Reuse across modules
If you’ll use this in a bunch of places, put the spec string in one location:
# settings.py
SESSION_FACTORY = "my_uv:make_session"
Then:
from settings import SESSION_FACTORY
from universe_reads import run
run(file_name="READS", session_factory=SESSION_FACTORY)
Or use the built-in env-based factory:
--session-factory universe_reads.session_factory:make_session
D) Pooling kwargs are optional (factory-friendly)
The CLI/library can pass pooling hints as keyword args:
pooling_onmin_pool_sizemax_pool_size
If your factory does not accept them, they are safely ignored.
Example of a kw-accepting factory:
import uopy
def make_session(*, pooling_on=None, min_pool_size=None, max_pool_size=None):
kwargs = {"user": "...", "password": "..."}
if pooling_on:
kwargs["min_pool_size"] = min_pool_size or 1
kwargs["max_pool_size"] = max_pool_size or 10
return uopy.connect(**kwargs)
3) Run
Count-only (fastest):
UV_FILE=READS universe-reads \
--session-factory my_uv:make_session \
--count-only
Using the built-in env-based factory:
UV_HOST=... UV_ACCOUNT=... UV_USER=... UV_PASSWORD=... UV_FILE=READS \
universe-reads --session-factory universe_reads.session_factory:make_session --count-only
More sessions (if you have licenses/CPU to spare):
UV_FILE=READS universe-reads \
--session-factory my_uv:make_session \
--sessions 10 \
--count-only
Connection pooling (optional)
If your uopy environment has pooling enabled (often via uopy.ini), you can keep it optional by enabling it only in your session factory.
Important caveats:
- Pooling is per Python process. Since this project uses multiprocessing for parallel sessions, each worker process has its own pool.
- Pooling is not “multi-user” by itself. A pool is typically keyed by connection parameters (host/account/user/password). Different users generally mean separate pools.
With the built-in factory you can enable pooling hints via CLI flags:
universe-reads \
--session-factory universe_reads.session_factory:make_session \
--pooling \
--min-pool-size 1 \
--max-pool-size 10 \
--count-only
Or via env vars:
export UV_POOLING_ON=1
export UV_MIN_POOL_SIZE=1
export UV_MAX_POOL_SIZE=10
universe-reads --session-factory universe_reads.session_factory:make_session --count-only
Write one NDJSON per worker:
UV_FILE=READS universe-reads \
--session-factory my_uv:make_session \
--output-dir out
Dry run (no UniVerse connection) to validate parallelism:
universe-reads --dry-run 300000 --count-only
Smoke-test the session factory mechanism (imports + kwargs filtering) without needing UniVerse:
PYTHONPATH=src:. python -m tests.test_dry_run --smoke-session-factory
Use as a library
from universe_reads import run
# Recommended: pass a module:function spec
results = run(
file_name="READS",
session_factory="my_uv:make_session",
sessions=5,
count_only=True,
)
print(results)
Dry-run (no UniVerse needed):
from universe_reads import run
results = run(file_name="", dry_run=300000)
Arguments
CLI (universe-reads)
--file(default:UV_FILE): UniVerse file name to read--session-factory(required unless--dry-run): session factory in the formmodule.sub:make_session--sessions(default:5): number of concurrent sessions/processes--count-only(default: off, but implied when no--output-dir): count records only--output-dir(default: unset): write oneworker-N.ndjsonper worker--batch-size(default:500): number of keys per worker IPC batch (also the batch read size)--progress-every(default:5.0): seconds between throughput logs--max-records(default: unset): debug cap (not a strict global cap)--dry-run N(default: unset): do not connect; generate N fake keys/records
Retry behaviour (for transient RPC / network failures):
--retries(default:3): number of retries per batch/key before giving up.0disables retries--retry-backoff(default:0.5): base delay in seconds between retries (doubles each attempt: 0.5s, 1s, 2s)
Pooling hints (optional; passed as kwargs to the session factory if it supports them):
--pooling/--no-pooling(default: neither): enable/disable pooling hints--min-pool-size(default: unset): minimum pool size hint--max-pool-size(default: unset): maximum pool size hint
Library (universe_reads.run)
run() accepts the same knobs as the CLI, as keyword args:
file_namesession_factory(spec string or top-level callable; optional whendry_runis set)sessions=5count_only=Trueoutput_dir=Nonebatch_size=500progress_every_seconds=5.0max_records=Nonedry_run=Noneretries=3,retry_backoff=0.5pooling_on=None,min_pool_size=None,max_pool_size=None
Built-in env-based session factory
If you use:
--session-factory universe_reads.session_factory:make_session
It supports these environment variables:
- Required:
UV_USER,UV_PASSWORD - Optional:
UV_HOST,UV_ACCOUNT,UV_PORT,UV_TIMEOUT,UV_SERVICE,UV_ENCODING,UV_SSL - Optional pooling hints:
UV_POOLING_ON,UV_MIN_POOL_SIZE,UV_MAX_POOL_SIZE
Where to plug in real uopy calls
All UniVerse file I/O calls are isolated in:
src/universe_reads/uopy_adapter.py
You manage session creation via --session-factory. The adapter implements:
iter_keys()(select list key streaming)read_record()/read_records()
Project details
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 universe_reads-0.1.5.tar.gz.
File metadata
- Download URL: universe_reads-0.1.5.tar.gz
- Upload date:
- Size: 26.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9aac644e3e3f2a28414136cc42a9eb491a1043ec7c2453fb82f0340bb98f7a3b
|
|
| MD5 |
e7d6d818b4f9efc707bde5194cb8b6f1
|
|
| BLAKE2b-256 |
e019b7f3084e9768bff4f8cae6f02412f1a60060d03e0f628729b6b3ef39d16c
|
Provenance
The following attestation bundles were made for universe_reads-0.1.5.tar.gz:
Publisher:
publish.yml on yangaxnkohla/universe-reads
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
universe_reads-0.1.5.tar.gz -
Subject digest:
9aac644e3e3f2a28414136cc42a9eb491a1043ec7c2453fb82f0340bb98f7a3b - Sigstore transparency entry: 1102297646
- Sigstore integration time:
-
Permalink:
yangaxnkohla/universe-reads@b905969585d688746bda36fadcf3c6211e207afb -
Branch / Tag:
refs/heads/main - Owner: https://github.com/yangaxnkohla
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b905969585d688746bda36fadcf3c6211e207afb -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file universe_reads-0.1.5-py3-none-any.whl.
File metadata
- Download URL: universe_reads-0.1.5-py3-none-any.whl
- Upload date:
- Size: 19.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86b715b991ba00b87edda779528d06943f04767b766ce877f45bce218f60714a
|
|
| MD5 |
e26532907f50f306d810de1c31b23c93
|
|
| BLAKE2b-256 |
25314e2c85a1b40aa70e35ca81ba97be38aa635afe97cd99a1013f89400ab231
|
Provenance
The following attestation bundles were made for universe_reads-0.1.5-py3-none-any.whl:
Publisher:
publish.yml on yangaxnkohla/universe-reads
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
universe_reads-0.1.5-py3-none-any.whl -
Subject digest:
86b715b991ba00b87edda779528d06943f04767b766ce877f45bce218f60714a - Sigstore transparency entry: 1102297681
- Sigstore integration time:
-
Permalink:
yangaxnkohla/universe-reads@b905969585d688746bda36fadcf3c6211e207afb -
Branch / Tag:
refs/heads/main - Owner: https://github.com/yangaxnkohla
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b905969585d688746bda36fadcf3c6211e207afb -
Trigger Event:
workflow_dispatch
-
Statement type: