greentic-session
Greentic’s session manager provides a multi-tenant coordination layer for connector flows.
It offers deterministic session key mapping, optimistic concurrency via compare-and-set tokens, outbox deduplication, TTL management, and pluggable persistence backends.
Crate Highlights
- Shared model – Reuses
greentic-typesprimitives (GResult,TenantCtx, etc.) while defining session-centric structs (Session,SessionMeta,SessionCursor,OutboxEntry,Cas). - Backends – In-memory (
dashmap+ lazy TTL cleanup) is always available; Redis backend is feature-gated (redis, default-enabled) and stores data undergreentic:session:{tenant}:{key}. - Deterministic keys – Helper mappers hash connector payload hints (e.g., Telegram updates, webhooks) to produce stable
SessionKeyvalues without leaking PII. - Concurrency guarantee – CAS tokens (
Cas) increment on every successful write, giving last-writer-wins semantics and protecting against racey updates. - Outbox dedupe – Repeated
(seq, payload_sha256)pairs are ignored in both backends, ensuring idempotent connector hand-offs.
Choosing a Backend
| Feature flag combo | Backend availability | Suggested usage |
|---|---|---|
default (redis) |
Redis + in-memory | Production deployments with Redis |
--no-default-features --features inmemory |
In-memory only | Tests, single-node dev |
--all-features |
Redis + schema export | CI / schema docs |
Enable the Redis backend with the default feature set. In environments without Redis, disable default features and opt into inmemory.
Quickstart
# optionally, export REDIS_URL and re-run to exercise Redis
The example walks through:
- Mapping a Telegram update onto a
SessionKey. - Creating a
Sessionand writing it withSessionStore::put. - Fetching the CAS token and issuing a CAS update with a new outbox entry.
- Refreshing TTL/
updated_atviatouchand finally deleting the session. - Running the same flow against Redis (when
REDIS_URLis configured).
You can adapt the snippet for your flow orchestration logic. A minimal excerpt:
use ;
use SessionStore;
use OffsetDateTime;
let store = new;
let key = telegram_update_to_session_key;
let session = Session ;
let cas = store.put?;
// later…
if let Some = store.get?
Deterministic Session Keys
mapping::telegram_update_to_session_key(bot_id, chat_id, user_id)mapping::webhook_to_session_key(source, subject, id_hint)
Both functions derive a SHA-256 digest and encode it as hex. Avoid placing secrets or raw PII in the inputs—hash inputs or substitute stable, non-sensitive identifiers extracted earlier in your pipeline.
TTL & Cleanup Semantics
- Each
Sessioncarries an absolutettl_secs.putandupdate_casstampupdated_attoOffsetDateTime::now_utc()and recompute the expiry. - In-memory backend performs lazy expiration during read/write/touch operations and periodically scans (no more often than every 60 s) to evict expired entries.
- Redis backend stores the envelope (
session + cas) in a single JSON blob and keeps a lookup key for tenant resolution. TTL changes are applied atomically via Lua, and lookup keys mirror the data TTL (or are persisted when TTL is zero). touchrefreshes bothupdated_atand TTL; passNoneto keep the existing TTL but still bumpupdated_at.
Thread-safety & Performance
InMemorySessionStoreusesDashMapfor lock-free reads and employs aparking_lotmutex only for occasional cleanup scheduling.RedisSessionStorerelies on Lua scripts for CAS updates to guarantee atomicity and uses Redis-native expiration for TTL enforcement. A secondary lookup key (greentic:session:lookup:{hash}) tracks tenant-to-key mapping.- Benchmarks (
cargo bench) provide baseline throughput forput,update_cas, andgeton the in-memory backend via Criterion.
Testing & Linting
Redis integration tests (ttl_and_touch, cas_races, outbox_dedupe) require REDIS_URL. CI spins up Redis automatically via a service container.
Local Redis for ad-hoc runs:
Versioning & Stability
- Crate metadata follows Semantic Versioning. Initial releases start at
0.xwhile APIs and models stabilize. - Publishing is tag-driven (
v*tags). GitHub Actions handle fmt/clippy/test for pull requests and main pushes. - Licensing: MIT (see
LICENSE). When embedding this crate elsewhere, keep license headers aligned.
Maintenance Notes
- Extend shared surface area (e.g., adding new fields to
Session) throughgreentic-typesfirst to avoid duplication. - Additional backends (SQL, DynamoDB, etc.) should live behind new feature flags and reuse the
SessionStoretrait. - Observe the
TenantCtxsemantics from the next-gen overview—tenant-aware routing is preserved via the lookup key strategy on Redis and via theSessionMetastruct for in-memory computations.
Releases & Publishing
- Crate versions are sourced directly from each
Cargo.toml. - Every push to
masterreruns the auto-tag workflow. When a crate’s manifest changes its version and no matching tag exists yet, a git tag<crate-name>-v<semver>is created and pushed. - The publish workflow lints, builds, and tests the full workspace (all features) before invoking
katyo/publish-crates@v2. - Publishing is idempotent: if a crate version is already on crates.io, the workflow succeeds without re-uploading.
- Configure the
CARGO_REGISTRY_TOKENsecret with a crates.io publish token to enable automated releases.