Esempio n. 1
0
        private void DeleteOldRevisions(DocumentsOperationContext context, Table table, Slice prefixSlice, CollectionName collectionName,
                                        RevisionsCollectionConfiguration configuration, long revisionsCount, NonPersistentDocumentFlags nonPersistentFlags, string changeVector, long lastModifiedTicks)
        {
            if ((nonPersistentFlags & NonPersistentDocumentFlags.FromSmuggler) == NonPersistentDocumentFlags.FromSmuggler)
            {
                return;
            }

            if (configuration.MinimumRevisionsToKeep.HasValue == false &&
                configuration.MinimumRevisionAgeToKeep.HasValue == false)
            {
                return;
            }

            var numberOfRevisionsToDelete = revisionsCount - configuration.MinimumRevisionsToKeep ?? 0;

            if (numberOfRevisionsToDelete <= 0)
            {
                return;
            }

            var deletedRevisionsCount = DeleteRevisions(context, table, prefixSlice, collectionName, numberOfRevisionsToDelete, configuration.MinimumRevisionAgeToKeep, changeVector, lastModifiedTicks);

            Debug.Assert(numberOfRevisionsToDelete >= deletedRevisionsCount);
            IncrementCountOfRevisions(context, prefixSlice, -deletedRevisionsCount);
        }
Esempio n. 2
0
            private void WriteRevisionsCollectionConfiguration(RevisionsCollectionConfiguration collectionConfiguration)
            {
                if (collectionConfiguration == null)
                {
                    _writer.WriteNull();
                    return;
                }

                _writer.WriteStartObject();

                if (collectionConfiguration.MinimumRevisionsToKeep.HasValue)
                {
                    _writer.WritePropertyName(nameof(collectionConfiguration.MinimumRevisionsToKeep));
                    _writer.WriteInteger(collectionConfiguration.MinimumRevisionsToKeep.Value);
                    _writer.WriteComma();
                }

                if (collectionConfiguration.MinimumRevisionAgeToKeep.HasValue)
                {
                    _writer.WritePropertyName(nameof(collectionConfiguration.MinimumRevisionAgeToKeep));
                    _writer.WriteString(collectionConfiguration.MinimumRevisionAgeToKeep.Value.ToString());
                    _writer.WriteComma();
                }

                _writer.WritePropertyName(nameof(collectionConfiguration.Disabled));
                _writer.WriteBool(collectionConfiguration.Disabled);
                _writer.WriteComma();

                _writer.WritePropertyName(nameof(collectionConfiguration.PurgeOnDelete));
                _writer.WriteBool(collectionConfiguration.PurgeOnDelete);

                _writer.WriteEndObject();
            }
 public ConfigureRevisionsForConflictsCommand(
     DocumentConventions conventions,
     string database,
     RevisionsCollectionConfiguration configuration)
 {
     _conventions   = conventions ?? throw new ArgumentNullException(nameof(conventions));
     _databaseName  = database ?? throw new ArgumentNullException(nameof(database));
     _configuration = configuration;
 }
Esempio n. 4
0
 private void DeleteOldRevisions(DocumentsOperationContext context, Table table, Slice lowerId, CollectionName collectionName,
                                 RevisionsCollectionConfiguration configuration, NonPersistentDocumentFlags nonPersistentFlags, string changeVector, long lastModifiedTicks)
 {
     using (GetKeyPrefix(context, lowerId, out Slice prefixSlice))
     {
         // We delete the old revisions after we put the current one,
         // because in case that MinimumRevisionsToKeep is 3 or lower we may get a revision document from replication
         // which is old. But because we put it first, we make sure to clean this document, because of the order to the revisions.
         var revisionsCount = IncrementCountOfRevisions(context, prefixSlice, 1);
         DeleteOldRevisions(context, table, prefixSlice, collectionName, configuration, revisionsCount, nonPersistentFlags, changeVector, lastModifiedTicks);
     }
 }
