예제 #1
0
 private void DeleteOldRevisions(DocumentsOperationContext context, Table table, Slice lowerId, CollectionName collectionName,
                                 RevisionsCollectionConfiguration configuration, NonPersistentDocumentFlags nonPersistentFlags, string changeVector)
 {
     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);
     }
 }
예제 #2
0
        public void AddConflict(
            DocumentsOperationContext context,
            string id,
            long lastModifiedTicks,
            BlittableJsonReaderObject incomingDoc,
            string incomingChangeVector,
            string incomingTombstoneCollection,
            DocumentFlags flags,
            NonPersistentDocumentFlags nonPersistentFlags = NonPersistentDocumentFlags.None)
        {
            if (_logger.IsInfoEnabled)
            {
                _logger.Info($"Adding conflict to {id} (Incoming change vector {incomingChangeVector})");
            }

            var tx             = context.Transaction.InnerTransaction;
            var conflictsTable = tx.OpenTable(ConflictsSchema, ConflictsSlice);

            var fromSmuggler = (nonPersistentFlags & NonPersistentDocumentFlags.FromSmuggler) == NonPersistentDocumentFlags.FromSmuggler;

            using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, id, out Slice lowerId, out Slice idPtr))
            {
                CollectionName collectionName;

                // ReSharper disable once ArgumentsStyleLiteral
                var existing = _documentsStorage.GetDocumentOrTombstone(context, id, throwOnConflict: false);
                if (existing.Document != null)
                {
                    var existingDoc = existing.Document;

                    if (fromSmuggler == false)
                    {
                        using (Slice.From(context.Allocator, existingDoc.ChangeVector, out Slice cv))
                            using (DocumentIdWorker.GetStringPreserveCase(context, CollectionName.GetLazyCollectionNameFrom(context, existingDoc.Data), out Slice collectionSlice))
                                using (conflictsTable.Allocate(out TableValueBuilder tvb))
                                {
                                    tvb.Add(lowerId);
                                    tvb.Add(SpecialChars.RecordSeparator);
                                    tvb.Add(cv);
                                    tvb.Add(idPtr);
                                    tvb.Add(existingDoc.Data.BasePointer, existingDoc.Data.Size);
                                    tvb.Add(Bits.SwapBytes(_documentsStorage.GenerateNextEtag()));
                                    tvb.Add(collectionSlice);
                                    tvb.Add(existingDoc.LastModified.Ticks);
                                    tvb.Add((int)existingDoc.Flags);
                                    if (conflictsTable.Set(tvb))
                                    {
                                        Interlocked.Increment(ref ConflictsCount);
                                    }
                                }
                    }

                    // we delete the data directly, without generating a tombstone, because we have a
                    // conflict instead
                    _documentsStorage.EnsureLastEtagIsPersisted(context, existingDoc.Etag);

                    collectionName = _documentsStorage.ExtractCollectionName(context, existingDoc.Data);

                    //make sure that the relevant collection tree exists
                    var table = tx.OpenTable(DocsSchema, collectionName.GetTableName(CollectionTableType.Documents));
                    table.Delete(existingDoc.StorageId);
                }
                else if (existing.Tombstone != null)
                {
                    var existingTombstone = existing.Tombstone;

                    if (fromSmuggler == false)
                    {
                        using (Slice.From(context.Allocator, existingTombstone.ChangeVector, out var cv))
                            using (DocumentIdWorker.GetStringPreserveCase(context, existingTombstone.Collection, out Slice collectionSlice))
                                using (conflictsTable.Allocate(out TableValueBuilder tvb))
                                {
                                    tvb.Add(lowerId);
                                    tvb.Add(SpecialChars.RecordSeparator);
                                    tvb.Add(cv);
                                    tvb.Add(idPtr);
                                    tvb.Add(null, 0);
                                    tvb.Add(Bits.SwapBytes(_documentsStorage.GenerateNextEtag()));
                                    tvb.Add(collectionSlice);
                                    tvb.Add(existingTombstone.LastModified.Ticks);
                                    tvb.Add((int)existingTombstone.Flags);
                                    if (conflictsTable.Set(tvb))
                                    {
                                        Interlocked.Increment(ref ConflictsCount);
                                    }
                                }
                    }

                    // we delete the data directly, without generating a tombstone, because we have a
                    // conflict instead
                    _documentsStorage.EnsureLastEtagIsPersisted(context, existingTombstone.Etag);

                    collectionName = _documentsStorage.GetCollection(existingTombstone.Collection, throwIfDoesNotExist: true);

                    var table = tx.OpenTable(TombstonesSchema, collectionName.GetTableName(CollectionTableType.Tombstones));
                    table.Delete(existingTombstone.StorageId);
                }
                else // has existing conflicts
                {
                    collectionName = _documentsStorage.ExtractCollectionName(context, incomingDoc);

                    using (GetConflictsIdPrefix(context, lowerId, out Slice prefixSlice))
                    {
                        var conflicts = GetConflictsFor(context, prefixSlice);
                        foreach (var conflict in conflicts)
                        {
                            var conflictStatus = ChangeVectorUtils.GetConflictStatus(incomingChangeVector, conflict.ChangeVector);
                            switch (conflictStatus)
                            {
                            case ConflictStatus.Update:
                                DeleteConflictsFor(context, conflict.ChangeVector);     // delete this, it has been subsumed
                                break;

                            case ConflictStatus.Conflict:
                                if (fromSmuggler &&
                                    DocumentCompare.IsEqualTo(conflict.Doc, incomingDoc, false) == DocumentCompareResult.Equal)
                                {
                                    return; // we already have a conflict with equal content, no need to create another one
                                }
                                break;      // we'll add this conflict if no one else also includes it

                            case ConflictStatus.AlreadyMerged:
                                return;     // we already have a conflict that includes this version

                            default:
                                throw new ArgumentOutOfRangeException("Invalid conflict status " + conflictStatus);
                            }
                        }
                    }
                }

                var etag = _documentsStorage.GenerateNextEtag();
                if (context.LastDatabaseChangeVector == null)
                {
                    context.LastDatabaseChangeVector = GetDatabaseChangeVector(context);
                }
                ChangeVectorUtils.TryUpdateChangeVector(_documentDatabase.ServerStore.NodeTag, _documentDatabase.DbId, etag, ref context.LastDatabaseChangeVector);

                byte * doc     = null;
                var    docSize = 0;
                string collection;
                if (incomingDoc != null) // can be null if it is a tombstone
                {
                    doc        = incomingDoc.BasePointer;
                    docSize    = incomingDoc.Size;
                    collection = CollectionName.GetLazyCollectionNameFrom(context, incomingDoc);
                }
                else
                {
                    collection = incomingTombstoneCollection;
                }

                using (Slice.From(context.Allocator, incomingChangeVector, out var cv))
                    using (DocumentIdWorker.GetStringPreserveCase(context, collection, out Slice collectionSlice))
                        using (conflictsTable.Allocate(out TableValueBuilder tvb))
                        {
                            tvb.Add(lowerId);
                            tvb.Add(SpecialChars.RecordSeparator);
                            tvb.Add(cv);
                            tvb.Add(idPtr);
                            tvb.Add(doc, docSize);
                            tvb.Add(Bits.SwapBytes(etag));
                            tvb.Add(collectionSlice);
                            tvb.Add(lastModifiedTicks);
                            tvb.Add((int)flags);
                            if (conflictsTable.Set(tvb))
                            {
                                Interlocked.Increment(ref ConflictsCount);
                            }
                        }

                context.Transaction.AddAfterCommitNotification(new DocumentChange
                {
                    ChangeVector   = incomingChangeVector,
                    CollectionName = collectionName.Name,
                    Id             = id,
                    Type           = DocumentChangeTypes.Conflict,
                });
            }
        }
