Skip to content

Add deleteAllDurableObjects() to workerd:unsafe#6357

Open
penalosa wants to merge 9 commits intomainfrom
penalosa/delete-all-durable-objects
Open

Add deleteAllDurableObjects() to workerd:unsafe#6357
penalosa wants to merge 9 commits intomainfrom
penalosa/delete-all-durable-objects

Conversation

@penalosa
Copy link
Copy Markdown
Contributor

@penalosa penalosa commented Mar 18, 2026

Adds a deleteAllDurableObjects() method to the workerd:unsafe module. This aborts all Durable Objects, deletes all their underlying storage (SQLite files on disk), and cancels all scheduled alarms. After calling this, DOs can be recreated with clean state.

Changes

  • workerd:unsafe module — new deleteAllDurableObjects() JS method
  • IoChannelFactory — new deleteAllActors() virtual, mirroring abortAllActors()
  • AlarmScheduler — new deleteAll() that cancels all in-memory alarm tasks and wipes the DB (DELETE FROM _cf_ALARM); called per-namespace since PR Store Durable Object alarms per-namespace on disk #6104 moved alarms to per-namespace schedulers
  • Server — new deleteAllActors() that iterates evictable namespaces, calling ActorNamespace::deleteAll() which: aborts running actors, calls AlarmScheduler::deleteAll(), then deletes all actor SQLite files from disk
  • Tests — verifies storage is gone after delete, alarms don't fire, preventEviction namespaces are unaffected

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

I'm Bonk, and I've done a quick review of your PR.

Adds deleteAllDurableObjects() to workerd:unsafe that aborts all evictable DOs, wipes their on-disk SQLite storage, and cancels alarms.

  1. [LOW] Code duplication between Server::deleteAllActors and Server::abortAllActors — The two methods share identical evictable-namespace iteration logic (~15 lines). If that logic ever changes (e.g. a new ActorConfig variant), both must be updated in lockstep. Consider factoring out the iteration into a shared helper that accepts a per-namespace callback, or having deleteAllActors delegate to abortAllActors and then do a second pass for storage cleanup only.

This review was generated by an AI code reviewer and may contain errors. Please verify any suggestions before applying them.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Mar 18, 2026

I'm Bonk, and I've done a quick review of your PR.

The PR adds deleteAllDurableObjects() to workerd:unsafe that aborts all evictable DOs, wipes their on-disk SQLite storage, and cancels alarms. The implementation is correct and well-tested.

I posted one LOW severity comment about code duplication between Server::deleteAllActors and Server::abortAllActors — they share ~15 lines of identical evictable-namespace iteration logic. The suggestion is to have deleteAllActors delegate to abortAllActors for the abort+alarm portion and then do a second pass for storage file cleanup only, reducing the maintenance burden if the iteration logic ever changes.

github run

@kentonv
Copy link
Copy Markdown
Member

kentonv commented Mar 18, 2026

Can you please wait until #6104 is landed and then re-prompt this?

I don't want to delay #6104 any more.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 18, 2026

Merging this PR will not alter performance

✅ 70 untouched benchmarks
⏩ 129 skipped benchmarks1


Comparing penalosa/delete-all-durable-objects (016ddd6) with main (32a5758)

Open in CodSpeed

Footnotes

  1. 129 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@penalosa
Copy link
Copy Markdown
Contributor Author

/bonk fix the conflicts in this PR

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

Re-review after #6104 has landed (per kentonv's request to wait).

  • [HIGH] Merge conflicts — The PR's mergeable state is dirty now that #6104 has landed. A rebase is needed before this can merge. The alarm scheduler changes in #6104 (per-namespace alarms, AlarmScheduler constructor changes) likely conflict with Server::deleteAllActors and possibly the abortAllActors call site.
  • [MEDIUM] Code duplication (still present)Server::deleteAllActors and Server::abortAllActors still share ~15 lines of identical evictable-namespace iteration logic, as flagged in my prior review. Since ActorNamespace::deleteAll already delegates to abortAll internally, Server::deleteAllActors could skip the per-namespace abort and just call abortAllActors(reason) first, then do a second pass for storage cleanup only. This would also mean the deleteAllAlarms() call wouldn't need to be duplicated — it's already done in abortAllActors. Worth considering during the rebase.
  • [LOW] Post-rebase consideration: per-namespace alarm deletion — With #6104's per-namespace alarm storage, you may want deleteAllActors to only delete alarms for evictable namespaces (matching the storage-deletion scope) rather than unconditionally calling deleteAllAlarms() on the global scheduler. Currently both abortAllActors and deleteAllActors wipe all alarms globally, which also affects preventEviction namespaces' alarms. This may be intentional, but worth verifying against #6104's new namespace-aware alarm model.

