public void DeleteRevisionsFor(DocumentsOperationContext context, string id) { using (DocumentIdWorker.GetSliceFromId(context, id, out Slice lowerId)) using (GetKeyPrefix(context, lowerId, out Slice prefixSlice)) { var collectionName = GetCollectionFor(context, prefixSlice); if (collectionName == null) { if (_logger.IsInfoEnabled) { _logger.Info($"Tried to delete all revisions for '{id}' but no revisions found."); } return; } var table = EnsureRevisionTableCreated(context.Transaction.InnerTransaction, collectionName); var newEtag = _documentsStorage.GenerateNextEtag(); var changeVector = _documentsStorage.GetNewChangeVector(context, newEtag); context.LastDatabaseChangeVector = changeVector; var lastModifiedTicks = _database.Time.GetUtcNow().Ticks; DeleteRevisions(context, table, prefixSlice, collectionName, long.MaxValue, null, changeVector, lastModifiedTicks); DeleteCountOfRevisions(context, prefixSlice); } }
public override void HandleDelete(Tombstone tombstone, string collection, Lazy <IndexWriteOperation> writer, TransactionOperationContext indexContext, IndexingStatsScope stats) { using (DocumentIdWorker.GetSliceFromId(indexContext, tombstone.LowerId, out Slice documentIdPrefixWithTsKeySeparator, SpecialChars.RecordSeparator)) _referencesStorage.RemoveReferencesByPrefix(documentIdPrefixWithTsKeySeparator, collection, null, indexContext.Transaction); }
public override int Execute(DocumentsOperationContext context) { if (Database.ServerStore.Configuration.Core.FeaturesAvailability == FeaturesAvailability.Stable) { FeaturesAvailabilityException.Throw("Cluster Transactions"); } var global = context.LastDatabaseChangeVector ?? (context.LastDatabaseChangeVector = DocumentsStorage.GetDatabaseChangeVector(context)); var dbGrpId = Database.DatabaseGroupId; var current = ChangeVectorUtils.GetEtagById(global, dbGrpId); foreach (var command in _batch) { Replies.Add(command.Index, new DynamicJsonArray()); Reply = Replies[command.Index]; var commands = command.Commands; var count = command.PreviousCount; var options = Options[command.Index] = command.Options; if (options.WaitForIndexesTimeout != null) { ModifiedCollections = new HashSet <string>(); } if (commands != null) { foreach (BlittableJsonReaderObject blittableCommand in commands) { count++; var changeVector = $"RAFT:{count}-{dbGrpId}"; var cmd = JsonDeserializationServer.ClusterTransactionDataCommand(blittableCommand); switch (cmd.Type) { case CommandType.PUT: if (current < count) { // delete the document to avoid exception if we put new document in a different collection. // TODO: document this behavior using (DocumentIdWorker.GetSliceFromId(context, cmd.Id, out Slice lowerId)) { Database.DocumentsStorage.Delete(context, lowerId, cmd.Id, expectedChangeVector: null, nonPersistentFlags: NonPersistentDocumentFlags.SkipRevisionCreation); } var putResult = Database.DocumentsStorage.Put(context, cmd.Id, null, cmd.Document.Clone(context), changeVector: changeVector, flags: DocumentFlags.FromClusterTransaction); context.DocumentDatabase.HugeDocuments.AddIfDocIsHuge(cmd.Id, cmd.Document.Size); AddPutResult(putResult); } else { try { var item = Database.DocumentsStorage.GetDocumentOrTombstone(context, cmd.Id); if (item.Missing) { AddPutResult(new DocumentsStorage.PutOperationResults { ChangeVector = changeVector, Id = cmd.Id, LastModified = DateTime.UtcNow, Collection = Database.DocumentsStorage.ExtractCollectionName(context, cmd.Document) }); continue; } var collection = GetCollection(context, item); AddPutResult(new DocumentsStorage.PutOperationResults { ChangeVector = changeVector, Id = cmd.Id, Flags = item.Document?.Flags ?? item.Tombstone.Flags, LastModified = item.Document?.LastModified ?? item.Tombstone.LastModified, Collection = collection }); } catch (DocumentConflictException) { AddPutResult(new DocumentsStorage.PutOperationResults { ChangeVector = changeVector, Id = cmd.Id, Collection = GetFirstConflictCollection(context, cmd) }); } } break; case CommandType.DELETE: if (current < count) { using (DocumentIdWorker.GetSliceFromId(context, cmd.Id, out Slice lowerId)) { var deleteResult = Database.DocumentsStorage.Delete(context, lowerId, cmd.Id, null, changeVector: changeVector, documentFlags: DocumentFlags.FromClusterTransaction); AddDeleteResult(deleteResult, cmd.Id); } } else { try { var item = Database.DocumentsStorage.GetDocumentOrTombstone(context, cmd.Id); if (item.Missing) { AddDeleteResult(new DocumentsStorage.DeleteOperationResult { ChangeVector = changeVector, Collection = null }, cmd.Id); continue; } var collection = GetCollection(context, item); AddDeleteResult(new DocumentsStorage.DeleteOperationResult { ChangeVector = changeVector, Collection = collection }, cmd.Id); } catch (DocumentConflictException) { AddDeleteResult(new DocumentsStorage.DeleteOperationResult { ChangeVector = changeVector, Collection = GetFirstConflictCollection(context, cmd) }, cmd.Id); } } break; default: throw new NotSupportedException($"{cmd.Type} is not supported in {nameof(ClusterTransactionMergedCommand)}."); } } } if (context.LastDatabaseChangeVector == null) { context.LastDatabaseChangeVector = global; } var result = ChangeVectorUtils.TryUpdateChangeVector("RAFT", dbGrpId, count, context.LastDatabaseChangeVector); if (result.IsValid) { context.LastDatabaseChangeVector = result.ChangeVector; } } return(Reply.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); }
public unsafe dynamic LoadDocument(LazyStringValue keyLazy, string keyString, string collectionName) { using (_loadDocumentStats?.Start() ?? (_loadDocumentStats = _stats?.For(IndexingOperation.LoadDocument))) { if (keyLazy == null && keyString == null) { return(DynamicNullObject.Null); } var source = Source; if (source == null) { throw new ArgumentException("Cannot execute LoadDocument. Source is not set."); } var id = source.GetId() as LazyStringValue; if (id == null) { throw new ArgumentException("Cannot execute LoadDocument. Source does not have a key."); } if (keyLazy != null && id.Equals(keyLazy)) { return(source); } if (keyString != null && id.Equals(keyString)) { return(source); } // we intentionally don't dispose of the scope here, this is being tracked by the references // and will be disposed there. // making sure that we normalize the case of the key so we'll be able to find // it in case insensitive manner // In addition, special characters need to be escaped Slice keySlice; if (keyLazy != null) { if (keyLazy.Length == 0) { return(DynamicNullObject.Null); } DocumentIdWorker.GetSliceFromId(_documentsContext, keyLazy, out keySlice); } else { if (keyString.Length == 0) { return(DynamicNullObject.Null); } DocumentIdWorker.GetSliceFromId(_documentsContext, keyString, out keySlice); } var references = GetReferencesForDocument(id); references.Add(keySlice); // when there is conflict, we need to apply same behavior as if the document would not exist var document = _documentsStorage.Get(_documentsContext, keySlice, throwOnConflict: false); if (document == null) { return(DynamicNullObject.Null); } // we can't share one DynamicBlittableJson instance among all documents because we can have multiple LoadDocuments in a single scope return(new DynamicBlittableJson(document)); } }