public override void OnPut(string key, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation) { if (key.StartsWith("Raven/", StringComparison.OrdinalIgnoreCase) && // we don't deal with system documents key.StartsWith("Raven/Hilo/", StringComparison.OrdinalIgnoreCase) == false) // except for hilos { return; } using (Database.DisableAllTriggersForCurrentThread()) { var documentMetadata = GetDocumentMetadata(key); if (documentMetadata != null) { RavenJArray history = new RavenJArray(ReplicationData.GetHistory(documentMetadata)); metadata[Constants.RavenReplicationHistory] = history; if (documentMetadata.ContainsKey(Constants.RavenReplicationVersion) && documentMetadata.ContainsKey(Constants.RavenReplicationSource)) { history.Add(new RavenJObject { { Constants.RavenReplicationVersion, documentMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, documentMetadata[Constants.RavenReplicationSource] } }); } while (history.Length > Constants.ChangeHistoryLength) { history.RemoveAt(0); } } metadata[Constants.RavenReplicationVersion] = RavenJToken.FromObject(HiLo.NextId()); metadata[Constants.RavenReplicationSource] = RavenJToken.FromObject(Database.TransactionalStorage.Id); } }
public override void OnPut(string key, Stream data, RavenJObject metadata) { if (key.StartsWith("Raven/")) // we don't deal with system attachment return; using (Database.DisableAllTriggersForCurrentThread()) { var attachmentMetadata = GetAttachmentMetadata(key); if (attachmentMetadata != null) { RavenJArray history = new RavenJArray(metadata.Value<RavenJArray>(Constants.RavenReplicationHistory)); metadata[Constants.RavenReplicationHistory] = history; if (attachmentMetadata.ContainsKey(Constants.RavenReplicationVersion) && attachmentMetadata.ContainsKey(Constants.RavenReplicationSource)) { history.Add(new RavenJObject { {Constants.RavenReplicationVersion, attachmentMetadata[Constants.RavenReplicationVersion]}, {Constants.RavenReplicationSource, attachmentMetadata[Constants.RavenReplicationSource]} }); } if (history.Length > Constants.ChangeHistoryLength) { history.RemoveAt(0); } } metadata[Constants.RavenReplicationVersion] = RavenJToken.FromObject(HiLo.NextId()); metadata[Constants.RavenReplicationSource] = RavenJToken.FromObject(Database.TransactionalStorage.Id); } }
public override void OnPut(string key, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation) { if (key.StartsWith("Raven/", StringComparison.OrdinalIgnoreCase) && // we don't deal with system documents key.StartsWith("Raven/Hilo/", StringComparison.OrdinalIgnoreCase) == false) // except for hilos return; using (Database.DisableAllTriggersForCurrentThread()) { var documentMetadata = GetDocumentMetadata(key); if (documentMetadata != null) { RavenJArray history = new RavenJArray(ReplicationData.GetHistory(documentMetadata)); metadata[Constants.RavenReplicationHistory] = history; if (documentMetadata.ContainsKey(Constants.RavenReplicationVersion) && documentMetadata.ContainsKey(Constants.RavenReplicationSource)) { history.Add(new RavenJObject { {Constants.RavenReplicationVersion, documentMetadata[Constants.RavenReplicationVersion]}, {Constants.RavenReplicationSource, documentMetadata[Constants.RavenReplicationSource]} }); } while (history.Length > Constants.ChangeHistoryLength) { history.RemoveAt(0); } } metadata[Constants.RavenReplicationVersion] = RavenJToken.FromObject(HiLo.NextId()); metadata[Constants.RavenReplicationSource] = RavenJToken.FromObject(Database.TransactionalStorage.Id); } }
public override void OnPut(string key, Stream data, RavenJObject metadata) { if (key.StartsWith("Raven/")) // we don't deal with system attachment { return; } using (Database.DisableAllTriggersForCurrentThread()) { var attachmentMetadata = GetAttachmentMetadata(key); if (attachmentMetadata != null) { RavenJArray history = new RavenJArray(ReplicationData.GetHistory(metadata)); metadata[Constants.RavenReplicationHistory] = history; if (attachmentMetadata.ContainsKey(Constants.RavenReplicationVersion) && attachmentMetadata.ContainsKey(Constants.RavenReplicationSource)) { history.Add(new RavenJObject { { Constants.RavenReplicationVersion, attachmentMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, attachmentMetadata[Constants.RavenReplicationSource] } }); } while (history.Length > Constants.ChangeHistoryLength) { history.RemoveAt(0); } } metadata[Constants.RavenReplicationVersion] = RavenJToken.FromObject(HiLo.NextId()); metadata[Constants.RavenReplicationSource] = RavenJToken.FromObject(Database.TransactionalStorage.Id); } }
private void RemoveValue(PatchRequest patchCmd, string propName, RavenJToken token) { EnsurePreviousValueMatchCurrentValue(patchCmd, token); if (token == null) { token = new RavenJArray(); document[propName] = token; } var array = GetArray(token, propName); array = new RavenJArray(array); document[propName] = array; var position = patchCmd.Position; var value = patchCmd.Value; if (position == null && (value == null || value.Type == JTokenType.Null)) { throw new InvalidOperationException("Cannot remove value from '" + propName + "' because position element does not exists or not an integer and no value was present"); } if (position != null && value != null && value.Type != JTokenType.Null) { throw new InvalidOperationException("Cannot remove value from '" + propName + "' because both a position and a value are set"); } if (position != null && (position.Value < 0 || position.Value >= array.Length)) { throw new IndexOutOfRangeException("Cannot remove value from '" + propName + "' because position element is out of bounds"); } if (value != null && value.Type != JTokenType.Null) { foreach (var ravenJToken in array.Where(x => RavenJToken.DeepEquals(x, value)).ToList()) { array.Remove(ravenJToken); } return; } if (position != null) { array.RemoveAt(position.Value); } }
public override void OnPut(string key, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation) { using (Database.DisableAllTriggersForCurrentThread()) { if (metadata.Remove(Constants.RavenReplicationConflictSkipResolution)) { if (key.IndexOf("/conflicts/", StringComparison.OrdinalIgnoreCase) == -1) { metadata["@Http-Status-Code"] = 409; metadata["@Http-Status-Description"] = "Conflict"; } return; } metadata.Remove(Constants.RavenReplicationConflict);// you can't put conflicts var oldVersion = Database.Documents.Get(key, transactionInformation); if (oldVersion == null) return; if (oldVersion.Metadata[Constants.RavenReplicationConflict] == null) return; var history = new RavenJArray(); metadata[Constants.RavenReplicationHistory] = history; // this is a conflict document, holding document keys in the // values of the properties var conflicts = oldVersion.DataAsJson.Value<RavenJArray>("Conflicts"); if(conflicts == null) return; var list = new List<RavenJArray> { new RavenJArray(ReplicationData.GetHistory(metadata)) // first item to interleave }; foreach (var prop in conflicts) { RavenJObject deletedMetadata; Database.Documents.Delete(prop.Value<string>(), null, transactionInformation, out deletedMetadata); if (deletedMetadata != null) { var conflictHistory = new RavenJArray(ReplicationData.GetHistory(deletedMetadata)); conflictHistory.Add(new RavenJObject { {Constants.RavenReplicationVersion, deletedMetadata[Constants.RavenReplicationVersion]}, {Constants.RavenReplicationSource, deletedMetadata[Constants.RavenReplicationSource]} }); list.Add(conflictHistory); } } int index = 0; bool added = true; while (added) // interleave the history from all conflicts { added = false; foreach (var deletedMetadata in list) { // add the conflict history to the mix, so we make sure that we mark that we resolved the conflict if (index < deletedMetadata.Length) { history.Add(deletedMetadata[index]); added = true; } } index++; } while (history.Length > Constants.ChangeHistoryLength) { history.RemoveAt(0); } } }
public override void OnPut(string key, RavenJObject jsonReplicationDocument, RavenJObject metadata, TransactionInformation transactionInformation) { if (key.StartsWith("Raven/", StringComparison.OrdinalIgnoreCase) && // we don't deal with system documents key.StartsWith("Raven/Hilo/", StringComparison.OrdinalIgnoreCase) == false) // except for hilos { return; } using (Database.DisableAllTriggersForCurrentThread()) { var documentMetadata = GetDocumentMetadata(key); if (documentMetadata == null) { var history = new RavenJArray() { new RavenJObject { { Constants.RavenReplicationVersion, metadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, metadata[Constants.RavenReplicationSource] } } }; } else { var history = new RavenJArray(ReplicationData.GetOrCreateHistory(documentMetadata)); if (documentMetadata.ContainsKey(Constants.RavenReplicationMergedHistory) == false) { if (documentMetadata.ContainsKey(Constants.RavenReplicationVersion) && documentMetadata.ContainsKey(Constants.RavenReplicationSource)) { history.Add(new RavenJObject { { Constants.RavenReplicationVersion, documentMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, documentMetadata[Constants.RavenReplicationSource] } }); } else { history.Add(new RavenJObject { { Constants.RavenReplicationVersion, 0 }, { Constants.RavenReplicationSource, RavenJToken.FromObject(Database.TransactionalStorage.Id) } }); } var sources = new HashSet <RavenJToken>(RavenJTokenEqualityComparer.Default); int pos = history.Length - 1; for (; pos >= 0; pos--) { var source = ((RavenJObject)history[pos])[Constants.RavenReplicationSource]; if (sources.Contains(source)) { history.RemoveAt(pos); continue; } sources.Add(source); } metadata[Constants.RavenReplicationMergedHistory] = true; metadata[Constants.RavenReplicationHistory] = history; } //If we have the flag we must have Constants.RavenReplicationVersion and Constants.RavenReplicationSource too //Here we assume that the replication history is in the form of a "sorted dictionary" so we just need to remove //the entry with the current source id and insert the new version at the end of the history. else { int i = history.Length - 1; for (; i >= 0; i--) { var currentEntry = history[i]; if (RavenJTokenEqualityComparer.Default.Equals(((RavenJObject)currentEntry) [Constants.RavenReplicationSource], documentMetadata[Constants.RavenReplicationSource])) { break; } } if (i != -1) { history.RemoveAt(i); } history.Add(new RavenJObject { { Constants.RavenReplicationVersion, documentMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, documentMetadata[Constants.RavenReplicationSource] } }); metadata[Constants.RavenReplicationHistory] = history; } } metadata[Constants.RavenReplicationVersion] = RavenJToken.FromObject(ReplicationHiLo.NextId(Database)); metadata[Constants.RavenReplicationSource] = RavenJToken.FromObject(Database.TransactionalStorage.Id); } }
private void RemoveValue(PatchRequest patchCmd, string propName, RavenJToken token) { EnsurePreviousValueMatchCurrentValue(patchCmd, token); if (token == null) { token = new RavenJArray(); document[propName] = token; } var array = GetArray(token, propName); array = new RavenJArray(array); document[propName] = array; var position = patchCmd.Position; var value = patchCmd.Value; if (position == null && (value == null || value.Type == JTokenType.Null)) throw new InvalidOperationException("Cannot remove value from '" + propName + "' because position element does not exists or not an integer and no value was present"); if (position != null && value != null && value.Type != JTokenType.Null) throw new InvalidOperationException("Cannot remove value from '" + propName + "' because both a position and a value are set"); if (position != null && (position.Value < 0 || position.Value >= array.Length)) throw new IndexOutOfRangeException("Cannot remove value from '" + propName + "' because position element is out of bounds"); if (value != null && value.Type != JTokenType.Null) { foreach (var ravenJToken in array.Where(x => RavenJToken.DeepEquals(x, value)).ToList()) { array.Remove(ravenJToken); } return; } if (position != null) array.RemoveAt(position.Value); }
private void ReplicateDelete(string id, RavenJObject metadata, TExternal incoming) { TInternal existingItem; Guid existingEtag; var existingMetadata = TryGetExisting(id, out existingItem, out existingEtag); if (existingMetadata == null) { log.Debug("Replicating deleted item {0} from {1} that does not exist, ignoring", id, Src); return; } if (existingMetadata.Value <bool>(Constants.RavenReplicationConflict)) // locally conflicted { log.Debug("Replicating deleted item {0} from {1} that is already conflicted, adding to conflicts.", id, Src); var savedConflictedItemId = SaveConflictedItem(id, metadata, incoming, existingEtag); AppendToCurrentItemConflicts(id, savedConflictedItemId, existingMetadata, existingItem); return; } if (existingMetadata.Value <bool>(Constants.RavenDeleteMarker)) //deleted locally as well { log.Debug("Replicating deleted item {0} from {1} that was deleted locally. Merging histories", id, Src); var existingHistory = new RavenJArray(existingMetadata.Value <RavenJArray>(Constants.RavenReplicationHistory)); var newHistory = new RavenJArray(metadata.Value <RavenJArray>(Constants.RavenReplicationHistory)); foreach (var item in newHistory) { existingHistory.Add(item); } if (metadata.ContainsKey(Constants.RavenReplicationVersion) && metadata.ContainsKey(Constants.RavenReplicationSource)) { existingHistory.Add(new RavenJObject { { Constants.RavenReplicationVersion, metadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, metadata[Constants.RavenReplicationSource] } }); } while (existingHistory.Length > Constants.ChangeHistoryLength) { existingHistory.RemoveAt(0); } MarkAsDeleted(id, metadata); return; } if (Historian.IsDirectChildOfCurrent(metadata, existingMetadata)) // not modified { log.Debug("Delete of existing item {0} was replicated successfully from {1}", id, Src); DeleteItem(id, existingEtag); MarkAsDeleted(id, metadata); return; } Database.TransactionalStorage.ExecuteImmediatelyOrRegisterForSynchronization(() => Database.RaiseNotifications(new DocumentChangeNotification { Id = id, Type = DocumentChangeTypes.ReplicationConflict })); var newConflictId = SaveConflictedItem(id, metadata, incoming, existingEtag); log.Debug("Existing item {0} is in conflict with replicated delete from {1}, marking item as conflicted", id, Src); // we have a new conflict move the existing doc to a conflict and create a conflict document var existingDocumentConflictId = id + "/conflicts/" + HashReplicationIdentifier(existingEtag); CreateConflict(id, newConflictId, existingDocumentConflictId, existingItem, existingMetadata); }
public override void OnPut(string key, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation) { using (Database.DisableAllTriggersForCurrentThread()) { metadata.Remove(Constants.RavenReplicationConflict); // you can't put conflicts var oldVersion = Database.Get(key, transactionInformation); if (oldVersion == null) { return; } if (oldVersion.Metadata[Constants.RavenReplicationConflict] == null) { return; } var history = new RavenJArray(); metadata[Constants.RavenReplicationHistory] = history; var ravenJTokenEqualityComparer = new RavenJTokenEqualityComparer(); // this is a conflict document, holding document keys in the // values of the properties var conflicts = oldVersion.DataAsJson.Value <RavenJArray>("Conflicts"); if (conflicts == null) { return; } var list = new List <RavenJArray> { new RavenJArray(ReplicationData.GetHistory(metadata)) // first item to interleave }; foreach (var prop in conflicts) { RavenJObject deletedMetadata; Database.Delete(prop.Value <string>(), null, transactionInformation, out deletedMetadata); if (deletedMetadata != null) { var conflictHistory = new RavenJArray(ReplicationData.GetHistory(deletedMetadata)); conflictHistory.Add(new RavenJObject { { Constants.RavenReplicationVersion, deletedMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, deletedMetadata[Constants.RavenReplicationSource] } }); list.Add(conflictHistory); } } int index = 0; bool added = true; while (added) // interleave the history from all conflicts { added = false; foreach (var deletedMetadata in list) { // add the conflict history to the mix, so we make sure that we mark that we resolved the conflict if (index < deletedMetadata.Length) { history.Add(deletedMetadata[index]); added = true; } } index++; } while (history.Length > Constants.ChangeHistoryLength) { history.RemoveAt(0); } } }
public override void OnPut(string key, RavenJObject jsonReplicationDocument, RavenJObject metadata) { using (Database.DisableAllTriggersForCurrentThread()) { if (metadata.Remove(Constants.RavenReplicationConflictSkipResolution)) { if (key.IndexOf("/conflicts/", StringComparison.OrdinalIgnoreCase) == -1) { metadata["@Http-Status-Code"] = 409; metadata["@Http-Status-Description"] = "Conflict"; } return; } metadata.Remove(Constants.RavenReplicationConflict);// you can't put conflicts var oldVersion = Database.Documents.Get(key); if (oldVersion == null) { return; } if (oldVersion.Metadata[Constants.RavenReplicationConflict] == null) { return; } var history = new RavenJArray(); metadata[Constants.RavenReplicationHistory] = history; // this is a conflict document, holding document keys in the // values of the properties var conflicts = oldVersion.DataAsJson.Value <RavenJArray>("Conflicts"); if (conflicts == null) { return; } var list = new List <RavenJArray> { new RavenJArray(ReplicationData.GetHistory(metadata)) // first item to interleave }; foreach (var prop in conflicts) { RavenJObject deletedMetadata; Database.Documents.Delete(prop.Value <string>(), null, out deletedMetadata); if (deletedMetadata != null) { var conflictHistory = new RavenJArray(ReplicationData.GetHistory(deletedMetadata)); conflictHistory.Add(new RavenJObject { { Constants.RavenReplicationVersion, deletedMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, deletedMetadata[Constants.RavenReplicationSource] } }); list.Add(conflictHistory); } } int index = 0; bool added = true; while (added) // interleave the history from all conflicts { added = false; foreach (var deletedMetadata in list) { // add the conflict history to the mix, so we make sure that we mark that we resolved the conflict if (index < deletedMetadata.Length) { history.Add(deletedMetadata[index]); added = true; } } index++; } while (history.Length > Constants.ChangeHistoryLength) { history.RemoveAt(0); } } }