TTL / Expiry
Records can carry a time-to-live (TTL). After the TTL elapses:
- The record is transparently filtered from
get(),list(),count(), andsearch()results - The data still occupies disk space until the next
compact(), which garbage-collects expired records permanently
Use TTL for session vectors, retrieval caches, ephemeral chat history, or any record with a natural expiry.
Setting a TTL
Python
# On insert/upsert (seconds from now)
db.upsert("session1", embedding, {"user": "alice"}, ttl=3600) # expires in 1 hour
# On an existing record
db.set_ttl("doc1", 86400) # expire 24 h from now
db.clear_ttl("doc1") # remove expiry, live forever
Node.js
db.upsert('session1', embedding, { user: 'alice' }, { ttl: 3600 })
db.setTtl('doc1', 86400)
db.clearTtl('doc1')
Inspecting expiry
expires_at (Python) / expiresAt (Node) on a Record exposes the absolute expiry timestamp in epoch seconds, or None/null when no TTL is set.
record = db.get("session1")
if record and record.get("expires_at"):
print(f"Expires at {record['expires_at']}")
Garbage collection
Expired records are invisible to reads immediately. They are physically removed from disk only on compact():
db.compact() # also folds the WAL into the snapshot
Schedule compact() periodically (cron, background job) when TTL is in heavy use, otherwise expired data accumulates on disk.
Transactions
The ttl parameter works inside transactions too:
with db.transaction() as tx:
tx.upsert("session1", emb1, {"user": "alice"}, ttl=3600)
tx.upsert("session2", emb2, {"user": "bob"}, ttl=3600)
Common patterns
Session vectors
db.upsert(session_id, user_embedding, {"user": user_id}, ttl=3600)
Retrieval cache
db.upsert(query_hash, query_embedding, {"results": result_ids}, ttl=300)
Sliding window
Call set_ttl() on every read to refresh the expiry — turns the store into an LRU-by-time cache.
record = db.get(key)
if record:
db.set_ttl(key, 3600) # refresh