Skip to content

Add support for FalkorDB graph store #38

Description

@dudizimber

Problem statement

Active Graph keeps the materialized graph — objects, relations, and patches —
in process memory (InMemoryGraphStore), rebuilt from the event log on every
run. That's the right default, but it leaves three things hard to do once a
run's current-state view gets interesting:

  • Query current state with a graph query language. There's no way to ask
    "what does the graph look like right now?" with Cypher — dashboards, ad-hoc
    exploration, and graph-algorithm work all have to walk Python objects.
  • Share the projection across processes. A live run and a separate
    read-only inspector (a dashboard, a sidecar) can't look at the same
    current-state view; each would rebuild its own in-memory copy.
  • Keep a large projection off the heap. Projections that don't fit
    comfortably in process memory have nowhere else to go.

The event log already has a durable home behind the EventStore seam
(SQLite/Postgres). The projection has no equivalent seam — it's hardwired to
process memory. The goal is to make where the current-state view is
materialized
a pluggable choice, without touching the "log is the source of
truth" model.

Current workaround

There isn't a clean one. Today you'd have to manually export the projection
into your own store after a run and re-query it out of band — with no
read-through, so it drifts the moment anything mutates:

rt.run_goal("…")

# Hand-roll an export into whatever external store you have.
for obj in rt.graph.all_objects():
    my_graph_db.upsert_node(obj.id, obj.type, obj.data)
for rel in rt.graph.all_relations():
    my_graph_db.upsert_edge(rel.id, rel.source, rel.target, rel.type)

This duplicates the projector logic, has to be re-run on every change, and
can't be used to inspect a recorded run (Runtime.load / Runtime.fork
always rebuild in memory). For larger graphs there's no workaround at all — the
projection must fit in the heap.

Proposed solution

Introduce a GraphStore seam for the materialized projection, parallel to the
existing EventStore seam for the log, plus a FalkorDB-backed implementation.

Concretely:

  • GraphStore abstraction with the current in-memory behavior as the default
    InMemoryGraphStore, and a FalkorDBGraphStore that backs the projection
    with a FalkorDB graph (objects/relations/patches as labelled nodes; all
    values passed as bound Cypher params).
  • Two FalkorDB connection modes: a running server via the falkordb client
    (explicit url=/host= args or FALKORDB_URL / FALKORDB_HOST
    (_PORT / _USERNAME / _PASSWORD) env vars), falling back to the embedded
    falkordblite engine. Behind optional extras
    (activegraph[falkordb] / activegraph[falkordb-embedded]), kept out of
    [all]/[dev].
  • Wire the seam in at the library level only — Graph(graph_store=…), and
    threaded through Runtime.load(…, graph_store=…) and
    Runtime.fork(…, graph_store=…) so recorded runs and forks can rematerialize
    in FalkorDB too. No CLI flag: the CLI's read commands operate on the log
    and are intentionally infrastructure-light, so the projection backend stays a
    programmatic choice.
from activegraph import Graph, Runtime, FalkorDBGraphStore

store = FalkorDBGraphStore(host="localhost", port=6379, graph_name="run-42")
graph = Graph(graph_store=store)
rt = Runtime(graph=graph, behaviors=[...])
# … or replay an existing run into FalkorDB:
rt = Runtime.load("runs.db", run_id="run-42", graph_store=store)

Explicitly out of scope: the event log stays the source of truth. The
GraphStore is a disposable projection — durability and audit continue to come
from the EventStore, and a wiped FalkorDB graph is fully rebuilt by replaying
the log. This is additive and the in-memory default is unchanged.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions