You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(opensearchapi): add partial failure errors for bulk, search, and write operations
OpenSearch returns HTTP 200 for partial failures (bulk item errors,
shard failures), forcing callers to double-check responses after
err == nil. This adds PartialBulkError, PartialSearchError, and
ShardFailureError types so callers only need the standard Go
if err != nil idiom. Both (resp, err) are non-nil on partial
failure — the response is fully populated.
Gated behind Config.ReturnQueryErrors (default false in v4, will
flip to true in v5). Existing behavior is unchanged unless opted in.
Adds helper functions IsPartialFailure, ToleratePartialFailures,
and RequireSuccessRate for threshold-based error tolerance.
Ref: opensearch-project#816
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
- Add partial failure error types (`PartialBulkError`, `PartialSearchError`, `ShardFailureError`) that surface HTTP 200 partial failures as Go errors when `Config.ReturnQueryErrors` is enabled ([#816](https://github.com/opensearch-project/opensearch-go/issues/816))
93
+
-`PartialBulkError` returned from `Bulk` when `resp.Errors` is true, carries `FailedItems` and `SucceededCount`
94
+
-`PartialSearchError` returned from `Search`, `MSearch`, `MSearchTemplate`, `SearchTemplate`, `Scroll.Get` when `_shards.failed > 0`
95
+
-`ShardFailureError` returned from `Index`, `Document.Create`, `Document.Delete`, `Update` when replica shards fail
96
+
-`PartialFailureError` marker interface with `IsPartial() bool` for type-switching across all partial failure types
97
+
- Helper functions: `IsPartialFailure`, `ToleratePartialFailures`, `RequireSuccessRate` for threshold-based error tolerance
-`Config.ReturnQueryErrors` defaults to `false` in v4 (opt-in), will flip to `true` in v5
100
+
-`OPENSEARCH_GO_PARTIAL_QUERY_ERRORS` environment variable overrides `Config.ReturnQueryErrors` at runtime
101
+
- Both `(resp, error)` are non-nil on partial failure -- response is fully populated
92
102
- Transport automatically sets `max_concurrent_shard_requests` query parameter on search requests routed through a coordinator node
93
103
- Value derived from a cluster-wide aggregate of all polled nodes' search pool wait-time and completion deltas, clamped to `[floor, cap]` (default: 5–256)
94
104
- Cluster-wide signal correctly models data-node fan-out capacity: single hot nodes are diluted by healthy peers, and MCSR only drops when aggregate cluster pressure rises
-[Response.Body becomes a method](#responsebody-becomes-a-method)
3
4
-[StringError for unknown JSON responses](#stringerror-for-unknown-json-responses)
4
5
-[Upgrading to >= 4.7.0](#upgrading-to->=-4.7.0)
@@ -28,6 +29,75 @@
28
29
29
30
## Upgrading to >= 5.0.0
30
31
32
+
### Partial Failure Errors (ReturnQueryErrors)
33
+
34
+
Version 5.0.0 changes `Config.ReturnQueryErrors` to default to `true`. When enabled, API methods return typed errors for partial failures (HTTP 200 responses with embedded errors), so callers only need the standard `if err != nil` pattern. Both the response and the error are non-nil on partial failure -- the response is fully populated.
35
+
36
+
**Why**: OpenSearch returns HTTP 200 for operations that partially succeed (bulk item failures, shard failures on search, replica failures on writes). Before this change, callers had to remember a second error check after every operation, which is easy to forget and non-idiomatic Go.
37
+
38
+
**What changes in v5**: If you relied on `err == nil` meaning "no failures of any kind", your code is already correct -- you were ignoring partial failures. With `ReturnQueryErrors: true` (now the default), those partial failures surface as errors. Your `if err != nil` blocks will now catch them.
39
+
40
+
**If you need the old behavior**, set `ReturnQueryErrors: false`:
// Suppress all partial failures (best-effort operations)
81
+
err = opensearchapi.ToleratePartialFailures(err)
82
+
83
+
// Fail only if success rate drops below threshold
84
+
err = opensearchapi.RequireSuccessRate(err, 0.99)
85
+
86
+
// Test whether an error is a partial failure
87
+
if opensearchapi.IsPartialFailure(err) { ... }
88
+
```
89
+
90
+
**Operation constants** for `ShardFailureError.Operation`:
91
+
92
+
```go
93
+
opensearchapi.OperationIndex// "index"
94
+
opensearchapi.OperationCreate// "create"
95
+
opensearchapi.OperationUpdate// "update"
96
+
opensearchapi.OperationDelete// "delete"
97
+
```
98
+
99
+
See [Error Handling and Partial Failures](guides/error_handling.md) for the full guide.
100
+
31
101
### StringError for Unknown JSON Responses
32
102
33
103
Version 5.0.0 returns `*opensearch.StringError` error type instead of `*fmt.wrapError` when response received from the server is an unknown JSON. For example, consider delete document API which returns an unknown JSON body when document is not found.
When `ReturnQueryErrors` is enabled, API methods return typed errors for partial failures alongside the fully populated response. This eliminates the need for manual double-checking and follows Go's `(result, error)` convention where both can be non-nil.
> **Migration note**: `ReturnQueryErrors` defaults to `false` in v4 for backward compatibility. It will default to `true` in v5.
47
+
>
48
+
> You can also enable this via the `OPENSEARCH_GO_PARTIAL_QUERY_ERRORS=true` environment variable, which takes priority over the `Config` field. This is useful for toggling the behavior at deploy time without code changes.
49
+
50
+
### Bulk Operations
51
+
52
+
When `ReturnQueryErrors` is enabled, bulk operations return a `*PartialBulkError` when any items fail. The response is still fully populated -- callers can inspect both the error and the response.
Multi-search (`MSearch`, `MSearchTemplate`) and scroll (`Scroll.Get`) operations also return `PartialSearchError` when any sub-response has shard failures. The error aggregates failures across all sub-responses.
95
+
96
+
### Write Operations
97
+
98
+
Index, Create, Update, and Delete operations return a `*ShardFailureError` when the primary shard succeeds but replica shards fail. The `Operation` field identifies which write operation was performed.
The simplest way to catch partial failures is to enable `ReturnQueryErrors` in the client config. This surfaces partial failures through the standard `error` return, so the idiomatic `if err != nil` catches everything:
@@ -298,7 +436,7 @@ When implementing retry logic for bulk operations:
298
436
-**Retry only the failed items**, not the entire batch. Items that succeeded in the original request do not need to be resubmitted (resubmitting may cause version conflicts or duplicate documents depending on whether document IDs are set).
299
437
-**Set client-assigned `_id` values on bulk items.** After a timeout or ambiguous failure, the client can query for expected document IDs to determine which items were persisted, turning a blind retry into a targeted one. See [Bulk: Use client-assigned document IDs for recoverability](bulk.md#use-client-assigned-document-ids-for-recoverability).
0 commit comments