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.
Files changed (40) hide show
  1. holypipe-1.0.0/LICENSE +21 -0
  2. holypipe-1.0.0/PKG-INFO +274 -0
  3. holypipe-1.0.0/README.md +216 -0
  4. holypipe-1.0.0/holypipe/__init__.py +1 -0
  5. holypipe-1.0.0/holypipe/api/__init__.py +0 -0
  6. holypipe-1.0.0/holypipe/api/routes.py +479 -0
  7. holypipe-1.0.0/holypipe/api/schemas.py +135 -0
  8. holypipe-1.0.0/holypipe/bus.py +68 -0
  9. holypipe-1.0.0/holypipe/cache.py +56 -0
  10. holypipe-1.0.0/holypipe/cli.py +19 -0
  11. holypipe-1.0.0/holypipe/config.py +37 -0
  12. holypipe-1.0.0/holypipe/connectors/__init__.py +15 -0
  13. holypipe-1.0.0/holypipe/connectors/base.py +177 -0
  14. holypipe-1.0.0/holypipe/connectors/dsn.py +86 -0
  15. holypipe-1.0.0/holypipe/connectors/mysql.py +415 -0
  16. holypipe-1.0.0/holypipe/connectors/postgres.py +594 -0
  17. holypipe-1.0.0/holypipe/connectors/registry.py +78 -0
  18. holypipe-1.0.0/holypipe/connectors/sqlite.py +334 -0
  19. holypipe-1.0.0/holypipe/db.py +62 -0
  20. holypipe-1.0.0/holypipe/engine/__init__.py +0 -0
  21. holypipe-1.0.0/holypipe/engine/cdc.py +277 -0
  22. holypipe-1.0.0/holypipe/engine/recovery.py +37 -0
  23. holypipe-1.0.0/holypipe/engine/scheduler.py +136 -0
  24. holypipe-1.0.0/holypipe/engine/sync.py +177 -0
  25. holypipe-1.0.0/holypipe/logging_util.py +41 -0
  26. holypipe-1.0.0/holypipe/main.py +58 -0
  27. holypipe-1.0.0/holypipe/models.py +114 -0
  28. holypipe-1.0.0/holypipe/timeutil.py +19 -0
  29. holypipe-1.0.0/holypipe/web/static/app.js +1258 -0
  30. holypipe-1.0.0/holypipe/web/static/favicon.svg +28 -0
  31. holypipe-1.0.0/holypipe/web/static/index.html +64 -0
  32. holypipe-1.0.0/holypipe/web/static/style.css +393 -0
  33. holypipe-1.0.0/holypipe.egg-info/PKG-INFO +274 -0
  34. holypipe-1.0.0/holypipe.egg-info/SOURCES.txt +38 -0
  35. holypipe-1.0.0/holypipe.egg-info/dependency_links.txt +1 -0
  36. holypipe-1.0.0/holypipe.egg-info/entry_points.txt +2 -0
  37. holypipe-1.0.0/holypipe.egg-info/requires.txt +10 -0
  38. holypipe-1.0.0/holypipe.egg-info/top_level.txt +1 -0
  39. holypipe-1.0.0/pyproject.toml +53 -0
  40. 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.
@@ -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).
@@ -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