zerobucket 0.10.0 → 0.10.1
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 +11 -7
- package/dist/bin.js +8 -6
- package/dist/index.cjs +8 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -485,18 +485,22 @@ MySQL `ALTER TABLE` at startup when the schema is current.
|
|
|
485
485
|
## Performance
|
|
486
486
|
|
|
487
487
|
Measured on one single-core container, so treat it as directional; the full tables (PostgreSQL, SQLite, MariaDB, per size,
|
|
488
|
-
memory, event-loop impact, throughput, and the
|
|
488
|
+
memory, event-loop impact, throughput, and the before and after of each release) are in the
|
|
489
489
|
[benchmark results](https://github.com/KedarGhadyalji/ZeroBucket/blob/main/benchmarks/node/RESULTS.md).
|
|
490
490
|
|
|
491
|
-
| Image size | `put()` (PostgreSQL) | `get()`
|
|
492
|
-
| ------------ | -------------------- |
|
|
493
|
-
| up to ~500KB | under 20ms | under 5ms
|
|
494
|
-
| 0.5-1.5MB | about 20-45ms | under 10ms
|
|
495
|
-
| about 5MB | about 170ms | about
|
|
496
|
-
| about 10MB | about 350ms | about
|
|
491
|
+
| Image size | `put()` (PostgreSQL) | `get()` (PostgreSQL) | Notes |
|
|
492
|
+
| ------------ | -------------------- | -------------------- | ----------------------------------------------------------------- |
|
|
493
|
+
| up to ~500KB | under 20ms | under 5ms | Feels like any other write. |
|
|
494
|
+
| 0.5-1.5MB | about 20-45ms | under 10ms | Fine for uploads, including in a loop. |
|
|
495
|
+
| about 5MB | about 170ms | about 35ms | A few MB of transient memory to write a baseline JPEG or PNG. |
|
|
496
|
+
| about 10MB | about 350ms | about 70ms | Default limit is 8 MiB. Memory depends on the format (see below). |
|
|
497
497
|
|
|
498
498
|
- Since 0.10.0, `put()` is 2.5-4x faster than 0.9.x and about as fast as the Python package for large images. Validation is now
|
|
499
499
|
roughly half of `put()` on PostgreSQL.
|
|
500
|
+
- Since 0.10.1, reading from PostgreSQL asks for base64 instead of PostgreSQL's default hex text: about 30% faster for images over
|
|
501
|
+
2MB, and `get()` needs 37-47% less memory (about 6.5 times the image size, down from about 10). Streaming, which is how
|
|
502
|
+
`createImageHandler` serves images, is bounded in memory either way. Between roughly 0.5MB and 1.3MB there is no reliable
|
|
503
|
+
difference (a cost of under a millisecond may exist). SQLite and MySQL/MariaDB were already fast readers and are unchanged.
|
|
500
504
|
- The decode runs off the main thread: the event loop stalled at most about 14ms during a 10MB `put()`.
|
|
501
505
|
- Transient memory depends on the format. A baseline JPEG or non-interlaced PNG validates in streaming fashion (about 11MB for a
|
|
502
506
|
45-megapixel JPEG, down from about 148MB in 0.9.x). WebP, progressive JPEG and interlaced PNG still need roughly the decoded
|
package/dist/bin.js
CHANGED
|
@@ -150,17 +150,17 @@ INSERT INTO zerobucket_images
|
|
|
150
150
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
151
151
|
RETURNING id;`;
|
|
152
152
|
var SELECT_FULL = `
|
|
153
|
-
SELECT id, data, mime_type, original_filename, size_bytes, width, height,
|
|
153
|
+
SELECT id, encode(data, 'base64') AS data_b64, mime_type, original_filename, size_bytes, width, height,
|
|
154
154
|
checksum_sha256, storage_backend, object_storage_bucket, object_storage_key
|
|
155
155
|
FROM zerobucket_images WHERE id = $1;`;
|
|
156
156
|
var SELECT_MANY = `
|
|
157
|
-
SELECT id, data, mime_type, original_filename, size_bytes, width, height,
|
|
157
|
+
SELECT id, encode(data, 'base64') AS data_b64, mime_type, original_filename, size_bytes, width, height,
|
|
158
158
|
checksum_sha256, storage_backend, object_storage_bucket, object_storage_key
|
|
159
159
|
FROM zerobucket_images WHERE id = ANY($1::uuid[]);`;
|
|
160
160
|
var SELECT_METADATA = `
|
|
161
161
|
SELECT id, mime_type, original_filename, size_bytes, width, height, checksum_sha256
|
|
162
162
|
FROM zerobucket_images WHERE id = $1;`;
|
|
163
|
-
var SELECT_CHUNK = `SELECT substring(data FROM $1::int FOR $2::int) AS chunk FROM zerobucket_images WHERE id = $3;`;
|
|
163
|
+
var SELECT_CHUNK = `SELECT encode(substring(data FROM $1::int FOR $2::int), 'base64') AS chunk FROM zerobucket_images WHERE id = $3;`;
|
|
164
164
|
var SELECT_STREAM_INFO = `SELECT size_bytes, storage_backend, object_storage_key FROM zerobucket_images WHERE id = $1;`;
|
|
165
165
|
var DELETE_RETURNING = `DELETE FROM zerobucket_images WHERE id = $1 RETURNING storage_backend, object_storage_key;`;
|
|
166
166
|
var DELETE_MANY = `DELETE FROM zerobucket_images WHERE id = ANY($1::uuid[]) RETURNING id, storage_backend, object_storage_key;`;
|
|
@@ -193,7 +193,7 @@ INSERT INTO zerobucket_blobs (checksum_sha256, data, mime_type, size_bytes, widt
|
|
|
193
193
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
194
194
|
ON CONFLICT (checksum_sha256) DO UPDATE SET ref_count = zerobucket_blobs.ref_count + EXCLUDED.ref_count;`;
|
|
195
195
|
var D_INSERT_REF = `INSERT INTO zerobucket_image_refs (checksum_sha256, original_filename) VALUES ($1, $2) RETURNING id;`;
|
|
196
|
-
var D_COLS = `r.id, b.data, b.mime_type, r.original_filename, b.size_bytes, b.width, b.height, r.checksum_sha256,
|
|
196
|
+
var D_COLS = `r.id, encode(b.data, 'base64') AS data_b64, b.mime_type, r.original_filename, b.size_bytes, b.width, b.height, r.checksum_sha256,
|
|
197
197
|
'postgres'::text AS storage_backend, NULL::text AS object_storage_bucket, NULL::text AS object_storage_key`;
|
|
198
198
|
var D_FROM = `FROM zerobucket_image_refs r JOIN zerobucket_blobs b ON r.checksum_sha256 = b.checksum_sha256`;
|
|
199
199
|
var D_DELETE_REF = `DELETE FROM zerobucket_image_refs WHERE id = $1 RETURNING checksum_sha256;`;
|
|
@@ -215,7 +215,7 @@ var DEDUP_SQL = {
|
|
|
215
215
|
selectMany: `SELECT ${D_COLS} ${D_FROM} WHERE r.id = ANY($1::uuid[]);`,
|
|
216
216
|
selectMetadata: `SELECT r.id, b.mime_type, r.original_filename, b.size_bytes, b.width, b.height, r.checksum_sha256 ${D_FROM} WHERE r.id = $1;`,
|
|
217
217
|
selectStreamInfo: `SELECT b.size_bytes, 'postgres'::text AS storage_backend, NULL::text AS object_storage_key ${D_FROM} WHERE r.id = $1;`,
|
|
218
|
-
selectChunk: `SELECT substring(b.data FROM $1::int FOR $2::int) AS chunk ${D_FROM} WHERE r.id = $3;`,
|
|
218
|
+
selectChunk: `SELECT encode(substring(b.data FROM $1::int FOR $2::int), 'base64') AS chunk ${D_FROM} WHERE r.id = $3;`,
|
|
219
219
|
exists: `SELECT 1 FROM zerobucket_image_refs WHERE id = $1;`
|
|
220
220
|
};
|
|
221
221
|
var RETRYABLE_SQLSTATES = /* @__PURE__ */ new Set([
|
|
@@ -672,7 +672,7 @@ var PostgresBackend = class {
|
|
|
672
672
|
`Image ${JSON.stringify(id)} was deleted while streaming (delivered ${delivered} of ${wanted} bytes). Pass connection with your own open transaction if you need a consistent read across concurrent writers.`
|
|
673
673
|
);
|
|
674
674
|
}
|
|
675
|
-
const chunk = row.chunk;
|
|
675
|
+
const chunk = row.chunk == null ? null : Buffer.from(row.chunk, "base64");
|
|
676
676
|
if (!chunk || chunk.length === 0) {
|
|
677
677
|
throw new StorageError(
|
|
678
678
|
`Image ${JSON.stringify(id)} ended early while streaming (delivered ${delivered} of ${wanted} bytes).`
|
|
@@ -996,6 +996,8 @@ var PostgresBackend = class {
|
|
|
996
996
|
}
|
|
997
997
|
// ---- helpers ---------------------------------------------------------
|
|
998
998
|
hydrate(row, id) {
|
|
999
|
+
row.data = row.data_b64 == null ? null : Buffer.from(row.data_b64, "base64");
|
|
1000
|
+
delete row.data_b64;
|
|
999
1001
|
return hydrateRow(row, id, this.opts.objectStorage);
|
|
1000
1002
|
}
|
|
1001
1003
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -255,17 +255,17 @@ INSERT INTO zerobucket_images
|
|
|
255
255
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
256
256
|
RETURNING id;`;
|
|
257
257
|
var SELECT_FULL = `
|
|
258
|
-
SELECT id, data, mime_type, original_filename, size_bytes, width, height,
|
|
258
|
+
SELECT id, encode(data, 'base64') AS data_b64, mime_type, original_filename, size_bytes, width, height,
|
|
259
259
|
checksum_sha256, storage_backend, object_storage_bucket, object_storage_key
|
|
260
260
|
FROM zerobucket_images WHERE id = $1;`;
|
|
261
261
|
var SELECT_MANY = `
|
|
262
|
-
SELECT id, data, mime_type, original_filename, size_bytes, width, height,
|
|
262
|
+
SELECT id, encode(data, 'base64') AS data_b64, mime_type, original_filename, size_bytes, width, height,
|
|
263
263
|
checksum_sha256, storage_backend, object_storage_bucket, object_storage_key
|
|
264
264
|
FROM zerobucket_images WHERE id = ANY($1::uuid[]);`;
|
|
265
265
|
var SELECT_METADATA = `
|
|
266
266
|
SELECT id, mime_type, original_filename, size_bytes, width, height, checksum_sha256
|
|
267
267
|
FROM zerobucket_images WHERE id = $1;`;
|
|
268
|
-
var SELECT_CHUNK = `SELECT substring(data FROM $1::int FOR $2::int) AS chunk FROM zerobucket_images WHERE id = $3;`;
|
|
268
|
+
var SELECT_CHUNK = `SELECT encode(substring(data FROM $1::int FOR $2::int), 'base64') AS chunk FROM zerobucket_images WHERE id = $3;`;
|
|
269
269
|
var SELECT_STREAM_INFO = `SELECT size_bytes, storage_backend, object_storage_key FROM zerobucket_images WHERE id = $1;`;
|
|
270
270
|
var DELETE_RETURNING = `DELETE FROM zerobucket_images WHERE id = $1 RETURNING storage_backend, object_storage_key;`;
|
|
271
271
|
var DELETE_MANY = `DELETE FROM zerobucket_images WHERE id = ANY($1::uuid[]) RETURNING id, storage_backend, object_storage_key;`;
|
|
@@ -298,7 +298,7 @@ INSERT INTO zerobucket_blobs (checksum_sha256, data, mime_type, size_bytes, widt
|
|
|
298
298
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
299
299
|
ON CONFLICT (checksum_sha256) DO UPDATE SET ref_count = zerobucket_blobs.ref_count + EXCLUDED.ref_count;`;
|
|
300
300
|
var D_INSERT_REF = `INSERT INTO zerobucket_image_refs (checksum_sha256, original_filename) VALUES ($1, $2) RETURNING id;`;
|
|
301
|
-
var D_COLS = `r.id, b.data, b.mime_type, r.original_filename, b.size_bytes, b.width, b.height, r.checksum_sha256,
|
|
301
|
+
var D_COLS = `r.id, encode(b.data, 'base64') AS data_b64, b.mime_type, r.original_filename, b.size_bytes, b.width, b.height, r.checksum_sha256,
|
|
302
302
|
'postgres'::text AS storage_backend, NULL::text AS object_storage_bucket, NULL::text AS object_storage_key`;
|
|
303
303
|
var D_FROM = `FROM zerobucket_image_refs r JOIN zerobucket_blobs b ON r.checksum_sha256 = b.checksum_sha256`;
|
|
304
304
|
var D_DELETE_REF = `DELETE FROM zerobucket_image_refs WHERE id = $1 RETURNING checksum_sha256;`;
|
|
@@ -320,7 +320,7 @@ var DEDUP_SQL = {
|
|
|
320
320
|
selectMany: `SELECT ${D_COLS} ${D_FROM} WHERE r.id = ANY($1::uuid[]);`,
|
|
321
321
|
selectMetadata: `SELECT r.id, b.mime_type, r.original_filename, b.size_bytes, b.width, b.height, r.checksum_sha256 ${D_FROM} WHERE r.id = $1;`,
|
|
322
322
|
selectStreamInfo: `SELECT b.size_bytes, 'postgres'::text AS storage_backend, NULL::text AS object_storage_key ${D_FROM} WHERE r.id = $1;`,
|
|
323
|
-
selectChunk: `SELECT substring(b.data FROM $1::int FOR $2::int) AS chunk ${D_FROM} WHERE r.id = $3;`,
|
|
323
|
+
selectChunk: `SELECT encode(substring(b.data FROM $1::int FOR $2::int), 'base64') AS chunk ${D_FROM} WHERE r.id = $3;`,
|
|
324
324
|
exists: `SELECT 1 FROM zerobucket_image_refs WHERE id = $1;`
|
|
325
325
|
};
|
|
326
326
|
var RETRYABLE_SQLSTATES = /* @__PURE__ */ new Set([
|
|
@@ -777,7 +777,7 @@ var PostgresBackend = class {
|
|
|
777
777
|
`Image ${JSON.stringify(id)} was deleted while streaming (delivered ${delivered} of ${wanted} bytes). Pass connection with your own open transaction if you need a consistent read across concurrent writers.`
|
|
778
778
|
);
|
|
779
779
|
}
|
|
780
|
-
const chunk = row.chunk;
|
|
780
|
+
const chunk = row.chunk == null ? null : Buffer.from(row.chunk, "base64");
|
|
781
781
|
if (!chunk || chunk.length === 0) {
|
|
782
782
|
throw new StorageError(
|
|
783
783
|
`Image ${JSON.stringify(id)} ended early while streaming (delivered ${delivered} of ${wanted} bytes).`
|
|
@@ -1101,6 +1101,8 @@ var PostgresBackend = class {
|
|
|
1101
1101
|
}
|
|
1102
1102
|
// ---- helpers ---------------------------------------------------------
|
|
1103
1103
|
hydrate(row, id) {
|
|
1104
|
+
row.data = row.data_b64 == null ? null : Buffer.from(row.data_b64, "base64");
|
|
1105
|
+
delete row.data_b64;
|
|
1104
1106
|
return hydrateRow(row, id, this.opts.objectStorage);
|
|
1105
1107
|
}
|
|
1106
1108
|
/**
|