Skip to content

Commit

Permalink
refactor(object): linearize pair semantic (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
sfroment authored and hoangquocvietuet committed Feb 18, 2025
1 parent 23abdfd commit 8ffdcec
Show file tree
Hide file tree
Showing 2 changed files with 263 additions and 28 deletions.
51 changes: 23 additions & 28 deletions packages/object/src/linearize/pairSemantics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,62 +6,57 @@ export function linearizePairSemantics(
origin: Hash,
subgraph: ObjectSet<string>
): Operation[] {
const order: Hash[] = hashGraph.topologicalSort(true, origin, subgraph);
const dropped = new Array(order.length).fill(false);
const result = [];
// alway remove the first operation
let i = 1;
const order = hashGraph.topologicalSort(true, origin, subgraph);
const dropped = new Array<boolean>(order.length).fill(false);
const result: Operation[] = [];

// Skip root operation
for (let i = 1; i < order.length; i++) {
if (dropped[i]) continue;

while (i < order.length) {
if (dropped[i]) {
i++;
continue;
}
let anchor = order[i];
let j = i + 1;
let modified = false;

while (j < order.length) {
if (hashGraph.areCausallyRelatedUsingBitsets(anchor, order[j]) || dropped[j]) {
j++;
// Compare with all later operations
for (let j = i + 1; j < order.length; j++) {
if (dropped[j] || hashGraph.areCausallyRelatedUsingBitsets(anchor, order[j])) {
continue;
}
const moving = order[j];

const v1 = hashGraph.vertices.get(anchor);
const v2 = hashGraph.vertices.get(moving);
let action: ActionType;
const v2 = hashGraph.vertices.get(order[j]);

if (!v1 || !v2) {
action = ActionType.Nop;
} else {
action = hashGraph.resolveConflicts([v1, v2]).action;
continue;
}

const { action } = hashGraph.resolveConflicts([v1, v2]);

switch (action) {
case ActionType.DropLeft:
dropped[i] = true;
j = order.length;
modified = true;
break;
case ActionType.DropRight:
dropped[j] = true;
j++;
break;
case ActionType.Swap:
hashGraph.swapReachablePredecessors(order[i], order[j]);
[order[i], order[j]] = [order[j], order[i]];
j = i + 1;
anchor = order[i];
break;
case ActionType.Nop:
j++;
break;
}

if (modified) break;
}

if (!dropped[i]) {
const op = hashGraph.vertices.get(order[i])?.operation;
if (op && op.value !== null) result.push(op);
const vertex = hashGraph.vertices.get(order[i]);
if (vertex?.operation && vertex.operation.value !== null) {
result.push(vertex.operation);
}
}
i++;
}

return result;
Expand Down
240 changes: 240 additions & 0 deletions packages/object/tests/linearize/multi.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import { newVertex } from "@ts-drp/object/src/index.js";
import { describe, expect, test } from "vitest";

import { ActionType, HashGraph, type Vertex, SemanticsType } from "../../src/hashgraph/index.js";
import { DrpType } from "../../src/index.js";
import { linearizeMultipleSemantics } from "../../src/linearize/multipleSemantics.js";
import { ObjectSet } from "../../src/utils/objectSet.js";

describe("linearizeMultipleSemantics", () => {
test("should linearize operations in a simple sequence", () => {
const hashGraph = new HashGraph(
"",
(_vertices: Vertex[]) => ({
action: ActionType.Nop,
}),
() => ({
action: ActionType.Nop,
}),
SemanticsType.multiple
);
const origin = HashGraph.rootHash;

// Add vertices to the graph
hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [1],
drpType: DrpType.DRP,
},
hashGraph.getFrontier(),
Date.now(),
new Uint8Array()
)
);

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [2],
drpType: DrpType.DRP,
},
hashGraph.getFrontier(),
Date.now(),
new Uint8Array()
)
);

const subgraph = new ObjectSet<string>();
hashGraph.getAllVertices().forEach((vertex) => subgraph.add(vertex.hash));

const result = linearizeMultipleSemantics(hashGraph, origin, subgraph);
expect(result.map((op) => op.value)).toEqual([[1], [2]]);
});

test("should handle concurrent operations with conflict resolution", () => {
const hashGraph = new HashGraph(
"",
(_vertices: Vertex[]) => ({
action: ActionType.Drop,
vertices: _vertices.filter((_, index) => index !== 0).map((vertex) => vertex.hash),
}),
(_vertices: Vertex[]) => ({
action: ActionType.Drop,
vertices: _vertices.filter((_, index) => index !== 0).map((vertex) => vertex.hash),
}),
SemanticsType.multiple
);
const origin = HashGraph.rootHash;
let frontier = hashGraph.getFrontier();

// Add concurrent vertices
hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [1],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [2],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [3],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);

// Get the frontier
frontier = hashGraph.getFrontier();

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [4],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);
console.log(`frontier: ${frontier}`);
hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [5],
drpType: DrpType.DRP,
},
frontier.filter((_, idx) => idx !== 0),
Date.now(),
new Uint8Array()
)
);

frontier = hashGraph.getFrontier();

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [6],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);

const subgraph = new ObjectSet<string>();
hashGraph.getAllVertices().forEach((vertex) => subgraph.add(vertex.hash));

const result = linearizeMultipleSemantics(hashGraph, origin, subgraph);
expect(result.map((op) => op.value)).toEqual([[1], [4], [6]]);

Check failure on line 169 in packages/object/tests/linearize/multi.test.ts

View workflow job for this annotation

GitHub Actions / tests

packages/object/tests/linearize/multi.test.ts > linearizeMultipleSemantics > should handle concurrent operations with conflict resolution

AssertionError: expected [ [ 2 ], [ 5 ], [ 6 ] ] to deeply equal [ [ 1 ], [ 4 ], [ 6 ] ] - Expected + Received [ [ - 1, + 2, ], [ - 4, + 5, ], [ 6, ], ] ❯ packages/object/tests/linearize/multi.test.ts:169:40
});

test("should handle operations with null values", () => {
const hashGraph = new HashGraph(
"",
() => ({
action: ActionType.Nop,
}),
() => ({
action: ActionType.Nop,
}),
SemanticsType.multiple
);
const origin = HashGraph.rootHash;

// Add vertices to the graph
hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: null,
drpType: DrpType.DRP,
},
hashGraph.getFrontier(),
Date.now(),
new Uint8Array()
)
);

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [2],
drpType: DrpType.DRP,
},
hashGraph.getFrontier(),
Date.now(),
new Uint8Array()
)
);

const subgraph = new ObjectSet<string>();
hashGraph.getAllVertices().forEach((vertex) => subgraph.add(vertex.hash));

const result = linearizeMultipleSemantics(hashGraph, origin, subgraph);
expect(result.map((op) => op.value)).toEqual([[2]]);
});

test("should handle empty subgraph", () => {
const hashGraph = new HashGraph(
"",
() => ({
action: ActionType.Nop,
}),
() => ({
action: ActionType.Nop,
}),
SemanticsType.multiple
);
const origin = HashGraph.rootHash;

const subgraph = new ObjectSet<string>();
subgraph.add(origin);

const result = linearizeMultipleSemantics(hashGraph, origin, subgraph);
expect(result).toEqual([]);
});
});

0 comments on commit 8ffdcec

Please sign in to comment.