ArcFlow
Company
Managed Services
Markets
  • News
  • LOG IN
  • GET STARTED

OZ brings Visual Intelligence to physical venues, a managed edge layer that lets real-world environments see, understand, and act in real time.

Talk to us

ArcFlow

  • World Models
  • Sensors

Managed Services

  • OZ VI Venue 1
  • Case Studies

Markets

  • Sports
  • Broadcasting
  • Robotics

Company

  • About
  • Technology
  • Careers
  • Contact

Ready to see it live?

Talk to the OZ team about deploying at your venues, from a single pilot match to a full regional rollout.

Schedule a deployment review

© 2026 OZ. All rights reserved.

LinkedIn
ArcFlow Docs
Start
  • Quickstart
  • Installation
  • Bindings
  • Platforms
  • Get Started
  • Cookbook
Concepts
  • World Model
  • Graph Model
  • Evidence Model
  • Observations
  • Confidence & Provenance
  • Proof Artifacts & Gates
  • SQL vs GQL
  • Graph Patterns
  • Parameters
  • Query Results
  • Persistence & WAL
  • Snapshot-Pinned Reads
  • Error Handling
  • Execution Models
  • Causal Edges
  • Adapter Discipline
  • Time Decay
  • Layers
  • 1. World Store
  • 1a. World Store · Smart Reader
  • 2. Perception Lake
  • 3. World Graph
  • 4. Query Engine
  • 5. Live Surface
  • 6. Event Bus
  • 7. Behavior Engine
  • 8. Algorithm Library
  • Virtual Computed Columns
  • Threading Model
  • Typed ID Contract
WorldCypher
  • Overview
  • Execution Options
  • Statements
  • MATCH
  • WHERE
  • RETURN
  • OPTIONAL MATCH
  • CREATE
  • SET
  • MERGE
  • DELETE
  • REMOVE
  • Composition
  • WITH
  • UNION
  • UNWIND
  • CASE
  • Schema
  • Schema Overview
  • Indexes
  • Constraints
  • Functions
  • Built-in Functions
  • Aggregations
  • Procedures
  • Shortest Path
  • EXPLAIN
  • PROFILE
  • Temporal Queriesfacet
  • Spatial Queriesfacet
  • Algorithmsfacet
  • Triggers
Capabilities
  • Live Queries
  • Vector Search
  • Trusted RAG
  • Spatial Knowledge
  • Temporal
  • Behavior Graphs
  • Graph Algorithms
  • Skills
  • CREATE SKILL
  • PROCESS NODE
  • REPROCESS EDGES
  • Sync
  • Programs
  • GPU Acceleration
  • Agent-Native
  • MCP Server
  • Event Sourcing
  • Intent Relay
  • Event Bus
Use Cases
  • Agent Tooling
  • Trusted RAG
  • Knowledge Management
  • Behavior Graphs
  • Autonomous Systems
  • Physical AI
  • Digital Twins
  • Robotics & Perception
  • Sports Analytics
  • Grounded Neural Objects
  • Fraud Detection
Walkthroughs
    Guides
  • Agent Integration
  • Building a World Model
  • Modeling a Social Graph
  • Build a RAG Pipeline
  • Using Skills
  • Behavior Graphs
  • Swarm & Multi-Agent
  • Fleet Coordination
  • Migrate from Cypher / Neo4j
  • From SQL to GQL
  • Filesystem Workspace
  • Data Quality
  • Code Intelligence
  • Scale Patterns
  • v0.7 → v0.8 Lakehouse Fast-Path
  • Tutorials
  • Knowledge Graph
  • Entity Linking
  • Vector Search
  • Graph Algorithms
  • Recipes
  • CRUD
  • Multi-MATCH
  • MERGE (Upsert)
  • Full-Text Search
  • Batch Projection
  • Multi-Source Observation
  • Sports Analytics
Operations
  • CLI
  • REPL Commands
  • Snapshot & Restore
  • Filesystem Projection
  • Plugin Management
  • Agent Governance
  • Server Modes & PG Wire
  • Persistence (ops)
  • Import & Export
  • Deployment
  • Deployment Modes
  • Daemon (UDS)
  • Why not Docker
  • Architecture
  • Engine Architecture
  • Cloud Architecture
  • Sync Protocol (Deep Dive)
  • World Graph Substrate (Preview)
Reference
  • TypeScript API
  • Glossary
  • Naming & Domain Map
  • Data Types
  • Operators
  • Error Codes
  • GQL Reference
  • Known Issues
  • Versioning
  • Licensing
  • Conformance
  • GQL Conformance
  • openCypher TCK
  • Extension Regressions
