Table of Contents

NRedisStack

OptunaSharp.Storage.NRedisStack provides NRedisStackJournalRedisBackend in OptunaSharp.Storages.Journal. It fits applications that already depend on NRedisStack and want to use their existing Redis connection for Optuna study persistence.

Install and use

Requires .NET 8 or .NET 10 and a reachable Redis server. The journal itself requires only ordinary Redis string and Lua commands; Redis Stack modules are not needed for its storage operations.

dotnet add package OptunaSharp.Storage.NRedisStack
using OptunaSharp;
using OptunaSharp.Storages.Journal;
using StackExchange.Redis;

using var connection = ConnectionMultiplexer.Connect(new ConfigurationOptions
{
    EndPoints = { "localhost:6379" },
    DefaultDatabase = 1,
    ClientName = "optunasharp-worker",
});

var backend = new NRedisStackJournalRedisBackend(connection, prefix: "experiments");
var study = Optuna.CreateStudy(
    storage: new JournalStorage(backend),
    studyName: "quadratic", loadIfExists: true);
study.Optimize(trial => Math.Pow(trial.SuggestFloat("x", -10, 10), 2), nTrials: 20);

NRedisStack brings StackExchange.Redis transitively, which is why configuration and connection types come from that namespace. The injected connection can also serve your application's NRedisStack module APIs.

Constructors and transport

First arguments Behavior
string url Parse a Redis URL and create a multiplexer
string url, Action<ConfigurationOptions> configure Modify the parsed native options before connecting
ConfigurationOptions options Use a complete native configuration
IConnectionMultiplexer connection Borrow an existing application connection

Every overload accepts useCluster = false and prefix = "". URL forms include redis://, rediss:// for TLS, and unix://. URL database selection sets DefaultDatabase; journal operations use the multiplexer's default database. URL-derived options set AbortOnConnectFail = false, while explicitly supplied options retain your settings.

For example, the convenience URL callback can set timeouts:

using OptunaSharp.Storages.Journal;

var backend = new NRedisStackJournalRedisBackend(
    "redis://localhost:6379/1",
    options =>
    {
        options.ConnectTimeout = 5000;
        options.SyncTimeout = 5000;
    },
    prefix: "experiments");

The URL and configuration constructors create a multiplexer internally, and the backend owns it: Dispose() closes it. An application-owned multiplexer, as in the first example, stays open when the backend is disposed.

Why the journal does not use RedisJSON

NRedisStack's module APIs do not change this backend's storage format. It uses the underlying IDatabase for StringGet, StringSet, StringIncrement, and ScriptEvaluate (on standalone Redis, every read and append is a script call); snapshots are raw byte values under {prefix}:snapshot:optunasharp. The URL and options constructors turn on StackExchange.Redis's DedicatedThreads flag before connecting unless you pass dedicatedThreads: false; see the Redis overview. Logs are ordinary JSON strings encoded by OptunaSharp, not RedisJSON documents, search indexes, or time-series entries.

That choice keeps log bytes interchangeable with StackExchange.Redis, ServiceStack.Redis, and Python Optuna. The Redis overview explains the remaining boundary: replay snapshots are runtime-specific (each runtime keeps its own key), and cluster mode separates counter allocation from the log write.

Implementation: NRedisStackJournalRedisBackend. Verification covers native constructor forms, cross-library byte parity, and Python journal interoperability.