public static dynamic IfEntityIs(this object self, string collection) { var document = self as DynamicBlittableJson; if (document == null) { return(DynamicNullObject.Null); } var collectionName = CollectionName.GetCollectionName(document); if (string.Equals(collection, collectionName, StringComparison.OrdinalIgnoreCase) == false) { return(DynamicNullObject.Null); } return(document); }
public static IEnumerable <dynamic> WhereEntityIs(this IEnumerable <dynamic> self, params string[] collections) { foreach (var document in self) { var dynamicBlittableJson = document as DynamicBlittableJson; if (dynamicBlittableJson == null) { continue; } var collectionName = CollectionName.GetCollectionName(dynamicBlittableJson); if (collections.Contains(collectionName, StringComparer.OrdinalIgnoreCase) == false) { continue; } yield return(document); } }
private bool TryResolveUsingDefaultResolver( DocumentsOperationContext context, string id, string collection, string incomingChangeVector, BlittableJsonReaderObject doc) { if (_conflictResolver.ConflictSolver?.DatabaseResolverId == null) { return(false); } var conflicts = new List <DocumentConflict>(_database.DocumentsStorage.ConflictsStorage.GetConflictsFor(context, id)); var localDocumentTuple = _database.DocumentsStorage.GetDocumentOrTombstone(context, id, false); var localDoc = DocumentConflict.From(context, localDocumentTuple.Document) ?? DocumentConflict.From(localDocumentTuple.Tombstone); if (localDoc != null) { conflicts.Add(localDoc); } conflicts.Add(new DocumentConflict { ChangeVector = incomingChangeVector, Collection = context.GetLazyStringForFieldWithCaching( collection ?? CollectionName.GetCollectionName(id, doc)), Doc = doc, LowerId = context.GetLazyString(id) }); if (_conflictResolver.TryResolveUsingDefaultResolverInternal( context, _conflictResolver.ConflictSolver.DatabaseResolverId, conflicts, out var resolved)) { _conflictResolver.PutResolvedDocument(context, resolved); return(true); } return(false); }
public static string GetDocumentCollection(string docId, DocumentDatabase documentDatabase, DocumentsOperationContext context, bool fromEtl, out Document doc) { try { doc = documentDatabase.DocumentsStorage.Get(context, docId, throwOnConflict: true); if (doc == null) { if (fromEtl) { return(null); } ThrowMissingDocument(docId); return(null); // never hit } if (doc.Flags.HasFlag(DocumentFlags.Artificial)) { ThrowArtificialDocument(doc); } return(CollectionName.GetCollectionName(doc.Data)); } catch (DocumentConflictException) { doc = null; if (fromEtl) { return(null); } // this is fine, we explicitly support // setting the flag if we are in conflicted state is // done by the conflict resolver // avoid loading same document again, we validate write using the metadata instance doc = new Document(); return(documentDatabase.DocumentsStorage.ConflictsStorage.GetCollection(context, docId)); } }
public void HandleConflictForDocument( DocumentsOperationContext documentsContext, string id, string collection, long lastModifiedTicks, BlittableJsonReaderObject doc, string changeVector, string conflictedChangeVector, DocumentFlags flags) { if (id.StartsWith("Raven/Hilo/", StringComparison.OrdinalIgnoreCase)) { HandleHiloConflict(documentsContext, id, doc, changeVector); return; } if (TryResolveIdenticalDocument( documentsContext, id, doc, lastModifiedTicks, changeVector)) { return; } var conflictedDoc = new DocumentConflict { Doc = doc, Collection = documentsContext.GetLazyStringForFieldWithCaching( collection ?? CollectionName.GetCollectionName(doc) ), LastModified = new DateTime(lastModifiedTicks), LowerId = documentsContext.GetLazyString(id), Id = documentsContext.GetLazyString(id), ChangeVector = changeVector }; if (TryResolveConflictByScript( documentsContext, conflictedDoc)) { return; } if (_conflictResolver.ConflictSolver?.ResolveToLatest == true) { if (conflictedChangeVector == null) //precaution { throw new InvalidOperationException( "Detected conflict on replication, but could not figure out conflicted vector. This is not supposed to happen and is likely a bug."); } var conflicts = new List <DocumentConflict> { conflictedDoc.Clone() }; conflicts.AddRange(documentsContext.DocumentDatabase.DocumentsStorage.ConflictsStorage.GetConflictsFor( documentsContext, id)); var localDocumentTuple = documentsContext.DocumentDatabase.DocumentsStorage.GetDocumentOrTombstone(documentsContext, id, false); var local = DocumentConflict.From(documentsContext, localDocumentTuple.Document) ?? DocumentConflict.From(localDocumentTuple.Tombstone); if (local != null) { conflicts.Add(local); } var resolved = _conflictResolver.ResolveToLatest(documentsContext, conflicts); _conflictResolver.PutResolvedDocument(documentsContext, resolved, conflictedDoc); return; } _database.DocumentsStorage.ConflictsStorage.AddConflict(documentsContext, id, lastModifiedTicks, doc, changeVector, collection, flags); }
private bool TryResolveConflictByScript( DocumentsOperationContext documentsContext, string id, string incomingChangeVector, BlittableJsonReaderObject doc) { var collection = CollectionName.GetCollectionName(id, doc); var hasScript = _conflictResolver.ScriptConflictResolversCache.TryGetValue(collection, out ScriptResolver scriptResolver); if (!hasScript || scriptResolver == null) { if (_log.IsInfoEnabled) { _log.Info($"Script not found to resolve the {collection} collection"); } return(false); } var conflictedDocs = new List <DocumentConflict>(documentsContext.DocumentDatabase.DocumentsStorage.ConflictsStorage.GetConflictsFor(documentsContext, id)); if (conflictedDocs.Count == 0) { var relevantLocalDoc = documentsContext.DocumentDatabase.DocumentsStorage .GetDocumentOrTombstone( documentsContext, id); if (relevantLocalDoc.Document != null) { conflictedDocs.Add(DocumentConflict.From(documentsContext, relevantLocalDoc.Document)); } else if (relevantLocalDoc.Tombstone != null) { conflictedDocs.Add(DocumentConflict.From(relevantLocalDoc.Tombstone)); } } if (conflictedDocs.Count == 0) { InvalidConflictWhenThereIsNone(id); } conflictedDocs.Add(new DocumentConflict { LowerId = conflictedDocs[0].LowerId, Id = conflictedDocs[0].Id, Collection = documentsContext.GetLazyStringForFieldWithCaching(collection), ChangeVector = incomingChangeVector, Doc = doc }); if (_conflictResolver.TryResolveConflictByScriptInternal( documentsContext, scriptResolver, conflictedDocs, documentsContext.GetLazyString(collection), out var resolved)) { _conflictResolver.PutResolvedDocument(documentsContext, resolved); return(true); } return(false); }
public unsafe void HandleConflictForDocument( DocumentsOperationContext documentsContext, string id, string collection, long lastModifiedTicks, BlittableJsonReaderObject doc, string changeVector, DocumentFlags flags) { if (id.StartsWith(HiLoHandler.RavenHiloIdPrefix, StringComparison.OrdinalIgnoreCase)) { HandleHiloConflict(documentsContext, id, doc, changeVector); return; } if (TryResolveIdenticalDocument( documentsContext, id, doc, lastModifiedTicks, changeVector)) { return; } var lazyId = documentsContext.GetLazyString(id); using (DocumentIdWorker.GetLower(documentsContext.Allocator, lazyId, out var loweredKey)) { var conflictedDoc = new DocumentConflict { Doc = doc, Collection = documentsContext.GetLazyStringForFieldWithCaching( collection ?? CollectionName.GetCollectionName(doc) ), LastModified = new DateTime(lastModifiedTicks), LowerId = documentsContext.AllocateStringValue(null, loweredKey.Content.Ptr, loweredKey.Content.Length), Id = lazyId, ChangeVector = changeVector, Flags = flags }; if (TryResolveConflictByScript( documentsContext, conflictedDoc)) { return; } if (_conflictResolver.ConflictSolver?.ResolveToLatest ?? true) { var conflicts = new List <DocumentConflict> { conflictedDoc.Clone() }; conflicts.AddRange(_database.DocumentsStorage.ConflictsStorage.GetConflictsFor(documentsContext, id)); var localDocumentTuple = _database.DocumentsStorage.GetDocumentOrTombstone(documentsContext, id, false); var local = DocumentConflict.From(documentsContext, localDocumentTuple.Document) ?? DocumentConflict.From(localDocumentTuple.Tombstone); if (local != null) { conflicts.Add(local); } var resolved = _conflictResolver.ResolveToLatest(conflicts); _conflictResolver.PutResolvedDocument(documentsContext, resolved, resolvedToLatest: true, conflictedDoc); return; } _database.DocumentsStorage.ConflictsStorage.AddConflict(documentsContext, id, lastModifiedTicks, doc, changeVector, collection, flags); } }
protected override int ExecuteCmd(DocumentsOperationContext context) { if (_database.ServerStore.Server.Configuration.Core.FeaturesAvailability == FeaturesAvailability.Stable) FeaturesAvailabilityException.Throw("Counters"); var ops = 0; var countersToAdd = new SortedSet<string>(StringComparer.OrdinalIgnoreCase); var countersToRemove = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (var kvp in _dictionary) { Document doc = null; var docId = kvp.Key; string docCollection = null; ops += kvp.Value.Count; foreach (var operation in kvp.Value) { switch (operation.Type) { case CounterOperationType.Increment: case CounterOperationType.Delete: case CounterOperationType.Put: LoadDocument(); break; } switch (operation.Type) { case CounterOperationType.Increment: LastChangeVector = _database.DocumentsStorage.CountersStorage.IncrementCounter(context, docId, docCollection, operation.CounterName, operation.Delta, out var exists); GetCounterValue(context, _database, docId, operation.CounterName, _replyWithAllNodesValues, CountersDetail); if (exists == false) { // if exists it is already on the document's metadata countersToAdd.Add(operation.CounterName); countersToRemove.Remove(operation.CounterName); } break; case CounterOperationType.Delete: if (_fromEtl && doc == null) break; LastChangeVector = _database.DocumentsStorage.CountersStorage.DeleteCounter(context, docId, docCollection, operation.CounterName); countersToAdd.Remove(operation.CounterName); countersToRemove.Add(operation.CounterName); break; case CounterOperationType.Put: if (_fromEtl && doc == null) break; // intentionally not setting LastChangeVector, we never use it for // etl / import and it isn't meaningful in those scenarios if (_fromEtl) { _database.DocumentsStorage.CountersStorage.PutCounter(context, docId, docCollection, operation.CounterName, operation.Delta); } else { _database.DocumentsStorage.CountersStorage.PutCounter(context, docId, docCollection, operation.CounterName, operation.ChangeVector, operation.Delta); } countersToAdd.Add(operation.CounterName); countersToRemove.Remove(operation.CounterName); break; case CounterOperationType.None: break; case CounterOperationType.Get: GetCounterValue(context, _database, docId, operation.CounterName, _replyWithAllNodesValues, CountersDetail); break; default: ThrowInvalidBatchOperationType(operation); break; } } if (doc?.Data != null) { var nonPersistentFlags = NonPersistentDocumentFlags.ByCountersUpdate; if (_fromSmuggler) nonPersistentFlags |= NonPersistentDocumentFlags.FromSmuggler; _database.DocumentsStorage.CountersStorage.UpdateDocumentCounters(context, doc, docId, countersToAdd, countersToRemove, nonPersistentFlags); doc.Data.Dispose(); // we cloned the data, so we can dispose it. } countersToAdd.Clear(); countersToRemove.Clear(); void LoadDocument() { if (doc != null) return; try { doc = _database.DocumentsStorage.Get(context, docId, throwOnConflict: true); if (doc == null) { if (_fromEtl) return; ThrowMissingDocument(docId); return; // never hit } if (doc.Flags.HasFlag(DocumentFlags.Artificial)) ThrowArtificialDocument(doc); docCollection = CollectionName.GetCollectionName(doc.Data); } catch (DocumentConflictException) { if (_fromEtl) return; // this is fine, we explicitly support // setting the flag if we are in conflicted state is // done by the conflict resolver // avoid loading same document again, we validate write using the metadata instance doc = new Document(); docCollection = _database.DocumentsStorage.ConflictsStorage.GetCollection(context, docId); } } } return ops; }
protected override int ExecuteCmd(DocumentsOperationContext context) { var countersToAdd = new SortedSet <string>(StringComparer.OrdinalIgnoreCase); var countersToRemove = new HashSet <string>(StringComparer.OrdinalIgnoreCase); Document doc = null; string docId = null; string docCollection = null; foreach (var operation in _list) { switch (operation.Type) { case CounterOperationType.Increment: case CounterOperationType.Delete: case CounterOperationType.Put: LoadDocument(operation); break; } docId = operation.DocumentId; switch (operation.Type) { case CounterOperationType.Increment: LastChangeVector = _database.DocumentsStorage.CountersStorage.IncrementCounter(context, docId, docCollection, operation.CounterName, operation.Delta, out var exists); GetCounterValue(context, _database, docId, operation.CounterName, _replyWithAllNodesValues, CountersDetail); if (exists == false) { // if exists it is already on the document's metadata countersToAdd.Add(operation.CounterName); countersToRemove.Remove(operation.CounterName); } break; case CounterOperationType.Delete: if (_fromEtl && doc == null) { break; } LastChangeVector = _database.DocumentsStorage.CountersStorage.DeleteCounter(context, docId, docCollection, operation.CounterName); countersToAdd.Remove(operation.CounterName); countersToRemove.Add(operation.CounterName); break; case CounterOperationType.Put: if (_fromEtl == false || doc == null) { break; } _database.DocumentsStorage.CountersStorage.PutCounter(context, docId, docCollection, operation.CounterName, operation.Delta); countersToAdd.Add(operation.CounterName); countersToRemove.Remove(operation.CounterName); break; case CounterOperationType.None: break; case CounterOperationType.Get: GetCounterValue(context, _database, docId, operation.CounterName, _replyWithAllNodesValues, CountersDetail); break; default: ThrowInvalidBatchOperationType(operation); break; } } if (doc?.Data != null) { var changeVector = _database .DocumentsStorage .CountersStorage .UpdateDocumentCounters(context, doc, docId, countersToAdd, countersToRemove, NonPersistentDocumentFlags.ByCountersUpdate); if (changeVector != null) { LastDocumentChangeVector = LastChangeVector = changeVector; } doc.Data.Dispose(); // we cloned the data, so we can dispose it. } countersToAdd.Clear(); countersToRemove.Clear(); void LoadDocument(CounterOperation counterOperation) { if (string.IsNullOrEmpty(counterOperation.DocumentId)) { throw new ArgumentException("Document ID can't be null"); } if (docId == counterOperation.DocumentId && doc != null) { return; } ApplyChangesForPreviousDocument(context, doc, docId, countersToAdd, countersToRemove); docId = counterOperation.DocumentId; try { doc = _database.DocumentsStorage.Get(context, docId, throwOnConflict: true); if (doc == null) { if (_fromEtl) { return; } ThrowMissingDocument(docId); return; // never hit } if (doc.Flags.HasFlag(DocumentFlags.Artificial)) { ThrowArtificialDocument(doc); } docCollection = CollectionName.GetCollectionName(doc.Data); } catch (DocumentConflictException) { if (_fromEtl) { return; } // this is fine, we explicitly support // setting the flag if we are in conflicted state is // done by the conflict resolver // avoid loading same document again, we validate write using the metadata instance doc = new Document(); docCollection = _database.DocumentsStorage.ConflictsStorage.GetCollection(context, docId); } } ApplyChangesForPreviousDocument(context, doc, docId, countersToAdd, countersToRemove); return(_list.Count); }
public override int Execute(DocumentsOperationContext context) { if (_database.ServerStore.Server.Configuration.Core.FeaturesAvailability == FeaturesAvailability.Stable) { FeaturesAvailabilityException.Throw("Counters"); } foreach (var kvp in _dictionary) { Document doc = null; BlittableJsonReaderObject metadata = null; var docId = kvp.Key; string docCollection = null; foreach (var operation in kvp.Value) { switch (operation.Type) { case CounterOperationType.Increment: case CounterOperationType.Delete: case CounterOperationType.Put: LoadDocument(); if (doc != null) { docCollection = CollectionName.GetCollectionName(doc.Data); } break; } switch (operation.Type) { case CounterOperationType.Increment: LastChangeVector = _database.DocumentsStorage.CountersStorage.IncrementCounter(context, docId, docCollection, operation.CounterName, operation.Delta); GetCounterValue(context, _database, docId, operation.CounterName, _replyWithAllNodesValues, CountersDetail); break; case CounterOperationType.Delete: if (_fromEtl && doc == null) { break; } LastChangeVector = _database.DocumentsStorage.CountersStorage.DeleteCounter(context, docId, docCollection, operation.CounterName); break; case CounterOperationType.Put: if (_fromEtl && doc == null) { break; } // intentionally not setting LastChangeVector, we never use it for // etl / import and it isn't meaningful in those scenarios if (_fromEtl) { _database.DocumentsStorage.CountersStorage.PutCounter(context, docId, docCollection, operation.CounterName, operation.Delta); } else { _database.DocumentsStorage.CountersStorage.PutCounter(context, docId, docCollection, operation.CounterName, operation.ChangeVector, operation.Delta); } break; case CounterOperationType.None: break; case CounterOperationType.Get: GetCounterValue(context, _database, docId, operation.CounterName, _replyWithAllNodesValues, CountersDetail); break; default: ThrowInvalidBatchOperationType(operation); break; } } if (metadata != null) { _database.DocumentsStorage.CountersStorage.UpdateDocumentCounters(context, doc.Data, docId, metadata, kvp.Value); } void LoadDocument() { if (doc != null) { return; } try { doc = _database.DocumentsStorage.Get(context, docId, throwOnConflict: true); if (doc == null) { if (_fromEtl) { return; } ThrowMissingDocument(docId); return; // never hit } if (doc.TryGetMetadata(out metadata) == false) { ThrowInvalidDocumentWithNoMetadata(doc); } if (doc.Flags.HasFlag(DocumentFlags.Artificial)) { ThrowArtificialDocument(doc); } } catch (DocumentConflictException) { if (_fromEtl) { return; } // this is fine, we explicitly support // setting the flag if we are in conflicted state is // done by the conflict resolver // avoid loading same document again, we validate write using the metadata instance doc = new Document(); } } } return(CountersDetail.Counters.Count); }
protected override int ExecuteCmd(DocumentsOperationContext context) { if (_database.ServerStore.Server.Configuration.Core.FeaturesAvailability == FeaturesAvailability.Stable) { FeaturesAvailabilityException.Throw("Counters"); } var countersToAdd = new SortedSet <string>(StringComparer.OrdinalIgnoreCase); var countersToRemove = new HashSet <string>(StringComparer.OrdinalIgnoreCase); string docId = null; Document doc = null; string docCollection = null; foreach (var operation in _list) { switch (operation.Type) { case CounterOperationType.Increment: case CounterOperationType.Delete: case CounterOperationType.Put: try { LoadDocument(operation); } catch (DocumentDoesNotExistException) { if (_fromSmuggler) { ErrorCount++; continue; } throw; } break; } docId = operation.DocumentId; switch (operation.Type) { case CounterOperationType.Increment: LastChangeVector = _database.DocumentsStorage.CountersStorage.IncrementCounter(context, docId, docCollection, operation.CounterName, operation.Delta, out var exists); GetCounterValue(context, _database, docId, operation.CounterName, _replyWithAllNodesValues, CountersDetail); if (exists == false) { // if exists it is already on the document's metadata countersToAdd.Add(operation.CounterName); countersToRemove.Remove(operation.CounterName); } break; case CounterOperationType.Delete: if (_fromEtl && doc == null) { break; } LastChangeVector = _database.DocumentsStorage.CountersStorage.DeleteCounter(context, docId, docCollection, operation.CounterName); countersToAdd.Remove(operation.CounterName); countersToRemove.Add(operation.CounterName); break; case CounterOperationType.Put: if (_fromEtl && doc == null) { break; } // intentionally not setting LastChangeVector, we never use it for // etl / import and it isn't meaningful in those scenarios if (_fromEtl) { _database.DocumentsStorage.CountersStorage.PutCounter(context, docId, docCollection, operation.CounterName, operation.Delta); } else { _database.DocumentsStorage.CountersStorage.PutCounter(context, docId, docCollection, operation.CounterName, operation.ChangeVector, operation.Delta); } countersToAdd.Add(operation.CounterName); countersToRemove.Remove(operation.CounterName); break; case CounterOperationType.None: break; case CounterOperationType.Get: GetCounterValue(context, _database, docId, operation.CounterName, _replyWithAllNodesValues, CountersDetail); break; default: ThrowInvalidBatchOperationType(operation); break; } void LoadDocument(CounterOperation counterOperation) { if (string.IsNullOrEmpty(counterOperation.DocumentId)) { throw new ArgumentException("Document ID can't be null"); } if (docId == counterOperation.DocumentId && doc != null) { return; } ApplyChangesForPreviousDocument(context, doc, docId, countersToAdd, countersToRemove); docId = counterOperation.DocumentId; try { doc = _database.DocumentsStorage.Get(context, docId, throwOnConflict: true); if (doc == null) { if (_fromEtl) { return; } ThrowMissingDocument(docId); return; // never hit } if (doc.Flags.HasFlag(DocumentFlags.Artificial)) { ThrowArtificialDocument(doc); } docCollection = CollectionName.GetCollectionName(doc.Data); } catch (DocumentConflictException) { if (_fromEtl) { return; } // this is fine, we explicitly support // setting the flag if we are in conflicted state is // done by the conflict resolver // avoid loading same document again, we validate write using the metadata instance doc = new Document(); docCollection = _database.DocumentsStorage.ConflictsStorage.GetCollection(context, docId); } } } ApplyChangesForPreviousDocument(context, doc, docId, countersToAdd, countersToRemove); return(_list.Count); }
private CollectionName PutCounters(UpdateStep step, DocumentsOperationContext context, HashSet <string> dbIds, Dictionary <string, List <CounterDetail> > allCountersBatch, string docId) { string collection = null; using (DocumentIdWorker.GetSliceFromId(context, docId, out Slice lowerId)) { var docsTable = new Table(DocsSchema, step.ReadTx); if (docsTable.ReadByKey(lowerId, out var tvr)) { using (var doc = new BlittableJsonReaderObject(tvr.Read((int)DocumentsTable.Data, out int size), size, context)) { collection = CollectionName.GetCollectionName(doc); } } else { // document does not exist return(null); } } var collectionName = new CollectionName(collection); using (DocumentIdWorker.GetSliceFromId(context, docId, out Slice documentKeyPrefix, separator: SpecialChars.RecordSeparator)) { var maxNumberOfCountersPerGroup = Math.Max(32, 2048 / (dbIds.Count * 32 + 1)); // rough estimate var orderedKeys = allCountersBatch.OrderBy(x => x.Key).ToList(); var listOfDbIds = dbIds.ToList(); for (int i = 0; i < orderedKeys.Count / maxNumberOfCountersPerGroup + (orderedKeys.Count % maxNumberOfCountersPerGroup == 0 ? 0 : 1); i++) { var currentBatch = allCountersBatch.Skip(maxNumberOfCountersPerGroup * i).Take(maxNumberOfCountersPerGroup); using (var data = WriteNewCountersDocument(context, listOfDbIds, currentBatch)) { var etag = step.DocumentsStorage.GenerateNextEtag(); var changeVector = ChangeVectorUtils.NewChangeVector( step.DocumentsStorage.DocumentDatabase.ServerStore.NodeTag, etag, _dbId); var table = step.DocumentsStorage.CountersStorage.GetCountersTable(step.WriteTx, collectionName); data.TryGet(CountersStorage.Values, out BlittableJsonReaderObject values); BlittableJsonReaderObject.PropertyDetails prop = default; values.GetPropertyByIndex(0, ref prop); using (table.Allocate(out TableValueBuilder tvb)) { using (Slice.From(context.Allocator, changeVector, out var cv)) using (DocumentIdWorker.GetStringPreserveCase(context, collectionName.Name, out Slice collectionSlice)) using (context.Allocator.Allocate(documentKeyPrefix.Size + prop.Name.Size, out var counterKeyBuffer)) using (Slice.From(context.Allocator, prop.Name, out var nameSlice)) using (CreateCounterKeySlice(context, counterKeyBuffer, documentKeyPrefix, nameSlice, out var counterKeySlice)) { if (i == 0) { tvb.Add(documentKeyPrefix); } else { tvb.Add(counterKeySlice); } tvb.Add(Bits.SwapBytes(etag)); tvb.Add(cv); tvb.Add(data.BasePointer, data.Size); tvb.Add(collectionSlice); tvb.Add(context.GetTransactionMarker()); table.Set(tvb); } } } } } return(collectionName); }
private void DeleteCounter(UpdateStep step, LazyStringValue tombstoneKey, DocumentsOperationContext context) { var(docId, counterName) = ExtractDocIdAndNameFromCounterTombstone(context, tombstoneKey); using (docId) using (counterName) using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, docId, out Slice lowerId, out _)) { string collection = null; var docsTable = new Table(DocsSchema, step.ReadTx); if (docsTable.ReadByKey(lowerId, out var tvr)) { using (var doc = new BlittableJsonReaderObject(tvr.Read((int)DocumentsTable.Data, out int size), size, context)) { collection = CollectionName.GetCollectionName(doc); } } var collectionName = new CollectionName(collection); var table = step.DocumentsStorage.CountersStorage.GetCountersTable(step.WriteTx, collectionName); if (table.ReadByKey(lowerId, out var existing) == false) { return; } // (int)CountersTable.Data = 3 var data = new BlittableJsonReaderObject(existing.Read(3, out int oldSize), oldSize, context); if (data.TryGet(CountersStorage.Values, out BlittableJsonReaderObject counters) == false || counters.TryGetMember(counterName, out object counterToDelete) == false || counterToDelete is LazyStringValue) // already deleted { return; } var deleteCv = step.DocumentsStorage.CountersStorage.GenerateDeleteChangeVectorFromRawBlob(data, counterToDelete as BlittableJsonReaderObject.RawBlob); counters.Modifications = new DynamicJsonValue(counters) { [counterName] = deleteCv }; using (var old = data) { data = context.ReadObject(data, null, BlittableJsonDocumentBuilder.UsageMode.ToDisk); } var newEtag = step.DocumentsStorage.GenerateNextEtag(); var newChangeVector = ChangeVectorUtils.NewChangeVector(step.DocumentsStorage.DocumentDatabase.ServerStore.NodeTag, newEtag, _dbId); using (data) using (Slice.From(context.Allocator, newChangeVector, out var cv)) using (DocumentIdWorker.GetStringPreserveCase(context, collectionName.Name, out Slice collectionSlice)) using (table.Allocate(out TableValueBuilder tvb)) { tvb.Add(lowerId); tvb.Add(Bits.SwapBytes(newEtag)); tvb.Add(cv); tvb.Add(data.BasePointer, data.Size); tvb.Add(collectionSlice); tvb.Add(context.GetTransactionMarker()); table.Set(tvb); } } }