The implementation logic itself (abort actors → release file handles → delete storage files) is sound. The test coverage is good — verifies storage deletion, alarm cancellation, and preventEviction exclusion.

This review was generated by an AI code reviewer and may contain errors. Please verify any suggestions before applying them.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Mar 25, 2026

Review posted on PR #6357. Here's a summary of the findings:

HIGH — Merge conflicts: The PR is now in dirty mergeable state after #6104 ("Store Durable Object alarms per-namespace on disk") merged. A rebase is needed.

MEDIUM — Code duplication: Server::deleteAllActors and Server::abortAllActors share ~15 lines of identical evictable-namespace iteration logic. Since ActorNamespace::deleteAll() already delegates to abortAll() internally, the Server::deleteAllActors method could call abortAllActors(reason) first, then do a second pass for storage cleanup only. This eliminates the duplication and makes the rebase easier.

LOW — Per-namespace alarm deletion: With #6104's per-namespace alarm model, it may be worth verifying whether deleteAllAlarms() should only target evictable namespaces' alarms rather than wiping all alarms globally (which also affects preventEviction namespaces).

The implementation logic (abort → release handles → delete storage) is sound, and test coverage is good.

github run

@penalosa penalosa force-pushed the penalosa/delete-all-durable-objects branch from b05011e to fb4c79f Compare March 26, 2026 14:23
@penalosa
Copy link
Copy Markdown
Contributor Author

@kentonv this should be ready for review now—I've recreated on top of main

@penalosa penalosa marked this pull request as ready for review March 26, 2026 17:03
@penalosa penalosa requested review from a team as code owners March 26, 2026 17:03
@penalosa penalosa requested a review from kentonv March 27, 2026 17:02
@kentonv
Copy link
Copy Markdown
Member

kentonv commented Mar 27, 2026

I want to note that this is sort of scary functionality to have. I would be extremely upset if, say, wrangler dev were to ever just delete all my local DO data. I'm guessing that's not the intended use, but please be very careful with how you use this.

@penalosa
Copy link
Copy Markdown
Contributor Author

I want to note that this is sort of scary functionality to have. I would be extremely upset if, say, wrangler dev were to ever just delete all my local DO data. I'm guessing that's not the intended use, but please be very careful with how you use this.

To clarify the intended use case here, this is specifically to support a user facing API in @cloudflare/vitest-pool-workers to allow people to clear populated test data. It won't be used in Wrangler or Miniflare, and won't be called automatically in Vitest—a user will have to import an API from cloudflare:test and call it themselves. In the testing use case this is a lot less dangerous, as most data is expected to be ephemeral and re-createable.

@penalosa
Copy link
Copy Markdown
Contributor Author

/bonk

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

Re-review after the rebase onto main and commit 9e4aed0 addressing kentonv's metadata.sqlite concern.

  • [MEDIUM] Code duplication at Server::deleteAllActors / Server::abortAllActors — Still present from prior reviews. The two methods share identical evictable-namespace iteration logic (~15 lines). Since ActorNamespace::deleteAll() already calls abortAll() internally, Server::deleteAllActors could call abortAllActors(reason) first and then do a storage-only cleanup pass. This would mean changes to the iteration logic only need to happen in one place. Noted inline — feel free to keep the current factoring if you prefer the explicitness.
  • kentonv's concern addressed — Commit 9e4aed0 correctly skips metadata.sqlite* files during storage deletion and instead wipes alarm rows via AlarmScheduler::deleteAll(). The startsWith("metadata.sqlite") check covers WAL/journal companions. The ordering (abort actors → clear alarms → delete files) is sound.
  • Alarm scheduler deleteAll() is safealarms.clear() cancels in-flight alarm promises before the DB wipe. Since actors are already aborted at this point, any STARTED alarm tasks would already be broken. Prepared statements survive the DELETE FROM since the table isn't dropped.
  • Test coverage is good — Tests verify storage wipe, alarm cancellation, and preventEviction exclusion. The switch from inMemory to localDisk storage correctly exercises on-disk file deletion.

