Mutable DAG in Dgraph

I’m wondering if it’s possible to maintain a mutable directed acyclic graph built on Dgraph. Whenever a new edge is to be added I need to make sure that it isn’t going to create a cycle. I’d be comfortable doing this on a single database instance, but with a distributed system I’m worried about race conditions, e.g. if I try and add an edge from node A to node B, and far away at a similar time someone adds an edge from node B to node A.

const dgraph = require('dgraph-js');
const grpc = require('grpc');

// Initialize Dgraph client
const dgraphClientStub = new dgraph.DgraphClientStub('localhost:9080', grpc.credentials.createInsecure());
const dgraphClient = new dgraph.DgraphClient(dgraphClientStub);

class MutableDAG {
    constructor() {
        this.client = dgraphClient;
    }

    async addEdge(from, to) {
        // Check for cycle before adding the edge
        if (await this.detectCycle(from, to)) {
            console.error("Adding this edge would create a cycle.");
            return false;
        }

        try {
            const txn = this.client.newTxn();
            await txn.mutate({
                setJson: {
                    uid: "_:" + from,  // Assuming from and to are string identifiers for nodes
                    edge: {
                        uid: "_:" + to
                    }
                },
                commitNow: true  // Commit immediately to avoid holding transaction open
            });
            console.log(`Edge added from ${from} to ${to}`);
            return true;
        } catch (error) {
            console.error("Failed to add edge:", error);
            return false;
        }
    }

    async detectCycle(start, end) {
        let visited = new Set();
        let stack = [start];
        let hasCycle = false;

        while (stack.length > 0 && !hasCycle) {
            const current = stack.pop();
            if (current === end) {
                hasCycle = true;
                break;
            }

            if (!visited.has(current)) {
                visited.add(current);
                // Query Dgraph for all edges from the current node
                const query = `
                query {
                    q(func: uid("${current}")) {
                        edge {
                            uid
                        }
                    }
                }`;
                
                try {
                    const res = await this.client.newTxn().query(query);
                    const edges = res.getJson().q[0].edge || [];

                    for (const edge of edges) {
                        stack.push(edge.uid.slice(2)); // Remove the '_:' prefix
                    }
                } catch (error) {
                    console.error("Failed to query for edges:", error);
                    return true; // Assume cycle to be safe (fail-safe approach)
                }
            }
        }
        return hasCycle;
    }
}

// Usage:
(async () => {
    const dag = new MutableDAG();
    await dag.addEdge('nodeA', 'nodeB');  // Should work if no cycle detected
    // await dag.addEdge('nodeB', 'nodeA');  // Should not work if it creates a cycle
})();