Skip to content

Commit

Permalink
fix merge issues, all fns vertex creation, and linearOps breaking aft…
Browse files Browse the repository at this point in the history
…er biome
  • Loading branch information
d-roak committed Aug 28, 2024
1 parent f1b399e commit 5139d1c
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 18 deletions.
20 changes: 15 additions & 5 deletions packages/crdt/src/cros/AddWinsSet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,29 @@ import {
} from "@topology-foundation/object";

export class AddWinsSet<T> implements CRO<T> {
operations: string[] = ["add", "remove"];
state: Map<T, number>;

constructor() {
this.state = new Map<T, number>();
}

add(value: T): void {
private _add(value: T): void {
if ((this.state.get(value) ?? 0) % 2 === 0) this.state.set(value, 1);
}

remove(value: T): void {
add(value: T): void {
this._add(value);
}

_remove(value: T): void {
if ((this.state.get(value) ?? 0) % 2 === 1) this.state.set(value, 0);
}

remove(value: T): void {
this._remove(value);
}

contains(value: T): boolean {
return (this.state.get(value) ?? 0) % 2 === 1;
}
Expand All @@ -30,7 +39,7 @@ export class AddWinsSet<T> implements CRO<T> {
.map(([value, _]) => value);
}

// in this case is an array of length 2
// in this case is an array of length 2 and there are only two possible operations
resolveConflicts(vertices: Vertex<T>[]): ActionType {
if (
vertices[0].operation.type !== vertices[1].operation.type &&
Expand All @@ -45,13 +54,14 @@ export class AddWinsSet<T> implements CRO<T> {

// merged at HG level and called as a callback
mergeCallback(operations: Operation<T>[]): void {
this.state = new Map<T, number>();
for (const op of operations) {
switch (op.type) {
case "add":
if (op.value !== null) this.add(op.value);
if (op.value !== null) this._add(op.value);
break;
case "remove":
if (op.value !== null) this.remove(op.value);
if (op.value !== null) this._remove(op.value);
break;
default:
break;
Expand Down
12 changes: 6 additions & 6 deletions packages/object/src/hashgraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ export class HashGraph<T> {
// Time complexity: O(d), where d is the number of dependencies
// Space complexity: O(d)
addVertex(operation: Operation<T>, deps: Hash[], nodeId: string): Hash {
const hash = computeHash(nodeId, operation, deps);
if (this.vertices.has(hash)) {
return hash; // Vertex already exists
}

// Temporary fix: don't add the vertex if the dependencies are not present in the local HG.
if (
!deps.every((dep) => this.forwardEdges.has(dep) || this.vertices.has(dep))
Expand All @@ -96,11 +101,6 @@ export class HashGraph<T> {
return "";
}

const hash = computeHash(nodeId, operation, deps);
if (this.vertices.has(hash)) {
return hash; // Vertex already exists
}

const vertex: Vertex<T> = {
hash,
nodeId,
Expand Down Expand Up @@ -193,7 +193,7 @@ export class HashGraph<T> {

if (shouldIncrementI) {
const op = this.vertices.get(order[i])?.operation;
if (op) result.push();
if (op?.value !== null) result.push(op);
i++;
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/object/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ function proxyCROHandler<T>(obj: TopologyObject<T>): ProxyHandler<object> {
if (typeof target[propKey as keyof object] === "function") {
return new Proxy(target[propKey as keyof object], {
apply(applyTarget, thisArg, args) {
callFn(obj, propKey as string, args);
if ((thisArg.operations as string[]).includes(propKey as string))
callFn(obj, propKey as string, args);
return Reflect.apply(applyTarget, thisArg, args);
},
});
Expand Down
107 changes: 101 additions & 6 deletions packages/object/tests/hashgraph.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { beforeEach, describe, expect, test } from "vitest";
import { AddWinsSet } from "../../crdt/src/cros/AddWinsSet/index.js";
import {
type TopologyObject,
callFn,
newTopologyObject,
} from "../src/index.js";
import { type TopologyObject, merge, newTopologyObject } from "../src/index.js";

describe("HashGraph for AddWinSet tests", () => {
let obj1: TopologyObject<number>;
Expand All @@ -21,8 +17,107 @@ describe("HashGraph for AddWinSet tests", () => {
/*
V1:NOP <- V2:ADD(1) <- V2:REMOVE(1)
*/
// root: 02465e287e3d086f12c6edd856953ca5ad0f01d6707bf8e410b4a601314c1ca5

const cro1 = obj1.cro as AddWinsSet<number>;
// df50d0a583cb46e651b1a05dcef7f7e0bf9851cd5d9f63ca425c9c9103f94417
cro1.add(1);
// 73205ffce4c18099fb5d8ce1d90620a4604c9169bf17a9b6b787265412d514cb
cro1.remove(1);
expect(cro1.contains(1)).toBe(false);

const linearOps = obj1.hashGraph.linearizeOperations();
console.log(linearOps);
expect(linearOps).toEqual([
{ type: "add", value: 1 },
{ type: "remove", value: 1 },
]);
});

test("Test: Add Two Concurrent Vertices With Same Value", () => {
/*
_ V2:REMOVE(1)
V1:ADD(1) /
\ _ V3:ADD(1)
*/
// root: 02465e287e3d086f12c6edd856953ca5ad0f01d6707bf8e410b4a601314c1ca5

const cro1 = obj1.cro as AddWinsSet<number>;
const cro2 = obj2.cro as AddWinsSet<number>;

cro1.add(1);
console.log(obj1);
merge(obj2, obj1.hashGraph.getAllVertices());

cro1.remove(1);
cro2.add(1);
merge(obj1, obj2.hashGraph.getAllVertices());
merge(obj2, obj1.hashGraph.getAllVertices());

console.log(obj1.cro);
expect(cro1.contains(1)).toBe(true);
expect(obj1.hashGraph.vertices).toEqual(obj2.hashGraph.vertices);

const linearOps = obj1.hashGraph.linearizeOperations();
expect(linearOps).toEqual([
{ type: "add", value: 1 },
{ type: "add", value: 1 },
]);
});

test("Test: Add Two Concurrent Vertices With Different Values", () => {
/*
_ V2:REMOVE(1)
V1:ADD(1) /
\ _ V3:ADD(2)
*/
// expect(linearOps).toEqual([op0, op1, op2]);
});

test("Test: Tricky Case", () => {
/*
___ V2:REMOVE(1) <- V4:ADD(10)
V1:ADD(1) /
\ ___ V3:ADD(1) <- V5:REMOVE(5)
*/
});

test("Test: Yuta Papa's Case", () => {
/*
___ V2:REMOVE(1) <- V4:ADD(2)
V1:ADD(1) /
\ ___ V3:REMOVE(2) <- V5:ADD(1)
*/
});

test("Test: Mega Complex Case", () => {
/*
__ V6:ADD(3)
/
___ V2:ADD(1) <-- V3:RM(2) <-- V7:RM(1) <-- V8:RM(3)
/ ______________/
V1:ADD(1)/ /
\ /
\ ___ V4:RM(2) <-- V5:ADD(2) <-- V9:RM(1)
*/
});

test("Test: Mega Complex Case 1", () => {
/*
__ V6:ADD(3)
/
___ V2:ADD(1) <-- V3:RM(2) <-- V7:RM(1) <-- V8:RM(3)
/ ^
V1:ADD(1)/ \
\ \
\ ___ V4:RM(2) <-------------------- V5:ADD(2) <-- V9:RM(1)
*/
});

test("Test: Joao's latest brain teaser", () => {
/*
__ V2:Add(2) <------------\
V1:Add(1) / \ - V5:RM(2)
\__ V3:RM(2) <- V4:RM(2) <--/
*/
});
});

0 comments on commit 5139d1c

Please sign in to comment.