Example #1
0
        public async Task BulkDocs()
        {
            DocumentsOperationContext readBatchCommandContext;
            DocumentsOperationContext readDocumentsContext;

            using (ContextPool.AllocateOperationContext(out readBatchCommandContext))
                using (ContextPool.AllocateOperationContext(out readDocumentsContext))
                {
                    BlittableJsonReaderArray commands;
                    try
                    {
                        commands = await readBatchCommandContext.ParseArrayToMemoryAsync(RequestBodyStream(), "bulk/docs",
                                                                                         // we will prepare the docs to disk in the actual PUT command
                                                                                         BlittableJsonDocumentBuilder.UsageMode.None);
                    }
                    catch (InvalidDataException)
                    {
                        throw;
                    }
                    catch (Exception ioe)
                    {
                        throw new InvalidDataException("Could not parse json", ioe);
                    }

                    CommandData[] parsedCommands = new CommandData[commands.Length];

                    for (int i = 0; i < commands.Length; i++)
                    {
                        var cmd = commands.GetByIndex <BlittableJsonReaderObject>(i);

                        if (cmd.TryGet(nameof(CommandData.Method), out parsedCommands[i].Method) == false)
                        {
                            throw new InvalidDataException($"Missing '{nameof(CommandData.Method)}' property");
                        }

                        cmd.TryGet(nameof(CommandData.Key), out parsedCommands[i].Key); // Key can be null, we will generate new one

                        // optional
                        cmd.TryGet(nameof(CommandData.Etag), out parsedCommands[i].Etag);
                        cmd.TryGet(nameof(CommandData.AdditionalData), out parsedCommands[i].AdditionalData);

                        // We have to do additional processing on the documents
                        // in particular, prepare them for disk by compressing strings, validating floats, etc

                        // We **HAVE** to do that outside of the write transaction lock, that is why we are handling
                        // it in this manner, first parse the commands, then prepare for the put, finally open
                        // the transaction and actually write
                        switch (parsedCommands[i].Method)
                        {
                        case "PUT":
                            BlittableJsonReaderObject doc;
                            if (cmd.TryGet(nameof(PutCommandData.Document), out doc) == false)
                            {
                                throw new InvalidDataException($"Missing '{nameof(PutCommandData.Document)}' property");
                            }

                            // we need to split this document to an independent blittable document
                            // and this time, we'll prepare it for disk.
                            doc.PrepareForStorage();
                            parsedCommands[i].Document = readDocumentsContext.ReadObject(doc, parsedCommands[i].Key,
                                                                                         BlittableJsonDocumentBuilder.UsageMode.ToDisk);
                            break;

                        case "PATCH":
                            cmd.TryGet(nameof(PatchCommandData.DebugMode), out parsedCommands[i].IsDebugMode);

                            BlittableJsonReaderObject patch;
                            if (cmd.TryGet(nameof(PatchCommandData.Patch), out patch) == false)
                            {
                                throw new InvalidDataException($"Missing '{nameof(PatchCommandData.Patch)}' property");
                            }

                            parsedCommands[i].Patch = PatchRequest.Parse(patch);
                            break;
                        }
                    }

                    var waitForIndexesTimeout = GetTimeSpanQueryString("waitForIndexesTimeout", required: false);

                    var mergedCmd = new MergedBatchCommand
                    {
                        Database       = Database,
                        ParsedCommands = parsedCommands,
                        Reply          = new DynamicJsonArray()
                    };
                    if (waitForIndexesTimeout != null)
                    {
                        mergedCmd.ModifiedCollections = new HashSet <string>();
                    }
                    try
                    {
                        await Database.TxMerger.Enqueue(mergedCmd);
                    }
                    catch (ConcurrencyException)
                    {
                        HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
                        throw;
                    }

                    var waitForReplicasTimeout = GetTimeSpanQueryString("waitForReplicasTimeout", required: false);
                    if (waitForReplicasTimeout != null)
                    {
                        await WaitForReplicationAsync(waitForReplicasTimeout.Value, mergedCmd);
                    }

                    if (waitForIndexesTimeout != null)
                    {
                        await WaitForIndexesAsync(waitForIndexesTimeout.Value, mergedCmd.LastEtag, mergedCmd.ModifiedCollections);
                    }

                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Created;

                    using (var writer = new BlittableJsonTextWriter(readBatchCommandContext, ResponseBodyStream()))
                    {
                        readBatchCommandContext.Write(writer, new DynamicJsonValue
                        {
                            ["Results"] = mergedCmd.Reply
                        });
                    }
                }
        }
Example #2
0
        private static async Task <CommandData> ReadSingleCommand(
            JsonOperationContext ctx,
            Stream stream,
            JsonParserState state,
            UnmanagedJsonParser parser,
            JsonOperationContext.ManagedPinnedBuffer buffer,
            CancellationToken token)
        {
            var commandData = new CommandData();

            if (state.CurrentTokenType != JsonParserToken.StartObject)
            {
                ThrowUnexpectedToken(JsonParserToken.StartObject, state);
            }

            while (true)
            {
                while (parser.Read() == false)
                {
                    await RefillParserBuffer(stream, buffer, parser, token);
                }

                if (state.CurrentTokenType == JsonParserToken.EndObject)
                {
                    break;
                }

                if (state.CurrentTokenType != JsonParserToken.String)
                {
                    ThrowUnexpectedToken(JsonParserToken.String, state);
                }
                switch (GetPropertyType(state))
                {
                case CommandPropertyName.Type:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    if (state.CurrentTokenType != JsonParserToken.String)
                    {
                        ThrowUnexpectedToken(JsonParserToken.String, state);
                    }
                    commandData.Type = GetCommandType(state, ctx);
                    break;

                case CommandPropertyName.Id:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    switch (state.CurrentTokenType)
                    {
                    case JsonParserToken.Null:
                        commandData.Id = null;
                        break;

                    case JsonParserToken.String:
                        commandData.Id = GetStringPropertyValue(state);
                        break;

                    default:
                        ThrowUnexpectedToken(JsonParserToken.String, state);
                        break;
                    }
                    break;

                case CommandPropertyName.Name:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    switch (state.CurrentTokenType)
                    {
                    case JsonParserToken.Null:
                        commandData.Name = null;
                        break;

                    case JsonParserToken.String:
                        commandData.Name = GetStringPropertyValue(state);
                        break;

                    default:
                        ThrowUnexpectedToken(JsonParserToken.String, state);
                        break;
                    }
                    break;

                case CommandPropertyName.DestinationId:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    switch (state.CurrentTokenType)
                    {
                    case JsonParserToken.Null:
                        commandData.DestinationId = null;
                        break;

                    case JsonParserToken.String:
                        commandData.DestinationId = GetStringPropertyValue(state);
                        break;

                    default:
                        ThrowUnexpectedToken(JsonParserToken.String, state);
                        break;
                    }
                    break;

                case CommandPropertyName.DestinationName:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    switch (state.CurrentTokenType)
                    {
                    case JsonParserToken.Null:
                        commandData.DestinationName = null;
                        break;

                    case JsonParserToken.String:
                        commandData.DestinationName = GetStringPropertyValue(state);
                        break;

                    default:
                        ThrowUnexpectedToken(JsonParserToken.String, state);
                        break;
                    }
                    break;

                case CommandPropertyName.ContentType:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    switch (state.CurrentTokenType)
                    {
                    case JsonParserToken.Null:
                        commandData.ContentType = string.Empty;
                        break;

                    case JsonParserToken.String:
                        commandData.ContentType = GetStringPropertyValue(state);
                        break;

                    default:
                        ThrowUnexpectedToken(JsonParserToken.String, state);
                        break;
                    }
                    break;

                case CommandPropertyName.Document:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    commandData.Document = await ReadJsonObject(ctx, stream, commandData.Id, parser, state, buffer, token);

                    break;

                case CommandPropertyName.Patch:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    var patch = await ReadJsonObject(ctx, stream, commandData.Id, parser, state, buffer, token);

                    commandData.Patch = PatchRequest.Parse(patch, out commandData.PatchArgs);
                    break;

                case CommandPropertyName.PatchIfMissing:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    var patchIfMissing = await ReadJsonObject(ctx, stream, commandData.Id, parser, state, buffer, token);

                    commandData.PatchIfMissing = PatchRequest.Parse(patchIfMissing, out commandData.PatchIfMissingArgs);
                    break;

                case CommandPropertyName.ChangeVector:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    if (state.CurrentTokenType == JsonParserToken.Null)
                    {
                        commandData.ChangeVector = null;
                    }
                    else
                    {
                        if (state.CurrentTokenType != JsonParserToken.String)
                        {
                            ThrowUnexpectedToken(JsonParserToken.String, state);
                        }

                        commandData.ChangeVector = GetLazyStringValue(ctx, state);
                    }
                    break;

                case CommandPropertyName.Index:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    if (state.CurrentTokenType != JsonParserToken.Integer)
                    {
                        ThrowUnexpectedToken(JsonParserToken.True, state);
                    }
                    commandData.Index = state.Long;

                    break;

                case CommandPropertyName.IdPrefixed:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }

                    if (state.CurrentTokenType != JsonParserToken.True && state.CurrentTokenType != JsonParserToken.False)
                    {
                        ThrowUnexpectedToken(JsonParserToken.True, state);
                    }

                    commandData.IdPrefixed = state.CurrentTokenType == JsonParserToken.True;
                    break;

                case CommandPropertyName.ReturnDocument:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }

                    if (state.CurrentTokenType != JsonParserToken.True && state.CurrentTokenType != JsonParserToken.False)
                    {
                        ThrowUnexpectedToken(JsonParserToken.True, state);
                    }

                    commandData.ReturnDocument = state.CurrentTokenType == JsonParserToken.True;
                    break;

                case CommandPropertyName.Counters:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    var counterOps = await ReadJsonObject(ctx, stream, commandData.Id, parser, state, buffer, token);

                    commandData.Counters = DocumentCountersOperation.Parse(counterOps);
                    break;

                case CommandPropertyName.FromEtl:
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }

                    if (state.CurrentTokenType != JsonParserToken.True && state.CurrentTokenType != JsonParserToken.False)
                    {
                        ThrowUnexpectedToken(JsonParserToken.True, state);
                    }

                    commandData.FromEtl = state.CurrentTokenType == JsonParserToken.True;
                    break;

                case CommandPropertyName.NoSuchProperty:
                    // unknown command - ignore it
                    while (parser.Read() == false)
                    {
                        await RefillParserBuffer(stream, buffer, parser, token);
                    }
                    if (state.CurrentTokenType == JsonParserToken.StartObject ||
                        state.CurrentTokenType == JsonParserToken.StartArray)
                    {
                        await ReadJsonObject(ctx, stream, commandData.Id, parser, state, buffer, token);
                    }
                    break;
                }
            }

            switch (commandData.Type)
            {
            case CommandType.None:
                ThrowInvalidType();
                break;

            case CommandType.PUT:
                if (commandData.Document == null)
                {
                    ThrowMissingDocumentProperty();
                }
                break;

            case CommandType.PATCH:
                if (commandData.Patch == null)
                {
                    ThrowMissingPatchProperty();
                }
                break;

            case CommandType.AttachmentPUT:
                if (commandData.Name == null)
                {
                    ThrowMissingNameProperty();
                }
                break;

            case CommandType.Counters:
                if (commandData.Counters == null)
                {
                    ThrowMissingNameProperty();
                }
                break;
            }

            return(commandData);
        }