GQL Reference
    Conformance
  • Conformance Dashboard
  • openCypher TCK Results
  • Extension Regressions
  • Features
  • MATCH Basic
  • CREATE Nodes Edges
  • SET REMOVE Properties
  • DELETE Detach DELETE
  • RETURN WITH WHERE
  • Order BY Limit Skip
  • Order BY Nulls First Last
  • UNWIND
  • Aggregate Functions
  • OPTIONAL MATCH
  • Variable Length Paths
  • Label OR AND NOT Expressions
  • Label Wildcard
  • Quantified Path Sugar
  • Path Modes Walk Trail Simple Acyclic
  • Shortest Path Variants
  • IS Labeled Predicate
  • Element ID Function
  • IS Type Predicate
  • Binary Literals
  • Line Comments Solidus
  • Line Comments Minus
  • GQLSTATUS Result Codes
  • GQL Error Code Mapping
  • Transaction Control Syntax
  • SET Session
  • Conditional Execution WHEN THEN ELSE
  • RETURN NEXT Pipeline
  • Primary Key Constraint
  • Unique Constraint
  • Deterministic MERGE Via PK
  • Undirected Edge MATCH
  • Cast Type Conversion
  • GQL Directories
  • Multiple Labels Per Node
  • GQL Flagger
  • NEXT Linear Composition
  • Cardinality Function
  • INT64 BIGINT Type Names
  • FLOAT64 Double Type Names
  • Log10 Log2 Functions
  • Trim Leading Trailing Both
  • FILTER Clause
  • LET Statement
  • Group BY Explicit
  • EXCEPT SET Operations
  • INTERSECT SET Operations
  • ALL Different Predicate
  • Same Predicate
  • Property Exists Function
  • Path Variable Binding
  • USE Graph Clause
  • FOR IN List
  • Typed Temporal Literals
  • Session SET Value Params
  • Typed List Annotations
  • arcflow.cosine() function
  • arcflow.embed() function
  • arcflow.similar() procedure
  • arcflow.graphrag() procedure
  • ArcFlow Extensions
  • LIVE Queries
  • Triggered Write-Back Views
  • Evidence Algebra
  • Relationship Skills
  • AI Function Namespace
  • Graph Embedding Algorithms
  • ASOF JOIN
  • Durable Workflows
  • Incremental Z-Set Engine
  • GPU GraphBLAS
  • Triggers
  • HNSW Vector Index
  • Extensions Moat

Threading Model

ArcFlow's threading model is MVCC-snapshot for reads, write-mutex for writes, and a typed-error guard at the handle boundary that surfaces concurrent-write contention immediately instead of letting it become a silent slowdown.

The model in one paragraph: any number of reader threads can scan the graph against an MVCC snapshot with zero coordination — readers never block readers, and readers never block writers. Writes through a single handle serialise on a per-handle write mutex; if a second thread tries to write through the same handle while one is in flight, the second call fails fast with HANDLE_BUSY_CONCURRENT_WRITER rather than queueing behind the first (which empirically regresses 10× under contention and spills into preceding indexing stages). The recovery is explicit: use multiple handles or serialise externally.

The matrix#

Thread AThread B (concurrent)Outcome
Read (MATCH … RETURN)Readboth succeed; lock-free via ArcSwap snapshot
ReadWriteboth succeed; reads never see BUSY
WriteRead (after A starts)A succeeds; B succeeds (reads bypass the guard)
WriteWrite through the same handleA succeeds; B returns HANDLE_BUSY_CONCURRENT_WRITER
WriteWrite through a different handleboth succeed independently
WriteWrite through the same handle, but serialised (A done before B starts)both succeed sequentially

The lock-free read path is the load-bearing property — analytical scans, live-view subscribers, snapshot replays, and EXPLAIN/PROFILE introspection all run concurrently with writers and with each other.

Why a typed-error guard, not a queue#

A naive single-handle implementation would queue concurrent writes behind a mutex. Empirically that produces a 10× slowdown under contention — the write_mutex's cache-line and NUMA invalidation spills into the preceding indexing stage as well, making the slowdown larger than the contention window itself. Worse: the slowdown is silent — the caller sees their writes complete, just slowly, and the cause hides inside the engine.

ArcFlow's guard inverts that:

  • Single-thread write path: unchanged. Same latency as before.
  • Multi-thread write path through one handle: second call returns immediately with a typed HANDLE_BUSY_CONCURRENT_WRITER error.
  • The caller decides what to do — retry-after-delay, fan out across handles, or wrap in a Python Lock.

The 10× slowdown is no longer a silent perf trap; it's a typed signal that surfaces the moment the caller hits it. Production code that intended to write concurrently fails fast and visibly; production code that intended to serialise still succeeds.

The typed error#

