workerd

repository·main·Indexed 27 days ago

https://github.com/cloudflare/workerd

Cloudflare's JavaScript and WebAssembly (Wasm) server runtime, the same engine that powers Cloudflare Workers. Designed for high-performance application hosting, local development, and programmable HTTP proxying, workerd allows developers to run worker-like code in local or custom server environments. It features configuration via Cap'n Proto and supports builds for Linux, macOS, and Windows.

Tokens
62.2K
Snippets
167
Records
318
Agent score
93%

What's inside workerd

  1. Overview of workerd internal structure

    main

    The workerd source tree is organized into several functional subdirectories that define the runtime's capabilities:

    • util: Unrelated, independent utilities.
    • jsg: A magic template library used for auto-generating FFI (Foreign Function Interface) glue between C++ and V8 JavaScript.
    • io: Handles the I/O layer, enabling APIs to communicate with external systems, and manages the basic Worker lifecycle and event delivery.
    • api: Contains the implementations of the publicly documented, application-visible JavaScript APIs.
    • server: Contains the high-level server implementation.
    • tools: Contains meta-programs, such as scripts for exporting API types.
  2. Understand the New Module Registry Architecture

    main

    The new jsg::modules::ModuleRegistry implementation uses a two-layer architecture to support thread-safe module sharing across multiple isolate replicas:

    1. Shared Layer (Isolate-Independent)

    Owned by Worker::Script, this layer is thread-safe and shared across all replicas of a worker. It contains:

    • ModuleRegistry: The central registry (using kj::AtomicRefcounted).
    • ModuleBundle: Abstractions for composing registries from different sources (worker bundle, builtins, internal-only, and fallback).
    • Module: Isolate-independent definitions of modules.

    2. Per-Isolate Layer (Isolate-Specific)

    Owned by JsContext and destroyed with the context. It contains:

    • IsolateModuleRegistry: Holds the per-isolate V8 handles and maintains a back-reference to the shared ModuleRegistry.
    • lookupCache: A triple-indexed kj::Table used for O(1) lookups by V8 module identity, (type, URL) pairs, or URL alone.
  3. Understand the New Module Registry (jsg::modules::ModuleRegistry)

    main

    The new module registry in workerd is designed for high performance and efficiency through several key characteristics:

    • Lazy Loading: Modules are only compiled upon the first import and evaluated upon the first use. Unreferenced modules incur no cost.
    • URL-based Resolution: All module identity and resolution use WHATWG URLs. Relative resolution follows standard URL semantics via ada-url.
    • O(1) Lookups: Both forward (specifier to module) and reverse (V8 module to definition) lookups use hash-indexing, eliminating the O(n) scans found in the legacy registry.
    • Cross-isolate Caching: ESM compile caches and Wasm compiled modules are shared across isolate replicas to reduce startup times.
    • Idempotent Evaluation: Synthetic module EvaluateCallback functions can be called multiple times from different isolates, creating fresh JS objects each time.
    • Top-level Await: After Evaluate, microtasks are drained. If the returned Promise is still pending, a "top-level await" error is thrown. Workers must initialize synchronously.
  4. Understand the Wrappable lifecycle and identity

    main

    The Wrappable base class manages the connection between C++ objects and their JavaScript wrappers.

    Key behaviors:

    • Lazy Wrapper Creation: Wrappers are created on-demand when a C++ object is first passed to JavaScript.
    • Identity Preservation: The same C++ object always returns the same JS wrapper, preserving object identity and monkey-patches.
    • Dual Reference Counting: Managed via kj::Refcounted (for the JS wrapper) and a second "strong ref" count for jsg::Ref<T> pointers.
    • Lifecycle: When a C++ object is destroyed, detachWrapper() is called, leaving the JS wrapper as an empty shell. If a wrapper is collected by GC but the C++ object is still alive, a new wrapper is created on the next JS access.

    To check if an object is a workerd API object, use jsg::Wrappable::isWorkerdApiObject(object).

  5. Understand ReadableStream.tee() behavior in workerd

    main

    The ReadableStream.tee() method splits a stream into two branches. While the WHATWG spec suggests that one branch reading faster than another causes unbounded memory growth due to data copying, workerd implements an optimized version:

    • Branches hold refcounted references (kj::Rc<Entry>) to shared data instead of making physical copies.
    • Backpressure signaling to the original (trunk) stream is determined by the branch with the most unconsumed data.

    This implementation helps prevent memory pileup, provided the underlying source respects backpressure signals.

  6. Understand Node.js compatibility in workerd

    main

    Node.js compatibility in workerd is a best-effort implementation. It is not intended to be 100% compatible with Node.js. Developers should be aware of the following behaviors:

    • Precedence: Web Platform Standard APIs and Workers-specific APIs take precedence over Node.js APIs. If a Node.js API overlaps with a Web Platform API, the Web Platform implementation is used.
    • Implementation Strategy: Node.js APIs are implemented as closely as possible to the Node.js source of truth, but runtime constraints or the need to avoid breaking changes may result in differences.
    • Availability: Enabling the nodejs_compat flag makes Node.js APIs available, but it does not guarantee that all APIs behave exactly as they do in Node.js.
    • Errors: If a Node.js API is explicitly not implemented, attempting to use it will result in a runtime error rather than silent failure.
    • Experimental APIs: Recently added experimental Node.js APIs are generally not implemented immediately to ensure stability and avoid breaking changes.
  7. Understand the two Streams implementations in workerd

    main

    The workerd runtime exposes two distinct implementations of the Streams API through the same ReadableStream, WritableStream, and TransformStream JavaScript interfaces. Understanding which one you are using is critical for performance and behavior:

    1. Internal Streams:

      • Used for system-level tasks like request.body or response.body.
      • They are a thin wrapper around kj asynchronous I/O primitives.
      • They are exclusively byte-oriented (TypedArray and ArrayBuffer).
      • They do not support multiple pending reads; calling read() twice without awaiting the first will result in an error.
      • They have no internal data queue and no pull algorithm.
    2. Standard Streams:

      • Created via new ReadableStream(...) with user-provided callbacks or via new TransformStream() (subject to compatibility flags).
      • They are fully spec-compliant with the WHATWG Streams standard.
      • They can be byte-oriented or value-oriented (handling any JavaScript value).
      • They use a pull algorithm and maintain internal queues for data and pending reads.