#
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();
warning Important
If you do not call `CommitBatchAsync()`, none of the batched operations will be executed.
#
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
- Always Commit: Ensure every
GetOrAddLockedAsyncis paired with aCommitAsync(orAbandonChangesin error cases). - Order of Locks: If locking multiple nodes, try to always lock them in a consistent order (e.g., by UID) to reduce deadlock risk.
- Short Critical Sections: Keep the time between acquiring a lock and committing it as short as possible. Do expensive computations before locking if possible.