public async Task BulkDocs() { using (ContextPool.AllocateOperationContext(out DocumentsOperationContext context)) using (var command = new MergedBatchCommand(Database)) { var contentType = HttpContext.Request.ContentType; if (contentType == null || contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { await BatchRequestParser.BuildCommandsAsync(context, command, RequestBodyStream(), Database, ServerStore); } else if (contentType.StartsWith("multipart/mixed", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase)) { await ParseMultipart(context, command); } else { ThrowNotSupportedType(contentType); } var waitForIndexesTimeout = GetTimeSpanQueryString("waitForIndexesTimeout", required: false); var waitForIndexThrow = GetBoolValueQueryString("waitForIndexThrow", required: false) ?? true; var specifiedIndexesQueryString = HttpContext.Request.Query["waitForSpecificIndex"]; if (command.IsClusterTransaction) { if (Server.Configuration.Core.FeaturesAvailability == FeaturesAvailability.Stable) { FeaturesAvailabilityException.Throw("Cluster Transactions"); } using (Database.ClusterTransactionWaiter.CreateTask(out var taskId)) { // Since this is a cluster transaction we are not going to wait for the write assurance of the replication. // Because in any case the user will get a raft index to wait upon on his next request. var options = new ClusterTransactionCommand.ClusterTransactionOptions(taskId) { WaitForIndexesTimeout = waitForIndexesTimeout, WaitForIndexThrow = waitForIndexThrow, SpecifiedIndexesQueryString = specifiedIndexesQueryString.Count > 0 ? specifiedIndexesQueryString.ToList() : null }; await HandleClusterTransaction(context, command, options); } return; } if (waitForIndexesTimeout != null) { command.ModifiedCollections = new HashSet <string>(); } try { await Database.TxMerger.Enqueue(command); command.ExceptionDispatchInfo?.Throw(); } catch (ConcurrencyException) { HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; throw; } var waitForReplicasTimeout = GetTimeSpanQueryString("waitForReplicasTimeout", required: false); if (waitForReplicasTimeout != null) { var numberOfReplicasStr = GetStringQueryString("numberOfReplicasToWaitFor", required: false) ?? "1"; var throwOnTimeoutInWaitForReplicas = GetBoolValueQueryString("throwOnTimeoutInWaitForReplicas", required: false) ?? true; await WaitForReplicationAsync(Database, waitForReplicasTimeout.Value, numberOfReplicasStr, throwOnTimeoutInWaitForReplicas, command.LastChangeVector); } if (waitForIndexesTimeout != null) { await WaitForIndexesAsync(ContextPool, Database, waitForIndexesTimeout.Value, specifiedIndexesQueryString.ToList(), waitForIndexThrow, command.LastChangeVector, command.LastTombstoneEtag, command.ModifiedCollections); } HttpContext.Response.StatusCode = (int)HttpStatusCode.Created; using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream())) { context.Write(writer, new DynamicJsonValue { [nameof(BatchCommandResult.Results)] = command.Reply }); } } }
public async Task BulkDocs() { using (ContextPool.AllocateOperationContext(out DocumentsOperationContext context)) using (var command = new MergedBatchCommand { Database = Database }) { var contentType = HttpContext.Request.ContentType; if (contentType == null || contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { command.ParsedCommands = await BatchRequestParser.BuildCommandsAsync(context, RequestBodyStream(), Database, ServerStore); } else if (contentType.StartsWith("multipart/mixed", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase)) { await ParseMultipart(context, command); } else { ThrowNotSupportedType(contentType); } var waitForIndexesTimeout = GetTimeSpanQueryString("waitForIndexesTimeout", required: false); if (waitForIndexesTimeout != null) { command.ModifiedCollections = new HashSet <string>(); } try { await Database.TxMerger.Enqueue(command); command?.ExceptionDispatchInfo?.Throw(); } catch (ConcurrencyException) { HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; throw; } var waitForReplicasTimeout = GetTimeSpanQueryString("waitForReplicasTimeout", required: false); if (waitForReplicasTimeout != null) { await WaitForReplicationAsync(waitForReplicasTimeout.Value, command); } if (waitForIndexesTimeout != null) { await WaitForIndexesAsync(waitForIndexesTimeout.Value, command.LastChangeVector, command.LastTombstoneEtag, command.ModifiedCollections); } HttpContext.Response.StatusCode = (int)HttpStatusCode.Created; using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream())) { context.Write(writer, new DynamicJsonValue { ["Results"] = command.Reply }); } } }
private async Task HandleClusterTransaction(DocumentsOperationContext context, MergedBatchCommand command, ClusterTransactionCommand.ClusterTransactionOptions options) { var clusterTransactionCommand = new ClusterTransactionCommand(Database.Name, command.ParsedCommands, options); var result = await ServerStore.SendToLeaderAsync(clusterTransactionCommand); if (result.Result is List <string> errors) { HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; throw new ConcurrencyException($"Failed to execute cluster transaction due to the following issues: {string.Join(Environment.NewLine, errors)}"); } // wait for the command to be applied on this node await ServerStore.WaitForCommitIndexChange(RachisConsensus.CommitIndexModification.GreaterOrEqual, result.Index); var array = new DynamicJsonArray(); if (clusterTransactionCommand.DatabaseCommandsCount > 0) { var reply = (ClusterTransactionCompletionResult)await Database.ClusterTransactionWaiter.WaitForResults(options.TaskId, HttpContext.RequestAborted); if (reply.IndexTask != null) { await reply.IndexTask; } array = reply.Array; } foreach (var clusterCommands in clusterTransactionCommand.ClusterCommands) { array.Add(new DynamicJsonValue { ["Type"] = clusterCommands.Type, ["Key"] = clusterCommands.Id, ["Index"] = result.Index }); } HttpContext.Response.StatusCode = (int)HttpStatusCode.Created; using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream())) { context.Write(writer, new DynamicJsonValue { [nameof(BatchCommandResult.Results)] = array, [nameof(BatchCommandResult.TransactionIndex)] = result.Index }); } }