zerobucket 0.7.0 → 0.8.0
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.
- package/README.md +31 -12
- package/dist/bin.js +984 -5
- package/dist/index.cjs +278 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +278 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,8 +10,7 @@ This is the Node.js/TypeScript sibling of the [Python package](https://pypi.org/
|
|
|
10
10
|
**It uses the exact same table**, so a Node service and a Python service can share one database
|
|
11
11
|
and read each other's images.
|
|
12
12
|
|
|
13
|
-
> **Status: early (0.
|
|
14
|
-
> mode; dedup and the CLI are not available for MySQL yet, see [What's not here yet](#whats-not-here-yet)), with
|
|
13
|
+
> **Status: early (0.8.x).** PostgreSQL, SQLite **and MySQL/MariaDB**, each in classic and dedup modes, with
|
|
15
14
|
> S3-compatible object-storage tiering, HEIC (iPhone photo) support and a `zerobucket` command line tool.
|
|
16
15
|
|
|
17
16
|
## Install
|
|
@@ -220,10 +219,13 @@ const id2 = await images.put(iphonePhoto, {
|
|
|
220
219
|
|
|
221
220
|
The package installs a `zerobucket` command (`npx zerobucket ...`, or install globally with `npm i -g zerobucket`).
|
|
222
221
|
It has the same commands, flags, messages and exit codes as the Python package's CLI, and additionally works with
|
|
223
|
-
**SQLite
|
|
222
|
+
**SQLite**, **MySQL/MariaDB** (a `mysql://` or `mariadb://` `--database-url`, or `ZEROBUCKET_DATABASE_URL`) and **dedup
|
|
223
|
+
mode**, and understands images that were tiered to object storage. On MySQL the on-disk size is read from
|
|
224
|
+
`information_schema` and is approximate (MySQL 8 caches those statistics for up to 24 hours).
|
|
224
225
|
|
|
225
226
|
```bash
|
|
226
227
|
export ZEROBUCKET_DATABASE_URL=postgresql://user:pass@localhost/mydb # or: --database-url URL
|
|
228
|
+
# MySQL/MariaDB: export ZEROBUCKET_DATABASE_URL=mysql://user:pass@localhost:3306/mydb # mariadb:// works too
|
|
227
229
|
# SQLite instead: export ZEROBUCKET_SQLITE_PATH=./images.db # or: --sqlite PATH
|
|
228
230
|
|
|
229
231
|
zerobucket init # create the schema (add --dedup for the dedup tables)
|
|
@@ -308,12 +310,16 @@ const images = new ZeroBucket({
|
|
|
308
310
|
|
|
309
311
|
Same API as everywhere else: `put`, `get`, `getMany`, `putMany`, `delete`, `deleteMany`, `exists`, `metadata`, streaming
|
|
310
312
|
and ranges, access-control hooks, the optimize pipeline, custom validators, HEIC, `onOperation` metrics, retry with
|
|
311
|
-
backoff, pooling (`poolMaxSize`, `poolTimeoutMs`), **object-storage tiering** and
|
|
312
|
-
|
|
313
|
-
|
|
313
|
+
backoff, pooling (`poolMaxSize`, `poolTimeoutMs`), **dedup mode** (`dedup: true`), **object-storage tiering** and
|
|
314
|
+
`connection` (your own transaction). The [`zerobucket` CLI](#command-line) works on it too: pass a `mysql://` or
|
|
315
|
+
`mariadb://` `--database-url`.
|
|
314
316
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
+
It uses the Python package's tables, byte for byte (`SHOW CREATE TABLE` is identical for the classic table and for both
|
|
318
|
+
dedup tables), so a Node app and a Python app can share one MySQL/MariaDB database. The conformance harness verifies both
|
|
319
|
+
directions in both modes, including tiered rows and shared dedup blobs.
|
|
320
|
+
|
|
321
|
+
As everywhere, dedup and tiering cannot be combined (`dedup: true` with `objectStorage` throws). Copying an existing
|
|
322
|
+
classic table into the dedup tables (`migrate --to-dedup`) is PostgreSQL-only, as in the Python package.
|
|
317
323
|
|
|
318
324
|
What is different from Postgres, and why (each verified against a real MariaDB 10.11):
|
|
319
325
|
|
|
@@ -331,6 +337,18 @@ What is different from Postgres, and why (each verified against a real MariaDB 1
|
|
|
331
337
|
- **Migration takes a server-side lock** (`GET_LOCK`), so many processes starting at once do not race on the DDL, and it
|
|
332
338
|
issues **no DDL at all** when the schema is current. (Even a no-op `ALTER TABLE` needs an exclusive metadata lock that
|
|
333
339
|
waits behind every open transaction, and everything else on that table then queues behind _it_.)
|
|
340
|
+
- **Dedup** stores each distinct image once (`zerobucket_blobs`, with a `ref_count`) and one row per image
|
|
341
|
+
(`zerobucket_image_refs`). Without `RETURNING`, a delete decrements with `UPDATE ... ref_count = ref_count - n` (which
|
|
342
|
+
takes the row lock) and then removes the blob once the count reaches zero; the foreign key makes deleting a blob that is
|
|
343
|
+
still referenced impossible. A batch groups identical images, touches each distinct blob **once, in sorted checksum
|
|
344
|
+
order**, and decrements in the same order, so overlapping batches cannot deadlock.
|
|
345
|
+
- **Batch deletes use one `SELECT ... FOR UPDATE` and then point deletes, never `DELETE ... WHERE id IN (...)`.** On
|
|
346
|
+
MariaDB 10.11, with a small table, the `IN` delete is planned as a full scan (`EXPLAIN` says `type=ALL`), and a scan
|
|
347
|
+
under REPEATABLE READ locks every row it passes, including rows another batch already holds. Two overlapping
|
|
348
|
+
`deleteMany` batches deadlocked in 126 of 300 transactions with an `IN` delete and in 0 with point deletes (also 0 with
|
|
349
|
+
a concurrent writer on the same blobs); `READ COMMITTED` and `FORCE INDEX` alone did not fix it. MySQL 8.0.46 plans the
|
|
350
|
+
same statement as a range over just the listed ids and did **not** deadlock in the same test. Point deletes do not
|
|
351
|
+
depend on the plan, so they are used on both.
|
|
334
352
|
- **Large images and `max_allowed_packet`.** An image bigger than the server's limit fails with an error that says so and
|
|
335
353
|
how to raise it (`SET GLOBAL max_allowed_packet = ...`). Images are sent as raw bytes, so the limit applies to the image
|
|
336
354
|
size, not twice that.
|
|
@@ -343,8 +361,9 @@ What is different from Postgres, and why (each verified against a real MariaDB 1
|
|
|
343
361
|
skipped on your connection (it cannot know whether you will commit).
|
|
344
362
|
- **Streaming** uses ranged `SUBSTRING()` reads, so a delete mid-stream raises `StorageError` (like Postgres). Tiered images
|
|
345
363
|
stream with real S3 `Range` requests.
|
|
346
|
-
- **Tested on MariaDB 10.11
|
|
347
|
-
|
|
364
|
+
- **Tested on MariaDB 10.11 and MySQL 8.0.46** (the MySQL, dedup and CLI test suites, the Node/Python conformance harness
|
|
365
|
+
and the schema comparison all pass on both). Other versions are expected to behave the same but have not been run. Use
|
|
366
|
+
MariaDB 10.2.1+ or MySQL 8.0.16+: older servers accept the `CHECK` constraint syntax but ignore it.
|
|
348
367
|
|
|
349
368
|
## Deduplication
|
|
350
369
|
|
|
@@ -443,8 +462,8 @@ same corpus of valid and corrupt files identically.
|
|
|
443
462
|
|
|
444
463
|
## What's not here yet
|
|
445
464
|
|
|
446
|
-
Planned for MySQL/MariaDB:
|
|
447
|
-
Tiering and dedup cannot be combined (same rule as the Python package).
|
|
465
|
+
Planned for MySQL/MariaDB: a MySQL-specific operations guide, and `migrate --to-dedup` (PostgreSQL-only today, as in the
|
|
466
|
+
Python package). Tiering and dedup cannot be combined (same rule as the Python package).
|
|
448
467
|
|
|
449
468
|
## Runtime notes
|
|
450
469
|
|