Deepanshu's Diary

Designing Real-Time Distributed Graphs as Service-to-Service Contracts for Tenant-Scoped Knowledge

--system-designgraphsdistributed-systems

Designing Real-Time Distributed Graphs as Service-to-Service Contracts for Tenant-Scoped Knowledge

I once ran into a small-sounding tension that quickly became existential: two teams had to share entity state—customers, leads, conversation memory—fresh, auditable, and tenant-isolated, but neither would accept a central database or exposing raw tables. Without a shared contract, caches diverged and audits failed. We needed a machine-readable promise covering schema, freshness, access, and provenance. In this post I unpack a mental model that treats distributed graph fragments as live interfaces, walk a pragmatic architecture and data flow, summarize prototypes I built (ARIL and companion tools), surface common failure modes, and end with a checklist to ship safely.

This post lays out a mental model for treating distributed graphs as the runtime shape of service-to-service contracts. I show an architecture/data-flow example, describe what I built while exploring this idea, enumerate how it breaks, and finish with a short operational checklist to take this from prototype toward production.

The mental model: graph + contract = live interface

Think of each service as the owner of a subgraph. Nodes and edges are first-class artifacts representing entities (user, document, lead) and relationships (owns, referenced-by, last-updated-by). Ownership implies authority: only the owning service can authoritatively mutate the node’s canonical state. Other services can cache derived nodes or edges but must keep a contract with the owner to understand freshness, version, and provenance.

A service-to-service contract is therefore: (1) a schema (what fields are offered), (2) a freshness policy (event streaming, TTLs, snapshot cadence), (3) an access policy (who can read which tenants), and (4) a provenance pointer (source document IDs, embeddings, citations). You can implement the contract with lightweight machine-readable artifacts (OpenAPI-like for graph reads, Kafka topics with well-defined schemas for change events, and a capability token for access).

This mental model separates control (who owns data) from access (who can read), and ties both to a live graph that can be traversed across service boundaries with predictable semantics.

Example architecture and data flow

Below is a compact architecture that I’ve found pragmatic while building tenant-scoped knowledge features.

- Each service stores its canonical graph fragment in a tenant-scoped Postgres schema. For fast semantic retrieval, services also compute embeddings and store them in a local pgvector index. - Services publish change events (node created/updated/deleted) to a message bus (Kafka or durable pub/sub). Events carry contract metadata: schema version, vector fingerprint, and citation pointers to source documents. - Consumers subscribe and either apply the event to a local cache or trigger a retrieval flow: query the owning service’s API for the current node (using contract tokens) and optionally fetch cited documents for justification. - For cross-service queries, a coordinator issues parallel reads to owner endpoints and composes results into a “virtual graph” to answer the query. Embedding-based retrieval can run locally against the consumer’s replica of embeddings for low-latency fallbacks.

A simple sequence:

1. Scraper service extracts a restaurant lead and creates a document chunk, computes an embedding and stores it in pgvector. It publishes a LeadCreated event with tenant-id, doc-id, and vector-hash. [3] 2. Lead-matching service consumes LeadCreated, fetches the document via contract URL, runs matching logic, and writes Match edges to its own graph. It emits MatchCreated with citations pointing back to the scraper document. 3. Downstream UI queries the match graph; the UI resolves citations by calling the owner service (scraper) which serves immutable chunk content and attachments, satisfying audit and compliance needs.

This flow keeps provenance anchored and lets teams optimize their storage independently.

What I built around this idea

I prototyped these concepts in ARIL and companion projects in my portfolio:

- ARIL local monorepo implements tenant-scoped knowledge bases, documents, chunks, embeddings, pgvector retrieval, citation pointers, evaluation metrics, and protected routes. This is the primary experimental workspace for tenant-isolated graph fragments and contract-aware retrieval. (Implemented: local monorepo features. Gate: real production Postgres migration and multi-tenant cloud deployment.) [1]

