private void GetCollectedNodesRecursive(Guid nodeId, AppendableChangeSet <Guid, object, EdgeData> changeSet, IParentMapProvider <Guid, object, EdgeData> mutableParentMap, IParentMapProvider <Guid, object, EdgeData> immutableParentMap, Hashtable collectedNodes, Hashtable visitedNodes) { if (visitedNodes.ContainsKey(nodeId)) { return; } visitedNodes.Add(nodeId, null); if (changeSet.ReusedNodes.ContainsKey(nodeId)) { // Reused nodes and their children are not to be collected return; } if (HasReusedParent(nodeId, changeSet, mutableParentMap, immutableParentMap, collectedNodes, new Hashtable())) { return; } collectedNodes.Add(nodeId, null); var node = dataNodes.GetNode(nodeId, NodeAccess.Read); foreach (var edge in node.Edges.Values) { if (edge.Data.Semantic != EdgeType.OfType && edge.ToNodeId != Constants.NullReferenceNodeId) { GetCollectedNodesRecursive(edge.ToNodeId, changeSet, mutableParentMap, immutableParentMap, collectedNodes, visitedNodes); } } }
private bool HasReusedParent(Guid nodeId, AppendableChangeSet <Guid, object, EdgeData> changeSet, IParentMapProvider <Guid, object, EdgeData> mutableParentMap, IParentMapProvider <Guid, object, EdgeData> immutableParentMap, Hashtable collectedNodes, Hashtable visitedNodes) { if (visitedNodes.ContainsKey(nodeId)) { return((bool)visitedNodes[nodeId]); } visitedNodes.Add(nodeId, false); if (changeSet.ReusedNodes.ContainsKey(nodeId)) { return(true); } if (collectedNodes.ContainsKey(nodeId)) { return(false); } using (var mutableParentEnumerator = mutableParentMap.ParentEdges(changeSet.SourceSnapshotId, nodeId)) { if (mutableParentEnumerator != null) { while (mutableParentEnumerator.MoveNext()) { if (HasReusedParent(mutableParentEnumerator.Current.ToNodeId, changeSet, mutableParentMap, immutableParentMap, collectedNodes, visitedNodes)) { return(true); } } } } using (var immutableParentEnumerator = immutableParentMap.ParentEdges(changeSet.SourceSnapshotId, nodeId)) { if (immutableParentEnumerator != null) { while (immutableParentEnumerator.MoveNext()) { if (HasReusedParent(immutableParentEnumerator.Current.ToNodeId, changeSet, mutableParentMap, immutableParentMap, collectedNodes, visitedNodes)) { return(true); } } } } return(false); }
/// <summary> /// Direct commit of data is appended on the existing nodes /// </summary> /// <param name="changeSet">Changes</param> private void CommitDirect(AppendableChangeSet <Guid, object, EdgeData> changeSet) { // Add all nodes to the provider foreach (Guid nodeId in changeSet.Nodes.EnumerateNodes()) { var node = changeSet.Nodes.GetNode(nodeId, NodeAccess.Read); nodes.SetNode(nodeId, node); } // Store the change set in the change set provider changeSetProvider.SetChangeSet(changeSet); // Calculate collected nodes in the source changeset collectedNodesProvider.StoreChangeset(changeSet, mutableParentProvider, immutableParentProvider); // Update parent information in the parent map provider mutableParentProvider.UpdateParents(changeSet, collectedNodesProvider); // Update parent information in the immutable parent map provider immutableParentProvider.UpdateParents(changeSet, collectedNodesProvider); // Add new snapshot making it visible snapshotsService.AddSnapshot(changeSet.DestinationSnapshotId); #if DEBUG Debug.WriteLine("Created snapshot " + changeSet.DestinationSnapshotId); Utils.LogNodesRecursive(changeSet.DestinationSnapshotId, nodes, changeSet.Nodes, 0, new Hashtable(), typesService); Debug.WriteLine("---------------------------------------------"); Debug.WriteLine("Collected nodes are:"); var enumerator = collectedNodesProvider.GetEdges(changeSet.SourceSnapshotId); if (enumerator != null) { using (enumerator) { while (enumerator.MoveNext()) { Debug.WriteLine(enumerator.Current.ToNodeId); } } } Debug.WriteLine("---------------------------------------------"); #endif }
/// <summary> /// Stores collectable nodes for a change set /// </summary> /// <param name="changeSet">Change set</param> /// <param name="mutableParentMap">Parent map of mutable data</param> /// <param name="immutableParentMap">Parent map of immutable data</param> public void StoreChangeset(AppendableChangeSet <Guid, object, EdgeData> changeSet, IParentMapProvider <Guid, object, EdgeData> mutableParentMap, IParentMapProvider <Guid, object, EdgeData> immutableParentMap) { Guid snapshotId = changeSet.SourceSnapshotId; var snapshotRoot = BPlusTreeOperations.CreateRootNode(NodeType.Collection, snapshotId); nodes.SetNode(snapshotId, snapshotRoot); Hashtable collectedNodes = new Hashtable(); GetCollectedNodesRecursive(changeSet.SourceSnapshotId, changeSet, mutableParentMap, immutableParentMap, collectedNodes, new Hashtable()); foreach (Guid nodeId in collectedNodes.Keys) { BPlusTreeOperations.InsertEdge(nodes, snapshotId, new Edge <Guid, EdgeData>(nodeId, new EdgeData(EdgeType.ListItem, nodeId)), TreeOrder); } }
/// <summary> /// Accepts incoming isolated commit /// </summary> /// <param name="isolatedChangeSet">Isolated changeset</param> /// <returns>Appended changes</returns> public CommitResult <Guid> AcceptCommit(IsolatedChangeSet <Guid, object, EdgeData> isolatedChangeSet) { lock (commitSync) { var latestSnapshot = snapshotsService.GetLatestSnapshotId(); if (latestSnapshot.Equals(isolatedChangeSet.SourceSnapshotId)) { AppendableChangeSet <Guid, object, EdgeData> appendableChangeSet = CreateAppendableChangeSet(isolatedChangeSet.SourceSnapshotId, Guid.NewGuid(), isolatedChangeSet); // Commit is directly on the last snapshot CommitDirect(appendableChangeSet); return(new CommitResult <Guid>(appendableChangeSet.DestinationSnapshotId, appendableChangeSet.Mapping)); } else { // There are snapshots in between #if DEBUG Debug.WriteLine("Source snapshot = " + isolatedChangeSet.SourceSnapshotId); #endif // Calculate changes between last snapshot and source snapshot var intermediateChanges = ChangesBetween(isolatedChangeSet.SourceSnapshotId, latestSnapshot); // Isolate portion of the last snapshot which is relevant for the change set var subTree = IsolateSubTree(latestSnapshot, isolatedChangeSet, intermediateChanges); // Create a brand new changeset which is compatible with last snapshot // Do this by recursive walk through the last snapshot and perform the "compatible" operations done in the incomming changeset var mergedChangeSet = CreateMergedChangeSet(latestSnapshot, subTree, isolatedChangeSet, intermediateChanges); // When complete perform the CommitDirect of the new changeset CommitDirect(mergedChangeSet); // Start with created change mapping var mapping = mergedChangeSet.Mapping; // Merge intermediate changes items to it foreach (var item in intermediateChanges) { AddChangeItem(mapping, item.Key, item.Value); } return(new CommitResult <Guid>(mergedChangeSet.DestinationSnapshotId, mapping)); } } }
/// <summary> /// Stores the change set /// </summary> /// <param name="changeSet">Change set to store</param> public void SetChangeSet(AppendableChangeSet <Guid, object, EdgeData> changeSet) { lock (sync) { var snapshotNode = BPlusTreeOperations.CreateRootNode(NodeType.Dictionary, changeSet.DestinationSnapshotId); nodes.SetNode(changeSet.DestinationSnapshotId, snapshotNode); // Add all changes from changeset to snapshot tree. foreach (var item in changeSet.Mapping) { BPlusTreeOperations.InsertEdge(nodes, changeSet.DestinationSnapshotId, new Edge <Guid, EdgeData>(item.Value, new EdgeData(EdgeType.ListItem, item.Key)), TreeOrder); } // Add snapshot to collection BPlusTreeOperations.InsertEdge(nodes, Constants.SnapshotsNodeId, new Edge <Guid, EdgeData>(changeSet.DestinationSnapshotId, new EdgeData(EdgeType.ListItem, changeSet.DestinationSnapshotId)), TreeOrder); } }
/// <summary> /// Direct commit of data is appended on the existing nodes /// </summary> /// <param name="changeSet">Changes</param> private void CommitDirect(AppendableChangeSet<Guid, object, EdgeData> changeSet) { // Add all nodes to the provider foreach (Guid nodeId in changeSet.Nodes.EnumerateNodes()) { var node = changeSet.Nodes.GetNode(nodeId, NodeAccess.Read); nodes.SetNode(nodeId, node); } // Store the change set in the change set provider changeSetProvider.SetChangeSet(changeSet); // Calculate collected nodes in the source changeset collectedNodesProvider.StoreChangeset(changeSet, mutableParentProvider, immutableParentProvider); // Update parent information in the parent map provider mutableParentProvider.UpdateParents(changeSet, collectedNodesProvider); // Update parent information in the immutable parent map provider immutableParentProvider.UpdateParents(changeSet, collectedNodesProvider); // Add new snapshot making it visible snapshotsService.AddSnapshot(changeSet.DestinationSnapshotId); #if DEBUG Debug.WriteLine("Created snapshot " + changeSet.DestinationSnapshotId); Utils.LogNodesRecursive(changeSet.DestinationSnapshotId, nodes, changeSet.Nodes, 0, new Hashtable(), typesService); Debug.WriteLine("---------------------------------------------"); Debug.WriteLine("Collected nodes are:"); var enumerator = collectedNodesProvider.GetEdges(changeSet.SourceSnapshotId); if (enumerator != null) { using (enumerator) { while (enumerator.MoveNext()) { Debug.WriteLine(enumerator.Current.ToNodeId); } } } Debug.WriteLine("---------------------------------------------"); #endif }
/// <summary> /// Updates parent information based on change set /// </summary> /// <param name="changeSet">Data change description</param> public void UpdateParents(AppendableChangeSet <Guid, object, EdgeData> changeSet, ICollectedNodesProvider <Guid, object, EdgeData> collectedNodesProvider) { lock (dataSync) { if (changeSet.SourceSnapshotId != lastSnapshotId) { // If the last snapshot is not in the memory, clear and return nodes.Clear(); createdNodes = false; return; } using (var enumerator = collectedNodesProvider.GetEdges(changeSet.SourceSnapshotId)) { if (enumerator != null) { while (enumerator.MoveNext()) { if (nodes.Contains(enumerator.Current.ToNodeId)) { DeleteTree(enumerator.Current.ToNodeId); } // Get the old node var node = dataNodeProvider.GetNode(enumerator.Current.ToNodeId, NodeAccess.Read); // For every edge in old node foreach (var edge in node.Edges.Values) { if (EdgeFilter(edge)) { // Find holder in destination snapshot for referenced node if (nodes.Contains(edge.ToNodeId)) { BPlusTreeOperations.RemoveEdge(nodes, edge.ToNodeId, new EdgeData(EdgeType.Contains, enumerator.Current.ToNodeId), ParentsTreeOrder); } } } } } } // Add new node ids to map foreach (Guid nodeId in changeSet.Nodes.EnumerateNodes()) { var holderNode = BPlusTreeOperations.CreateRootNode(NodeType.Collection, nodeId); nodes.SetNode(nodeId, holderNode); } // Add reused nodes if needed foreach (Guid nodeId in changeSet.ReusedNodes.Keys) { if (!nodes.Contains(nodeId)) { var holderNode = BPlusTreeOperations.CreateRootNode(NodeType.Collection, nodeId); nodes.SetNode(nodeId, holderNode); } } // Add new node edges to map foreach (Guid nodeId in changeSet.Nodes.EnumerateNodes()) { var node = changeSet.Nodes.GetNode(nodeId, NodeAccess.Read); // Add this id into all referenced nodes foreach (var edge in node.Edges.Values) { if (EdgeFilter(edge)) { var edgeData = new EdgeData(EdgeType.Contains, nodeId); Edge <Guid, EdgeData> existingEdge = null; if (!BPlusTreeOperations.TryFindEdge(nodes, edge.ToNodeId, edgeData, out existingEdge)) { BPlusTreeOperations.InsertEdge(nodes, edge.ToNodeId, new Edge <Guid, EdgeData>(nodeId, edgeData), ParentsTreeOrder); } } } } // Set last snapshot as the destination ID lastSnapshotId = changeSet.DestinationSnapshotId; } }