Add deleteAllDurableObjects() to workerd:unsafe#6357
Conversation
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.
There was a problem hiding this comment.
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.
- [LOW] Code duplication between
Server::deleteAllActorsandServer::abortAllActors— The two methods share identical evictable-namespace iteration logic (~15 lines). If that logic ever changes (e.g. a newActorConfigvariant), both must be updated in lockstep. Consider factoring out the iteration into a shared helper that accepts a per-namespace callback, or havingdeleteAllActorsdelegate toabortAllActorsand 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.
| 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(); | ||
| } |
There was a problem hiding this comment.
[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:
| 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.
|
I'm Bonk, and I've done a quick review of your PR. The PR adds I posted one LOW severity comment about code duplication between |
Merging this PR will improve performance by 12.33%
Performance Changes
Comparing Footnotes
|
Adds a
deleteAllDurableObjects()method to theworkerd:unsafemodule. 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:unsafemodule — newdeleteAllDurableObjects()JS methodIoChannelFactory— newdeleteAllActors()virtual, mirroringabortAllActors()Server— newdeleteAllActors()that iterates evictable namespaces, callingActorNamespace::deleteAll()(abort + wipe storage directory) thenAlarmScheduler::deleteAllAlarms()preventEvictionnamespaces are unaffected