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); } }
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) { using (Database.DisableAllTriggersForCurrentThread()) { metadata.Remove(Constants.RavenReplicationConflict); // you can't put conflicts var oldVersion = Database.Attachments.GetStatic(key); if (oldVersion == null) { return; } if (oldVersion.Metadata[Constants.RavenReplicationConflict] == null) { return; } var history = new RavenJArray(ReplicationData.GetHistory(metadata)); metadata[Constants.RavenReplicationHistory] = history; var ravenJTokenEqualityComparer = new RavenJTokenEqualityComparer(); // this is a conflict document, holding document keys in the // values of the properties var conflictData = oldVersion.Data().ToJObject(); var conflicts = conflictData.Value <RavenJArray>("Conflicts"); if (conflicts == null) { return; } foreach (var prop in conflicts) { var id = prop.Value <string>(); Attachment attachment = Database.Attachments.GetStatic(id); if (attachment == null) { continue; } Database.Attachments.DeleteStatic(id, null); // add the conflict history to the mix, so we make sure that we mark that we resolved the conflict var conflictHistory = new RavenJArray(ReplicationData.GetHistory(attachment.Metadata)); conflictHistory.Add(new RavenJObject { { Constants.RavenReplicationVersion, attachment.Metadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, attachment.Metadata[Constants.RavenReplicationSource] } }); foreach (var item in conflictHistory) { if (history.Any(x => ravenJTokenEqualityComparer.Equals(x, item))) { continue; } history.Add(item); } } } }
private void HandleAttachment(Attachment document) { deletedHistory.Value = new RavenJArray(ReplicationData.GetHistory(document.Metadata)) { new RavenJObject { { Constants.RavenReplicationVersion, document.Metadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, document.Metadata[Constants.RavenReplicationSource] } } }; }
private void HandleDocument(JsonDocumentMetadata metadata) { deletedHistory.Value = new RavenJArray(ReplicationData.GetHistory(metadata.Metadata)) { new RavenJObject { { Constants.RavenReplicationVersion, metadata.Metadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, metadata.Metadata[Constants.RavenReplicationSource] } } }; }
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; } RavenJArray history = new RavenJArray(ReplicationData.GetHistory(metadata)); 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; } foreach (var prop in conflicts) { RavenJObject deletedMetadata; Database.Delete(prop.Value <string>(), null, transactionInformation, out deletedMetadata); // add the conflict history to the mix, so we make sure that we mark that we resolved the conflict var conflictHistory = new RavenJArray(ReplicationData.GetHistory(deletedMetadata)); conflictHistory.Add(new RavenJObject { { Constants.RavenReplicationVersion, deletedMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, deletedMetadata[Constants.RavenReplicationSource] } }); foreach (var item in conflictHistory) { if (history.Any(x => ravenJTokenEqualityComparer.Equals(x, item))) { continue; } history.Add(item); } } } }
public void ShouldResolveConflictsToLocal() { ResolveConflictsCore(StraightforwardConflictResolution.ResolveToLocal, (store, id) => { using (var session = store.OpenSession()) { var company = session.Load <Company>(id); Assert.Equal(company.Name, "Local"); var metadata = session.Advanced.GetMetadataFor(company); Assert.Equal(2, ReplicationData.GetHistory(metadata).ToList().Count); return(true); } }); }
private void ReplicateDelete(string id, RavenJObject newMetadata, TExternal incoming) { TInternal existingItem; Etag existingEtag; bool deleted; var existingMetadata = TryGetExisting(id, out existingItem, out existingEtag, out deleted); if (existingMetadata == null) { if (log.IsDebugEnabled) { log.Debug("Replicating deleted item {0} from {1} that does not exist, ignoring.", id, Src); } return; } if (existingMetadata.ContainsKey(Constants.RavenEntityName)) { newMetadata[Constants.RavenEntityName] = existingMetadata[Constants.RavenEntityName]; } RavenJObject currentReplicationEntry = null; if (newMetadata.ContainsKey(Constants.RavenReplicationVersion) && newMetadata.ContainsKey(Constants.RavenReplicationSource)) { currentReplicationEntry = new RavenJObject { { Constants.RavenReplicationVersion, newMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, newMetadata[Constants.RavenReplicationSource] } }; } var existingHistory = ReplicationData.GetHistory(existingMetadata); if (currentReplicationEntry != null && existingHistory.Any(x => RavenJTokenEqualityComparer.Default.Equals( ((RavenJObject)x)[Constants.RavenReplicationSource], currentReplicationEntry[Constants.RavenReplicationSource]) && ((RavenJObject)x)[Constants.RavenReplicationVersion].Value <long>() >= currentReplicationEntry[Constants.RavenReplicationVersion].Value <long>())) { if (log.IsDebugEnabled) { log.Debug("Replicated delete for {0} already exist in item history, ignoring", id); } return; } if (existingMetadata.Value <bool>(Constants.RavenDeleteMarker)) //deleted locally as well { if (log.IsDebugEnabled) { log.Debug("Replicating deleted item {0} from {1} that was deleted locally. Merging histories.", id, Src); } var newHistory = ReplicationData.GetHistory(newMetadata); if (currentReplicationEntry != null) { newHistory.Add(currentReplicationEntry); } //Merge histories ReplicationData.SetHistory(newMetadata, Historian.MergeReplicationHistories(newHistory, existingHistory)); newMetadata[Constants.RavenReplicationMergedHistory] = true; MarkAsDeleted(id, newMetadata); return; } if (Historian.IsDirectChildOfCurrent(newMetadata, existingMetadata)) // not modified { if (log.IsDebugEnabled) { log.Debug("Delete of existing item {0} was replicated successfully from {1}", id, Src); } DeleteItem(id, existingEtag); MarkAsDeleted(id, newMetadata); return; } CreatedConflict createdConflict; if (existingMetadata.Value <bool>(Constants.RavenReplicationConflict)) // locally conflicted { if (log.IsDebugEnabled) { log.Debug("Replicating deleted item {0} from {1} that is already conflicted, adding to conflicts.", id, Src); } var savedConflictedItemId = SaveConflictedItem(id, newMetadata, incoming, existingEtag); createdConflict = AppendToCurrentItemConflicts(id, savedConflictedItemId, existingMetadata, existingItem); } else { RavenJObject resolvedMetadataToSave; TExternal resolvedItemToSave; if (TryResolveConflict(id, newMetadata, incoming, existingItem, out resolvedMetadataToSave, out resolvedItemToSave)) { AddWithoutConflict(id, existingEtag, resolvedMetadataToSave, resolvedItemToSave); return; } var newConflictId = SaveConflictedItem(id, newMetadata, incoming, existingEtag); if (log.IsDebugEnabled) { 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/" + GetReplicationIdentifierForCurrentDatabase(); createdConflict = CreateConflict(id, newConflictId, existingDocumentConflictId, existingItem, existingMetadata); } Database.TransactionalStorage.ExecuteImmediatelyOrRegisterForSynchronization(() => Database.Notifications.RaiseNotifications(new ReplicationConflictNotification() { Id = id, Etag = createdConflict.Etag, Conflicts = createdConflict.ConflictedIds, ItemType = ReplicationConflictTypes.DocumentReplicationConflict, OperationType = ReplicationOperationTypes.Delete })); }
public override void OnPut(string key, RavenJObject jsonReplicationDocument, 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 metadata.Remove(Constants.RavenReplicationConflictDocument); 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; } Dictionary <string, RavenJObject> conflictHistoryAsDictionary = new Dictionary <string, RavenJObject>(); 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] } }); Historian.MergeSingleHistory(conflictHistory, conflictHistoryAsDictionary); } } metadata[Constants.RavenReplicationHistory] = new RavenJArray(conflictHistoryAsDictionary.Values); metadata[Constants.RavenReplicationMergedHistory] = true; } }
private void ReplicateDelete(string id, RavenJObject metadata, TExternal incoming) { TInternal existingItem; Etag existingEtag; bool deleted; var existingMetadata = TryGetExisting(id, out existingItem, out existingEtag, out deleted); if (existingMetadata == null) { log.Debug("Replicating deleted item {0} from {1} that does not exist, ignoring", id, Src); 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(ReplicationData.GetHistory(existingMetadata)); var newHistory = new RavenJArray(ReplicationData.GetHistory(metadata)); 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; } CreatedConflict createdConflict; 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); createdConflict = AppendToCurrentItemConflicts(id, savedConflictedItemId, existingMetadata, existingItem); } else { RavenJObject resolvedMetadataToSave; TExternal resolvedItemToSave; if (TryResolveConflict(id, metadata, incoming, existingItem, out resolvedMetadataToSave, out resolvedItemToSave)) { AddWithoutConflict(id, existingEtag, resolvedMetadataToSave, resolvedItemToSave); return; } 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/" + GetReplicationIdentifierForCurrentDatabase(); createdConflict = CreateConflict(id, newConflictId, existingDocumentConflictId, existingItem, existingMetadata); } Database.TransactionalStorage.ExecuteImmediatelyOrRegisterForSynchronization(() => Database.Notifications.RaiseNotifications(new ReplicationConflictNotification() { Id = id, Etag = createdConflict.Etag, Conflicts = createdConflict.ConflictedIds, ItemType = ReplicationConflictTypes.DocumentReplicationConflict, OperationType = ReplicationOperationTypes.Delete })); }
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, 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) { var history = new RavenJArray(ReplicationData.GetHistory(attachmentMetadata)); if (attachmentMetadata.ContainsKey(Constants.RavenReplicationMergedHistory) == false) { if (attachmentMetadata.ContainsKey(Constants.RavenReplicationVersion) && attachmentMetadata.ContainsKey(Constants.RavenReplicationSource)) { history.Add(new RavenJObject { { Constants.RavenReplicationVersion, attachmentMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, attachmentMetadata[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], attachmentMetadata[Constants.RavenReplicationSource])) { break; } } if (i != -1) { history.RemoveAt(i); } history.Add(new RavenJObject { { Constants.RavenReplicationVersion, attachmentMetadata[Constants.RavenReplicationVersion] }, { Constants.RavenReplicationSource, attachmentMetadata[Constants.RavenReplicationSource] } }); metadata[Constants.RavenReplicationHistory] = history; } } metadata[Constants.RavenReplicationVersion] = RavenJToken.FromObject(ReplicationHiLo.NextId(Database)); metadata[Constants.RavenReplicationSource] = RavenJToken.FromObject(Database.TransactionalStorage.Id); } }
public bool TryResolveConflict(string id, RavenJObject metadata, RavenJObject document, JsonDocument existingDoc, Func <string, JsonDocument> getDocument, out RavenJObject metadataToSave, out RavenJObject documentToSave) { var success = TryResolve(id, metadata, document, existingDoc, getDocument, out metadataToSave, out documentToSave); if (success == false) { return(false); } var history = ReplicationData.GetHistory(metadata); var existingHistory = ReplicationData.GetHistory(existingDoc.Metadata); ReplicationData.SetHistory(metadataToSave, Historian.MergeReplicationHistories(history, existingHistory)); metadataToSave[Constants.RavenReplicationMergedHistory] = true; // here we make sure that we keep a deleted document deleted, rather than "reviving" it. var ravenDeleteMarker = existingDoc.Metadata.Value <string>("Raven-Delete-Marker"); bool markerValue; if (ravenDeleteMarker != null && bool.TryParse(ravenDeleteMarker, out markerValue) && markerValue) { existingDoc.Metadata["Raven-Remove-Document-Marker"] = true; } var docToSave = documentToSave; var metaToSave = metadataToSave; if (log.IsDebugEnabled) { log.Debug(() => { var builder = new StringBuilder(); builder.AppendLine(string.Format("Conflict on document with key '{0}' resolved by '{1}'.", id, GetType().Name)); builder.AppendLine(string.Format("Existing document:")); if (existingDoc != null && existingDoc.DataAsJson != null) { builder.AppendLine(existingDoc.DataAsJson.ToString()); } builder.AppendLine(string.Format("Existing metadata:")); if (existingDoc != null && existingDoc.Metadata != null) { builder.AppendLine(existingDoc.Metadata.ToString()); } builder.AppendLine(string.Format("Incoming document:")); if (document != null) { builder.AppendLine(document.ToString()); } builder.AppendLine(string.Format("Incoming metadata:")); if (metadata != null) { builder.AppendLine(metadata.ToString()); } builder.AppendLine(string.Format("Output document:")); if (docToSave != null) { builder.AppendLine(docToSave.ToString()); } builder.AppendLine(string.Format("Output metadata:")); if (metaToSave != null) { builder.AppendLine(metaToSave.ToString()); } return(builder.ToString()); }); } return(true); }
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); } } }