예제 #3
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);
            }
        }
        private unsafe bool AboutToReadPropertyNameInMetadataUnlikely(IJsonParser reader, JsonParserState state, out bool aboutToReadPropertyName)
        {
            aboutToReadPropertyName = true;

            switch (state.StringSize)
            {
            default:     // accept this property
            {
                return(true);
            }

            case -2:     // IgnoreArrayProperty
            {
                if (state.CurrentTokenType != JsonParserToken.StartArray)
                {
                    ThrowInvalidArrayType(state, reader);
                }

                while (state.CurrentTokenType != JsonParserToken.EndArray)
                {
                    if (reader.Read() == false)
                    {
                        _state = State.IgnoreArray;
                        aboutToReadPropertyName = false;
                        return(true);
                    }
                }
                break;
            }

            case -1:     // IgnoreProperty
            {
                if (reader.Read() == false)
                {
                    _state = State.IgnoreProperty;
                    aboutToReadPropertyName = false;
                    return(true);
                }
                if (state.CurrentTokenType == JsonParserToken.StartArray ||
                    state.CurrentTokenType == JsonParserToken.StartObject)
                {
                    ThrowInvalidMetadataProperty(state, reader);
                }
                break;
            }

            case 3:     // @id
                if (state.StringBuffer[0] != (byte)'@' ||
                    *(short *)(state.StringBuffer + 1) != 25705)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if (reader.Read() == false)
                {
                    _state = State.ReadingId;
                    aboutToReadPropertyName = false;
                    return(true);
                }
                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Id, state, reader);
                }
                Id = CreateLazyStringValueFromParserState(state);
                break;

            case 5:     // @etag
                if (state.StringBuffer[0] != (byte)'@' ||
                    *(int *)(state.StringBuffer + 1) != 1734440037)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if (ReadFirstEtagOfLegacyRevision &&
                    (NonPersistentFlags & NonPersistentDocumentFlags.LegacyRevision) == NonPersistentDocumentFlags.LegacyRevision)
                {
                    if (FirstEtagOfLegacyRevision == null)
                    {
                        if (reader.Read() == false)
                        {
                            _state = State.ReadingFirstEtagOfLegacyRevision;
                            aboutToReadPropertyName = false;
                            return(true);
                        }
                        if (state.CurrentTokenType != JsonParserToken.String)
                        {
                            ThrowExpectedFieldTypeOfString("@etag", state, reader);
                        }
                        FirstEtagOfLegacyRevision = LegacyEtag = CreateLazyStringValueFromParserState(state);
                        ChangeVector = ChangeVectorUtils.NewChangeVector("RV", ++LegacyRevisionsCount, new Guid(FirstEtagOfLegacyRevision).ToBase64Unpadded());
                        break;
                    }

                    ChangeVector = ChangeVectorUtils.NewChangeVector("RV", ++LegacyRevisionsCount, new Guid(FirstEtagOfLegacyRevision).ToBase64Unpadded());
                }

                if (ReadLegacyEtag)
                {
                    if (reader.Read() == false)
                    {
                        _state = State.ReadingEtag;
                        aboutToReadPropertyName = false;
                        return(true);
                    }

                    if (state.CurrentTokenType != JsonParserToken.String)
                    {
                        ThrowExpectedFieldTypeOfString("@etag", state, reader);
                    }
                    LegacyEtag = CreateLazyStringValueFromParserState(state);
                    break;
                }

                goto case -1;

            case 6:     // @flags
                if (state.StringBuffer[0] != (byte)'@' ||
                    *(int *)(state.StringBuffer + 1) != 1734437990 ||
                    state.StringBuffer[1 + sizeof(int)] != (byte)'s')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if (reader.Read() == false)
                {
                    _state = State.ReadingFlags;
                    aboutToReadPropertyName = false;
                    return(true);
                }
                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Flags, state, reader);
                }
                Flags = ReadFlags(state);
                break;

            case 9:     // @counters
                // always remove the @counters metadata
                // not doing so might cause us to have counter on the document but not in the storage.
                // the counters will be updated when we import the counters themselves
                if (state.StringBuffer[0] != (byte)'@' ||
                    *(long *)(state.StringBuffer + 1) != 8318823012450529123)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if (reader.Read() == false)
                {
                    _verifyStartArray       = true;
                    _state                  = State.IgnoreArray;
                    aboutToReadPropertyName = false;
                    return(true);
                }
                goto case -2;

            case 12:    // @index-score OR @attachments
                if (state.StringBuffer[0] == (byte)'@')
                {       // @index-score
                    if (*(long *)(state.StringBuffer + 1) == 7166121427196997225 &&
                        *(short *)(state.StringBuffer + 1 + sizeof(long)) == 29295 &&
                        state.StringBuffer[1 + sizeof(long) + sizeof(short)] == (byte)'e')
                    {
                        goto case -1;
                    }
                    // @attachments
                    if (OperateOnTypes.HasFlag(DatabaseItemType.Attachments) == false &&
                        *(long *)(state.StringBuffer + 1) == 7308612546338255969 &&
                        *(short *)(state.StringBuffer + 1 + sizeof(long)) == 29806 &&
                        state.StringBuffer[1 + sizeof(long) + sizeof(short)] == (byte)'s')
                    {
                        if (reader.Read() == false)
                        {
                            _verifyStartArray       = true;
                            _state                  = State.IgnoreArray;
                            aboutToReadPropertyName = false;
                            return(true);
                        }
                        goto case -2;
                    }
                }

                aboutToReadPropertyName = true;
                return(true);

            case 13:     //Last-Modified
                if (*(long *)state.StringBuffer != 7237087983830262092 ||
                    *(int *)(state.StringBuffer + sizeof(long)) != 1701406313 ||
                    state.StringBuffer[12] != (byte)'d')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 14:
                if (state.StringBuffer[0] == (byte)'@')
                {
                    // @change-vector
                    if (*(long *)(state.StringBuffer + 1) == 8515573965335390307 &&
                        *(int *)(state.StringBuffer + 1 + sizeof(long)) == 1869898597 &&
                        state.StringBuffer[1 + sizeof(long) + sizeof(int)] == (byte)'r')
                    {
                        if (reader.Read() == false)
                        {
                            _state = State.ReadingChangeVector;
                            aboutToReadPropertyName = false;
                            return(true);
                        }
                        if (state.CurrentTokenType != JsonParserToken.String)
                        {
                            ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.ChangeVector, state, reader);
                        }
                        ChangeVector = CreateLazyStringValueFromParserState(state);
                        break;
                    }

                    // @last-modified
                    if (*(long *)(state.StringBuffer + 1) == 7237123168202350956 &&
                        *(int *)(state.StringBuffer + 1 + sizeof(long)) == 1701406313 &&
                        state.StringBuffer[1 + sizeof(long) + sizeof(int)] == (byte)'d')
                    {
                        if (reader.Read() == false)
                        {
                            _state = State.ReadingLastModified;
                            aboutToReadPropertyName = false;
                            return(true);
                        }
                        if (state.CurrentTokenType != JsonParserToken.String)
                        {
                            ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.LastModified, state, reader);
                        }
                        LastModified = ReadDateTime(state, reader, State.ReadingLastModified);
                        break;
                    }
                }

                aboutToReadPropertyName = true;
                return(true);

            case 15:     //Raven-Read-Only
                if (*(long *)state.StringBuffer != 7300947898092904786 ||
                    *(int *)(state.StringBuffer + sizeof(long)) != 1328374881 ||
                    *(short *)(state.StringBuffer + sizeof(long) + sizeof(int)) != 27758 ||
                    state.StringBuffer[14] != (byte)'y')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 17:     //Raven-Entity-Name --> @collection
                if (*(long *)state.StringBuffer != 7945807069737017682 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 7881666780093245812 ||
                    state.StringBuffer[16] != (byte)'e')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var collection = _metadataCollections;
                state.StringBuffer      = collection.AllocatedMemoryData.Address;
                state.StringSize        = collection.Size;
                aboutToReadPropertyName = true;
                return(true);

            case 19:     //Raven-Last-Modified
                if (*(long *)state.StringBuffer != 7011028672080929106 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 7379539893622240371 ||
                    *(short *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 25961 ||
                    state.StringBuffer[18] != (byte)'d')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if (reader.Read() == false)
                {
                    _state = State.ReadingLegacyLastModified;
                    aboutToReadPropertyName = false;
                    return(true);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(LegacyLastModified, state, reader);
                }
                LastModified = ReadDateTime(state, reader, State.ReadingLegacyLastModified);
                break;

            case 21:     //Raven-Expiration-Date
                if (*(long *)state.StringBuffer != 8666383010116297042 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 7957695015158966640 ||
                    *(short *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 17453 ||
                    state.StringBuffer[20] != (byte)'e')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var expires = _metadataExpires;
                state.StringBuffer      = expires.AllocatedMemoryData.Address;
                state.StringSize        = expires.Size;
                aboutToReadPropertyName = true;
                return(true);

            case 23:     //Raven-Document-Revision
                if (*(long *)state.StringBuffer != 8017583188798234962 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 5921517102558967139 ||
                    *(int *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 1936291429 ||
                    *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(int)) != 28521 ||
                    state.StringBuffer[22] != (byte)'n')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 24:     //Raven-Replication-Source
                if (*(long *)state.StringBuffer != 7300947898092904786 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928 ||
                    *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7305808869229538670)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 25:     //Raven-Replication-Version OR Raven-Replication-History
                if (*(long *)state.StringBuffer != 7300947898092904786 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var value    = *(long *)(state.StringBuffer + sizeof(long) + sizeof(long));
                var lastByte = state.StringBuffer[24];
                if ((value != 8028074745928232302 || lastByte != (byte)'n') &&
                    (value != 8245937481775066478 || lastByte != (byte)'y'))
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var isReplicationHistory = lastByte == (byte)'y';
                if (reader.Read() == false)
                {
                    _verifyStartArray       = isReplicationHistory;
                    _state                  = isReplicationHistory ? State.IgnoreArray : State.IgnoreProperty;
                    aboutToReadPropertyName = false;
                    return(true);
                }

                // Raven-Replication-History is an array
                if (isReplicationHistory)
                {
                    goto case -2;
                }
                else if (state.CurrentTokenType == JsonParserToken.StartArray ||
                         state.CurrentTokenType == JsonParserToken.StartObject)
                {
                    ThrowInvalidMetadataProperty(state, reader);
                }
                break;

            case 29:     //Non-Authoritative-Information
                if (*(long *)state.StringBuffer != 7526769800038477646 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 8532478930943832687 ||
                    *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7886488383206796645 ||
                    *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1869182049 ||
                    state.StringBuffer[28] != (byte)'n')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 30:     //Raven-Document-Parent-Revision OR Raven-Document-Revision-Status
                if (*(long *)state.StringBuffer != 8017583188798234962)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if ((*(long *)(state.StringBuffer + sizeof(long)) != 5777401914483111267 ||
                     *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7300947924012593761 ||
                     *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1769171318 ||
                     *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long) + sizeof(int)) != 28271) &&
                    (*(long *)(state.StringBuffer + sizeof(long)) != 5921517102558967139 ||
                     *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 3273676477843469925 ||
                     *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1952543827 ||
                     *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long) + sizeof(int)) != 29557))
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var isRevisionStatusProperty = state.StringBuffer[29] == 's';
                if (reader.Read() == false)
                {
                    _state = isRevisionStatusProperty ? State.IgnoreRevisionStatusProperty : State.IgnoreProperty;
                    aboutToReadPropertyName = false;
                    return(true);
                }

                if (state.CurrentTokenType == JsonParserToken.StartArray ||
                    state.CurrentTokenType == JsonParserToken.StartObject)
                {
                    ThrowInvalidMetadataProperty(state, reader);
                }

                if (isRevisionStatusProperty)
                {
                    switch (CreateLazyStringValueFromParserState(state))
                    {
                    case LegacyHasRevisionsDocumentState:
                        NonPersistentFlags |= NonPersistentDocumentFlags.LegacyHasRevisions;
                        break;

                    case LegacyRevisionState:
                        NonPersistentFlags |= NonPersistentDocumentFlags.LegacyRevision;
                        break;
                    }
                }

                break;

            case 32:     //Raven-Replication-Merged-History
                if (*(long *)state.StringBuffer != 7300947898092904786 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928 ||
                    *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7234302117464059246 ||
                    *(long *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 8751179571877464109)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;
            }
            return(false);
        }
        private bool AboutToReadWithStateUnlikely(IJsonParser reader, JsonParserState state)
        {
            switch (_state)
            {
            case State.None:
                break;

            case State.IgnoreProperty:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType == JsonParserToken.StartArray ||
                    state.CurrentTokenType == JsonParserToken.StartObject)
                {
                    ThrowInvalidMetadataProperty(state, reader);
                }
                break;

            case State.IgnoreArray:
                if (_verifyStartArray)
                {
                    if (reader.Read() == false)
                    {
                        return(false);
                    }

                    _verifyStartArray = false;

                    if (state.CurrentTokenType != JsonParserToken.StartArray)
                    {
                        ThrowInvalidReplicationHistoryType(state, reader);
                    }
                }
                while (state.CurrentTokenType != JsonParserToken.EndArray)
                {
                    if (reader.Read() == false)
                    {
                        return(false);
                    }
                }
                break;

            case State.IgnoreRevisionStatusProperty:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String &&
                    state.CurrentTokenType != JsonParserToken.Integer)
                {
                    ThrowInvalidEtagType(state, reader);
                }

                switch (CreateLazyStringValueFromParserState(state))
                {
                case LegacyHasRevisionsDocumentState:
                    NonPersistentFlags |= NonPersistentDocumentFlags.LegacyHasRevisions;
                    break;

                case LegacyRevisionState:
                    NonPersistentFlags |= NonPersistentDocumentFlags.LegacyRevision;
                    break;
                }
                break;

            case State.ReadingId:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Id, state, reader);
                }
                Id = CreateLazyStringValueFromParserState(state);
                break;

            case State.ReadingFlags:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Flags, state, reader);
                }
                Flags = ReadFlags(state);
                break;

            case State.ReadingLastModified:
            case State.ReadingLegacyLastModified:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.LastModified, state, reader);
                }
                LastModified = ReadDateTime(state, reader, _state);
                break;

            case State.ReadingChangeVector:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.ChangeVector, state, reader);
                }
                ChangeVector = CreateLazyStringValueFromParserState(state);

                break;

            case State.ReadingFirstEtagOfLegacyRevision:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString("@etag", state, reader);
                }
                FirstEtagOfLegacyRevision = LegacyEtag = CreateLazyStringValueFromParserState(state);
                ChangeVector = ChangeVectorUtils.NewChangeVector("RV", ++LegacyRevisionsCount, new Guid(FirstEtagOfLegacyRevision).ToBase64Unpadded());
                break;

            case State.ReadingEtag:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString("@etag", state, reader);
                }
                LegacyEtag = CreateLazyStringValueFromParserState(state);

                break;
            }
            return(true);
        }
