Cache values with a time to live
In this tutorial, you cache values that expire on their own. You write entries with a time to live (TTL), read the remaining TTL back, watch an entry disappear when its TTL elapses, and learn how to change or remove a TTL on a key that already exists. This is the pattern behind session tokens, short-lived credentials, and any cached value that should not outlive its usefulness. You need the .NET 8 SDK or later and a text editor.
You set a TTL at write time with Put.WithTtl, and you read what is left with GetTimeToLiveAsync. Both work against an in-process store, so you can complete the core of this tutorial with nothing but the SDK.
1. Create a project and add the SDK
Create a console project and add the Zaris SDK.
dotnet new console -o zaris-ttl
cd zaris-ttl
dotnet add package Clustron.Zaris.SDK
2. Get a client
In Program.cs, register an in-process store and resolve the client. TTL on writes and reads is available on the base client, so you do not need the IZaris cast for this tutorial.
using Clustron.Zaris.Client;
using Clustron.Zaris.Client.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection()
.AddClustronZaris("demo", "zaris://inproc/demo")
.BuildServiceProvider();
var client = (IZaris)await services
.GetRequiredService<IZarisClientProvider>()
.GetAsync("demo");
3. Write a value with a TTL
Pass Put.WithTtl as the put options to store a value that expires. Here a session token is cached for two seconds so the example stays short; in real code you would use minutes or hours.
var put = await client.PutAsync(
"session:alice",
"token-abc123",
Put.WithTtl(TimeSpan.FromSeconds(2)));
Console.WriteLine($"Stored: {put.IsSuccess}");
Put.WithTtl returns a PutOptions carrying the TTL, and the server stamps the expiry when it stores the entry. You do not schedule anything yourself.
4. Read the remaining time to live
Use GetTimeToLiveAsync to see how long a key has left. It returns a KvResult<TimeSpan?>: on success, Value is the remaining time, or null when the key exists but has no TTL. If the key is absent, IsSuccess is false with Status set to KvStatus.NotFound.
var ttl = await client.GetTimeToLiveAsync("session:alice");
if (ttl.IsSuccess)
{
Console.WriteLine(ttl.Value.HasValue
? $"Expires in {ttl.Value.Value.TotalSeconds:0.0}s"
: "No expiry set (permanent)");
}
5. Observe the value expire
The value is readable while the TTL is running and gone once it elapses. Read it immediately, wait past the two-second window, then read again. After expiry, GetAsync reports KvStatus.NotFound.
var before = await client.GetAsync<string>("session:alice");
Console.WriteLine($"Before: success={before.IsSuccess}, value={before.Value}");
await Task.Delay(TimeSpan.FromSeconds(2.5));
var after = await client.GetAsync<string>("session:alice");
Console.WriteLine($"After: success={after.IsSuccess}, status={after.Status}");
The first read succeeds and prints the token; the second read fails with Status == KvStatus.NotFound because the entry has expired. You never delete the key yourself.
6. Change or remove a TTL on an existing key
Sometimes you want to re-arm the expiry on a key you already stored, or make a key permanent, without rewriting its value. ExpireAsync sets or replaces a key's TTL, and PersistAsync removes it. Both return a KvResult, so check IsSuccess before assuming they took effect.
var expire = await client.ExpireAsync("session:alice", TimeSpan.FromMinutes(30));
if (expire.IsSuccess)
{
Console.WriteLine("TTL extended to 30 minutes");
}
else
{
Console.WriteLine($"ExpireAsync unavailable here: {expire.Status} - {expire.Error}");
}
ExpireAsync and PersistAsync route the TTL change through the cluster's partition map. On a running cluster node that check passes and the call succeeds. In a bare single-process demo like this one the partition map is not initialized, so these two calls return KvStatus.Unavailable with the message "partition map not initialized" — that is expected, not a bug. Guarding on IsSuccess, as above, keeps the sample honest whether you run it in-process or against a cluster. To set a TTL that always works in-process, set it at write time with Put.WithTtl, as in step 3.
What you built
You built a cache whose entries clean themselves up. You set expiry at write time with Put.WithTtl, inspected the remaining time with GetTimeToLiveAsync (reading Value as a TimeSpan?, where null means no expiry), and watched a key vanish once its TTL elapsed, after which GetAsync returns KvStatus.NotFound. You also saw that ExpireAsync and PersistAsync re-arm or remove a TTL on an existing key against a running cluster, and how to detect the KvStatus.Unavailable they return in a single-process demo. Prefer Put.WithTtl when you write the value anyway; reach for ExpireAsync/PersistAsync when you need to adjust the lifetime of a key you already stored.