- Universal Scraper provides a three-stage extraction pipeline and outputs document chunks with citations and embeddings that can be ingested into ARIL’s per-tenant pgvector store. (Implemented: extraction pipeline and fixture tests. Gate: long-running scraper runs must still honor robots, rate limits, and provider blocking.) [4]

- Google WhatsApp Scraper and Amazon Voice Agent explore the operational side of contracts: consented memory, audit events, approval gates, encrypted provider keys and health probes. These demonstrate auditability and operator controls you need when exposing chunks across services. (Implemented: mock-first runtime, consent model, operator UI. Gates: live provider acceptance, live streaming.) [2]

I used these projects to validate the shape of events, citation linking, and retrieval evaluation metrics before committing to any production Postgres migration or external provider traffic. See the code and example flows in my portfolio. [1][3][4]

Where this breaks (failure modes and mitigations)

1. Stale or inconsistent reads across services (event delivery delays or dropped events). - Mitigation: offer both streaming subscription and on-demand authoritative fetch with sequence numbers; clients prefer authoritative fetch if sequence indicates potential gap.

2. Schema drift between services (consumer expects field X that producer removed). - Mitigation: enforce semver on contract schemas, run compatibility tests in CI, and include schema-version in events so consumers can route to a translator/adapter.

3. Query fan-out explosion (a single UI request touches many services and amplifies latency). - Mitigation: use a coordinator that parallelizes calls with timeouts and returns partial results with provenance flags; maintain small materialized views for high-traffic joins.

4. Embedding drift and retrieval mismatch (vectors change as models evolve, causing mismatches between event-time vectors and current queries). - Mitigation: store vector fingerprints with events; provide re-embedding pipelines and include vector version in contract metadata so consumers know when to reindex.

5. Security/authorization leaks when citations expose tenant data. - Mitigation: rigorous tenant scoping in storage, short-lived access tokens, and an approval gate for outbound content. Audit events should record every citation access.

A practical checklist (5–8 checks)

- Define a contract artifact: schema + vector-version + freshness policy + access token model. - Implement tenant-scoped storage with explicit ownership and protected routes for reads/writes. (I used per-tenant schemas in ARIL.) [1] - Emit events with schema-version and provenance pointers; include vector fingerprints for retrieval tracing. - Provide authoritative read endpoints for each owner that include sequence numbers for reconciliation. - Build a lightweight coordinator for cross-service queries that supports parallel calls, timeouts, and partial results with provenance tags. - Add CI contract tests that run consumer queries against a mock producer before deploys. (Mock-first runtimes helped in my voice-agent work.) [2] - Monitor event lag, reconciliation failures, and citation access logs; alert on anomalies.

Conclusion

Treating distributed graphs as the execution surface for service-to-service contracts clarifies ownership, improves provenance, and lets teams evolve independently while maintaining reliable interoperation. The trick is operational: emit rich event metadata, provide authoritative fallbacks, and make contract changes a first-class CI artifact. I’ve experimented with these ideas in ARIL and associated projects—there’s a clear path from local prototypes to production, but beware the operational gates (storage migration, provider acceptance, and policy constraints) that must be crossed carefully.

References

[1] ARIL local monorepo (tenant-scoped KBs, embeddings, pgvector): https://github.com/deepanshuvermaa/my-portfolio

[2] Amazon Voice Agent (mock-first runtime, consented memory, audit events): https://github.com/deepanshuvermaa/air-canvas

[3] Universal Scraper (three-stage extraction and chunk outputs): https://github.com/deepanshuvermaa/museum-of-failure

[4] Model Context Protocol (design inspiration for context and interface wiring): https://modelcontextprotocol.io/introduction

[5] Neural Relational Inference / graph modelling papers (for background on graph reasoning and representation): https://arxiv.org/abs/2005.11401

[6] Graph learning and robustness literature (for embedding drift and related mitigations): https://arxiv.org/abs/2307.03172

Copied!
Back to all posts