Esempio n. 5
0
        public bool ShouldVersionDocument(CollectionName collectionName, NonPersistentDocumentFlags nonPersistentFlags,
                                          BlittableJsonReaderObject existingDocument, BlittableJsonReaderObject document, ref DocumentFlags documentFlags,
                                          out RevisionsCollectionConfiguration configuration)
        {
            configuration = GetRevisionsConfiguration(collectionName.Name);
            if (configuration.Disabled)
            {
                return(false);
            }

            try
            {
                if ((nonPersistentFlags & NonPersistentDocumentFlags.FromSmuggler) != NonPersistentDocumentFlags.FromSmuggler)
                {
                    return(true);
                }
                if (existingDocument == null)
                {
                    if ((nonPersistentFlags & NonPersistentDocumentFlags.SkipRevisionCreation) == NonPersistentDocumentFlags.SkipRevisionCreation)
                    {
                        // Smuggler is configured to avoid creating new revisions during import
                        return(false);
                    }

                    // we are not going to create a revision if it's an import from v3
                    // (since this import is going to import revisions as well)
                    return((nonPersistentFlags & NonPersistentDocumentFlags.LegacyHasRevisions) != NonPersistentDocumentFlags.LegacyHasRevisions);
                }

                // compare the contents of the existing and the new document
                if (DocumentCompare.IsEqualTo(existingDocument, document, false) != DocumentCompareResult.NotEqual)
                {
                    // no need to create a new revision, both documents have identical content
                    return(false);
                }

                return(true);
            }
            finally
            {
                documentFlags |= DocumentFlags.HasRevisions;
            }
        }
Esempio n. 6
0
        public void Put(DocumentsOperationContext context, string id, BlittableJsonReaderObject document,
                        DocumentFlags flags, NonPersistentDocumentFlags nonPersistentFlags, string changeVector, long lastModifiedTicks,
                        RevisionsCollectionConfiguration configuration = null, CollectionName collectionName = null)
        {
            Debug.Assert(changeVector != null, "Change vector must be set");
            Debug.Assert(lastModifiedTicks != DateTime.MinValue.Ticks, "last modified ticks must be set");

            BlittableJsonReaderObject.AssertNoModifications(document, id, assertChildren: true);

            if (collectionName == null)
            {
                collectionName = _database.DocumentsStorage.ExtractCollectionName(context, document);
            }

            using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, id, out Slice lowerId, out Slice idPtr))
            {
                var fromSmuggler    = (nonPersistentFlags & NonPersistentDocumentFlags.FromSmuggler) == NonPersistentDocumentFlags.FromSmuggler;
                var fromReplication = (nonPersistentFlags & NonPersistentDocumentFlags.FromReplication) == NonPersistentDocumentFlags.FromReplication;

                var table = EnsureRevisionTableCreated(context.Transaction.InnerTransaction, collectionName);

                // We want the revision's attachments to have a lower etag than the revision itself
                if ((flags & DocumentFlags.HasAttachments) == DocumentFlags.HasAttachments &&
                    fromSmuggler == false)
                {
                    using (Slice.From(context.Allocator, changeVector, out Slice changeVectorSlice))
                    {
                        if (table.VerifyKeyExists(changeVectorSlice) == false)
                        {
                            _documentsStorage.AttachmentsStorage.RevisionAttachments(context, lowerId, changeVectorSlice);
                        }
                    }
                }

                if (fromReplication)
                {
                    void PutFromRevisionIfChangeVectorIsGreater()
                    {
                        bool             hasDoc;
                        TableValueReader tvr;

                        try
                        {
                            hasDoc = _documentsStorage.GetTableValueReaderForDocument(context, lowerId, throwOnConflict: true, tvr: out tvr);
                        }
                        catch (DocumentConflictException)
                        {
                            // Do not modify the document.
                            return;
                        }

                        if (hasDoc == false)
                        {
                            PutFromRevision();
                            return;
                        }

                        var docChangeVector = TableValueToChangeVector(context, (int)DocumentsTable.ChangeVector, ref tvr);

                        if (ChangeVectorUtils.GetConflictStatus(changeVector, docChangeVector) == ConflictStatus.Update)
                        {
                            PutFromRevision();
                        }

                        void PutFromRevision()
                        {
                            _documentsStorage.Put(context, id, null, document, lastModifiedTicks, changeVector,
                                                  flags & ~DocumentFlags.Revision, nonPersistentFlags | NonPersistentDocumentFlags.FromRevision);
                        }
                    }

                    PutFromRevisionIfChangeVectorIsGreater();
                }

                flags |= DocumentFlags.Revision;
                var newEtag          = _database.DocumentsStorage.GenerateNextEtag();
                var newEtagSwapBytes = Bits.SwapBytes(newEtag);

                using (table.Allocate(out TableValueBuilder tvb))
                    using (Slice.From(context.Allocator, changeVector, out var cv))
                    {
                        tvb.Add(cv.Content.Ptr, cv.Size);
                        tvb.Add(lowerId);
                        tvb.Add(SpecialChars.RecordSeparator);
                        tvb.Add(newEtagSwapBytes);
                        tvb.Add(idPtr);
                        tvb.Add(document.BasePointer, document.Size);
                        tvb.Add((int)flags);
                        tvb.Add(NotDeletedRevisionMarker);
                        tvb.Add(lastModifiedTicks);
                        tvb.Add(context.GetTransactionMarker());
                        if (flags.Contain(DocumentFlags.Resolved))
                        {
                            tvb.Add((int)DocumentFlags.Resolved);
                        }
                        else
                        {
                            tvb.Add(0);
                        }
                        tvb.Add(Bits.SwapBytes(lastModifiedTicks));
                        var isNew = table.Set(tvb);
                        if (isNew == false)
                        {
                            // It might be just an update from replication as we call this twice, both for the doc delete and for deleteRevision.
                            return;
                        }
                    }

                if (configuration == null)
                {
                    configuration = GetRevisionsConfiguration(collectionName.Name);
                }

                DeleteOldRevisions(context, table, lowerId, collectionName, configuration, nonPersistentFlags, changeVector, lastModifiedTicks);
            }
        }
