holypipe 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- holypipe-1.0.0/LICENSE +21 -0
- holypipe-1.0.0/PKG-INFO +274 -0
- holypipe-1.0.0/README.md +216 -0
- holypipe-1.0.0/holypipe/__init__.py +1 -0
- holypipe-1.0.0/holypipe/api/__init__.py +0 -0
- holypipe-1.0.0/holypipe/api/routes.py +479 -0
- holypipe-1.0.0/holypipe/api/schemas.py +135 -0
- holypipe-1.0.0/holypipe/bus.py +68 -0
- holypipe-1.0.0/holypipe/cache.py +56 -0
- holypipe-1.0.0/holypipe/cli.py +19 -0
- holypipe-1.0.0/holypipe/config.py +37 -0
- holypipe-1.0.0/holypipe/connectors/__init__.py +15 -0
- holypipe-1.0.0/holypipe/connectors/base.py +177 -0
- holypipe-1.0.0/holypipe/connectors/dsn.py +86 -0
- holypipe-1.0.0/holypipe/connectors/mysql.py +415 -0
- holypipe-1.0.0/holypipe/connectors/postgres.py +594 -0
- holypipe-1.0.0/holypipe/connectors/registry.py +78 -0
- holypipe-1.0.0/holypipe/connectors/sqlite.py +334 -0
- holypipe-1.0.0/holypipe/db.py +62 -0
- holypipe-1.0.0/holypipe/engine/__init__.py +0 -0
- holypipe-1.0.0/holypipe/engine/cdc.py +277 -0
- holypipe-1.0.0/holypipe/engine/recovery.py +37 -0
- holypipe-1.0.0/holypipe/engine/scheduler.py +136 -0
- holypipe-1.0.0/holypipe/engine/sync.py +177 -0
- holypipe-1.0.0/holypipe/logging_util.py +41 -0
- holypipe-1.0.0/holypipe/main.py +58 -0
- holypipe-1.0.0/holypipe/models.py +114 -0
- holypipe-1.0.0/holypipe/timeutil.py +19 -0
- holypipe-1.0.0/holypipe/web/static/app.js +1258 -0
- holypipe-1.0.0/holypipe/web/static/favicon.svg +28 -0
- holypipe-1.0.0/holypipe/web/static/index.html +64 -0
- holypipe-1.0.0/holypipe/web/static/style.css +393 -0
- holypipe-1.0.0/holypipe.egg-info/PKG-INFO +274 -0
- holypipe-1.0.0/holypipe.egg-info/SOURCES.txt +38 -0
- holypipe-1.0.0/holypipe.egg-info/dependency_links.txt +1 -0
- holypipe-1.0.0/holypipe.egg-info/entry_points.txt +2 -0
- holypipe-1.0.0/holypipe.egg-info/requires.txt +10 -0
- holypipe-1.0.0/holypipe.egg-info/top_level.txt +1 -0
- holypipe-1.0.0/pyproject.toml +53 -0
- holypipe-1.0.0/setup.cfg +4 -0
holypipe-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tbwahacker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
holypipe-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: holypipe
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A scoped ELT / CDC data-replication tool for PostgreSQL, MySQL, and SQLite
|
|
5
|
+
Author: tbwahacker
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 tbwahacker
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/tbwahacker/holypipe
|
|
29
|
+
Project-URL: Documentation, https://github.com/tbwahacker/holypipe/blob/main/docs/USER_GUIDE.md
|
|
30
|
+
Project-URL: Repository, https://github.com/tbwahacker/holypipe
|
|
31
|
+
Project-URL: Issues, https://github.com/tbwahacker/holypipe/issues
|
|
32
|
+
Keywords: etl,elt,cdc,replication,postgresql,mysql,sqlite,data-pipeline
|
|
33
|
+
Classifier: Development Status :: 4 - Beta
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: Intended Audience :: System Administrators
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Topic :: Database
|
|
42
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
43
|
+
Classifier: Framework :: FastAPI
|
|
44
|
+
Requires-Python: >=3.11
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
License-File: LICENSE
|
|
47
|
+
Requires-Dist: fastapi==0.115.6
|
|
48
|
+
Requires-Dist: uvicorn[standard]==0.34.0
|
|
49
|
+
Requires-Dist: SQLAlchemy==2.0.36
|
|
50
|
+
Requires-Dist: pydantic==2.10.4
|
|
51
|
+
Requires-Dist: psycopg2-binary==2.9.10
|
|
52
|
+
Requires-Dist: PyMySQL==1.1.1
|
|
53
|
+
Requires-Dist: cryptography==44.0.0
|
|
54
|
+
Requires-Dist: mysql-replication==1.0.9
|
|
55
|
+
Requires-Dist: python-dotenv==1.0.1
|
|
56
|
+
Requires-Dist: croniter==2.0.5
|
|
57
|
+
Dynamic: license-file
|
|
58
|
+
|
|
59
|
+
<p align="center">
|
|
60
|
+
<img src="assets/logo-wordmark.svg" alt="HolyPipe" width="360">
|
|
61
|
+
</p>
|
|
62
|
+
|
|
63
|
+
A small, purpose-built ELT / data-replication tool — the parts of Airbyte you
|
|
64
|
+
actually need when you only care about **PostgreSQL, MySQL and SQLite** as
|
|
65
|
+
sources and destinations. Runs entirely in Docker (Windows, macOS, and Linux
|
|
66
|
+
alike), syncs in real time via change-data-capture (CDC), and ships with a
|
|
67
|
+
live web dashboard.
|
|
68
|
+
|
|
69
|
+
📖 **Guides:** [User Guide](docs/USER_GUIDE.md) ·
|
|
70
|
+
[Administrator Guide](docs/ADMIN_GUIDE.md) ·
|
|
71
|
+
[Developer Guide](docs/DEVELOPER_GUIDE.md)
|
|
72
|
+
|
|
73
|
+
## What it does
|
|
74
|
+
|
|
75
|
+
- **Sources & destinations**: PostgreSQL, MySQL, SQLite, in any combination
|
|
76
|
+
(e.g. Postgres → MySQL, MySQL → SQLite, Postgres → Postgres warehouse).
|
|
77
|
+
- **Real-time sync (CDC)**:
|
|
78
|
+
- Postgres: logical replication (`wal2json` if available, falls back to the
|
|
79
|
+
built-in `test_decoding` plugin — no extension install required).
|
|
80
|
+
- MySQL: row-based binlog streaming via `mysql-replication`.
|
|
81
|
+
- SQLite: trigger-based change journal, polled sub-second (SQLite has no
|
|
82
|
+
external replication protocol, so this is the closest equivalent).
|
|
83
|
+
- Every CDC connection starts with a **one-time initial snapshot** of each
|
|
84
|
+
selected table (captured right after the replication slot/binlog
|
|
85
|
+
position/trigger journal is created, so nothing that changes during the
|
|
86
|
+
snapshot is missed) before it switches to streaming — so you get all
|
|
87
|
+
existing rows *and* every future change, not just the future ones. The
|
|
88
|
+
snapshot runs once per connection; stopping and restarting CDC resumes
|
|
89
|
+
from where it left off instead of re-copying.
|
|
90
|
+
- **Batch sync**: full-refresh, incremental (cursor-column), or — for
|
|
91
|
+
Postgres sources — **xmin** (tracks the system `xmin` column, so you get
|
|
92
|
+
change detection without picking a cursor column at all).
|
|
93
|
+
- **Update method per stream**: choose Full Refresh / Incremental / Xmin per
|
|
94
|
+
table when the connection is in batch mode, or CDC at the connection level
|
|
95
|
+
for continuous log-based streaming.
|
|
96
|
+
- **Flexible scheduling**: batch connections run on a **fixed interval** or a
|
|
97
|
+
**cron expression** (5-field crontab syntax); the connection list shows the
|
|
98
|
+
configured frequency, last sync time, and next scheduled run.
|
|
99
|
+
- **SSL modes**: pick a PostgreSQL `sslmode` (disable/allow/prefer/require/
|
|
100
|
+
verify-ca/verify-full) or MySQL SSL mode (DISABLED/PREFERRED/REQUIRED/
|
|
101
|
+
VERIFY_CA/VERIFY_IDENTITY) per source/destination.
|
|
102
|
+
- **Two ways to configure a connector**: fill in the form fields, or flip to
|
|
103
|
+
"Connection URI" and paste a DSN (`postgresql://user:pass@host:5432/db?
|
|
104
|
+
sslmode=require`, `mysql://user:pass@host:3306/db`, `sqlite:////data/a.db`)
|
|
105
|
+
— either way you can **Test connection** before saving.
|
|
106
|
+
- **Schema discovery & auto-provisioning**: pick tables/columns from the
|
|
107
|
+
source; destination tables and columns are created and evolved
|
|
108
|
+
automatically, with `_hp_extracted_at` / `_hp_op` / `_hp_deleted` metadata
|
|
109
|
+
columns for auditability and soft-deletes.
|
|
110
|
+
- **Live dashboard**: create sources/destinations/connections, start/stop
|
|
111
|
+
syncs, and watch a live log/event stream over a websocket — all from
|
|
112
|
+
`http://localhost:8090`.
|
|
113
|
+
- **Everything else Airbyte doesn't need to be**: no Java, no Temporal, no
|
|
114
|
+
connector marketplace. One Python process, one container.
|
|
115
|
+
|
|
116
|
+
## Install
|
|
117
|
+
|
|
118
|
+
No cloning required — pick whichever fits how you run things. Works
|
|
119
|
+
identically on Windows, macOS, and Linux either way.
|
|
120
|
+
|
|
121
|
+
**Docker (recommended)** — pulls a prebuilt image, nothing to build:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
docker run -d --name holypipe -p 8090:8000 -v holypipe_data:/data \
|
|
125
|
+
ghcr.io/tbwahacker/holypipe:latest
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Open `http://localhost:8090` and add your own sources/destinations
|
|
129
|
+
straight away (see the [User Guide](docs/USER_GUIDE.md)).
|
|
130
|
+
|
|
131
|
+
**Python (no Docker)**:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pip install holypipe
|
|
135
|
+
holypipe # starts the dashboard on http://localhost:8000
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Needs Python 3.11+. `psycopg2` requires `libpq` on the host (its wheel
|
|
139
|
+
usually bundles this already); everything else is pure Python. Config is
|
|
140
|
+
the same environment variables either way — see
|
|
141
|
+
[Configuration](docs/ADMIN_GUIDE.md#configuration).
|
|
142
|
+
|
|
143
|
+
**Try the full demo stack** (clone this repo) — spins up seeded Postgres
|
|
144
|
+
and MySQL sources plus a warehouse destination alongside HolyPipe, so
|
|
145
|
+
there's real data to sync immediately:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
git clone https://github.com/tbwahacker/holypipe.git && cd holypipe
|
|
149
|
+
docker compose up --build
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
This starts:
|
|
153
|
+
- `holypipe` — the app, at http://localhost:8090
|
|
154
|
+
- `postgres_demo` — a sample Postgres source (`shop` db, port 5433), with
|
|
155
|
+
`wal_level=logical` already configured, seeded with `customers`/`orders`
|
|
156
|
+
- `mysql_demo` — a sample MySQL source (`shop` db, port 3307), with binlog
|
|
157
|
+
row-format already configured, seeded with `products`/`inventory`
|
|
158
|
+
- `warehouse` — an empty Postgres destination (port 5434) to sync into
|
|
159
|
+
|
|
160
|
+
Open http://localhost:8090, then:
|
|
161
|
+
1. **Sources** → New Source → type `postgres`, host `postgres_demo`, port
|
|
162
|
+
`5432`, database `shop`, user/password `holypipe`/`holypipe`. Test, Save.
|
|
163
|
+
(Do the same for `mysql_demo`, port `3306`, if you want a MySQL source.)
|
|
164
|
+
2. **Destinations** → New Destination → type `postgres`, host `warehouse`,
|
|
165
|
+
port `5432`, database `warehouse`, user/password `holypipe`/`holypipe`.
|
|
166
|
+
Or pick `sqlite` with path `/data/demo.db` to land data in HolyPipe's own
|
|
167
|
+
volume — no extra container needed.
|
|
168
|
+
3. **Connections** → New Connection → pick the source/destination, choose
|
|
169
|
+
**Real-time (CDC)** or **Batch**, Discover tables, select the ones you
|
|
170
|
+
want, Create. CDC connections start streaming immediately; batch
|
|
171
|
+
connections run on the interval you set (or click "Run now").
|
|
172
|
+
4. Watch the **Live Logs** tab, or `psql`/`mysql` into the source containers
|
|
173
|
+
and insert/update/delete rows — changes land in the destination within
|
|
174
|
+
about a second under CDC mode.
|
|
175
|
+
|
|
176
|
+
## Configuration (environment variables)
|
|
177
|
+
|
|
178
|
+
| Variable | Default | Meaning |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| `HOLYPIPE_METADATA_URL` | `sqlite:///./data/holypipe.db` | Where HolyPipe stores its own config/state (sources, destinations, connections, run history). |
|
|
181
|
+
| `HOLYPIPE_SCHEDULER` | `1` | Set `0` to disable the background scheduler/CDC supervisor (e.g. for a read-only API replica). |
|
|
182
|
+
| `HOLYPIPE_BATCH_SIZE` | `1000` | Rows per batch for full-refresh/incremental reads. |
|
|
183
|
+
| `HOLYPIPE_CDC_FLUSH_SECONDS` | `1.0` | Max latency before a buffered CDC batch is flushed to the destination. |
|
|
184
|
+
| `HOLYPIPE_CDC_FLUSH_RECORDS` | `500` | Max buffered CDC events before an early flush. |
|
|
185
|
+
| `HOLYPIPE_SCHEDULER_WORKERS` | `4` | Concurrent batch syncs the scheduler runs at once (CDC connections are unaffected — each gets its own thread). |
|
|
186
|
+
| `HOLYPIPE_DISCOVERY_CACHE_TTL` | `300` | Seconds a source's discovered schema is cached before a "Discover tables" click re-scans it. |
|
|
187
|
+
| `HOLYPIPE_LOG_LEVEL` | `INFO` | stdout log verbosity. |
|
|
188
|
+
| `HOLYPIPE_LOG_RETENTION` | `2000` | How many log rows to keep in the metadata DB. |
|
|
189
|
+
|
|
190
|
+
## Requirements for CDC on your own databases
|
|
191
|
+
|
|
192
|
+
- **PostgreSQL**: `wal_level = logical` (restart required), and a user with
|
|
193
|
+
`REPLICATION` privilege. Tables benefit from `REPLICA IDENTITY FULL` (or a
|
|
194
|
+
primary key) so updates/deletes carry the old row. No `wal2json` extension
|
|
195
|
+
is required — HolyPipe falls back to Postgres's built-in `test_decoding`
|
|
196
|
+
plugin automatically if `wal2json` isn't installed.
|
|
197
|
+
- **MySQL**: `log_bin = ON`, `binlog_format = ROW`, and a user with
|
|
198
|
+
`REPLICATION SLAVE, REPLICATION CLIENT` privileges. Give each concurrent
|
|
199
|
+
CDC connection a unique `server_id` in its source config.
|
|
200
|
+
- **SQLite**: nothing special — HolyPipe installs its own triggers on the
|
|
201
|
+
file the first time CDC starts.
|
|
202
|
+
|
|
203
|
+
## Scaling to large tables and staying stable
|
|
204
|
+
|
|
205
|
+
- **Streaming, not buffering**: sources never load a full table into memory.
|
|
206
|
+
Postgres uses a named server-side cursor, MySQL uses `SSCursor`/
|
|
207
|
+
`SSDictCursor`, and SQLite streams via `fetchmany` — all bounded by
|
|
208
|
+
`HOLYPIPE_BATCH_SIZE` (default 1000 rows/batch) regardless of table size.
|
|
209
|
+
Writes are batched the same way (`execute_values`/`executemany`), so a
|
|
210
|
+
multi-million-row table moves in a steady stream of bounded batches, not
|
|
211
|
+
one giant transaction.
|
|
212
|
+
- **Reused connections**: each running sync/CDC worker holds one connection
|
|
213
|
+
per source and destination for its whole lifetime instead of reconnecting
|
|
214
|
+
per batch — important for CDC, which flushes as often as once a second.
|
|
215
|
+
A dropped/broken connection is detected and transparently reconnected on
|
|
216
|
+
the next call.
|
|
217
|
+
- **Bounded CDC memory**: real-time changes are buffered up to
|
|
218
|
+
`HOLYPIPE_CDC_FLUSH_RECORDS` (default 500) or `HOLYPIPE_CDC_FLUSH_SECONDS`
|
|
219
|
+
(default 1.0), whichever comes first, then flushed and cleared — buffer
|
|
220
|
+
size never grows with total table size, only with how fast changes arrive.
|
|
221
|
+
- **Pick the right update method for the table size**: `full_refresh`
|
|
222
|
+
re-copies the whole table every run — fine for small reference tables,
|
|
223
|
+
wasteful for millions of rows. Use `incremental`, `xmin`, or CDC mode for
|
|
224
|
+
anything large, so each run only moves what actually changed.
|
|
225
|
+
- **Concurrency**: batch connections run on a thread pool sized by
|
|
226
|
+
`HOLYPIPE_SCHEDULER_WORKERS` (default 4); raise it if you have many batch
|
|
227
|
+
connections you want running in parallel. Each CDC connection gets its own
|
|
228
|
+
dedicated thread regardless of that limit.
|
|
229
|
+
- **Caching**: schema discovery (scanning `information_schema` /
|
|
230
|
+
`sqlite_master`) is cached per source for `HOLYPIPE_DISCOVERY_CACHE_TTL`
|
|
231
|
+
seconds (default 300) so opening the connection wizard repeatedly on a
|
|
232
|
+
database with thousands of tables doesn't re-scan every time; a "↻
|
|
233
|
+
Refresh" button in the wizard bypasses the cache on demand, and the cache
|
|
234
|
+
is invalidated automatically whenever a source's connection details change.
|
|
235
|
+
- **HolyPipe's own metadata store never holds your data** — only
|
|
236
|
+
connection/run/log bookkeeping — so its size stays tiny no matter how much
|
|
237
|
+
data flows through the pipelines it manages.
|
|
238
|
+
|
|
239
|
+
## Architecture
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
holypipe/
|
|
243
|
+
connectors/ Source & destination drivers (postgres, mysql, sqlite)
|
|
244
|
+
behind a shared canonical-type interface.
|
|
245
|
+
engine/
|
|
246
|
+
sync.py Batch full-refresh / incremental runner.
|
|
247
|
+
cdc.py Long-running CDC worker threads + supervisor.
|
|
248
|
+
scheduler.py Polls connections, dispatches batch runs, keeps CDC
|
|
249
|
+
workers matched to enabled connections.
|
|
250
|
+
api/ FastAPI routes + websocket event stream.
|
|
251
|
+
web/static/ Plain HTML/CSS/JS dashboard (no build step).
|
|
252
|
+
models.py Metadata schema (sources, destinations, connections,
|
|
253
|
+
run history, logs) — stored in HolyPipe's own DB.
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Each sync direction only ever needs one thing from a connector: rows in,
|
|
257
|
+
rows out, expressed through a small canonical type system (string, integer,
|
|
258
|
+
number, boolean, timestamp, date, time, json, binary) so a Postgres `numeric`
|
|
259
|
+
column and a MySQL `decimal` column land the same way in a SQLite `REAL`.
|
|
260
|
+
|
|
261
|
+
## Local development (without Docker)
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
python -m venv .venv && . .venv/Scripts/activate # or source .venv/bin/activate
|
|
265
|
+
pip install -e .
|
|
266
|
+
uvicorn holypipe.main:app --reload
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
You'll need your own Postgres/MySQL to point sources/destinations at, or use
|
|
270
|
+
`sqlite` (just a file path) to try it with zero extra setup.
|
|
271
|
+
|
|
272
|
+
## License
|
|
273
|
+
|
|
274
|
+
MIT — see [LICENSE](LICENSE).
|
holypipe-1.0.0/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo-wordmark.svg" alt="HolyPipe" width="360">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
A small, purpose-built ELT / data-replication tool — the parts of Airbyte you
|
|
6
|
+
actually need when you only care about **PostgreSQL, MySQL and SQLite** as
|
|
7
|
+
sources and destinations. Runs entirely in Docker (Windows, macOS, and Linux
|
|
8
|
+
alike), syncs in real time via change-data-capture (CDC), and ships with a
|
|
9
|
+
live web dashboard.
|
|
10
|
+
|
|
11
|
+
📖 **Guides:** [User Guide](docs/USER_GUIDE.md) ·
|
|
12
|
+
[Administrator Guide](docs/ADMIN_GUIDE.md) ·
|
|
13
|
+
[Developer Guide](docs/DEVELOPER_GUIDE.md)
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
- **Sources & destinations**: PostgreSQL, MySQL, SQLite, in any combination
|
|
18
|
+
(e.g. Postgres → MySQL, MySQL → SQLite, Postgres → Postgres warehouse).
|
|
19
|
+
- **Real-time sync (CDC)**:
|
|
20
|
+
- Postgres: logical replication (`wal2json` if available, falls back to the
|
|
21
|
+
built-in `test_decoding` plugin — no extension install required).
|
|
22
|
+
- MySQL: row-based binlog streaming via `mysql-replication`.
|
|
23
|
+
- SQLite: trigger-based change journal, polled sub-second (SQLite has no
|
|
24
|
+
external replication protocol, so this is the closest equivalent).
|
|
25
|
+
- Every CDC connection starts with a **one-time initial snapshot** of each
|
|
26
|
+
selected table (captured right after the replication slot/binlog
|
|
27
|
+
position/trigger journal is created, so nothing that changes during the
|
|
28
|
+
snapshot is missed) before it switches to streaming — so you get all
|
|
29
|
+
existing rows *and* every future change, not just the future ones. The
|
|
30
|
+
snapshot runs once per connection; stopping and restarting CDC resumes
|
|
31
|
+
from where it left off instead of re-copying.
|
|
32
|
+
- **Batch sync**: full-refresh, incremental (cursor-column), or — for
|
|
33
|
+
Postgres sources — **xmin** (tracks the system `xmin` column, so you get
|
|
34
|
+
change detection without picking a cursor column at all).
|
|
35
|
+
- **Update method per stream**: choose Full Refresh / Incremental / Xmin per
|
|
36
|
+
table when the connection is in batch mode, or CDC at the connection level
|
|
37
|
+
for continuous log-based streaming.
|
|
38
|
+
- **Flexible scheduling**: batch connections run on a **fixed interval** or a
|
|
39
|
+
**cron expression** (5-field crontab syntax); the connection list shows the
|
|
40
|
+
configured frequency, last sync time, and next scheduled run.
|
|
41
|
+
- **SSL modes**: pick a PostgreSQL `sslmode` (disable/allow/prefer/require/
|
|
42
|
+
verify-ca/verify-full) or MySQL SSL mode (DISABLED/PREFERRED/REQUIRED/
|
|
43
|
+
VERIFY_CA/VERIFY_IDENTITY) per source/destination.
|
|
44
|
+
- **Two ways to configure a connector**: fill in the form fields, or flip to
|
|
45
|
+
"Connection URI" and paste a DSN (`postgresql://user:pass@host:5432/db?
|
|
46
|
+
sslmode=require`, `mysql://user:pass@host:3306/db`, `sqlite:////data/a.db`)
|
|
47
|
+
— either way you can **Test connection** before saving.
|
|
48
|
+
- **Schema discovery & auto-provisioning**: pick tables/columns from the
|
|
49
|
+
source; destination tables and columns are created and evolved
|
|
50
|
+
automatically, with `_hp_extracted_at` / `_hp_op` / `_hp_deleted` metadata
|
|
51
|
+
columns for auditability and soft-deletes.
|
|
52
|
+
- **Live dashboard**: create sources/destinations/connections, start/stop
|
|
53
|
+
syncs, and watch a live log/event stream over a websocket — all from
|
|
54
|
+
`http://localhost:8090`.
|
|
55
|
+
- **Everything else Airbyte doesn't need to be**: no Java, no Temporal, no
|
|
56
|
+
connector marketplace. One Python process, one container.
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
No cloning required — pick whichever fits how you run things. Works
|
|
61
|
+
identically on Windows, macOS, and Linux either way.
|
|
62
|
+
|
|
63
|
+
**Docker (recommended)** — pulls a prebuilt image, nothing to build:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
docker run -d --name holypipe -p 8090:8000 -v holypipe_data:/data \
|
|
67
|
+
ghcr.io/tbwahacker/holypipe:latest
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Open `http://localhost:8090` and add your own sources/destinations
|
|
71
|
+
straight away (see the [User Guide](docs/USER_GUIDE.md)).
|
|
72
|
+
|
|
73
|
+
**Python (no Docker)**:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install holypipe
|
|
77
|
+
holypipe # starts the dashboard on http://localhost:8000
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Needs Python 3.11+. `psycopg2` requires `libpq` on the host (its wheel
|
|
81
|
+
usually bundles this already); everything else is pure Python. Config is
|
|
82
|
+
the same environment variables either way — see
|
|
83
|
+
[Configuration](docs/ADMIN_GUIDE.md#configuration).
|
|
84
|
+
|
|
85
|
+
**Try the full demo stack** (clone this repo) — spins up seeded Postgres
|
|
86
|
+
and MySQL sources plus a warehouse destination alongside HolyPipe, so
|
|
87
|
+
there's real data to sync immediately:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/tbwahacker/holypipe.git && cd holypipe
|
|
91
|
+
docker compose up --build
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This starts:
|
|
95
|
+
- `holypipe` — the app, at http://localhost:8090
|
|
96
|
+
- `postgres_demo` — a sample Postgres source (`shop` db, port 5433), with
|
|
97
|
+
`wal_level=logical` already configured, seeded with `customers`/`orders`
|
|
98
|
+
- `mysql_demo` — a sample MySQL source (`shop` db, port 3307), with binlog
|
|
99
|
+
row-format already configured, seeded with `products`/`inventory`
|
|
100
|
+
- `warehouse` — an empty Postgres destination (port 5434) to sync into
|
|
101
|
+
|
|
102
|
+
Open http://localhost:8090, then:
|
|
103
|
+
1. **Sources** → New Source → type `postgres`, host `postgres_demo`, port
|
|
104
|
+
`5432`, database `shop`, user/password `holypipe`/`holypipe`. Test, Save.
|
|
105
|
+
(Do the same for `mysql_demo`, port `3306`, if you want a MySQL source.)
|
|
106
|
+
2. **Destinations** → New Destination → type `postgres`, host `warehouse`,
|
|
107
|
+
port `5432`, database `warehouse`, user/password `holypipe`/`holypipe`.
|
|
108
|
+
Or pick `sqlite` with path `/data/demo.db` to land data in HolyPipe's own
|
|
109
|
+
volume — no extra container needed.
|
|
110
|
+
3. **Connections** → New Connection → pick the source/destination, choose
|
|
111
|
+
**Real-time (CDC)** or **Batch**, Discover tables, select the ones you
|
|
112
|
+
want, Create. CDC connections start streaming immediately; batch
|
|
113
|
+
connections run on the interval you set (or click "Run now").
|
|
114
|
+
4. Watch the **Live Logs** tab, or `psql`/`mysql` into the source containers
|
|
115
|
+
and insert/update/delete rows — changes land in the destination within
|
|
116
|
+
about a second under CDC mode.
|
|
117
|
+
|
|
118
|
+
## Configuration (environment variables)
|
|
119
|
+
|
|
120
|
+
| Variable | Default | Meaning |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `HOLYPIPE_METADATA_URL` | `sqlite:///./data/holypipe.db` | Where HolyPipe stores its own config/state (sources, destinations, connections, run history). |
|
|
123
|
+
| `HOLYPIPE_SCHEDULER` | `1` | Set `0` to disable the background scheduler/CDC supervisor (e.g. for a read-only API replica). |
|
|
124
|
+
| `HOLYPIPE_BATCH_SIZE` | `1000` | Rows per batch for full-refresh/incremental reads. |
|
|
125
|
+
| `HOLYPIPE_CDC_FLUSH_SECONDS` | `1.0` | Max latency before a buffered CDC batch is flushed to the destination. |
|
|
126
|
+
| `HOLYPIPE_CDC_FLUSH_RECORDS` | `500` | Max buffered CDC events before an early flush. |
|
|
127
|
+
| `HOLYPIPE_SCHEDULER_WORKERS` | `4` | Concurrent batch syncs the scheduler runs at once (CDC connections are unaffected — each gets its own thread). |
|
|
128
|
+
| `HOLYPIPE_DISCOVERY_CACHE_TTL` | `300` | Seconds a source's discovered schema is cached before a "Discover tables" click re-scans it. |
|
|
129
|
+
| `HOLYPIPE_LOG_LEVEL` | `INFO` | stdout log verbosity. |
|
|
130
|
+
| `HOLYPIPE_LOG_RETENTION` | `2000` | How many log rows to keep in the metadata DB. |
|
|
131
|
+
|
|
132
|
+
## Requirements for CDC on your own databases
|
|
133
|
+
|
|
134
|
+
- **PostgreSQL**: `wal_level = logical` (restart required), and a user with
|
|
135
|
+
`REPLICATION` privilege. Tables benefit from `REPLICA IDENTITY FULL` (or a
|
|
136
|
+
primary key) so updates/deletes carry the old row. No `wal2json` extension
|
|
137
|
+
is required — HolyPipe falls back to Postgres's built-in `test_decoding`
|
|
138
|
+
plugin automatically if `wal2json` isn't installed.
|
|
139
|
+
- **MySQL**: `log_bin = ON`, `binlog_format = ROW`, and a user with
|
|
140
|
+
`REPLICATION SLAVE, REPLICATION CLIENT` privileges. Give each concurrent
|
|
141
|
+
CDC connection a unique `server_id` in its source config.
|
|
142
|
+
- **SQLite**: nothing special — HolyPipe installs its own triggers on the
|
|
143
|
+
file the first time CDC starts.
|
|
144
|
+
|
|
145
|
+
## Scaling to large tables and staying stable
|
|
146
|
+
|
|
147
|
+
- **Streaming, not buffering**: sources never load a full table into memory.
|
|
148
|
+
Postgres uses a named server-side cursor, MySQL uses `SSCursor`/
|
|
149
|
+
`SSDictCursor`, and SQLite streams via `fetchmany` — all bounded by
|
|
150
|
+
`HOLYPIPE_BATCH_SIZE` (default 1000 rows/batch) regardless of table size.
|
|
151
|
+
Writes are batched the same way (`execute_values`/`executemany`), so a
|
|
152
|
+
multi-million-row table moves in a steady stream of bounded batches, not
|
|
153
|
+
one giant transaction.
|
|
154
|
+
- **Reused connections**: each running sync/CDC worker holds one connection
|
|
155
|
+
per source and destination for its whole lifetime instead of reconnecting
|
|
156
|
+
per batch — important for CDC, which flushes as often as once a second.
|
|
157
|
+
A dropped/broken connection is detected and transparently reconnected on
|
|
158
|
+
the next call.
|
|
159
|
+
- **Bounded CDC memory**: real-time changes are buffered up to
|
|
160
|
+
`HOLYPIPE_CDC_FLUSH_RECORDS` (default 500) or `HOLYPIPE_CDC_FLUSH_SECONDS`
|
|
161
|
+
(default 1.0), whichever comes first, then flushed and cleared — buffer
|
|
162
|
+
size never grows with total table size, only with how fast changes arrive.
|
|
163
|
+
- **Pick the right update method for the table size**: `full_refresh`
|
|
164
|
+
re-copies the whole table every run — fine for small reference tables,
|
|
165
|
+
wasteful for millions of rows. Use `incremental`, `xmin`, or CDC mode for
|
|
166
|
+
anything large, so each run only moves what actually changed.
|
|
167
|
+
- **Concurrency**: batch connections run on a thread pool sized by
|
|
168
|
+
`HOLYPIPE_SCHEDULER_WORKERS` (default 4); raise it if you have many batch
|
|
169
|
+
connections you want running in parallel. Each CDC connection gets its own
|
|
170
|
+
dedicated thread regardless of that limit.
|
|
171
|
+
- **Caching**: schema discovery (scanning `information_schema` /
|
|
172
|
+
`sqlite_master`) is cached per source for `HOLYPIPE_DISCOVERY_CACHE_TTL`
|
|
173
|
+
seconds (default 300) so opening the connection wizard repeatedly on a
|
|
174
|
+
database with thousands of tables doesn't re-scan every time; a "↻
|
|
175
|
+
Refresh" button in the wizard bypasses the cache on demand, and the cache
|
|
176
|
+
is invalidated automatically whenever a source's connection details change.
|
|
177
|
+
- **HolyPipe's own metadata store never holds your data** — only
|
|
178
|
+
connection/run/log bookkeeping — so its size stays tiny no matter how much
|
|
179
|
+
data flows through the pipelines it manages.
|
|
180
|
+
|
|
181
|
+
## Architecture
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
holypipe/
|
|
185
|
+
connectors/ Source & destination drivers (postgres, mysql, sqlite)
|
|
186
|
+
behind a shared canonical-type interface.
|
|
187
|
+
engine/
|
|
188
|
+
sync.py Batch full-refresh / incremental runner.
|
|
189
|
+
cdc.py Long-running CDC worker threads + supervisor.
|
|
190
|
+
scheduler.py Polls connections, dispatches batch runs, keeps CDC
|
|
191
|
+
workers matched to enabled connections.
|
|
192
|
+
api/ FastAPI routes + websocket event stream.
|
|
193
|
+
web/static/ Plain HTML/CSS/JS dashboard (no build step).
|
|
194
|
+
models.py Metadata schema (sources, destinations, connections,
|
|
195
|
+
run history, logs) — stored in HolyPipe's own DB.
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Each sync direction only ever needs one thing from a connector: rows in,
|
|
199
|
+
rows out, expressed through a small canonical type system (string, integer,
|
|
200
|
+
number, boolean, timestamp, date, time, json, binary) so a Postgres `numeric`
|
|
201
|
+
column and a MySQL `decimal` column land the same way in a SQLite `REAL`.
|
|
202
|
+
|
|
203
|
+
## Local development (without Docker)
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
python -m venv .venv && . .venv/Scripts/activate # or source .venv/bin/activate
|
|
207
|
+
pip install -e .
|
|
208
|
+
uvicorn holypipe.main:app --reload
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
You'll need your own Postgres/MySQL to point sources/destinations at, or use
|
|
212
|
+
`sqlite` (just a file path) to try it with zero extra setup.
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
File without changes
|