This review was generated by an AI code reviewer and may contain errors. Please verify any suggestions before applying them.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Mar 30, 2026

Review posted on PR #6357. Here's a summary:

Overall: The implementation is sound. The abort → alarm wipe → file deletion ordering is correct, and the fix for kentonv's metadata.sqlite concern (commit 9e4aed0) properly skips the alarm DB file and its WAL/journal companions while clearing rows via DELETE FROM.

Findings posted:

  1. [MEDIUM] Code duplication at Server::deleteAllActors / Server::abortAllActors — both share ~15 lines of identical evictable-namespace iteration. Since ActorNamespace::deleteAll() already delegates to abortAll(), the Server:: method could call abortAllActors() first then do a storage-only pass. Low risk, matter of preference.

  2. No safety issues foundAlarmScheduler::deleteAll() correctly clears in-memory alarm promises (cancelling them) before wiping DB rows. Prepared statements survive the DELETE FROM since the table isn't dropped. The const qualification chain through IoChannelFactoryServerActorNamespace is consistent. Repeated calls are safe (idempotent on empty state).

  3. Test coverage is good — verifies storage wipe, alarm cancellation after delete, and preventEviction exclusion.

Reviews:

github run

@penalosa penalosa requested a review from kentonv March 30, 2026 13:58
@penalosa penalosa force-pushed the penalosa/delete-all-durable-objects branch 2 times, most recently from 75913d9 to 067c94f Compare April 7, 2026 10:05
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

The generated output of @cloudflare/workers-types matches the snapshot in types/generated-snapshot 🎉

@penalosa penalosa force-pushed the penalosa/delete-all-durable-objects branch from 067c94f to d0e0dac Compare April 7, 2026 22:21
@penalosa penalosa enabled auto-merge (squash) April 8, 2026 13:38
penalosa added 2 commits April 9, 2026 10:51
Adds a new deleteAllDurableObjects() method to the workerd:unsafe module
that aborts all Durable Objects, deletes all their underlying storage
(SQLite files on disk), and cancels all scheduled alarms. After calling
this, DOs can be recreated with clean state.

This is useful for test isolation — clearing all DO state between tests
without restarting the workerd process.

Like abortAllDurableObjects(), namespaces with preventEviction are not
affected.

The per-namespace AlarmScheduler introduced in PR #6104 is cleared via a
new deleteAll() method that cancels in-memory tasks and wipes the DB.
Actor storage files are then deleted from disk.
penalosa added 5 commits April 9, 2026 10:51
The AlarmScheduler keeps metadata.sqlite open for the lifetime of the
namespace. Deleting it from disk would unlink the file while the
scheduler still has it open. Use startsWith to also skip WAL/journal
companion files (metadata.sqlite-wal, metadata.sqlite-journal).
@penalosa penalosa force-pushed the penalosa/delete-all-durable-objects branch from 59daea7 to 7bbefa4 Compare April 9, 2026 09:51
penalosa added 2 commits April 9, 2026 11:52
…se file handles

When sqlite3_close() returns SQLITE_BUSY, the previous code fell back to
sqlite3_close_v2() which defers the close, leaving native VFS file handles
and memory-mapped sections (CreateFileMapping) active. On Windows this
blocks file deletion with ERROR_SHARING_VIOLATION and
ERROR_USER_MAPPED_FILE. Instead, iterate any remaining unfinalized
statements with sqlite3_next_stmt(), finalize them, then retry
sqlite3_close().
On Windows, even after sqlite3_close() returns SQLITE_OK, the OS may not
immediately release the file handle. The native SQLite VFS opens files
without FILE_SHARE_DELETE, so DeleteFileW() fails with
ERROR_SHARING_VIOLATION until the handle is fully released. Add a retry
loop (up to 500ms) on Windows only. This is acceptable since
deleteAllDurableObjects() is a workerd:unsafe test-only API.
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.

2 participants