@dstanesc/o-o-o-o-o-o-o
Version:
O-O-O-O-O-O-O is a collection of content addressed persistent data structures
353 lines • 14.1 kB
JavaScript
import { fastCloneEdge, fastCloneProp, fastCloneVertex } from './clone';
import { deltaFactory } from './delta';
import { Graph } from './graph';
import { blockIndexFactory } from './block-index';
import { graphStore } from './graph-store';
import { OFFSET_INCREMENTS } from './serde';
import { versionStoreFactory } from './version-store';
var MergePolicyEnum;
(function (MergePolicyEnum) {
MergePolicyEnum[MergePolicyEnum["LastWriterWins"] = 0] = "LastWriterWins";
MergePolicyEnum[MergePolicyEnum["MultiValueRegistry"] = 1] = "MultiValueRegistry";
})(MergePolicyEnum || (MergePolicyEnum = {}));
// account on the fact that only crdt operations will be supported on the graph
const mergePolicyLastWriterWins = () => {
const mergeVertex = async (tx, baseline, first, second) => {
const target = fastCloneVertex(first);
if (second.nextEdge !== baseline.nextEdge)
target.nextEdge = second.nextEdge;
if (second.nextProp !== baseline.nextProp)
target.nextProp = second.nextProp;
return target;
};
const mergeEdge = async (tx, baseline, first, second) => {
const target = fastCloneEdge(first);
if (second.sourceNext !== baseline.sourceNext)
target.sourceNext = second.sourceNext;
if (second.nextProp !== baseline.nextProp)
target.nextProp = second.nextProp;
return target;
};
const mergeProp = async (tx, baseline, first, second) => {
const target = fastCloneProp(first);
if (second.nextProp !== baseline.nextProp)
target.nextProp = second.nextProp;
return target;
};
return { mergeVertex, mergeEdge, mergeProp };
};
const mergePolicyMultiValueRegistry = () => {
const mergeVertex = async (tx, baseline, first, second) => {
const target = fastCloneVertex(first);
if (second.nextEdge !== baseline.nextEdge)
await tx.linkVertexEdge(target, second.nextEdge);
if (second.nextProp !== baseline.nextProp)
await tx.linkVertexProp(target, second.nextProp);
return target;
};
const mergeEdge = async (tx, baseline, first, second) => {
const target = fastCloneEdge(first);
if (second.sourceNext !== undefined &&
second.sourceNext !== baseline.sourceNext)
await tx.linkEdge(target, second.sourceNext);
if (second.nextProp !== baseline.nextProp)
await tx.linkEdgeProp(target, second.nextProp);
return target;
};
const mergeProp = async (tx, baseline, first, second) => {
const target = fastCloneProp(first);
if (second.nextProp !== baseline.nextProp)
await tx.linkProp(target, second.nextProp);
return target;
};
return { mergeVertex, mergeEdge, mergeProp };
};
const merge = async ({ baseRoot, baseStore, currentRoot, currentStore, otherRoot, otherStore, }, policy, chunk, linkCodec, valueCodec) => {
const mergeOrder = ({ current, other }) => {
return currentRoot.bytes[0] < otherRoot.bytes[0]
? { first: current, second: other }
: { first: other, second: current };
};
let mergePolicy;
switch (policy) {
case MergePolicyEnum.LastWriterWins:
mergePolicy = mergePolicyLastWriterWins();
break;
case MergePolicyEnum.MultiValueRegistry:
mergePolicy = mergePolicyMultiValueRegistry();
break;
default:
throw new Error(`Unknown merge policy ${policy}`);
}
const { baselineDelta } = deltaFactory({ linkCodec, valueCodec });
const { buildRootIndex: buildBaseIndex } = blockIndexFactory({
linkCodec,
blockStore: baseStore,
});
const { buildRootIndex: buildCurrentIndex } = blockIndexFactory({
linkCodec,
blockStore: currentStore,
});
const { buildRootIndex: buildOtherIndex } = blockIndexFactory({
linkCodec,
blockStore: otherStore,
});
const { root: baseRootRef, index: baseIndex } = await buildBaseIndex(baseRoot);
const { root: currentRootRef, index: currentIndex } = await buildCurrentIndex(currentRoot);
const { root: otherRootRef, index: otherIndex } = await buildOtherIndex(otherRoot);
const { first: { root: firstRoot, index: firstIndex, blockStore: firstStore }, second, } = mergeOrder({
current: {
root: currentRootRef,
index: currentIndex,
blockStore: currentStore,
},
other: {
root: otherRootRef,
index: otherIndex,
blockStore: otherStore,
},
});
const story = await versionStoreFactory({
chunk,
linkCodec,
valueCodec,
blockStore: firstStore,
});
const storeRoot = await story.rootSet({
root: firstRoot,
index: firstIndex,
});
const store = graphStore({
chunk,
linkCodec,
valueCodec,
blockStore: firstStore,
});
const graph = new Graph(story, store);
const tx = graph.tx();
await tx.start();
await policyMerge(tx, mergeOrder, mergePolicy, baselineDelta, {
baseRoot,
baseIndex,
baseStore,
currentRoot,
currentIndex,
currentStore,
otherRoot,
otherIndex,
otherStore,
});
return await tx.commit({});
};
const policyMerge = async (tx, mergeOrder, mergePolicy, baselineDelta, { baseRoot, baseIndex, baseStore, currentRoot, currentIndex, currentStore, otherRoot, otherIndex, otherStore, }) => {
const { vertexIndex, edgeIndex, propIndex } = baseIndex;
const { byteArraySize: vertexRebaseThreshold } = vertexIndex.indexStruct;
const { byteArraySize: edgeRebaseThreshold } = edgeIndex.indexStruct;
const { byteArraySize: propRebaseThreshold } = propIndex.indexStruct;
const currentDelta = await baselineDelta({
baseRoot,
baseIndex,
baseStore,
currentRoot,
currentIndex,
currentStore,
});
const otherDelta = await baselineDelta({
baseRoot,
baseIndex,
baseStore,
currentRoot: otherRoot,
currentIndex: otherIndex,
currentStore: otherStore,
});
const { first: verticesFirst, second: verticesSecond } = mergeOrder({
current: currentDelta.vertices.added,
other: otherDelta.vertices.added,
});
const { rebased: verticesRebased, rebaseOffsetDelta: vertexRebaseOffsetDelta, } = await rebaseAdditionOffsets({ first: verticesFirst, second: verticesSecond }, fastCloneVertex, OFFSET_INCREMENTS.VERTEX_INCREMENT);
const { first: edgesFirst, second: edgesSecond } = mergeOrder({
current: currentDelta.edges.added,
other: otherDelta.edges.added,
});
const { rebased: edgesRebased, rebaseOffsetDelta: edgeRebaseOffsetDelta } = await rebaseAdditionOffsets({ first: edgesFirst, second: edgesSecond }, fastCloneEdge, OFFSET_INCREMENTS.EDGE_INCREMENT);
const { first: propsFirst, second: propsSecond } = mergeOrder({
current: currentDelta.props.added,
other: otherDelta.props.added,
});
const { rebased: propsRebased, rebaseOffsetDelta: propRebaseOffsetDelta } = await rebaseAdditionOffsets({ first: propsFirst, second: propsSecond }, fastCloneProp, OFFSET_INCREMENTS.PROP_INCREMENT);
await rebaseVertexEdgeRef({
verticesRebased,
edgeRebaseThreshold,
edgeRebaseOffsetDelta,
});
await rebaseVertexPropRef({
verticesRebased,
propRebaseThreshold,
propRebaseOffsetDelta,
});
await rebaseEdgeVertexRef({
edgesRebased,
vertexRebaseThreshold,
vertexRebaseOffsetDelta,
});
await rebaseEdgeEdgeRef({
edgesRebased,
edgeRebaseThreshold,
edgeRebaseOffsetDelta,
});
await rebaseEdgePropRef({
edgesRebased,
propRebaseThreshold,
propRebaseOffsetDelta,
});
await rebasePropPropRef({
propsRebased,
propRebaseThreshold,
propRebaseOffsetDelta,
});
const { first: verticesFirstUpdated, second: verticesSecondUpdated } = mergeOrder({
current: currentDelta.vertices.updated,
other: otherDelta.vertices.updated,
});
await rebaseVertexEdgeRef({
verticesRebased: verticesSecondUpdated,
edgeRebaseThreshold,
edgeRebaseOffsetDelta,
});
await rebaseVertexPropRef({
verticesRebased: verticesSecondUpdated,
propRebaseThreshold,
propRebaseOffsetDelta,
});
const { first: edgesFirstUpdated, second: edgesSecondUpdated } = mergeOrder({ current: currentDelta.edges.updated, other: otherDelta.edges.updated });
await rebaseEdgeVertexRef({
edgesRebased: edgesSecondUpdated,
vertexRebaseThreshold,
vertexRebaseOffsetDelta,
});
await rebaseEdgeEdgeRef({
edgesRebased: edgesSecondUpdated,
edgeRebaseThreshold,
edgeRebaseOffsetDelta,
});
await rebaseEdgePropRef({
edgesRebased: edgesSecondUpdated,
propRebaseThreshold,
propRebaseOffsetDelta,
});
const { first: propsFirstUpdated, second: propsSecondUpdated } = mergeOrder({ current: currentDelta.props.updated, other: otherDelta.props.updated });
await rebasePropPropRef({
propsRebased: propsSecondUpdated,
propRebaseThreshold,
propRebaseOffsetDelta,
});
for (const [vertexOffset, vertex] of verticesRebased)
tx.vertices.added.set(vertexOffset, vertex);
for (const [edgeOffset, edge] of edgesRebased)
tx.edges.added.set(edgeOffset, edge);
for (const [propOffset, prop] of propsRebased)
tx.props.added.set(propOffset, prop);
const verticesUpdated = await applyUpdates(tx, {
first: verticesFirstUpdated,
second: verticesSecondUpdated,
baseline: currentDelta.vertices.updateBaseline,
}, mergePolicy.mergeVertex);
const edgesUpdated = await applyUpdates(tx, {
first: edgesFirstUpdated,
second: edgesSecondUpdated,
baseline: currentDelta.edges.updateBaseline,
}, mergePolicy.mergeEdge);
const propsUpdated = await applyUpdates(tx, {
first: propsFirstUpdated,
second: propsSecondUpdated,
baseline: currentDelta.props.updateBaseline,
}, mergePolicy.mergeProp);
for (const [vertexOffset, vertex] of verticesUpdated)
tx.vertices.updated.set(vertexOffset, vertex);
for (const [edgeOffset, edge] of edgesUpdated)
tx.edges.updated.set(edgeOffset, edge);
for (const [propOffset, prop] of propsUpdated)
tx.props.updated.set(propOffset, prop);
};
const rebaseAdditionOffsets = async ({ first, second }, fastClone, recordSize) => {
let rebased = new Map();
let rebaseOffsetDelta = 0;
if (first.size > 0 && second.size > 0) {
const firstAddedArray = Array.from(first.keys());
const firstOffset = firstAddedArray[0];
const lastOffset = firstAddedArray[first.size - 1];
rebaseOffsetDelta = lastOffset - firstOffset + recordSize;
for (const secondPart of second.values()) {
const secondPartClone = fastClone(secondPart);
secondPartClone.offset += rebaseOffsetDelta;
rebased.set(secondPartClone.offset, secondPartClone);
}
}
return { rebased, rebaseOffsetDelta };
};
const rebaseVertexEdgeRef = async ({ verticesRebased, edgeRebaseThreshold, edgeRebaseOffsetDelta, }) => {
for (const vertex of verticesRebased.values()) {
if (vertex.nextEdge >= edgeRebaseThreshold)
vertex.nextEdge += edgeRebaseOffsetDelta;
}
};
const rebaseVertexPropRef = async ({ verticesRebased, propRebaseThreshold, propRebaseOffsetDelta, }) => {
for (const vertex of verticesRebased.values()) {
if (vertex.nextProp >= propRebaseThreshold)
vertex.nextProp += propRebaseOffsetDelta;
}
};
const rebaseEdgeVertexRef = async ({ edgesRebased, vertexRebaseThreshold, vertexRebaseOffsetDelta, }) => {
for (const edge of edgesRebased.values()) {
if (edge.source >= vertexRebaseThreshold)
edge.source += vertexRebaseOffsetDelta;
if (edge.target >= vertexRebaseThreshold)
edge.target += vertexRebaseOffsetDelta;
}
};
const rebaseEdgeEdgeRef = async ({ edgesRebased, edgeRebaseThreshold, edgeRebaseOffsetDelta, }) => {
for (const edge of edgesRebased.values()) {
if (edge.sourcePrev >= edgeRebaseThreshold)
edge.sourcePrev += edgeRebaseOffsetDelta;
if (edge.sourceNext >= edgeRebaseThreshold)
edge.sourceNext += edgeRebaseOffsetDelta;
}
};
const rebaseEdgePropRef = async ({ edgesRebased, propRebaseThreshold, propRebaseOffsetDelta, }) => {
for (const edge of edgesRebased.values()) {
if (edge.nextProp >= propRebaseThreshold)
edge.nextProp += propRebaseOffsetDelta;
}
};
const rebasePropPropRef = async ({ propsRebased, propRebaseThreshold, propRebaseOffsetDelta, }) => {
for (const prop of propsRebased.values()) {
if (prop.nextProp >= propRebaseThreshold)
prop.nextProp += propRebaseOffsetDelta;
}
};
const applyUpdates = async (tx, { first, second, baseline, }, merge) => {
let updated;
if (first.size > 0 && second.size > 0) {
updated = new Map(first);
for (const secondPart of second.values()) {
if (first.has(secondPart.offset)) {
const baselineVertex = baseline.get(secondPart.offset);
const firstVertex = first.get(secondPart.offset);
const merged = await merge(tx, baselineVertex, firstVertex, secondPart);
updated.set(secondPart.offset, merged);
}
else {
updated.set(secondPart.offset, secondPart);
}
}
}
else if (first.size > 0) {
updated = new Map(first);
}
else {
updated = new Map(second);
}
return updated;
};
export { merge, MergePolicyEnum };
//# sourceMappingURL=merge.js.map