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 }); } } }
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); }
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); }