simple magicDATA

SMKV / Build applications

Use the Go SDK

Applies to: the SDK supplied with your SMKV build; examples of outcome classification and cluster retries describe the current development SDK. Requires Go 1.22 or newer.

Add the local module

The SDK is currently a local module named go-smkv, not a publicly published Go module. Obtain the matching SDK source through your release/support channel. In your application module, adjust the replacement path:

go mod edit [email protected]
go mod edit -replace=go-smkv=../smkv/go-smkv

Connect and write

Save this as main.go, start a standalone node, then run go run .:

package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "time"
    smkv "go-smkv"
)

func main() {
    client, err := smkv.NewClient("127.0.0.1:7379", smkv.Options{
        PoolSize: 4,
        Timeout: 5 * time.Second,
    })
    if err != nil { log.Fatal(err) }
    defer client.Close()
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    key := []byte("greeting")
    if err := client.Put(ctx, key, []byte("hello"), smkv.WithTTL(time.Minute), smkv.WithSync()); err != nil {
        log.Fatal(err)
    }
    value, err := client.Get(ctx, key)
    if errors.Is(err, smkv.ErrNotFound) { fmt.Println("cache miss"); return }
    if err != nil { log.Fatal(err) }
    fmt.Println(string(value))
}

Clients are safe for concurrent goroutines and use a bounded connection pool. Reuse clients, close them during shutdown, and do not mutate input slices until a call returns. Connections are lazy; constructing a direct client does not verify a live server. Empty byte values are valid.

Route across a cluster

Use management seeds, not data addresses. With an existing ctx:

cluster, err := smkv.NewClusterClient(ctx,
    []string{"10.0.0.11:7381", "10.0.0.12:7381"},
    smkv.Options{PoolSize: 4, Timeout: 5 * time.Second})
if err != nil { return err }
defer cluster.Close()
err = cluster.Put(ctx, []byte("key"), []byte("value"))

This fragment belongs inside a function returning error. The SDK discovers the map and routes to the primary owner. Use GetReplica explicitly when stale replica reads are acceptable; primary reads do not silently fall back to replicas. All advertised data and management addresses must be reachable from the application.

Set deadlines and durability

The earlier of the context deadline and configured timeout bounds the entire call, including pool waits and connection setup. Await a write before issuing a dependent read. Concurrent calls across connections have no ordering guarantee.

Omit WithTTL for no expiration. WithTTL(0) expires immediately. WithSync() waits for local sync, not replica confirmation. Current quorum RF2 builds additionally support WithReplicaAck() and WithDurableReplicaAck(). Keep the client timeout above the server's confirmation budget, with transport headroom.

Handle outcomes deliberately

Error or condition Application response
ErrNotFound Treat as absent or expired; choose your cache-miss path
ErrBusy Request rejected before admission; back off within your deadline
ErrCapacity Inspect capacity; repeated immediate retries cannot create headroom
ErrOutcomeUnknown Mutation may have executed; reconcile before retrying
ErrUnauthorized Repair identity, role, or credential configuration
Context cancellation/deadline Stop waiting; still check mutation outcome classification

Use errors.Is, not message matching. Check ErrOutcomeUnknown before deciding a failed mutation is safe to retry. Direct clients never retry automatically. The current cluster client can retry bounded transient reads and explicit wrong-owner rejections; it never blindly replays uncertain mutations. SDK behavior has evolved, so deploy clients and servers from compatible releases.

For typed data, continue with tables and columns.