/// <summary> /// Create new master fork with the data from an exsiting fork /// This is used to prune unused forks /// The data is trasferred to a new master fork to allow checking the data before deleting the old fork tree. /// </summary> /// <param name="forkId">Fork to prune</param> /// <returns>New master fork id with the data from the old fork</returns> public int PruneForks(int forkId) { var usedKeys = new HashSet <string>(); var deletedKeys = new HashSet <string>(); var valuesToSet = new List <Tuple <string, TDataTypesEnum, byte[], object> >(); var fork = ForkProvider.GetFork(forkId); var currentFork = fork; while (currentFork != null) { var forkPattern = KeyGenerator.GenerateForkValuePattern(AppId, currentFork.Id); var keys = KeyValueStore.Keys($"{forkPattern}*"); foreach (var key in keys) { var originalKey = key.Substring(forkPattern.Length); // If the key was used/deleted in a lower fork, this key is not relevant if (usedKeys.Contains(originalKey) || deletedKeys.Contains(originalKey)) { continue; } if (originalKey.EndsWith(KeyGenerator.NullKeyPostFix)) { deletedKeys.Add(originalKey.Substring(0, originalKey.Length - KeyGenerator.NullKeyPostFix.Length)); } else { var keyDataCollection = KeyValueStore.GetKeyData(key); foreach (var keyData in keyDataCollection) { valuesToSet.Add(Tuple.Create(originalKey, keyData.Item1, keyData.Item2, keyData.Item3)); } usedKeys.Add(originalKey); } } currentFork = currentFork.Parent; } var newForkId = CreateFork("master", $"Pruned from {fork.Id}:{fork.Name}"); var wrapper = GetWrapper(newForkId); foreach (var value in valuesToSet) { wrapper.Set(value.Item2, value.Item1, value.Item3, value.Item4); } return(newForkId); }
/// <summary> /// Merge all the keys from one fork to another fork, a new fork is created from the target with the new data /// </summary> /// <param name="originForkId">Origin fork id</param> /// <param name="targetForkId">Target fork id</param> /// <returns>New fork id, Target fork id is the parent</returns> public int MergeFork(int originForkId, int targetForkId) { var usedKeys = new HashSet <string>(); var valuesToSet = new List <Tuple <string, TDataTypesEnum, byte[], object> >(); var keysToDelete = new Dictionary <string, TDataTypesEnum>(); var currentFork = ForkProvider.GetFork(originForkId); var targetFork = ForkProvider.GetFork(targetForkId); while (!IsCommonParent(currentFork, targetFork)) { if (currentFork == null) { throw new ArgumentException($"No common parents between {originForkId} and {targetForkId}"); } var forkPattern = KeyGenerator.GenerateForkValuePattern(AppId, currentFork.Id); var keys = KeyValueStore.Keys($"{forkPattern}*"); foreach (var key in keys) { var originalKey = key.Substring(forkPattern.Length); // If the key was used/deleted in a lower fork, this key is not relevant if (usedKeys.Contains(originalKey) || keysToDelete.ContainsKey(originalKey)) { continue; } if (originalKey.EndsWith(KeyGenerator.NullKeyPostFix)) { var type = (TDataTypesEnum)BinarySerializerHelper.DeserializeObject(KeyValueStore.Get(KeyValueStore.DefaultType, key, null)); keysToDelete.Add(originalKey.Substring(0, originalKey.Length - KeyGenerator.NullKeyPostFix.Length), type); } else { var keyDataCollection = KeyValueStore.GetKeyData(key); foreach (var keyData in keyDataCollection) { valuesToSet.Add(Tuple.Create(originalKey, keyData.Item1, keyData.Item2, keyData.Item3)); } usedKeys.Add(originalKey); } } currentFork = currentFork.Parent; } var newForkId = CreateFork($"{targetFork.Name} Merge", "", targetFork.Id); var wrapper = GetWrapper(newForkId); foreach (var keyToDelete in keysToDelete) { wrapper.Delete(keyToDelete.Value, keyToDelete.Key); } foreach (var value in valuesToSet) { wrapper.Set(value.Item2, value.Item1, value.Item3, value.Item4); } return(newForkId); }