Esempio n. 7
0
        public async Task Foo()
        {
            using (var documentStore = new DocumentStore())
            {
                using (var session = documentStore.OpenSession())
                {
                    #region operation_sync
                    // Create a configuration for the Employees collection
                    var employeesRevConfig = new RevisionsCollectionConfiguration()
                    {
                        MinimumRevisionAgeToKeep = new TimeSpan(hours: 1, minutes: 23, seconds: 45),
                        MinimumRevisionsToKeep   = 42,
                        PurgeOnDelete            = true
                    };

                    // Create a configuration for the Products collection
                    var productsRevConfig = new RevisionsCollectionConfiguration()
                    {
                        Disabled = true
                    };

                    // Create a default collection configuration
                    var defaultRevConfig = new RevisionsCollectionConfiguration()
                    {
                        MinimumRevisionAgeToKeep = new TimeSpan(days: 7, 0, 0, 0),
                        MinimumRevisionsToKeep   = 100,
                        PurgeOnDelete            = false
                    };

                    // Combine to create a configuration for the database
                    var northwindRevConfig = new RevisionsConfiguration()
                    {
                        Collections = new Dictionary <string, RevisionsCollectionConfiguration>()
                        {
                            { "Employees", employeesRevConfig },
                            { "Products", productsRevConfig }
                        },

                        Default = defaultRevConfig
                    };

                    // Execute the operation to update the database
                    documentStore.Maintenance.Send(new ConfigureRevisionsOperation(northwindRevConfig));
                    #endregion
                }

                using (var asyncSession = documentStore.OpenAsyncSession())
                {
                    #region operation_async
                    // Create a configuration for the Employees collection
                    var employeesRevConfig = new RevisionsCollectionConfiguration()
                    {
                        MinimumRevisionAgeToKeep = new TimeSpan(hours: 1, minutes: 23, seconds: 45),
                        MinimumRevisionsToKeep   = 42,
                        PurgeOnDelete            = true
                    };

                    // Create a configuration for the Products collection
                    var productsRevConfig = new RevisionsCollectionConfiguration()
                    {
                        Disabled = true
                    };

                    // Create a default collection configuration
                    var defaultRevConfig = new RevisionsCollectionConfiguration()
                    {
                        MinimumRevisionAgeToKeep = new TimeSpan(days: 7, 0, 0, 0),
                        MinimumRevisionsToKeep   = 100,
                        PurgeOnDelete            = false
                    };

                    // Combine to create a configuration for the database
                    var northwindRevConfig = new RevisionsConfiguration()
                    {
                        Collections = new Dictionary <string, RevisionsCollectionConfiguration>()
                        {
                            { "Employees", employeesRevConfig },
                            { "Products", productsRevConfig }
                        },

                        Default = defaultRevConfig
                    };

                    // Execute the operation to update the database
                    await documentStore.Maintenance.SendAsync(new ConfigureRevisionsOperation(northwindRevConfig));

                    #endregion
                }
            }
        }
 public ConfigureRevisionsForConflictsOperation(string database, RevisionsCollectionConfiguration configuration)
 {
     ResourceNameValidator.AssertValidDatabaseName(database);
     _database      = database;
     _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
 }
 public EditRevisionsForConflictsConfigurationCommand(RevisionsCollectionConfiguration configuration, string databaseName, string uniqueRequestId) : base(databaseName, uniqueRequestId)
 {
     Configuration = configuration;
 }