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"]);