예제 #6
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);
        }
예제 #7
0
        private void Delete(DocumentsOperationContext context, Slice lowerId, Slice idSlice, string id, CollectionName collectionName,
                            BlittableJsonReaderObject deleteRevisionDocument, string changeVector,
                            long lastModifiedTicks, NonPersistentDocumentFlags nonPersistentFlags, DocumentFlags flags)
        {
            Debug.Assert(changeVector != null, "Change vector must be set");

            if (flags.Contain(DocumentFlags.HasAttachments))
            {
                flags &= ~DocumentFlags.HasAttachments;
            }

            var configuration = GetRevisionsConfiguration(collectionName.Name, flags);

            if (configuration.Disabled)
            {
                return;
            }

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

            if (configuration.PurgeOnDelete)
            {
                using (GetKeyPrefix(context, lowerId, out Slice prefixSlice))
                {
                    DeleteRevisions(context, table, prefixSlice, collectionName, long.MaxValue, null, changeVector, lastModifiedTicks);
                    DeleteCountOfRevisions(context, prefixSlice);
                }

                return;
            }

            var fromReplication = (nonPersistentFlags & NonPersistentDocumentFlags.FromReplication) == NonPersistentDocumentFlags.FromReplication;

            if (fromReplication)
            {
                void DeleteFromRevisionIfChangeVectorIsGreater()
                {
                    TableValueReader tvr;

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

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

                    if (ChangeVectorUtils.GetConflictStatus(changeVector, docChangeVector) == ConflictStatus.Update)
                    {
                        _documentsStorage.Delete(context, lowerId, id, null, lastModifiedTicks, changeVector, collectionName,
                                                 nonPersistentFlags | NonPersistentDocumentFlags.FromRevision);
                    }
                }

                DeleteFromRevisionIfChangeVectorIsGreater();
            }

            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(idSlice);
                    tvb.Add(deleteRevisionDocument.BasePointer, deleteRevisionDocument.Size);
                    tvb.Add((int)(DocumentFlags.DeleteRevision | flags));
                    tvb.Add(newEtagSwapBytes);
                    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;
                    }
                }

            DeleteOldRevisions(context, table, lowerId, collectionName, configuration, nonPersistentFlags, changeVector, lastModifiedTicks);
        }
예제 #8
0
 public static bool Contain(this NonPersistentDocumentFlags current, NonPersistentDocumentFlags flag)
 {
     return((current & flag) == flag);
 }
예제 #9
0
        private bool AboutToReadWithStateUnlikely(IJsonParser reader, JsonParserState state)
        {
            switch (_state)
            {
            case State.None:
                break;

            case State.IgnoreProperty:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType == JsonParserToken.StartArray ||
                    state.CurrentTokenType == JsonParserToken.StartObject)
                {
                    ThrowInvalidMetadataProperty(state);
                }
                break;

            case State.IgnoreArray:
                if (_verifyStartArray)
                {
                    if (reader.Read() == false)
                    {
                        return(false);
                    }

                    _verifyStartArray = false;

                    if (state.CurrentTokenType != JsonParserToken.StartArray)
                    {
                        ThrowInvalidReplicationHistoryType(state);
                    }
                }
                while (state.CurrentTokenType != JsonParserToken.EndArray)
                {
                    if (reader.Read() == false)
                    {
                        return(false);
                    }
                }
                break;

            case State.IgnoreRevisionStatusProperty:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String &&
                    state.CurrentTokenType != JsonParserToken.Integer)
                {
                    ThrowInvalidEtagType(state);
                }

                switch (CreateLazyStringValueFromParserState(state))
                {
                case LegacyHasRevisionsDocumentState:
                    NonPersistentFlags |= NonPersistentDocumentFlags.LegacyHasRevisions;
                    break;

                case LegacyRevisionState:
                    NonPersistentFlags |= NonPersistentDocumentFlags.LegacyRevision;
                    break;
                }
                break;

            case State.ReadingId:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Id, state);
                }
                Id = CreateLazyStringValueFromParserState(state);
                break;

            case State.ReadingFlags:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Flags, state);
                }
                Flags = ReadFlags(state);
                break;

            case State.ReadingChangeVector:
                if (reader.Read() == false)
                {
                    return(false);
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.ChangeVector, state);
                }
                ChangeVector = CreateLazyStringValueFromParserState(state);

                break;
            }
            return(true);
        }
예제 #10
0
        private unsafe bool AboutToReadPropertyNameInMetadataUnlikely(IJsonParser reader, JsonParserState state, out bool aboutToReadPropertyName)
        {
            aboutToReadPropertyName = true;

            switch (state.StringSize)
            {
            default:     // accept this property
            {
                return(true);
            }

            case -1:     // IgnoreProperty
            {
                if (reader.Read() == false)
                {
                    _state = State.IgnoreProperty;
                    {
                        aboutToReadPropertyName = false;
                        return(true);
                    }
                }
                if (state.CurrentTokenType == JsonParserToken.StartArray ||
                    state.CurrentTokenType == JsonParserToken.StartObject)
                {
                    ThrowInvalidMetadataProperty(state);
                }
                break;
            }

            case 3:     // @id
                if (state.StringBuffer[0] != (byte)'@' ||
                    *(short *)(state.StringBuffer + 1) != 25705)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if (reader.Read() == false)
                {
                    _state = State.ReadingId;
                    {
                        aboutToReadPropertyName = false;
                        return(true);
                    }
                }
                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Id, state);
                }
                Id = CreateLazyStringValueFromParserState(state);
                break;

            case 6:     // @flags
                if (state.StringBuffer[0] != (byte)'@' ||
                    *(int *)(state.StringBuffer + 1) != 1734437990 ||
                    state.StringBuffer[1 + sizeof(int)] != (byte)'s')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if (reader.Read() == false)
                {
                    _state = State.ReadingFlags;
                    {
                        aboutToReadPropertyName = false;
                        return(true);
                    }
                }
                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.Flags, state);
                }
                Flags = ReadFlags(state);
                break;

            case 12:     // @index-score
                if (state.StringBuffer[0] != (byte)'@' ||
                    *(long *)(state.StringBuffer + 1) != 7166121427196997225 ||
                    *(short *)(state.StringBuffer + 1 + sizeof(long)) != 29295 ||
                    state.StringBuffer[1 + sizeof(long) + sizeof(short)] != (byte)'e')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 13:     //Last-Modified
                if (*(long *)state.StringBuffer != 7237087983830262092 ||
                    *(int *)(state.StringBuffer + sizeof(long)) != 1701406313 ||
                    state.StringBuffer[12] != (byte)'d')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 14:
                if (state.StringBuffer[0] == (byte)'@')
                {
                    // @change-vector
                    if (*(long *)(state.StringBuffer + 1) == 8515573965335390307 &&
                        *(int *)(state.StringBuffer + 1 + sizeof(long)) == 1869898597 &&
                        state.StringBuffer[1 + sizeof(long) + sizeof(int)] == (byte)'r')
                    {
                        if (reader.Read() == false)
                        {
                            _state = State.ReadingChangeVector;
                            {
                                aboutToReadPropertyName = false;
                                return(true);
                            }
                        }
                        if (state.CurrentTokenType != JsonParserToken.String)
                        {
                            ThrowExpectedFieldTypeOfString(Constants.Documents.Metadata.ChangeVector, state);
                        }
                        ChangeVector = CreateLazyStringValueFromParserState(state);
                        break;
                    }

                    // @last-modified
                    if (*(long *)(state.StringBuffer + 1) == 7237123168202350956 &&
                        *(int *)(state.StringBuffer + 1 + sizeof(long)) == 1701406313 &&
                        state.StringBuffer[1 + sizeof(long) + sizeof(int)] == (byte)'d')
                    {
                        goto case -1;
                    }
                }

                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

            case 15:     //Raven-Read-Only
                if (*(long *)state.StringBuffer != 7300947898092904786 ||
                    *(int *)(state.StringBuffer + sizeof(long)) != 1328374881 ||
                    *(short *)(state.StringBuffer + sizeof(long) + sizeof(int)) != 27758 ||
                    state.StringBuffer[14] != (byte)'y')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 17:     //Raven-Entity-Name --> @collection
                if (*(long *)state.StringBuffer != 7945807069737017682 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 7881666780093245812 ||
                    state.StringBuffer[16] != (byte)'e')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var collection = _metadataCollections;
                state.StringBuffer = collection.AllocatedMemoryData.Address;
                state.StringSize   = collection.Size;
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

            case 19:     //Raven-Last-Modified
                if (*(long *)state.StringBuffer != 7011028672080929106 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 7379539893622240371 ||
                    *(short *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 25961 ||
                    state.StringBuffer[18] != (byte)'d')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 21:     //Raven-Expiration-Date
                if (*(long *)state.StringBuffer != 8666383010116297042 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 7957695015158966640 ||
                    *(short *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 17453 ||
                    state.StringBuffer[20] != (byte)'e')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var expires = _metadataExpires;
                state.StringBuffer = expires.AllocatedMemoryData.Address;
                state.StringSize   = expires.Size;
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

            case 23:     //Raven-Document-Revision
                if (*(long *)state.StringBuffer != 8017583188798234962 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 5921517102558967139 ||
                    *(int *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 1936291429 ||
                    *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(int)) != 28521 ||
                    state.StringBuffer[22] != (byte)'n')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 24:     //Raven-Replication-Source
                if (*(long *)state.StringBuffer != 7300947898092904786 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928 ||
                    *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7305808869229538670)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 25:     //Raven-Replication-Version OR Raven-Replication-History
                if (*(long *)state.StringBuffer != 7300947898092904786 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var value    = *(long *)(state.StringBuffer + sizeof(long) + sizeof(long));
                var lastByte = state.StringBuffer[24];
                if ((value != 8028074745928232302 || lastByte != (byte)'n') &&
                    (value != 8245937481775066478 || lastByte != (byte)'y'))
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var isReplicationHistory = lastByte == (byte)'y';
                if (reader.Read() == false)
                {
                    _verifyStartArray = isReplicationHistory;
                    _state            = isReplicationHistory ? State.IgnoreArray : State.IgnoreProperty;
                    {
                        aboutToReadPropertyName = false;
                        return(true);
                    }
                }

                // Raven-Replication-History is an array
                if (isReplicationHistory)
                {
                    if (state.CurrentTokenType != JsonParserToken.StartArray)
                    {
                        ThrowInvalidReplicationHistoryType(state);
                    }

                    do
                    {
                        if (reader.Read() == false)
                        {
                            _state = State.IgnoreArray;
                            {
                                aboutToReadPropertyName = false;
                                return(true);
                            }
                        }
                    } while (state.CurrentTokenType != JsonParserToken.EndArray);
                }
                else if (state.CurrentTokenType == JsonParserToken.StartArray ||
                         state.CurrentTokenType == JsonParserToken.StartObject)
                {
                    ThrowInvalidMetadataProperty(state);
                }
                break;

            case 29:     //Non-Authoritative-Information
                if (*(long *)state.StringBuffer != 7526769800038477646 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 8532478930943832687 ||
                    *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7886488383206796645 ||
                    *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1869182049 ||
                    state.StringBuffer[28] != (byte)'n')
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;

            case 30:     //Raven-Document-Parent-Revision OR Raven-Document-Revision-Status
                if (*(long *)state.StringBuffer != 8017583188798234962)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                if ((*(long *)(state.StringBuffer + sizeof(long)) != 5777401914483111267 ||
                     *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7300947924012593761 ||
                     *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1769171318 ||
                     *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long) + sizeof(int)) != 28271) &&
                    (*(long *)(state.StringBuffer + sizeof(long)) != 5921517102558967139 ||
                     *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 3273676477843469925 ||
                     *(int *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 1952543827 ||
                     *(short *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long) + sizeof(int)) != 29557))
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                var isRevisionStatusProperty = state.StringBuffer[29] == 's';
                if (reader.Read() == false)
                {
                    _state = isRevisionStatusProperty ? State.IgnoreRevisionStatusProperty : State.IgnoreProperty;
                    {
                        aboutToReadPropertyName = false;
                        return(true);
                    }
                }

                if (state.CurrentTokenType == JsonParserToken.StartArray ||
                    state.CurrentTokenType == JsonParserToken.StartObject)
                {
                    ThrowInvalidMetadataProperty(state);
                }

                if (isRevisionStatusProperty)
                {
                    switch (CreateLazyStringValueFromParserState(state))
                    {
                    case LegacyHasRevisionsDocumentState:
                        NonPersistentFlags |= NonPersistentDocumentFlags.LegacyHasRevisions;
                        break;

                    case LegacyRevisionState:
                        NonPersistentFlags |= NonPersistentDocumentFlags.LegacyRevision;
                        break;
                    }
                }

                break;

            case 32:     //Raven-Replication-Merged-History
                if (*(long *)state.StringBuffer != 7300947898092904786 ||
                    *(long *)(state.StringBuffer + sizeof(long)) != 8028075772393122928 ||
                    *(long *)(state.StringBuffer + sizeof(long) + sizeof(long)) != 7234302117464059246 ||
                    *(long *)(state.StringBuffer + sizeof(long) + sizeof(long) + sizeof(long)) != 8751179571877464109)
                {
                    aboutToReadPropertyName = true;
                    return(true);
                }

                goto case -1;
            }
            return(false);
        }
예제 #11
0
        private bool ShouldRecreateAttachments(DocumentsOperationContext context, Slice lowerId, BlittableJsonReaderObject oldDoc,
                                               BlittableJsonReaderObject document, ref DocumentFlags flags, NonPersistentDocumentFlags nonPersistentFlags)
        {
            if ((nonPersistentFlags & NonPersistentDocumentFlags.ResolveAttachmentsConflict) == NonPersistentDocumentFlags.ResolveAttachmentsConflict)
            {
                document.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject metadata);
                RecreateAttachments(context, lowerId, document, metadata, ref flags);
                return(true);
            }

            if ((flags & DocumentFlags.HasAttachments) == DocumentFlags.HasAttachments &&
                (nonPersistentFlags & NonPersistentDocumentFlags.ByAttachmentUpdate) != NonPersistentDocumentFlags.ByAttachmentUpdate &&
                (nonPersistentFlags & NonPersistentDocumentFlags.FromReplication) != NonPersistentDocumentFlags.FromReplication)
            {
                if (oldDoc != null &&
                    oldDoc.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject oldMetadata) &&
                    oldMetadata.TryGet(Constants.Documents.Metadata.Attachments, out BlittableJsonReaderArray oldAttachments))
                {
                    // Make sure the user did not changed the value of @attachments in the @metadata
                    // In most cases it won't be changed so we can use this value
                    // instead of recreating the document's blitable from scratch
                    if (document.TryGet(Constants.Documents.Metadata.Key, out BlittableJsonReaderObject metadata) == false ||
                        metadata.TryGet(Constants.Documents.Metadata.Attachments, out BlittableJsonReaderArray attachments) == false ||
                        attachments.Equals(oldAttachments) == false)
                    {
                        RecreateAttachments(context, lowerId, document, metadata, ref flags);
                        return(true);
                    }
                }
            }

            return(false);
        }
