Add transactional batch support (batch command) - #135
Conversation
Implements issue #105: a 'batch' command that executes multiple write operations against a single partition key as one atomic Cosmos DB transactional batch. - Single-shot: 'batch run <json> --partition-key <pk>' (also reads piped input) - Stateful: 'batch begin/add/execute/cancel/status' with a [batch:N] prompt indicator - Supports create, upsert, replace, delete, and patch operations - Extracts shared patch-op building into PatchOperationFactory (reused by patch command) - Adds en.ftl strings, docs, offline parser tests, and emulator integration tests
Prints the queued operations of the active stateful batch as a JSON array (the same shape accepted by 'batch run'/'batch add'). Each spec now retains its raw operation JSON for faithful round-tripping. Includes docs, localization, and offline + integration tests.
There was a problem hiding this comment.
Pull request overview
Adds first-class Cosmos DB transactional batch support to CosmosDBShell via a new batch command, enabling atomic multi-operation writes scoped to a single partition key, with both single-shot and stateful workflows.
Changes:
- Introduces
batchcommand with subcommands forrun/begin/add/execute/cancel/status/show, plus a queued-op prompt indicator ([batch:N]). - Adds shared batch parsing/execution components (
BatchOperationParser,BatchExecutor) and state tracking (PendingBatchState). - Extracts patch sub-operation construction into a reusable
PatchOperationFactory, reused by bothpatchandbatch, with unit + integration test coverage.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Adds batch to the feature list. |
| docs/commands.md | Documents batch usage, schema, output, examples, and errors. |
| CosmosDBShell/lang/en.ftl | Adds localized help text and error/success messages for batch. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.cs | Stores/clears the active pending batch on connect/disconnect. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/PendingBatchState.cs | New state object holding db/container/pk and queued operations. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/CosmosShellPrompt.cs | Adds [batch:N] indicator and refresh logic to prompt rendering. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/BatchOperationSpec.cs | New operation model for batch execution + round-trippable raw JSON. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/PatchOperationFactory.cs | New shared builder for patch operations (used by patch + batch). |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/PatchCommand.cs | Refactors to use PatchOperationFactory. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/BatchOperationParser.cs | Parses/validates batch operation JSON into specs (incl. patch operations). |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/BatchExecutor.cs | Builds/executes TransactionalBatch and emits JSON summary results. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/BatchCommand.cs | New batch CLI command implementation (single-shot + stateful modes). |
| CosmosDBShell.Tests/Integration/BatchOperationTests.cs | Emulator integration coverage for batch success/failure/stateful flows. |
| CosmosDBShell.Tests/CommandTests/BatchCommandTests.cs | Offline unit tests for batch JSON parsing/validation + raw round-trip. |
…ey docs batch run now wraps service CosmosExceptions in the localized command-batch-error-execution_failed message, matching batch execute. Docs clarify --partition-key is only required for run and begin.
PatchOperationFactory.Build now takes an unsupportedOpMessageKey so batch patch entries surface command-batch-error-unsupported_patch_op instead of the patch command usage line. missing_data text now notes a single operation object is also accepted.
Code Coverage OverviewLanguages: C# C# / code-coverage/dotnetThe overall line coverage in commit 26afe53 in the Show a line coverage summary of the most impacted files.
Updated |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Suppressed comments (1)
CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/BatchCommand.cs:265
command-batch-addedpluralizes on$count, butspecs.Count.ToString(...)passes a string and can break singular/plural selection in Fluent. Pass counts as integers (and you can also passtotalas an int for consistency).
batch.Operations.AddRange(specs);
ShellInterpreter.WriteLine(MessageService.GetArgsString(
"command-batch-added",
"count",
specs.Count.ToString(CultureInfo.InvariantCulture),
"total",
batch.Operations.Count.ToString(CultureInfo.InvariantCulture)));
return new CommandState();
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
CosmosDBShell/Azure.Data.Cosmos.Shell.Core/CosmosShellPrompt.cs:65
- Theme.FormatMuted(...) already applies Markup.Escape internally. Passing Markup.Escape(...) here double-escapes the text, so the prompt will render the literal escape sequences (e.g., showing "[[batch:1]]" instead of "[batch:1]") when a batch is active.
if (batch is not null)
{
basePrompt += " " + Theme.FormatMuted(Markup.Escape($"[batch:{batch.Operations.Count}]"));
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Suppressed comments (2)
CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/BatchCommand.cs:234
batch beginprints a human-facing message unconditionally. In machine mode this can introduce non-structured output; consider suppressing this message whenshell.IsMachineModeis true so only structured results are emitted.
ShellInterpreter.WriteLine(MessageService.GetArgsString(
"command-batch-begun",
"database",
databaseName!,
"container",
CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/BatchCommand.cs:262
batch addprints a human-facing message unconditionally. In machine mode this can pollute stdout and make output harder to consume programmatically; consider suppressing the message whenshell.IsMachineModeis true.
ShellInterpreter.WriteLine(MessageService.GetArgsString(
"command-batch-added",
"count",
specs.Count,
"total",
Return structured error states for rolled-back transactional batches so CLI and MCP callers receive failure semantics without losing per-operation results. Restrict MCP batch calls to the stateless run workflow and document the contract.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/BatchCommand.cs:66
- For stateful subcommands (
add/execute/cancel/status/show), the command still accepts--partition-key/-db/-conoptions but silently ignores them in favor of the active batch’s bound context. This can mislead users into thinking they’re queuing/executing against a different container or partition key than the one that will actually be used.
public override async Task<CommandState> ExecuteAsync(ShellInterpreter shell, CommandState commandState, string commandText, CancellationToken token)
{
var subcommand = this.Subcommand.Trim().ToLowerInvariant();
return subcommand switch
Use human-oriented interactive output for batch status and execution while preserving structured machine results. Classify transactional failures by HTTP status, keep structured error details, and harden MCP batch validation.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
CosmosDBShell/Azure.Data.Cosmos.Shell.Mcp/ToolOperations.cs:466
(cmd as BatchCommand)?.Subcommand.Trim()can throw if MCP binds a JSONnullforsubcommand(the binder sets the string property to null). Use a null-conditional trim to avoid a NullReferenceException and let the normal missing/invalid-subcommand path handle it.
var batchSubcommand = (cmd as BatchCommand)?.Subcommand.Trim();
Closes #105.
Adds a
batchcommand for executing Cosmos DB transactional batches — atomic multi-operation transactions scoped to a single partition key.Design
Supports both a single-shot mode and a stateful mode:
batch run <json> --partition-key <pk>executes an array of operations atomically.batch begin→batch add→batch execute, withbatch cancel,batch status, andbatch showfor inspection/control.The partition key is always supplied explicitly via
--partition-key(--pk). Operations may becreate,upsert,replace,delete, orpatch. Input can be piped (e.g.cat batch.json | batch run --pk myPk) or passed inline.Subcommands
runbeginaddexecute(exec,commit)cancel(abort)statusshowrun/add).When a stateful batch is active, the prompt shows a
[batch:N]indicator with the queued operation count.Implementation notes
BatchOperationParserparses the operation JSON (array or single object) intoBatchOperationSpecrecords; each spec retains its raw operation JSON sobatch showcan faithfully round-trip the input.BatchExecutorbuilds theTransactionalBatchand emits a JSON result summary (success, status code, request charge, per-operation results).PatchCommandinto a sharedPatchOperationFactoryreused by bothpatchandbatch.PendingBatchStateholds the in-progress stateful batch onShellInterpreter; it is cleared on connect/disconnect.Validation
BatchCommandTests(including raw-operation round-trip).BatchOperationTests(multi-create, create-then-patch, rollback on failure, begin/add/execute, cancel, show, and error paths) using a dedicated/pk-partitioned container.Docs
docs/commands.md: fullbatchsection with usage, subcommand table, and examples.README.md: feature bullet.Workflow: