EpochDB memory engine server and cluster.
3.5K
EpochDB Distributed Server is an asynchronous, high-concurrency memory and vector database server designed for agentic workloads. It supports multi-node clustering with automatic horizontal sharding, consistent hashing write distribution, and direct prefix-based routing.
The cluster follows a MongoDB-style coordinator/gateway and storage shard architecture:
┌──────────────────┐
│ HTTP Clients │
└────────┬─────────┘
│ HTTP POST/GET
▼
┌──────────────────┐
│ Coordinator │ (Gateway / Router Node)
└─┬──────┬──────┬──┘
│ │ │ HTTP (Routed / Broadcasted)
┌──────────┘ │ └──────────┐
▼ ▼ ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ Shard 0 │ │ Shard 1 │ │ Shard 2 │ (Storage Nodes)
└────────────────┘ └────────────────┘ └────────────────┘
NODE_MODE)A server instance runs in one of two modes depending on environment variables:
AsyncEpochDB engine.STORAGE_DIR.SHARD_NODES.text using MD5 consistent hashing to assign the write to a target shard node.shard{index}-{uuid_hex}. When fetching, updating, or deleting a memory, the coordinator parses the prefix to route the request directly to the target shard, avoiding expensive cluster-wide broadcasts. Broadcast is used as a fallback only if the prefix is missing or invalid.k candidates./entity_graph, nodes and edges are retrieved from all shards and merged (set union) dynamically.A pre-configured 3-shard cluster and 1 coordinator gateway are defined in docker-compose.yml.
Run the following command to start the cluster:
docker compose up --build -d
This will run:
shard0 at http://localhost:8081shard1 at http://localhost:8082shard2 at http://localhost:8083coordinator gateway at http://localhost:8080 (public interface)Environment variables used by the server:
NODE_MODE: "shard" (default) or "coordinator".SHARD_NODES: Comma-separated list of backend shard URLs (e.g. http://shard0:8080,http://shard1:8080). Required in coordinator mode.STORAGE_DIR: Local data storage directory (default: ./shared_memory).All requests and responses use JSON.
POST /rememberIngests a new memory.
{
"text": "Factual text to store.",
"metadata": { "optional": "metadata", "triples": [["subject", "predicate", "object"]] },
"id": "optional-predefined-id"
}
{ "status": "success", "id": "shard1-46bb87d8..." }
POST /getRetrieves a memory by ID.
{ "memory_id": "shard1-46bb87d8..." }{
"id": "shard1-46bb87d8...",
"payload": "Factual text to store.",
"payload_type": "text",
"embedding": [0.012, -0.054, ...],
"triples": [["subject", "predicate", "object"]],
"created_at": 1719542000.123,
"access_count": 1,
"epoch_id": "epoch_active",
"metadata": { "optional": "metadata" }
}
POST /queryPerforms semantic search.
{
"query": "search query string",
"k": 5,
"filters": { "optional": "metadata-filters" }
}
{
"results": [
{
"id": "shard1-46bb87d8...",
"text": "Factual text to store.",
"metadata": { "optional": "metadata" },
"created_at": 1719542000.123,
"score": 0.892
}
]
}
POST /updateUpdates an existing memory's text or metadata.
{
"memory_id": "shard1-46bb87d8...",
"text": "new text payload",
"metadata": { "updated": "metadata" }
}
{ "status": "success" }POST /deleteDeletes a memory.
{
"memory_id": "shard1-46bb87d8...",
"hard": false
}
{ "status": "success" }GET /entity_graphRetrieves the combined entity graph.
entity_id (string, required), depth (integer, default: 2){
"nodes": ["Marie Curie", "radium"],
"edges": [
{
"source": "Marie Curie",
"target": "radium",
"predicate": "discovered",
"memory_id": "shard0-abc..."
}
]
}
POST /get_timelineGets chronological memories across all nodes.
{ "entity_id": "optional", "start": 0.0, "end": 1719542000.0 }{ "memories": [...] }GET /statsRetrieves aggregated metrics.
{
"memory_count": 12,
"l1_size": 2,
"l2_size": 10,
"entity_count": 5
}
POST /compactCompact all cluster nodes.
{ "status": "compaction completed" }Use the provided client.py wrapper to interact with the database:
import asyncio
from epochdb import AsyncRemoteEpochDB
async def main():
# Connect to the coordinator gateway
db = AsyncRemoteEpochDB(host="127.0.0.1", port=8080, api_key="test-api-key-12345")
# Store a memory
memory_id = await db.remember(
"Marie Curie discovered radium.",
metadata={"triples": [("Marie Curie", "discovered", "radium")]}
)
print(f"Stored: {memory_id}")
# Semantic Query
res = await db.query("Marie Curie discoveries", k=1)
if res:
print(f"Query match: {res[0].text} (Score: {res[0].score})")
# Entity Graph
graph = await db.entity_graph("Marie Curie")
print(f"Graph: {graph}")
await db.close()
if __name__ == "__main__":
asyncio.run(main())
This project is licensed under the MIT License - see the LICENSE file for details.
Content type
Image
Digest
sha256:451d61928…
Size
629.7 MB
Last updated
5 days ago
docker pull jersobh/epochdb