Understanding the I/O, worker lifecycle, and actor storage in workerd
The I/O subsystem manages worker lifecycle, request handling, actor storage, and concurrency control. It bridges the JavaScript runtime with the underlying system I/O.
// Isolate: V8 isolate wrapper, shared across workersclass Worker::Isolate { // Create a new isolate static kj::Ref<Isolate> create(/* ... */); // Compile a script in this isolate kj::Ref<Script> newScript(/* ... */);};// Script: Compiled code bound to an isolateclass Worker::Script { // Create a worker instance from this script kj::Ref<Worker> createWorker(/* ... */);};// Worker: Ref-counted worker instanceclass Worker { // Handle a request kj::Promise<void> handleRequest(/* ... */); // Get an actor instance kj::Ref<Actor> getActor(kj::StringPtr id);};
Relationships:
Multiple Scripts can share one Isolate (same compatibility settings)
class ActorCache { // Get a value kj::OneOf<kj::Maybe<Value>, kj::Promise<kj::Maybe<Value>>> get(Key key); // Put a value kj::OneOf<void, kj::Promise<void>> put(Key key, Value value); // List keys kj::OneOf<ResultList, kj::Promise<ResultList>> list(ListOptions options); // Delete a key kj::OneOf<bool, kj::Promise<bool>> delete_(Key key);};
Key features:
Returns kj::OneOf<Result, kj::Promise<Result>> - synchronous when cached, async otherwise
Write-back caching - writes are batched and flushed periodically
LRU eviction - least recently used entries are evicted under memory pressure
Transactional semantics - operations within an output gate are atomic
class InputGate { // Wait until gate is open kj::Promise<void> wait(); // Critical section that must succeed or permanently break gate kj::Promise<void> onBroken();};
Sematics:
Requests wait at the input gate before processing
If a critical section fails, the gate breaks permanently
class Worker::Actor { // Get or create actor instance static kj::Ref<Actor> get( Worker& worker, kj::StringPtr id); // Make request to actor kj::Promise<Response> request(Request req); // Hibernate/resume for WebSockets void hibernate(); void resume();};
// GOOD: Get context when neededvoid myFunction() { auto& context = IoContext::current(); auto ioOwn = context.addObject(kj::mv(client));}// BAD: Store IoContext referenceclass Bad { IoContext& context; // Dangling after request ends};
// GOOD: Use awaitIo with continuationreturn js.awaitIo(kjPromise, [](jsg::Lock& js, Result r) { return processResult(js, kj::mv(r));});// BAD: Use awaitIoLegacy (deprecated)return js.awaitIoLegacy(kjPromise); // Don't use in new code