Source: openaf-opacks
87b07e97. See documentation baseline for availability and verification.
Build and test commands use the package’s source directory; installation and library examples use the installed oPack.
Redis oPack
Wrapper around the Jedis client to provide a convenient Redis API for OpenAF scripts. Besides exposing the underlying Jedis object, the oPack includes helpers to manage keys, hashes, lists, sets, and sorted sets with native JavaScript data structures.
This package bundles Jedis 8.0.1.
Installation
opack install Redis
Quick start
loadLib("redis.js");
var redis = new Redis("localhost", 6379);
redis.set("greeting", "hello world");
print(redis.get("greeting"));
print("Number of keys: " + redis.size());
redis.close();
The wrapper automatically loads the bundled dependencies (jedis, commons-pool2, authentication helpers, and JSON support) so
that you can focus on automation logic. For advanced scenarios call redis.getObj() to access the underlying Jedis instance.
Constructor
var redis = new Redis(aHost, aPort, aDBId);
aHostis the Redis server hostname or IP address.aPortis the Redis server port, usually6379.aDBIdis optional and selects the Redis logical database after connecting.
Always call redis.close() when the script is finished with the connection.
Common operations
loadLib("redis.js");
var redis = new Redis("localhost", 6379, 0);
// Strings
redis.set("app:greeting", "hello");
print(redis.get("app:greeting"));
// Hashes
redis.set("app:user:1", {
name: "Ana",
role: "admin"
});
print(redis.get("app:user:1").name);
// Lists
redis.set("app:queue", [ "first", "second" ]);
print(redis.get("app:queue"));
// Sets
redis.set("app:tags", [ "blue", "green" ], "set");
print(redis.get("app:tags"));
// Sorted sets
redis.sortedSets_set("app:rank", "low", 1);
redis.sortedSets_set("app:rank", "high", 2);
print(redis.get("app:rank"));
redis.close();
redis.set(key, value) infers the Redis type for strings, JavaScript maps, and arrays. Arrays are stored as lists by default. Pass
"set" as the third argument to store an array as a Redis set. Use sortedSets_set(key, element, score) for sorted sets.
Key management
print(redis.getKeys("app:*"));
print(redis.type("app:greeting"));
redis.rename("app:greeting", "app:message");
redis.move("app:message", 1);
redis.del("app:message");
getKeys(pattern) defaults to "*". Avoid broad key scans in large production databases unless that is acceptable for your Redis
deployment.
OpenAF channels
The opack registers a Redis-backed $ch type. Values and map keys are serialized to JSON when needed.
loadLib("redis.js");
$ch("cache").create(1, "redis", {
host: "localhost",
port: 6379,
dbid: 0
});
$ch("cache").set({ key: "app:answer" }, { value: { answer: 42 } });
print($ch("cache").get({ key: "app:answer" }).answer);
$ch("cache").destroy();
Connection pool
The opack also registers ow.obj.pool.REDIS(host, port, dbid) for scripts that need pooled Redis connections:
loadLib("redis.js");
var pool = ow.obj.pool.REDIS("localhost", 6379, 0);
var redis = pool.checkOut();
try {
print(redis.ping());
} finally {
pool.checkIn(redis);
}
JSON documents
Native Redis JSON is available in Redis 8 or older installations with RedisJSON enabled.
The existing set() inference remains unchanged. Use the explicit JSON helpers to
preserve nested maps, arrays, booleans, numbers and nulls:
var redis = new Redis("localhost", 6379, 0);
try {
if (redis.supports("JSON.SET")) {
redis.json_set("app:document", { stats: { hits: 0 }, events: [] });
redis.json_increment("app:document", "$.stats.hits");
redis.json_arrayAppend("app:document", "$.events", [{ kind: "visit" }]);
print(redis.json_get("app:document"));
print(redis.json_get("app:document", "$.stats.hits")); // [1]
}
} finally {
redis.close();
}
| Helper | Behavior |
|---|---|
supports(command) |
Checks COMMAND INFO; returns false for an unavailable command. Connection and ACL errors propagate. Availability does not guarantee execution permission. |
json_set(key, value, path) |
Sets a JSON value; path defaults to $ (whole document). Returns "OK", or undefined when Redis reports no update. |
json_get(key, path) |
With no path, returns the document as native JavaScript values. Explicit JSONPath expressions such as $.stats.hits return arrays of matches, including an empty array for no matches. Missing keys return undefined; stored JSON null returns null. |
json_del(key, path) |
Deletes matching values; defaults to deleting the whole document. Returns the deletion count. |
json_increment(key, path, amount) |
Atomically increments matching numbers; amount defaults to 1. |
json_arrayAppend(key, path, values) |
Atomically appends every item in the nonempty values array. Wrap a nested array in another array to append it as one value. |
json_arrayPop(key, path, index) |
Atomically removes and returns elements; index defaults to -1. |
json_setWithTTL(key, value, ttlms) |
Atomically writes a whole JSON document and sets a positive lifetime in milliseconds. Requires Redis 7+ with JSON support and EVAL, JSON.SET, and PEXPIRE permissions. |
Path-based operations follow Redis response semantics: $ JSONPath expressions
return arrays of matches/results; legacy paths can return scalar results. Use $
paths for consistent new code. JSON values must be JSON serializable; JavaScript
functions, undefined, cycles and custom Java objects are not supported document values.
get(key) also recognizes native JSON keys. JSON helpers use the same connection
as getObj(), which continues to return a Jedis instance. Unsupported JSON commands
fail explicitly. Existing string, hash or list keys are never automatically migrated
to JSON; JSON writes to those keys fail with a Redis type error. Use separate keys or
perform an explicit migration. Ordinary json_set() follows Redis JSON TTL behavior;
use json_setWithTTL() to assign or refresh a lifetime.
Cache lifetime and conditional string writes
redis.strings_set("app:cache", "cached text", { ttlms: 60000 });
redis.strings_set("app:claim", "worker-1", { ttlms: 10000, nx: true });
redis.strings_set("app:cache", "replacement", { ttlms: 60000, xx: true });
redis.expire("app:cache", 30000);
print(redis.ttl("app:cache"));
redis.persist("app:cache");
strings_set(key, value, options) accepts ttlms, nx (only create) and xx
(only replace). NX and XX are mutually exclusive. It returns "OK" on success or
undefined when the condition is unmet. Expiration is applied in the same SET
command. A successful plain SET without ttlms removes the old expiration.
All wrapper lifetime arguments are milliseconds, and must be positive safe
integers. expire() returns whether an existing key received expiration;
persist() returns whether expiration was removed. ttl() returns remaining
milliseconds, -1 for a persistent key, or -2 for a missing key.
JSON and cache channels
Channel creation accepts two additional options:
var channel = redis.getCh("documents", { json: true, ttlms: 60000 });
// Equivalent: $ch("documents").create(1, "redis", {
// host: "localhost", port: 6379, dbid: 0, json: true, ttlms: 60000
// });
try {
channel.set({ key: "app:channel-document" }, { value: { nested: [1, false, null] } });
print(channel.get({ key: "app:channel-document" }));
} finally {
channel.destroy();
}
json: trueopts into native JSON documents. Creation checks JSON command availability; it fails and closes the new connection when unavailable.ttlmssets a cache lifetime refreshed on every successful write. It works with the default string-backed channels as well as JSON channels. JSON writes with TTL use a Lua script that checks write/expiration permissions before changing data.- Defaults retain the existing channel storage behavior. JSON channels can read legacy keys, but writing native JSON over a legacy key requires explicit migration.
- Nested nulls are supported. Root null is supported by the direct JSON API, but
JSON channel writes reject it because OpenAF’s public channel
get()cannot return root null safely. Wrap it in a map or array. Reading an externally written root null through channelget()raises an explanatory error; usejson_get(). - Keys and the existing
{key: ...}/{value: ...}envelopes retain their usual meaning. Wrap literal documents containing avaluefield inside the value envelope. getCh(name, options)takes host, port and database from the Redis object. Destroying the channel closes its own connection, independently of that object.
Incremental key scanning
var cursor = "0";
do {
var page = redis.scan(cursor, "app:*", 100);
page.keys.forEach(key => print(key));
cursor = page.cursor;
} while (cursor != "0");
redis.scanKeys("app:*", function(key) {
print(key);
// Return false to stop early.
}, 100);
scan(cursor, pattern, count) returns {cursor, keys}. Defaults are "0", "*"
and 100. Keep cursors as strings. COUNT is a work hint, not a page-size limit;
empty pages can have a nonzero cursor. SCAN can return duplicates and does not
provide a snapshot when keys change. scanKeys(pattern, callback, count) iterates
incrementally without collecting all keys in memory. Existing keys() and
getKeys() retain their KEYS behavior.
Tests
Run the local smoke tests from this folder:
ojob tests/tests.yaml
By default, tests that need a live Redis server are skipped. To run the full integration suite:
REDIS_TEST_HOST=localhost REDIS_TEST_PORT=6379 REDIS_TEST_DB=15 ojob tests/tests.yaml
Run a disposable Docker matrix (Redis 8.10.2 with JSON and Redis 7 without JSON):
bash tests/docker.sh
The runner requires Docker, oaf and ojob, binds random ports on localhost,
and removes its containers on exit. It runs the service-independent regressions
and both integration suites. For manual integration runs, optional
REDIS_TEST_JSON=true or false asserts the expected JSON availability.
JSON integration includes ACL failure tests and requires permission to create
and delete a temporary openaf-test-json-* user. Use an isolated test server/database.
The integration tests clean up keys matching openaf-test:* in the selected database before and after each Redis-backed test.
Corrections (2026-09-18)
List pops use the single-item Jedis overload. set(key, [{ element: "member", score: 3 }]) writes sorted-set members with their scores. Increment one member with sortedSets_increment(key, amount, member); the amount defaults to 1 when omitted using __.
select(dbid) also updates the database used by subsequent getCh calls. Channel reads support hashes and lists, and public $ch pop/shift operations return and remove the selected value.
Run the service-independent regression checks from this directory:
oaf -f tests/regression.js
These checks use local fixtures and test doubles; they do not verify a live external service.
Corrections (2026-10-04)
Channel array values are stored as JSON, preserving order, nested values and empty
arrays. Repeated channel writes replace the previous value instead of appending
to a Redis list. Existing native Redis lists remain readable; writing an array
through the channel replaces that key with a JSON string. Direct Redis.set list
operations retain their existing behavior.