예제 #12
0
        public PutOperationResults PutDocument(DocumentsOperationContext context, string id,
                                               string expectedChangeVector,
                                               BlittableJsonReaderObject document,
                                               long?lastModifiedTicks = null,
                                               string changeVector    = null,
                                               DocumentFlags flags    = DocumentFlags.None,
                                               NonPersistentDocumentFlags nonPersistentFlags = NonPersistentDocumentFlags.None)
        {
            if (context.Transaction == null)
            {
                ThrowRequiresTransaction();
                return(default(PutOperationResults)); // never hit
            }

#if DEBUG
            var documentDebugHash = document.DebugHash;
            document.BlittableValidation();
            BlittableJsonReaderObject.AssertNoModifications(document, id, assertChildren: true);
            AssertMetadataWasFiltered(document);
#endif

            var newEtag       = _documentsStorage.GenerateNextEtag();
            var modifiedTicks = _documentsStorage.GetOrCreateLastModifiedTicks(lastModifiedTicks);

            id = BuildDocumentId(id, newEtag, out bool knownNewId);
            using (DocumentIdWorker.GetLowerIdSliceAndStorageKey(context, id, out Slice lowerId, out Slice idPtr))
            {
                var collectionName = _documentsStorage.ExtractCollectionName(context, document);
                var table          = context.Transaction.InnerTransaction.OpenTable(DocsSchema, collectionName.GetTableName(CollectionTableType.Documents));

                var oldValue = default(TableValueReader);
                if (knownNewId == false)
                {
                    // delete a tombstone if it exists, if it known that it is a new ID, no need, so we can skip it
                    DeleteTombstoneIfNeeded(context, collectionName, lowerId.Content.Ptr, lowerId.Size);

                    table.ReadByKey(lowerId, out oldValue);
                }

                BlittableJsonReaderObject oldDoc = null;
                if (oldValue.Pointer == null)
                {
                    // expectedChangeVector being null means we don't care,
                    // and empty means that it must be new
                    if (string.IsNullOrEmpty(expectedChangeVector) == false)
                    {
                        ThrowConcurrentExceptionOnMissingDoc(id, expectedChangeVector);
                    }
                }
                else
                {
                    // expectedChangeVector  has special meaning here
                    // null - means, don't care, don't check
                    // "" / empty - means, must be new
                    // anything else - must match exactly
                    if (expectedChangeVector != null)
                    {
                        var oldChangeVector = TableValueToChangeVector(context, (int)DocumentsTable.ChangeVector, ref oldValue);
                        if (string.Compare(expectedChangeVector, oldChangeVector, StringComparison.Ordinal) != 0)
                        {
                            ThrowConcurrentException(id, expectedChangeVector, oldChangeVector);
                        }
                    }

                    oldDoc = new BlittableJsonReaderObject(oldValue.Read((int)DocumentsTable.Data, out int oldSize), oldSize, context);
                    var oldCollectionName = _documentsStorage.ExtractCollectionName(context, oldDoc);
                    if (oldCollectionName != collectionName)
                    {
                        ThrowInvalidCollectionNameChange(id, oldCollectionName, collectionName);
                    }

                    var oldFlags = TableValueToFlags((int)DocumentsTable.Flags, ref oldValue);

                    if ((nonPersistentFlags & NonPersistentDocumentFlags.ByAttachmentUpdate) != NonPersistentDocumentFlags.ByAttachmentUpdate &&
                        (nonPersistentFlags & NonPersistentDocumentFlags.FromReplication) != NonPersistentDocumentFlags.FromReplication)
                    {
                        if ((oldFlags & DocumentFlags.HasAttachments) == DocumentFlags.HasAttachments)
                        {
                            flags |= DocumentFlags.HasAttachments;
                        }
                    }
                }

                var result = BuildChangeVectorAndResolveConflicts(context, id, lowerId, newEtag, document, changeVector, expectedChangeVector, flags, oldValue);

                if (string.IsNullOrEmpty(result.ChangeVector))
                {
                    ChangeVectorUtils.ThrowConflictingEtag(id, changeVector, newEtag, _documentsStorage.Environment.Base64Id, _documentDatabase.ServerStore.NodeTag);
                }

                changeVector        = result.ChangeVector;
                nonPersistentFlags |= result.NonPersistentFlags;
                if (nonPersistentFlags.Contain(NonPersistentDocumentFlags.Resolved))
                {
                    flags |= DocumentFlags.Resolved;
                }

                if (collectionName.IsHiLo == false &&
                    (flags & DocumentFlags.Artificial) != DocumentFlags.Artificial)
                {
                    if (ShouldRecreateAttachments(context, lowerId, oldDoc, document, ref flags, nonPersistentFlags))
                    {
#if DEBUG
                        if (document.DebugHash != documentDebugHash)
                        {
                            throw new InvalidDataException("The incoming document " + id + " has changed _during_ the put process, " +
                                                           "this is likely because you are trying to save a document that is already stored and was moved");
                        }
#endif
                        document = context.ReadObject(document, id, BlittableJsonDocumentBuilder.UsageMode.ToDisk);
#if DEBUG
                        documentDebugHash = document.DebugHash;
                        document.BlittableValidation();
                        BlittableJsonReaderObject.AssertNoModifications(document, id, assertChildren: true);
                        AssertMetadataWasFiltered(document);
                        AttachmentsStorage.AssertAttachments(document, flags);
#endif
                    }

                    if (nonPersistentFlags.Contain(NonPersistentDocumentFlags.FromReplication) == false &&
                        (flags.Contain(DocumentFlags.Resolved) ||
                         _documentDatabase.DocumentsStorage.RevisionsStorage.Configuration != null
                        ))
                    {
                        var shouldVersion = _documentDatabase.DocumentsStorage.RevisionsStorage.ShouldVersionDocument(collectionName, nonPersistentFlags, oldDoc, document,
                                                                                                                      ref flags, out RevisionsCollectionConfiguration configuration);
                        if (shouldVersion)
                        {
                            _documentDatabase.DocumentsStorage.RevisionsStorage.Put(context, id, document, flags, nonPersistentFlags,
                                                                                    changeVector, modifiedTicks, configuration, collectionName);
                        }
                    }
                }

                using (Slice.From(context.Allocator, changeVector, out var cv))
                    using (table.Allocate(out TableValueBuilder tvb))
                    {
                        tvb.Add(lowerId);
                        tvb.Add(Bits.SwapBytes(newEtag));
                        tvb.Add(idPtr);
                        tvb.Add(document.BasePointer, document.Size);
                        tvb.Add(cv.Content.Ptr, cv.Size);
                        tvb.Add(modifiedTicks);
                        tvb.Add((int)flags);
                        tvb.Add(context.GetTransactionMarker());

                        if (oldValue.Pointer == null)
                        {
                            table.Insert(tvb);
                        }
                        else
                        {
                            table.Update(oldValue.Id, tvb);
                        }
                    }

                if (collectionName.IsHiLo == false)
                {
                    _documentsStorage.ExpirationStorage.Put(context, lowerId, document);
                }

                context.LastDatabaseChangeVector = changeVector;
                _documentDatabase.Metrics.Docs.PutsPerSec.MarkSingleThreaded(1);
                _documentDatabase.Metrics.Docs.BytesPutsPerSec.MarkSingleThreaded(document.Size);

                context.Transaction.AddAfterCommitNotification(new DocumentChange
                {
                    ChangeVector   = changeVector,
                    CollectionName = collectionName.Name,
                    Id             = id,
                    Type           = DocumentChangeTypes.Put,
                });

#if DEBUG
                if (document.DebugHash != documentDebugHash)
                {
                    throw new InvalidDataException("The incoming document " + id + " has changed _during_ the put process, " +
                                                   "this is likely because you are trying to save a document that is already stored and was moved");
                }
                document.BlittableValidation();
                BlittableJsonReaderObject.AssertNoModifications(document, id, assertChildren: true);
                AssertMetadataWasFiltered(document);
                AttachmentsStorage.AssertAttachments(document, flags);
#endif
                return(new PutOperationResults
                {
                    Etag = newEtag,
                    Id = id,
                    Collection = collectionName,
                    ChangeVector = changeVector,
                    Flags = flags,
                    LastModified = new DateTime(modifiedTicks)
                });
            }
        }