Troubleshooting Coding and Queries
This guide covers common issues and performance tips when working with custom code and queries in Curiosity Workspace.
Deadlocking
Deadlocks occur when two concurrent operations wait for each other to release a lock, causing both to freeze.
Common Causes
- Inconsistent Locking Order:
- Thread A locks Node 1, then tries to lock Node 2.
- Thread B locks Node 2, then tries to lock Node 1.
- Long-held Locks: Holding a lock while performing long network requests or heavy computation increases the window for conflicts.
Solutions
Consistent Ordering: If you need to lock multiple nodes, always sort them by their UID before acquiring locks.
// CORRECT: Consistent order prevents deadlocks var uidsToLock = new[] { uidA, uidB }.OrderBy(u => u).ToList(); // Lock in this order// WRONG: Locking in arbitrary order await Graph.GetOrAddLockedAsync(uidA); // ... some work ... await Graph.GetOrAddLockedAsync(uidB); // Risk of deadlock if another thread locked B then ALock Late, Commit Early: Acquire locks immediately before you need to modify data, and call
CommitAsyncas soon as the modification is done.Avoid Expensive Operations Inside Locks: Do not perform network requests or heavy calculations while holding a lock.
// WRONG var node = await Graph.GetOrAddLockedAsync(uid); var data = await CallSlowExternalApi(); // Slow! Blocks other threads needing 'node' node.UpdateProperty("data", data); await Graph.CommitAsync(node); // CORRECT var data = await CallSlowExternalApi(); // Do slow work first var node = await Graph.GetOrAddLockedAsync(uid); // Lock briefly node.UpdateProperty("data", data); await Graph.CommitAsync(node);
Uncommitted Nodes
One of the most common errors is forgetting to commit a locked node.
var node = await Graph.GetOrAddLockedAsync(uid);
node.UpdateProperty("status", "processing");
// ... exception thrown here ...
// Commit is never reached! Node remains locked forever (until restart).
await Graph.CommitAsync(node);
Fix: Use try/finally or ensure code paths are safe. If an endpoint finishes and a node is still locked, the system will eventually detect it, abandon the changes, and throw an exception to warn you, but this causes instability.
Performance Optimization
Curiosity's GraphDB is an in-memory graph database, but understanding its performance characteristics is crucial for writing efficient code.
Edge Traversal is Fast
Relationships are stored as direct memory pointers. Operations like Out(), IsRelatedTo(), and SortByConnectivity() are extremely fast because they traverse these pointers without deserializing data.
Property Access can be Slower
Reading a property value (e.g., node.GetString("name")) involves deserializing the node's content blob. While fast, doing this for millions of nodes in a loop is significantly slower than edge traversal.
Implication:
- Avoid
Where(n => n.GetString("status") == "Active")if possible. This forces the engine to load and deserialize every node to check the property. - Instead, use
WhereString("status", "Active"). This uses internal indexes to filter UIDs before loading the content.
Indexing
- Exact Match: Use
StartWhereStringorWhereStringto leverage hash indexes for exact value lookups. - Full Text: Use
StartSearchfor fuzzy matching or relevance-based search. This uses the inverted index.
Large Result Sets
If a query returns thousands of nodes:
- Don't use
ToList()immediately if you don't need all data in memory. - Do use
AsEnumerableAsync()to stream results. - Do use
Take()andSkip()for pagination.
Common Exceptions
| Exception | Cause | Fix |
|---|---|---|
RequestedAlreadyLockedNodeException |
You tried to lock a node that is already locked by another thread (or your own thread in a different context). | Use retry logic or review locking strategy. |
DeadlockHadToBePreventedException |
The system detected a potential deadlock or unreleased lock. | Check for missing CommitAsync calls. |
NodeNotFoundException |
You tried to perform an operation on a UID that doesn't exist. | Check if the node exists using Graph.HasNode(uid) before proceeding. |