UNPKG

@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
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