Transienta is a version-aware invalidation and caching coordination layer for microservices. It tracks request dependencies across services and invalidates cached results when underlying data changes, using a lightweight wire protocol over NanoMsg and a simple Go SDK for instrumentation.
- Consistency: Cached responses are only reused if none of their dependent data changed since the request started.
- Simplicity: Instrument code with a few calls: start a request, add dependencies as you read, invalidate on writes, finish the request.
- Performance: Uses a monotonic timestamped version store for constant-time conflict checks; messaging uses FlatBuffers for zero-copy serialization.
- Manager (Rust): A daemon that receives events over NanoMsg, maintains a dependency graph and a per-key version store, and decides whether requests are valid to cache.
- Versioning logic:
VersionedHistoryStorageincrements a global timestamp on writes/invalidations and validates requests by comparing dependency versions with the request start timestamp. - Transport: NanoMsg Pair; schema encoded via FlatBuffers.
- Versioning logic:
- SDK (Go): A client library to instrument services.
- Exposes
Start,AddDependency,Invalidate,Finish.
- Exposes
Flow:
- Service should run Start() before a request is being processed.
- Service reads data and calls
AddDependency(...)per dependency path (e.g.,users/123). - On writes / updates, service invokes
Invalidate(key)to notify the cache that a dependency has changed. - Service sends
Finish(resp); manager validates: request is valid if none of thedepschanged after the start timestamp. If valid, it records dependency graph and sends the save request to save the response in thecallercache.
src/main.rs,app.rs: wiring and setupmanager/manager.rs: message loop and request handlingadapters/storage/version_storage.rs: version store, validation, dependency graphadapters/comms/: NanoMsg + FlatBuffers helperscache/: cache provider abstraction (Redis stub)config/: YAML config loader/types
sdk/go/: Go SDK (client and internal comms/fbs)fbs/requests.fbs: FlatBuffers schemabuild/: Dockerfiles (alpine, scratch)
Prerequisites: Rust toolchain (edition 2024), NanoMsg (mangos for Go SDK; Rust uses nng crate).
cargo build --releaseRun the manager (default config path is /etc/transienta/config.yaml unless overridden):
export TRANSIENTA_CONFIG_PATH=/path/to/config.yaml
cargo run --releaseProvide a YAML file; see example-config.yaml:
manager:
name: shard-1
port: 5532
shard_id: 0
neighbours:
shard-2: "127.0.0.1"
cache:
type: "redis"
config:
tls: false
port: 6379
db: 0
host: localhost
username: ""
password: ""If TRANSIENTA_CONFIG_PATH is not set, the manager reads /etc/transienta/config.yaml.
import (
"context"
client "github.com/hertzcodes/transienta/go-sdk/client"
)
cfg := client.ClientConfig{
ManagerIP: "127.0.0.1:5532", // informational; used in hashing
SocketURL: "tcp://127.0.0.1:5600", // your NanoMsg endpoint
On: true, // enable/disable SDK
}
c := client.New(cfg)
base := context.Background()
ctx := c.Start([]byte("request-bytes"), "my-service", base)
defer c.Finish(ctx, nil)
// When reading data or calling other services:
_ = c.AddDependency(ctx, "users/123")
//
//
// Request processes here
//
_ = c.AddDependency(ctx, "orders/987")
// When writing/updating data:
c.Invalidate("users/123")
// request does not cache since users/123 is invalidated.Notes:
Startcreates a contextual ID and sendsStartRequest.AddDependencyaccumulates dependency paths for the request.InvalidateemitsInvalidationRequestand persists to an outbox on failure.FinishsendsEndRequestwith the dependency list and request args hash.
Inside the manager (VersionedHistoryStorage):
- Every invalidation/write increments a global
current_timestampand updates the version for the impacted dependency path. - When a request starts, its
call_idis mapped to the current timestamp. - On
EndRequest, the manager verifies all dependency paths have a version <= the start timestamp; if so, the request is valid to cache and the dependency and subscriber maps are updated.
This ensures cached results are only reused when none of the dependencies were modified after the request began.
Two Dockerfiles are provided in build/ (Dockerfile-alpine, Dockerfile-scratch). Build and run as desired, mounting your config path to the container and exposing the NanoMsg/manager ports you use.
- Rust unit tests:
cargo testcover validation edge cases. - Go SDK tests/benchmarks:
sdk/go/client/client_test.goincludes concurrency tests; rungo test ./...(use-racefor race detection).
See LICENSE.