Example #3
0
        public Task Patch()
        {
            var id = GetQueryStringValueAndAssertIfSingleAndNotEmpty("id");

            var etag       = GetLongFromHeaders("If-Match");
            var isTestOnly = GetBoolValueQueryString("test", required: false) ?? false;

            DocumentsOperationContext context;

            using (ContextPool.AllocateOperationContext(out context))
            {
                var request = context.Read(RequestBodyStream(), "ScriptedPatchRequest");

                BlittableJsonReaderObject patchCmd, patchIsMissingCmd;
                if (request.TryGet("Patch", out patchCmd) == false)
                {
                    throw new ArgumentException("The 'Patch' field in the body request is mandatory");
                }
                var patch = PatchRequest.Parse(patchCmd);

                PatchRequest patchIfMissing = null;
                if (request.TryGet("PatchIfMissing", out patchIsMissingCmd))
                {
                    patchIfMissing = PatchRequest.Parse(patchCmd);
                }

                // TODO: In order to properly move this to the transaction merger, we need
                // TODO: move a lot of the costs (such as script parsing) out, so we create
                // TODO: an object that we'll apply, otherwise we'll slow down a lot the transactions
                // TODO: just by doing the javascript parsing and preparing the engine

                PatchResultData patchResult;
                using (context.OpenWriteTransaction())
                {
                    patchResult = Database.Patch.Apply(context, id, etag, patch, patchIfMissing, isTestOnly);
                    context.Transaction.Commit();
                }

                Debug.Assert(patchResult.PatchResult == PatchResult.Patched == isTestOnly == false);

                using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream()))
                {
                    writer.WriteStartObject();

                    writer.WritePropertyName(("Patched"));
                    writer.WriteBool(isTestOnly == false);
                    writer.WriteComma();

                    writer.WritePropertyName(("Debug"));
                    writer.WriteObject(patchResult.ModifiedDocument);

                    if (isTestOnly)
                    {
                        writer.WriteComma();
                        writer.WritePropertyName(("Document"));
                        writer.WriteObject(patchResult.OriginalDocument);
                    }

                    writer.WriteEndObject();
                }
            }

            return(Task.CompletedTask);
        }