{
  "class": "Integration",
  "code": "HANDLE_BUSY_CONCURRENT_WRITER",
  "message": "ArcFlow handle is already executing a write from another thread; concurrent writes via a single handle regress 10× under contention. Use multiple handles (arcflow.sharded) for write-side parallelism, or serialise writes via a Python Lock.",
  "recovery_suggestion": "Use db_a / db_b / ... (multiple handles); or wrap writes in threading.Lock()"
}

The error payload names the recovery shape — agents and applications consuming the error can branch on the recovery_suggestion field without parsing the message. The code value is stable across minor versions.

The two supported parallel-write patterns#

Pattern 1 — Multiple handles (preferred for high-throughput parallel writes)#

Each thread holds its own ArcFlow handle. The handles share the underlying graph data via MVCC, so writes from any handle are visible to readers everywhere. Writes through different handles never contend on the guard:

from concurrent.futures import ThreadPoolExecutor
import arcflow
 
# Open multiple handles against the same workspace.
db_a = arcflow.ArcFlow("./workspace")
db_b = arcflow.ArcFlow("./workspace")
db_c = arcflow.ArcFlow("./workspace")
 
handles = [db_a, db_b, db_c]
 
def shard(idx: int, batch: list[tuple]) -> int:
    handle = handles[idx % len(handles)]
    handle.bulk_create_nodes_from_arrow("Event", batch)
    return len(batch)
 
with ThreadPoolExecutor(max_workers=len(handles)) as pool:
    totals = list(pool.map(lambda b: shard(b[0], b[1]), enumerate(batches)))

This is what arcflow.sharded is for — see the bulk-ingest patterns when you need multi-thread write parallelism on a single workspace.

Pattern 2 — Single handle plus threading.Lock#

When you have one logical handle and want to keep things simple, wrap writes in a Python Lock. The lock makes the writes serial at the caller; the engine sees one writer at a time; the guard never fires:

import threading
import arcflow
 
db = arcflow.ArcFlow("./workspace")
write_lock = threading.Lock()
 
def safe_write(query: str, params: dict) -> None:
    with write_lock:
        db.execute(query, params=params)

This pattern is the right answer for low-volume write workloads, agent-driven pipelines where the writer is rare, or any setup where simplicity beats throughput.

What about async / asyncio?#

asyncio doesn't change the picture — the constraint is "two writes through one handle in flight at the same time," and that can happen in async code just as easily as in threaded code:

# WRONG — two writes through one handle in flight concurrently:
await asyncio.gather(
    db.execute_async("CREATE (n:A {...})"),
    db.execute_async("CREATE (n:B {...})"),
)  # one of these raises HANDLE_BUSY_CONCURRENT_WRITER

The fix is the same as Pattern 1 — use multiple handles, or sequence the awaits:

# Right — sequenced:
await db.execute_async("CREATE (n:A {...})")
await db.execute_async("CREATE (n:B {...})")
 
# Right — fanned across handles:
await asyncio.gather(
    db_a.execute_async("CREATE (n:A {...})"),
    db_b.execute_async("CREATE (n:B {...})"),
)

What about read parallelism?#

Reads are always lock-free and never trigger the guard. You can fan out read work across as many threads as you want, all from a single handle:

from concurrent.futures import ThreadPoolExecutor
 
db = arcflow.ArcFlow("./workspace")
 
with ThreadPoolExecutor(max_workers=16) as pool:
    results = list(pool.map(db.execute, read_queries))

The underlying ConcurrentStore carries a lock-free ArcSwap<Snapshot> — each reader takes a snapshot, scans, and drops it. Writers swap a new snapshot in atomically; in-flight readers continue scanning the old snapshot to completion. No coordination between readers; no blocking by writers.

Substrate detail#

The guard is a writer_busy: Arc<AtomicBool> on ConcurrentStore. The mutating branch of execute() claims it via compare_exchange before entering the write transaction:

let _writer_claim = if query.is_mutating {
    Some(self.try_claim_writer()?)   // returns HANDLE_BUSY on contention
} else {
    None                              // reads bypass the guard entirely
};

WriterClaim is an RAII guard: Drop resets the AtomicBool even when the write panics. The flag never leaks; concurrent retries after a write failure proceed cleanly.

is_mutating is determined at parse time by the query compiler — MATCH, RETURN, EXPLAIN, CALL (against a read-only procedure) all skip the guard; CREATE, MERGE, SET, DELETE, CALL (against a writing procedure) all engage it. This means concurrent reads + a single concurrent writer never see contention even within a single handle.

See also#

  • Architecture — the in-process SoC monolith the threading model sits inside.
  • Bulk Ingest Patterns — the multi-handle arcflow.sharded pattern for high-throughput parallel writes.
  • Snapshot-Pinned Reads — how MVCC snapshots stay consistent across read parallelism.
  • Error Codes — the canonical HANDLE_BUSY_CONCURRENT_WRITER entry.
← PreviousVirtual Computed ColumnsNext →Typed ID Contract