Redis examples

Determining what to do

It's usually helpful to start by creating a request logging filter and a response logging filter, and studying the flow of requests and responses.

Most Redis drivers tend to issue quite a few commands besides the ones you explicitly ask for, therefore it's a good idea to understand the dialog between client and server before attempting to change anything. For instance, it's common for drivers to issue a PING command at frequent intervals to determine if the connection is still valid. Redis normally responds with a PONG message.

Once you have a clear idea of which requests and/or responses you need to intercept, you can then create corresponding filters. Try to make them as specific as possible to minimize the performance penalty. You could in theory have a filter executing for every request and every response, but that would be inefficient: it's much better to specify for which conditions these filters should be executed. You can start with very broad conditions, and narrow these conditions down once you get closer.

Changing a request

A typical query might take the form of:

SCAN 0 MATCH product* COUNT 500

This will be presented as a Redis Array in the context.packet object:

["SCAN", "0", "MATCH", "product*", "COUNT", "500"]

We want to create a filter that will change the COUNT parameter to a value of 100.

To do so, we create a request filter of type JavaScript request filter - Redis and set its Command patterns parameter to:

SCAN

regex:\d+

MATCH

product*

COUNT

500

This will match the query, regardless of the value for SCAN (the regular expression \d+ means 1 or more digits).

The code for the filter can then simply be:

context.packet[5].string = "100";

Rejecting a request

If you don't want a request to be forwarded to Redis, and you want to return an error message to the client, you can simply do this in your request filter :

context.result.errorMessage = "ERR Your request has been rejected because reasons";

The format of the error message is up to you, but by convention Redis error messages usually start with an error code in all caps, followed by a space, followed by the text of the error.

Changing a response

We want to change the response:

[Poland, Czech Republic, Slovakia]

to:

[Poland, Czechia, Slovakia]

To do so, we create a response filter of type JavaScript response filter - Redis, set its Command patterns parameter to something like:

regex:(GETRANGE|LRANGE|SCAN)

depending of course on which commands are used to retrieve this value, and the Response patterns parameter to:

Czech Republic

The code for the filter can then simply be:

for (var c of context.packet) {

if (c.string === 'Czech Republic') {

c.string = 'Czechia';

break;

}

}

Publish/subscribe filter

Note: shard subscriptions (SSUBSCRIBE, SUNSUBSCRIBE, SPUBLISH) are available only in Redis 7.0 and above.

A PUBLISH command (and its cousin SPUBLISH) is like any other command and therefore can be handled as such.

A SUBSCRIBE command (and its two cousins PSUBSCRIBE and SSUBSCRIBE) is different: it starts a listen mode (if not already in that mode) in which the client can only issue a limited set of commands, namely SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE, UNSUBSCRIBE, PUNSUBSCRIBE, SUNSUBSCRIBE, PING, RESET and QUIT. Any other requests will be rejected with an error. (This has nothing to do with Gallium Data -- that's just how Redis works).

As long as there is at least one subscription left, the client can expect messages to be sent by Redis whenever somebody issues a relevant PUBLISH command.

These messages have the format:

  • ["message", "<channel>", "<published message>"] for SUBSCRIBE

  • ["pmessage", "<pattern>", "<channel>", "<published message>"] for PSUBSCRIBE

  • ["smessage", "<shard>", "<published message>"] for SSUBSCRIBE (Redis 7.0 and above)

A response filter can therefore be applied to pub/sub messages simply by setting its Response patterns parameter to e.g.:

message,MyChannel

If you wanted to also catch pattern subscriptions, you could use a regular expression, e.g.:

regex:(message|pmessage),regex:Channel\d+

This would be invoked for any pub/sub messages from either channel subscriptions (SUBSCRIBE) or pattern subscriptions (PSUBSCRIBE), for any channel starting with "Channel" followed by one or more numeral.

Creating a response from scratch

Sometimes you may want to intercept a request and handle it yourself, without having the request forwarded to Redis.

This can be done by setting context.result.response to a Redis object. The original request will be thrown away, and your response will be sent back to the client.

Obviously you need to make sure that the client expects this response.

context.result.response = context.redisUtil.createArray(["PONG", "from Gallium Data"]);