Multithreading

True multithreading for JavaScript applications

Encore.ts runs using a high-performance Rust runtime that uses multiple threads to handle incoming requests. The Encore.ts Rust runtime handles virtually everything outside of your core business logic:

  • Parsing and validating incoming requests
  • Making API calls to other services
  • Serializing and writing API responses
  • Observability integrations like distributed tracing
  • Infrastructure integrations, like executing database queries, reading and writing from object storage, publishing and consuming messages from Pub/Sub, and more

This architecture allows for much higher performance and scalability compared to traditional JavaScript frameworks. By offloading most of this to multithreaded Rust, the single-threaded JavaScript event loop becomes free to focus on executing your core business logic.

But for more CPU-intensive workloads, the single-threaded JavaScript event loop can still become a performance bottleneck. For these use cases Encore.ts offers Worker Pooling. With Worker Pooling enabled, Encore.ts starts up multiple NodeJS event loops and load-balances incoming requests across them. This can provide a significant performance boost for CPU-intensive workloads.

Enabling Worker Pooling

To enable Worker Pooling, add "build": {"worker_pooling": true} to your encore.app file.

Designing your application to work with Worker Pooling

Most application code will work with Worker Pooling without any changes. However, it's important to understand the implications of running in a multi-threaded environment.

When utilizing Worker Pooling, Encore.ts will automatically spin up multiple NodeJS isolates (one per CPU) to handle incoming requests. Each NodeJS isolate is a separate JavaScript runtime, with its own event loop and memory space.

This means that you cannot rely on global shared state that is shared across all incoming requests, since each request may be handled by a different NodeJS isolate.