# Graph

The Mosaik.GraphDB.Safe.Graph class (often referred to simply as Graph) is the primary way to interact with the Curiosity Graph Database in custom code. It wraps the low-level GraphDB instance to provide thread safety and, most importantly, mechanisms to prevent deadlocks when modifying the graph.

# Purpose

Curiosity's graph database is highly concurrent. When multiple operations attempt to modify the same nodes simultaneously, there is a risk of deadlocks (e.g., Process A locks Node 1 and waits for Node 2, while Process B locks Node 2 and waits for Node 1).

Graph enforces patterns that help avoid these situations or detect them early.

# Key Methods

# Locking Nodes for Modification

To modify a node (update properties, add edges), you must first acquire a lock on it.

// Get or create a node and lock it
var lockedNode = await Graph.GetOrAddLockedAsync("Person", "john.doe@example.com");

// Try to get an existing node and lock it (returns null if not found)
var lockedNode = await Graph.TryGetLockedAsync(someUID);

# Committing Changes

Once you have modified a LockedNode, you must commit the changes to release the lock and persist the data.

lockedNode.UpdateProperty("age", 30);
await Graph.CommitAsync(lockedNode);

If you do not commit (e.g., due to an exception), the lock might remain held, potentially causing issues. Graph includes safety nets for this (see below).

# Batch Operations

When you need to perform many small updates to a single node (perhaps from different parts of your logic) without committing immediately each time, you can use batching.

The Batch method queues an action to be performed on a node. The operations are automatically grouped by UID, ensuring that when the batch is committed, all operations for a specific node are applied in a single lock-update-commit cycle.

// Queue an operation (does not execute immediately)
Graph.Batch(someUID, (lockedNode) => {
    lockedNode.UpdateProperty("status", "active");
});

Graph.Batch(someUID, (lockedNode) => {
    lockedNode.AddEdge("processed_by", userUID);
});

// MANDATORY: Commit all batched operations
// This will lock nodes one by one, apply all queued actions, and commit.
await Graph.CommitBatchAsync();

# Read Operations

Graph exposes all standard reading methods.

  • Get(uid) / TryGet(uid, out node): Retrieve read-only nodes.
  • GetWithEdges(uid): Retrieve node with edge data.
  • Query(): Start a query.

# Best Practices

  1. Always Commit: Ensure every GetOrAddLockedAsync is paired with a CommitAsync (or AbandonChanges in error cases).
  2. Order of Locks: If locking multiple nodes, try to always lock them in a consistent order (e.g., by UID) to reduce deadlock risk.
  3. Short Critical Sections: Keep the time between acquiring a lock and committing it as short as possible. Do expensive computations before locking if possible.