Move whole tables between databases at wire speed, in bounded memory
Project description
apitap
Move whole tables between databases at wire speed, in bounded memory.
apitap is the open-source transfer engine behind apitap cloud — a Rust core with Python bindings, in the spirit of Polars. It moves data the way the databases themselves would: raw wire-format streams, parallel range pipes, atomic swaps, and memory that stays flat no matter how big the table is.
pip install apitap
import apitap
report = apitap.transfer(
"postgres://user:pass@src-host/db",
"postgres://user:pass@dst-host/db",
table="public.events",
)
print(f"{report.rows:,} rows in {report.elapsed_ms} ms over {report.parallel} pipes")
Routes
The URL schemes pick the route; each pair negotiates the fastest wire format both sides speak:
| route | how it moves |
|---|---|
postgres:// → postgres:// |
raw binary COPY passthrough — no row decode at all |
postgres:// → clickhouse:// |
binary COPY transcoded in-flight to RowBinary |
mysql:// → clickhouse:// |
binary wire protocol decoded straight into RowBinary |
mysql:// → postgres:// |
wire decode → binary COPY (exact decimals up to DECIMAL(65,30)) |
Every transfer stages into <table>__apitap_staging and swaps in atomically —
readers never see a partial table, an empty source never wipes a good one, and a
mid-run failure leaves the previous table untouched.
How fast?
10M rows, every tool capped at 16 vCPU / 4 GB, auto settings, stock Docker
databases — apitap measured from this exact published wheel (pip install apitap),
every number checksum-validated across engines:
| route | apitap | ingestr 1.0.75 | dlt 1.x (default) | dlt + pyarrow |
|---|---|---|---|---|
| Postgres → Postgres | 20.2 s | 500 s | 2 604 s | 708 s |
| Postgres → ClickHouse | 9.9 s | 111 s | 1 893 s | 360 s |
| MySQL → ClickHouse | 10.4 s | 97 s | 2 231 s | failed¹ |
| MySQL → Postgres | 22.5 s | 481 s | 2 899 s | failed¹ |
| Postgres → BigQuery² | 28.4 s | 860 s | 2 160 s | — |
² Measured at 0.5.0, 8 pipes uncapped (40.3 s at 2 vCPU); the wall is upload +
BigQuery-side parsing, not local CPU. 100% free-path ingestion — load and copy
jobs only, no insertAll, works on sandbox (no-billing) projects. At
0.5 vCPU / 256 MB apitap moves 1M rows in 17.9 s while ingestr and dlt+pyarrow
are both OOM-killed before finishing.
¹ dlt's pyarrow backend refuses MySQL DOUBLE without hand-written schema hints;
its connectorx backend was OOM-killed on all four routes at the same 4 GB cap apitap
runs in. Full methodology, validation queries, and honest caveats:
benchmarks/README.md.
In a 0.5 vCPU / 256 MB container apitap still completes every route (28–68 s) — the pipe count auto-derives from the cgroup's CPU and memory, so it never OOM-kills itself.
API
apitap.transfer(
src, dst, table, *,
dest_table=None, # defaults to `table`
parallel=None, # auto: CPU- and memory-aware; explicit value never overridden
cursor=None, # auto: integer PK; PK-less Postgres tables use TID ranges
chunk_bytes=None, # per-send coalescing, default 4 MiB
durable=True, # False = UNLOGGED staging on Postgres dests (~-30% wall,
# table stays unlogged until ALTER TABLE … SET LOGGED)
) -> TransferReport # .rows, .elapsed_ms, .parallel
mode="append" loads only rows past the last synced watermark; mode="merge"
(Postgres destinations) upserts the delta by the destination's primary key. The
watermark lives in _apitap_state — a plain, queryable table in the destination
database, one row per (table, source), written in the same transaction as the
data on Postgres. No local state files, no opaque blobs, no extra columns injected
into your rows: state that is backed up, restored, and debugged together with the
data it describes. A 1M-row delta lands on a 10M-row table in ~10 s — cost is
proportional to the delta, not the table.
The GIL is released for the whole transfer. Errors are ValueError for bad input
(unknown table, unsupported type — always at probe time, never mid-copy) and
RuntimeError for transfer failures.
Full usage guide — connection URLs, per-route type mappings, durability semantics, troubleshooting: docs/usage.md.
Roadmap
- Postgres → Postgres · Postgres → ClickHouse · MySQL → ClickHouse · MySQL → Postgres
- Postgres → BigQuery — dual Parquet/CSV lanes, free-path load+copy jobs, DML-free incremental state (sandbox-safe)
- Incremental sync —
mode="append"/mode="merge"(transactional state table) - ClickHouse table engines —
engine=,order_by=,on_cluster= - MySQL → BigQuery
-
read_postgres()→ Arrow / Polars - Snowflake destination
- aarch64 + macOS wheels
License
MIT. Source: github.com/apitap/apitap-lib. The managed cloud (scheduling, always-on per-tenant workers, monitoring, a UI) is apitap.dev.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
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 apitap-0.13.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: apitap-0.13.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 5.4 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04dde06944d560177bc3331e7bbb2fbd6e58901110c8b3fde8a91736c273a260
|
|
| MD5 |
bc79a207a7f5046e4893ae4e542e6670
|
|
| BLAKE2b-256 |
b0caa0e116d0543b8cc3c1e644c6e93187ed69b58af6f491f36e0f38dddf0a4a
|
Provenance
The following attestation bundles were made for apitap-0.13.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on apitap/apitap-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
apitap-0.13.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
04dde06944d560177bc3331e7bbb2fbd6e58901110c8b3fde8a91736c273a260 - Sigstore transparency entry: 2195708682
- Sigstore integration time:
-
Permalink:
apitap/apitap-lib@4f89376da941eac45ae5a478371439f59684e2b9 -
Branch / Tag:
refs/tags/v0.13.0 - Owner: https://github.com/apitap
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4f89376da941eac45ae5a478371439f59684e2b9 -
Trigger Event:
push
-
Statement type: