Skip to content

feat(zero-client): add delete() to zero client#5579

Merged
0xcadams merged 4 commits intomainfrom
0xcadams/drop-db
Feb 20, 2026
Merged

feat(zero-client): add delete() to zero client#5579
0xcadams merged 4 commits intomainfrom
0xcadams/drop-db

Conversation

@0xcadams
Copy link
Member

@0xcadams 0xcadams commented Feb 17, 2026

  • Added zero.delete() that:
    • closes the instance first,
    • deletes all local DBs with matching replicacheName,
    • returns {deleted, errors}.

The current docs are awkward about dropping all databases, and this is a core workflow for Zero client. Currently it requires passing the same kvStore as initialized in the Zero client.

https://zero.rocicorp.dev/docs/auth#logging-out

@0xcadams 0xcadams requested review from aboodman and arv February 17, 2026 23:38
@vercel
Copy link

vercel bot commented Feb 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
replicache-docs Ready Ready Preview, Comment Feb 20, 2026 5:08pm
zbugs Ready Ready Preview, Comment Feb 20, 2026 5:08pm

Request Review

@github-actions
Copy link

github-actions bot commented Feb 17, 2026

🐰 Bencher Report

Branch0xcadams/drop-db
TestbedLinux
Click to view all benchmark results
BenchmarkFile SizeBenchmark Result
kilobytes (KB)
(Result Δ%)
Upper Boundary
kilobytes (KB)
(Limit %)
zero-package.tgz📈 view plot
🚷 view threshold
1,903.53 KB
(+0.03%)Baseline: 1,903.03 KB
1,941.09 KB
(98.06%)
zero.js📈 view plot
🚷 view threshold
247.10 KB
(+0.09%)Baseline: 246.88 KB
251.82 KB
(98.13%)
zero.js.br📈 view plot
🚷 view threshold
67.68 KB
(+0.13%)Baseline: 67.59 KB
68.94 KB
(98.17%)
🐰 View full continuous benchmarking report in Bencher

@github-actions
Copy link

github-actions bot commented Feb 17, 2026

🐰 Bencher Report

Branch0xcadams/drop-db
Testbedself-hosted
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
1 exists: track.exists(album)📈 view plot
🚷 view threshold
13,043.58 ops/s
(-1.13%)Baseline: 13,192.52 ops/s
11,554.54 ops/s
(88.58%)
10 exists (AND)📈 view plot
🚷 view threshold
188,839.02 ops/s
(-3.28%)Baseline: 195,252.10 ops/s
173,529.40 ops/s
(91.89%)
10 exists (OR)📈 view plot
🚷 view threshold
3,754.78 ops/s
(-1.27%)Baseline: 3,803.19 ops/s
3,358.54 ops/s
(89.45%)
12 exists (AND)📈 view plot
🚷 view threshold
166,441.92 ops/s
(-3.86%)Baseline: 173,121.32 ops/s
152,430.78 ops/s
(91.58%)
12 exists (OR)📈 view plot
🚷 view threshold
3,177.95 ops/s
(-1.44%)Baseline: 3,224.49 ops/s
2,854.52 ops/s
(89.82%)
12 level nesting📈 view plot
🚷 view threshold
2,739.66 ops/s
(-2.86%)Baseline: 2,820.25 ops/s
2,488.60 ops/s
(90.84%)
2 exists (AND): track.exists(album).exists(genre)📈 view plot
🚷 view threshold
4,743.05 ops/s
(-4.59%)Baseline: 4,971.46 ops/s
4,389.11 ops/s
(92.54%)
3 exists (AND)📈 view plot
🚷 view threshold
1,883.01 ops/s
(-3.03%)Baseline: 1,941.88 ops/s
1,719.10 ops/s
(91.30%)
3 exists (OR)📈 view plot
🚷 view threshold
953.59 ops/s
(-1.54%)Baseline: 968.51 ops/s
852.56 ops/s
(89.40%)
5 exists (AND)📈 view plot
🚷 view threshold
296.44 ops/s
(-2.93%)Baseline: 305.40 ops/s
270.99 ops/s
(91.41%)
5 exists (OR)📈 view plot
🚷 view threshold
156.43 ops/s
(-2.94%)Baseline: 161.17 ops/s
142.25 ops/s
(90.93%)
Nested 2 levels: track > album > artist📈 view plot
🚷 view threshold
4,265.54 ops/s
(-1.51%)Baseline: 4,331.02 ops/s
3,883.84 ops/s
(91.05%)
Nested 4 levels: playlist > tracks > album > artist📈 view plot
🚷 view threshold
705.40 ops/s
(-1.05%)Baseline: 712.86 ops/s
636.69 ops/s
(90.26%)
Nested with filters: track > album > artist (filtered)📈 view plot
🚷 view threshold
3,512.95 ops/s
(-2.59%)Baseline: 3,606.50 ops/s
3,184.16 ops/s
(90.64%)
planned: playlist.exists(tracks)📈 view plot
🚷 view threshold
244.65 ops/s
(-36.44%)Baseline: 384.91 ops/s
-33.62 ops/s
(-13.74%)
planned: track.exists(album) OR exists(genre)📈 view plot
🚷 view threshold
128.70 ops/s
(-7.16%)Baseline: 138.63 ops/s
94.91 ops/s
(73.74%)
planned: track.exists(album) where title="Big Ones"📈 view plot
🚷 view threshold
6,506.21 ops/s
(-4.12%)Baseline: 6,785.56 ops/s
5,520.82 ops/s
(84.85%)
planned: track.exists(album).exists(genre)📈 view plot
🚷 view threshold
7.18 ops/s
(-62.51%)Baseline: 19.15 ops/s
-17.48 ops/s
(-243.47%)
planned: track.exists(album).exists(genre) with filters📈 view plot
🚷 view threshold
1,989.04 ops/s
(-37.67%)Baseline: 3,191.35 ops/s
-626.99 ops/s
(-31.52%)
planned: track.exists(playlists)📈 view plot
🚷 view threshold
1.58 ops/s
(-36.21%)Baseline: 2.47 ops/s
-0.26 ops/s
(-16.77%)
unplanned: playlist.exists(tracks)📈 view plot
🚷 view threshold
235.87 ops/s
(-37.15%)Baseline: 375.30 ops/s
-32.29 ops/s
(-13.69%)
unplanned: track.exists(album) OR exists(genre)📈 view plot
🚷 view threshold
19.42 ops/s
(-32.79%)Baseline: 28.90 ops/s
0.30 ops/s
(1.52%)
unplanned: track.exists(album) where title="Big Ones"📈 view plot
🚷 view threshold
52.10 ops/s
(-1.30%)Baseline: 52.79 ops/s
46.80 ops/s
(89.83%)
unplanned: track.exists(album).exists(genre)📈 view plot
🚷 view threshold
7.23 ops/s
(-62.01%)Baseline: 19.02 ops/s
-17.28 ops/s
(-239.06%)
unplanned: track.exists(album).exists(genre) with filters📈 view plot
🚷 view threshold
51.61 ops/s
(+0.35%)Baseline: 51.43 ops/s
45.10 ops/s
(87.40%)
unplanned: track.exists(playlists)📈 view plot
🚷 view threshold
1.59 ops/s
(-35.70%)Baseline: 2.47 ops/s
-0.26 ops/s
(-16.46%)
zpg: all playlists📈 view plot
🚷 view threshold
5.18 ops/s
(+11.54%)Baseline: 4.65 ops/s
1.37 ops/s
(26.42%)
zql: all playlists📈 view plot
🚷 view threshold
6.01 ops/s
(-9.00%)Baseline: 6.61 ops/s
4.74 ops/s
(78.76%)
zql: edit for limited query, inside the bound📈 view plot
🚷 view threshold
196,037.67 ops/s
(-1.33%)Baseline: 198,672.40 ops/s
174,218.97 ops/s
(88.87%)
zql: edit for limited query, outside the bound📈 view plot
🚷 view threshold
207,309.04 ops/s
(-0.58%)Baseline: 208,514.73 ops/s
184,914.27 ops/s
(89.20%)
zql: push into limited query, inside the bound📈 view plot
🚷 view threshold
117,953.67 ops/s
(+5.73%)Baseline: 111,562.60 ops/s
96,730.90 ops/s
(82.01%)
zql: push into limited query, outside the bound📈 view plot
🚷 view threshold
369,672.27 ops/s
(-4.49%)Baseline: 387,059.89 ops/s
338,622.76 ops/s
(91.60%)
zql: push into unlimited query📈 view plot
🚷 view threshold
398,358.59 ops/s
(+4.29%)Baseline: 381,969.03 ops/s
263,698.34 ops/s
(66.20%)
zqlite: all playlists📈 view plot
🚷 view threshold
1.52 ops/s
(-7.00%)Baseline: 1.63 ops/s
1.36 ops/s
(90.02%)
zqlite: edit for limited query, inside the bound📈 view plot
🚷 view threshold
67,413.98 ops/s
(-1.54%)Baseline: 68,466.01 ops/s
54,780.12 ops/s
(81.26%)
zqlite: edit for limited query, outside the bound📈 view plot
🚷 view threshold
66,883.01 ops/s
(-4.52%)Baseline: 70,051.17 ops/s
56,263.45 ops/s
(84.12%)
zqlite: push into limited query, inside the bound📈 view plot
🚷 view threshold
3,752.73 ops/s
(-1.88%)Baseline: 3,824.69 ops/s
3,440.55 ops/s
(91.68%)
zqlite: push into limited query, outside the bound📈 view plot
🚷 view threshold
84,695.75 ops/s
(+2.04%)Baseline: 83,005.43 ops/s
70,749.86 ops/s
(83.53%)
zqlite: push into unlimited query📈 view plot
🚷 view threshold
127,033.65 ops/s
(+1.42%)Baseline: 125,253.07 ops/s
111,962.38 ops/s
(88.14%)
🐰 View full continuous benchmarking report in Bencher

@github-actions
Copy link

github-actions bot commented Feb 17, 2026

🐰 Bencher Report

Branch0xcadams/drop-db
Testbedself-hosted
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
src/client/custom.bench.ts > big schema📈 view plot
🚷 view threshold
128,718.00 ops/s
(-2.26%)Baseline: 131,691.64 ops/s
116,098.99 ops/s
(90.20%)
src/client/zero.bench.ts > basics > All 1000 rows x 10 columns (numbers)📈 view plot
🚷 view threshold
1,406.87 ops/s
(-19.94%)Baseline: 1,757.37 ops/s
505.57 ops/s
(35.94%)
src/client/zero.bench.ts > pk compare > pk = N📈 view plot
🚷 view threshold
59,679.51 ops/s
(-0.52%)Baseline: 59,989.76 ops/s
52,429.72 ops/s
(87.85%)
src/client/zero.bench.ts > with filter > Lower rows 500 x 10 columns (numbers)📈 view plot
🚷 view threshold
2,813.68 ops/s
(-8.06%)Baseline: 3,060.30 ops/s
1,820.15 ops/s
(64.69%)
🐰 View full continuous benchmarking report in Bencher

@aboodman
Copy link
Contributor

What happens if you delete data for an IDB Zero that is currently open?

There can be multiple Zero instances open for same datastore. If they just stop working that's OK with me. If the delete is blocked don't love that.

@0xcadams
Copy link
Member Author

What happens if you delete data for an IDB Zero that is currently open?

There can be multiple Zero instances open for same datastore. If they just stop working that's OK with me. If the delete is blocked don't love that.

It's a good question - I thought about just doing delete for only the current zero replicache db only, but we have this delete all that we recommend currently in docs. This should've been a draft PR - bringing it up for feedback.

I'll test this more.

@0xcadams 0xcadams marked this pull request as draft February 18, 2026 04:14
@arv
Copy link
Contributor

arv commented Feb 20, 2026

It does seem a bit odd. I would prefer that it only deletes the databases belonging to the current client group.

@0xcadams 0xcadams marked this pull request as ready for review February 20, 2026 17:06
@0xcadams 0xcadams enabled auto-merge February 20, 2026 17:12
@0xcadams 0xcadams changed the title feat(zero-client): add drop db to zero client feat(zero-client): add delete() to zero client Feb 20, 2026
@0xcadams 0xcadams added this pull request to the merge queue Feb 20, 2026
Merged via the queue into main with commit 23e3702 Feb 20, 2026
24 checks passed
@0xcadams 0xcadams deleted the 0xcadams/drop-db branch February 20, 2026 17:26
Copy link
Contributor

@arv arv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

}
}

async delete(): Promise<{deleted: string[]; errors: unknown[]}> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could use some JSDoc explaining that it closes the instance and then deletes the related databases.

const idbDatabasesStore = new IDBDatabasesStore(kvStoreProvider.create);
try {
const databases = await idbDatabasesStore.getDatabases();
const dbNamesToDelete = Object.values(databases)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can there be more than one??

@0xcadams
Copy link
Member Author

0xcadams commented Feb 27, 2026 via email

@aboodman
Copy link
Contributor

aboodman commented Feb 27, 2026 via email

@0xcadams
Copy link
Member Author

0xcadams commented Feb 27, 2026 via email

@aboodman
Copy link
Contributor

So the replicacheName you are comparing to is computed like so:

name: `zero-${userID}-${hashedKey}`,

and hashedKey is:

    // Create a hash that includes storage key, URL configuration, and query parameters
    const nameKey = JSON.stringify({
      storageKey: this.storageKey,
      mutateUrl: options.mutateURL ?? '',
      queryUrl: options.queryURL ?? options.getQueriesURL ?? '',
    });
    const hashedKey = h64(nameKey).toString(36);

These replicacheName then become the IDB database name just prefixed with rep:. They are also used as keys within replicache-dbs-v0:

CleanShot 2026-02-27 at 07 39 44@2x

So I think this iteration is just unnecessary. It can only ever find one item. You can just use dropDatabase and pass this.#rep.name and be done with it.

I would also still like to see a deleteAll() convenience that calls through to dropAllDatabases with the kvStore and logging options from Zero.

@0xcadams
Copy link
Member Author

0xcadams commented Feb 27, 2026 via email

@aboodman
Copy link
Contributor

aboodman commented Feb 27, 2026 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants