Skip to main content

Getting a client

Every operation in Clustron Zaris runs through an IZarisClient. A client is created from one thing: a connection string. The connection string says where the store is and how to reach it; nothing else creates a client.

zaris://inproc/orders                          # in-process (embedded, no network)
zaris://node-a:7861,node-b:7861/orders # remote cluster, plaintext
zariss://node-a:7863/orders?ca=/etc/zaris/ca.pem # remote cluster over TLS

The path segment is the store name, and the store name equals the cluster id — it is required. Switching from an embedded store to a cluster is a change of connection string, not a change of code: zaris://inproc/orders becomes zaris://node-a:7861/orders, and everything downstream is identical.

This page shows every way to turn a connection string into a client. Pick the one that fits your app; they all resolve to the same IZarisClient.

Where connection strings live

As with SQL Server and every other database, connection strings belong in the standard ConnectionStrings configuration section, keyed by a name you choose. You then select one by name in code. Hardcoding a literal string in code works, but — exactly as with a SQL connection string — it is discouraged because it can't be changed per environment without a rebuild.

appsettings.json
{
"ConnectionStrings": {
"orders": "zaris://orders-node:7861/orders"
}
}

1. Register every store from configuration

AddClustronZarisFromConnectionStrings reads the standard ConnectionStrings section and registers every entry whose value is a Zaris connection string (zaris:// or zariss://). Non-Zaris entries — a SQL Server string, for instance — are ignored, so it is safe to call even when ConnectionStrings is shared with other databases.

using Clustron.Zaris.Client.DependencyInjection;

services.AddClustronZarisFromConnectionStrings(configuration);

Resolve any registered store at runtime by injecting IZarisClientProvider and calling GetAsync with the store's configuration key:

var provider = serviceProvider.GetRequiredService<IZarisClientProvider>();

var client = await provider.GetAsync("orders");

GetAsync is asynchronous because resolving a store may open a connection to the cluster. It returns the same IZarisClient regardless of whether the connection string was in-process or remote.

2. Register one named store from configuration

When you want just one entry from ConnectionStrings, pass the configuration and the name. This looks up ConnectionStrings:orders and registers it:

using Clustron.Zaris.Client.DependencyInjection;

services.AddClustronZaris(configuration, "orders");

Resolve it by the same name:

var client = await provider.GetAsync("orders");

3. Register a single literal string

You can register a store with an explicit connection string. The first argument is a local DI key — the name you resolve by — and it does not have to match the store name in the connection string's path. Here the DI key is orders and the store (cluster id) is also orders, but they are independent labels:

using Clustron.Zaris.Client.DependencyInjection;

services.AddClustronZaris("orders", "zaris://orders-node:7861/orders");

Prefer to pull the literal from configuration rather than typing it inline, so the value stays environment-specific:

services.AddClustronZaris("orders", configuration.GetConnectionString("orders")!);

Resolve by the DI key you chose:

var client = await provider.GetAsync("orders");

4. Advanced: options a connection string can't carry

Some settings can't be expressed as text in a connection string — a callback that supplies a rotating token, a client log-file path, or a fully-populated TLS options object. Use the advanced overload with a callback that configures ZarisClientOptions:

using Clustron.Zaris.Client;
using Clustron.Zaris.Client.DependencyInjection;

services.AddClustronZaris("orders", "zariss://orders-node:7863/orders", o =>
{
// Read on every reconnect — return the current token for rotating credentials.
o.TokenProvider = () => TokenStore.CurrentAccessToken;

// Client SDK log file.
o.LogFilePath = "logs/zaris-client.log";

// Full TLS options when a ?ca= path in the string isn't enough.
o.Tls = new ZarisTlsClientOptions
{
CaCertificate = File.ReadAllText("/etc/zaris/ca.pem")
};
});

o.TokenProvider is a Func<string?> invoked on every reconnect, which is why it fits rotating tokens better than a static ?token= value baked into the string. The connection string and the callback compose: put the address and simple options in the string, and use the callback only for what the string can't express.

5. Imperative, without dependency injection

If you aren't using a DI container, call ZarisClient.ConnectAsync — the single imperative entry point. It parses the connection string and returns a connected IZarisClient, whether the string is in-process or remote:

using Clustron.Zaris.Client;

var client = await ZarisClient.ConnectAsync(
configuration.GetConnectionString("orders")!);

There is an advanced overload here too, taking the same ZarisClientOptions callback:

var client = await ZarisClient.ConnectAsync(
configuration.GetConnectionString("orders")!,
o => o.TokenProvider = () => TokenStore.CurrentAccessToken);

6. Hardcoded literal (discouraged)

A literal string passed directly compiles and runs:

var client = await ZarisClient.ConnectAsync("zaris://inproc/orders");

Do this only for a throwaway sample or a test. As with a hardcoded SQL Server connection string, a literal in code can't be varied per environment and mixes deployment configuration into your build. Prefer configuration.GetConnectionString("orders") in anything you ship.

Same key, different environments

Because a store is just a connection string, dev and prod differ only in the string bound to a key. Keep the key identical and let per-environment appsettings files supply the value. Development runs the store in-process; production points the same orders key at a cluster over TLS:

appsettings.Development.json
{
"ConnectionStrings": {
"orders": "zaris://inproc/orders"
}
}
appsettings.Production.json
{
"ConnectionStrings": {
"orders": "zariss://orders-node:7863/orders?ca=/etc/zaris/ca.pem&token=env:ZARIS_TOKEN"
}
}
// Identical in both environments — the value behind "orders" changes, the code does not.
services.AddClustronZarisFromConnectionStrings(configuration);
var client = await provider.GetAsync("orders");

This is the whole point of the connection-string API: the in-process-versus-cluster decision lives in configuration, and your operation code never changes.

Access the coordination features

IZarisClient exposes the key-value operations (PutAsync, GetAsync, DeleteAsync, and their variants). The coordination features — counters, leases, locks, watches, and scan or query — live on IZaris, which extends IZarisClient. The client returned by every method above implements IZaris, so cast to it when you need those features.

var client = (IZaris)await provider.GetAsync("orders");

// Key-value operations are available directly.
await client.PutAsync("key", "value");

// Coordination features are available through IZaris.
await client.Counters.AddAsync("hits", 1);

The rest of the developer guide assumes an IZaris client for any page that uses counters, leases, locks, watch, or scan.

Why use the provider

IZarisClientProvider owns the connection lifecycle for you. Rather than constructing clients yourself, you let the provider create and cache one client per name. This matters because:

  • The provider reuses a single client instance per name, so repeated GetAsync calls for the same name return the same connected client instead of opening a new connection each time.
  • Connection setup, seed discovery, and cleanup are handled centrally.

Resolve the provider from dependency injection and reuse it. Do not construct clients manually.

Use multiple stores

Register as many stores as you need — each is one entry under ConnectionStrings — and resolve each by name. This is useful when different data sets live in different clusters, or when you mix a cluster store with an in-process one:

appsettings.json
{
"ConnectionStrings": {
"orders": "zaris://orders-node:7861/orders",
"scratch": "zaris://inproc/scratch"
}
}
services.AddClustronZarisFromConnectionStrings(configuration);

var ordersClient = await provider.GetAsync("orders");
var scratchClient = await provider.GetAsync("scratch");

Next steps