Editor – This post describes an early version of nginScript, which is now called the NGINX JavaScript module. For a description of the current implementation of the module, which became generally available in NGINX Plus R12, see Introduction to the NGINX JavaScript Module.

Last week at our user conference, nginx.conf 2015, we announced a preview of our new JavaScript implementation, called nginScript.

Over the course of its history, the JavaScript language has been used in a number of ways, first client‑side and then server‑side. With nginScript we’re introducing one of the first “proxy‑side” use cases, which raises a unique set of requirements that we discussed at length at the conference. After the announcement, many people were curious about our decision to implement a new JavaScript runtime rather than use V8, SpiderMonkey, or other implementations, so this post explains some of our reasoning behind that unusual choice.

What Are We Doing?

We are creating our own JavaScript runtime, called nginScript. The nginScript parser currently supports a subset of ECMAScript 5 (which we will extend in the future) and this is compiled down to an internal bytecode which we then execute within NGINX (or NGINX Plus) each time the JavaScript is evaluated, using a register‑based virtual machine (VM).

In our current, preview implementation, NGINX can evaluate JavaScript code in two cases: on demand to generate the value of a variable in the NGINX configuration, and at the content‑generation phase of NGINX execution. Future releases will support more opportunities to evaluate JavaScript.

Why Create Our Own JavaScript Runtime?

NGINX has some unique requirements that lead us to create our own runtime. Our goal is for nginScript snippets to be run when NGINX processes a request, in order to extend and inform the processing of requests and responses. We’re not seeking to create a persistent JavaScript application execution environment – Node.js already does that very well.

Current JavaScript runtimes are designed to run persistently within a web browser, and they rely on garbage collectors for memory management. It’s challenging to control the resources used by the JavaScript VM, which can exit abnormally if it runs out of memory; that is tolerable for a browser but not for a server with thousands of connections. The current runtimes don’t provide easy ways to control execution (preempting and resuming VM execution), and the number of supported platforms is limited by the JIT capabilities of the JavaScript runtime.

We expect that typical nginScript rules will be simple and quick to execute. The resulting state is saved outside the VM. Because the rules are simple, support for the full JavaScript language is not necessary. NGINX already has a very finely tuned event‑driven architecture, and we absolutely don’t want to suffer unpredictable performance from a runtime with garbage collection.

Instead, we’re creating a very simple runtime matched to our requirements:

Architecture – The single‑threaded, bytecode execution is designed for quick initialization and disposal. VMs are allocated per request. Startup is extremely quick, because there is no complex state or helpers to initialize. Memory is accumulated in pools during execution and released at completion by freeing the pools. This memory management scheme eliminates the need to track and free individual objects or to use a garbage collector.

– The single‑threaded, bytecode execution is designed for quick initialization and disposal. VMs are allocated per request. Startup is extremely quick, because there is no complex state or helpers to initialize. Memory is accumulated in pools during execution and released at completion by freeing the pools. This memory management scheme eliminates the need to track and free individual objects or to use a garbage collector. Helper functions – Built‑in operations are implemented natively in NGINX. For example, complex mathematical operations such as hash functions can be evaluated much more quickly when run natively, and when nginScript interfaces with NGINX, this too will be native. You can regard nginScript as a programmatic way to drive the native operations of NGINX.

– Built‑in operations are implemented natively in NGINX. For example, complex mathematical operations such as hash functions can be evaluated much more quickly when run natively, and when nginScript interfaces with NGINX, this too will be native. You can regard nginScript as a programmatic way to drive the native operations of NGINX. Pre‑emption – NGINX’s event‑driven model schedules the execution of individual nginScript VMs. When an nginScript rule performs a blocking operation (such as reading network data or issuing an external subrequest), NGINX transparently suspends execution of that VM and reschedules it when the event completes. This means that you can write rules in a simple, linear fashion and NGINX will schedule them without internal blocking.

Finally, with our own VM, we are not susceptible to changing APIs and standards, and we can ensure that our VM supports the wide range of platforms we target for NGINX.

About Performance

It’s early days to talk about performance, and we’re concentrating on building the functionality first. nginScript compiles to internal bytecode and executes in a register‑based VM; we’d like to add JIT compilation at some point, but that may limit the range of supported platforms. Our tests indicate that the nginScript VM offers performance similar to other interpreted languages (PHP, Ruby, etc.) but is not as fast as JIT implementations.

Remember that the expected use case for nginScript is to execute short rules that drive internal, native operations in NGINX. We don’t intend for anyone to implement compute‑intensive operations directly in nginScript, because internal operations and C‑based modules already address that need. We’ll always strive to optimize performance where sensible, but a simple comparison with a JIT language gives a skewed picture.

The Future

nginScript is at a very early stage in its development. We are calling the current version a “preview,” and its capabilities are an indicator of our full vision for nginScript. Our development priorities center around finishing the core language implementation (such as the addition of closures), and implementing many of the built‑in JavaScript objects (Date, Math, and so on).

We’ll also focus on integration – how are nginScript rules declared or referenced in the NGINX configuration, and what are the integration points where nginScript can access and control NGINX internals? How can we share data between scripts, and across a cluster? How can users instrument and debug nginScript rules?

For long‑running scripts (such as those associated with WebSocket), we might even add a garbage collector, but that’s a long way in the future.

The full path for nginScript is not mapped out yet. We’d love to get your feedback on use cases and gaps. Please share your ideas, rules and insights on our mailing list and we’ll develop this feature together. Thank you.