Skip to content

Add deleteAllDurableObjects() to workerd:unsafe#6357

Draft
penalosa wants to merge 1 commit intomainfrom
penalosa/delete-all-durable-objects
Draft

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

Conversation

@penalosa
Copy link
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()
  • Server — new deleteAllActors() that iterates evictable namespaces, calling ActorNamespace::deleteAll() (abort + wipe storage directory) then AlarmScheduler::deleteAllAlarms()
  • Tests — verifies storage is gone after delete, alarms don't fire, preventEviction namespaces are unaffected

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.
Copy link
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.

Comment on lines +4018 to +4037
void Server::deleteAllActors(kj::Maybe<const kj::Exception&> reason) {
for (auto& service: services) {
if (WorkerService* worker = dynamic_cast<WorkerService*>(&*service.value)) {
for (auto& [className, ns]: worker->getActorNamespaces()) {
bool isEvictable = true;
KJ_SWITCH_ONEOF(ns->getConfig()) {
KJ_CASE_ONEOF(c, Durable) {
isEvictable = c.isEvictable;
}
KJ_CASE_ONEOF(c, Ephemeral) {
isEvictable = c.isEvictable;
}
}
if (isEvictable) ns->deleteAll(reason);
}
}
}

alarmScheduler->deleteAllAlarms();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

[LOW] This method duplicates the evictable-namespace iteration from abortAllActors() right above. If a new ActorConfig variant is added or the evictability logic changes, both methods need to stay in sync.

One option: extract a helper or have deleteAllActors delegate:

Suggested change
void Server::deleteAllActors(kj::Maybe<const kj::Exception&> reason) {
for (auto& service: services) {
if (WorkerService* worker = dynamic_cast<WorkerService*>(&*service.value)) {
for (auto& [className, ns]: worker->getActorNamespaces()) {
bool isEvictable = true;
KJ_SWITCH_ONEOF(ns->getConfig()) {
KJ_CASE_ONEOF(c, Durable) {
isEvictable = c.isEvictable;
}
KJ_CASE_ONEOF(c, Ephemeral) {
isEvictable = c.isEvictable;
}
}
if (isEvictable) ns->deleteAll(reason);
}
}
}
alarmScheduler->deleteAllAlarms();
}
void Server::deleteAllActors(kj::Maybe<const kj::Exception&> reason) {
// Abort all actors and delete all alarms first (same as abortAllActors).
abortAllActors(reason);
// Then delete the underlying storage for all evictable Durable namespaces.
for (auto& service: services) {
if (WorkerService* worker = dynamic_cast<WorkerService*>(&*service.value)) {
for (auto& [className, ns]: worker->getActorNamespaces()) {
KJ_IF_SOME(as, ns->getActorStorageForTesting()) {
bool isEvictable = true;
KJ_SWITCH_ONEOF(ns->getConfig()) {
KJ_CASE_ONEOF(c, Durable) {
isEvictable = c.isEvictable;
}
KJ_CASE_ONEOF(c, Ephemeral) {
isEvictable = c.isEvictable;
}
}
if (isEvictable) {
for (auto& entry: as.directory->listNames()) {
as.directory->remove(kj::Path({entry}));
}
}
}
}
}
}
}

This is just a sketch (would need an accessor for actorStorage) — but the key idea is to avoid duplicating the abort+alarm loop. Feel free to ignore if you prefer the current factoring.

@ask-bonk
Copy link
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
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

codspeed-hq bot commented Mar 18, 2026

Merging this PR will improve performance by 12.33%

⚡ 1 improved benchmark
✅ 69 untouched benchmarks
⏩ 129 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
Encode_ASCII_32[TextEncoder][0/0/32] 3.5 ms 3.1 ms +12.33%

Comparing penalosa/delete-all-durable-objects (b05011e) with main (7c3e268)

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.

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