June 25, 2023·4 min read·engineeringvalkeyredismodulesjavascript

Redis Stack previews JavaScript functions and polygon queries

Redis Stack 7.2 RC2 adds JavaScript triggers through RedisGears and polygon search through RediSearch. These module capabilities have their own APIs and execution constraints.

Running logic beside stored data can remove application round trips, but it also puts executable code into the database's operating environment. Redis Stack 7.2.0 RC2, published June 25, previews JavaScript triggers and functions through RedisGears. The same distribution introduces polygon indexing and queries through its search module.

The distribution boundary is essential to understanding the announcement. Its release lists Redis 7.2 RC2 alongside separate module versions, including RedisGears 2.0.6 and RediSearch 2.8.3. JavaScript execution and polygon search are capabilities supplied by those components. They should not be presented as commands available in every installation of the core Redis server.

I have used Redis since its early releases, and I find the prospect of colocated application logic useful when it removes a concrete data-access problem. It also changes what the database process must execute and retain. The preview deserves attention for its execution contract, rather than for the language choice alone.

JavaScript libraries declare their API requirement

The RedisGears README at the bundled module tag describes JavaScript execution through V8. It warns that the module is under active development and not generally available, with an API that may change. The Stack announcement repeats that the API is not final.

A library starts with a prologue identifying the engine, library name, and required API version. The JavaScript then registers functions through the module's API. An illustrative library using the documented interface can return a fixed readiness label.

#!js name=statuslib api_version=1.0

redis.registerFunction('status', function() {
    return 'ready';
});

The prologue is part of the module's library format. This is not an ordinary JavaScript file intended to execute unchanged in a browser. The function registration also depends on the redis object supplied by the module's execution environment.

The tagged documentation loads a file through TFUNCTION LOAD and invokes a function with a library-qualified name. For the example above, the corresponding commands are as follows.

redis-cli -x TFUNCTION LOAD < statuslib.js
redis-cli TFCALL statuslib.status 0

The zero specifies that no key arguments follow. The Stack notes explain that TFCALL combines the library and function name with a full stop. Keeping that syntax tied to this candidate matters because the release also changes earlier command names and registration conventions.

Registration is only part of execution semantics

The module can supply a client object to a function so it can invoke Redis commands. That lets code work with data inside the server environment. It does not make every function equivalent to one existing core command, since the registered code can contain its own sequence of operations.

The release distinguishes TFCALL and TFCALLASYNC in its discussion of coroutine execution and shared client connections. It also changes registrations toward mandatory arguments plus an optional object. These are signs of an API still being refined around how callers and database execution interact.

The candidate includes limits on the total memory used by combined libraries and checks current usage when uploading a library. Memory ownership therefore belongs to the module's operational contract as well as to the application authoring the function. Loading code into the server introduces another resource consumer within the database deployment.

A second constraint concerns persistence loading. RedisGears pull request 913 suppresses keyspace notifications while Redis loads persisted data and re-enables them when loading ends or fails. Its test checks that reloading the AOF does not increment a notification counter again for an already recorded change.

That is a substantive trigger rule. Reconstructing stored state and receiving a new application write are different events. A trigger that performs business logic should not automatically interpret every persistence reconstruction as a new occurrence of the original application action. The test makes the difference observable through the counter's value.

Polygon queries require an indexed shape

The Stack release adds the GEOSHAPE schema field for polygon data written in well-known text notation. It distinguishes that representation from the existing point-oriented geo field. The initial polygon predicates are WITHIN and CONTAINS, which describe relationships between shapes.

The documented query form uses a geometry predicate with a parameter containing the polygon text and requests dialect 3. That makes polygon search an indexed query-language capability. It should not be confused with issuing a core point-radius command against an ordinary geo key.

The release also describes Cartesian coordinates and geographic longitude-latitude coordinates, with spherical indexing as the geographic default. Coordinate interpretation is part of the query's meaning. The same numeric pair cannot be assumed to describe the same physical relationship under both models.

These capabilities belong to the search module version included in the distribution. A client needs to support its schema and query arguments, while the server needs the module that implements them. Sharing the Redis wire connection does not eliminate those dependencies.

Faster sorting changes the counting tradeoff

The release describes sorting optimizations under dialect 4 for search and aggregation. Some query shapes can stop after obtaining enough results for the requested limit. Numeric sorting can restrict the range examined, while filtering may require additional iterations to obtain enough accepted results.

It also adds WITHCOUNT for accurate counts in sorted search results. Producing that count processes all results, sacrificing the opportunity to stop after satisfying the requested limit. The release therefore makes a useful distinction between retrieving the requested page and counting the complete matching set.

The bundled JSON module adds another example of behavior that belongs to a component contract. The release describes JSON.MERGE with null values deleting attributes and array updates replacing the entire array. It also adds JSON.MSET for atomic multiple sets within one hash slot. Those semantics differ from assuming that every JSON update merges array elements or that every multi-key operation has unrestricted placement.

I prefer these explicit tradeoffs to treating every feature in a distribution as interchangeable server functionality. JavaScript libraries add an execution environment, polygon queries add indexed shape semantics, and exact counting can require more work than page retrieval. Each belongs to a named module and a specific candidate API, which gives an application a concrete set of dependencies to evaluate.