Skip to content

Commit 597b12d

Browse files
committed
Add connection pool health checking documentation and update CHANGELOG
New guide: guides/connection_pool.md documenting the connection pool architecture, active/standby partitioning, lifecycle state management, weighted round-robin, and capacity modeling. Update existing guides: - guides/cluster_health_checking.md: document tiered health checking with two-phase readiness and load shedding. - guides/request_routing.md: update for weighted routing and core-count auto-discovery. Update USER_GUIDE.md with new configuration options. Update DEVELOPER_GUIDE.md with test infrastructure and heterogeneous cluster testing instructions. Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
1 parent d630797 commit 597b12d

11 files changed

Lines changed: 2454 additions & 19 deletions

CHANGELOG.md

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,91 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
66

77
### Added
88

9+
- Enhanced cluster readiness checking for improved test reliability: `testutil.NewClient()` now includes readiness validation (health + cluster state + nodes info)
10+
- Test parallelization support via TEST_PARALLEL environment variable (default: CPU cores - 1, minimum 1)
11+
- opensearchapi/testutil package with test suite, client helpers, and JSON comparison utilities
12+
- opensearchtransport/testutil package with PollUntil helper for eventual consistency testing (ISM policies, index readiness, cluster state changes)
913
- Configuration option `IncludeDedicatedClusterManagers` for controlling cluster manager node routing ([#765](https://github.com/opensearch-project/opensearch-go/issues/765))
14+
- Policy-based routing system for improved request routing and service availability ([#771](https://github.com/opensearch-project/opensearch-go/pull/771))
15+
- `Policy` interface for composable routing strategies with lifecycle management
16+
- `Router` interface with `Route()` method for request-based connection selection
17+
- `NewPolicy()` implementing chain-of-responsibility pattern for composable routing strategies
18+
- `NewIfEnabledPolicy()` for conditional routing with runtime evaluation
19+
- `NewMuxPolicy()` for custom HTTP pattern matching using `http.ServeMux`
20+
- `NewRolePolicy()` for role-based node selection
21+
- `NewDefaultRouter()` with coordinating node preference and round-robin fallback
22+
- `NewSmartRouter()` providing smart request routing with graceful fallback (recommended for most users)
23+
- Automatic routing of bulk operations (including streaming bulk) to ingest nodes
24+
- Automatic routing of search operations (search, count, explain, by-query operations) to data nodes
25+
- Automatic routing of document retrieval operations (get, mget, source, termvectors) to data nodes for read locality
26+
- Add connection pool health probes with cluster-aware resurrection timing ([#786](https://github.com/opensearch-project/opensearch-go/pull/786))
27+
- Auto-discover server core count from `/_nodes/http,os` to derive all rate-limiting parameters (default: 8 cores)
28+
- Weighted round-robin for heterogeneous clusters: nodes with more cores get proportionally more traffic via GCD-normalized duplicate pointers in the ready list
29+
- `lcNeedsHardware` lifecycle bit tracks connections needing hardware info; per-node fallback via `/_nodes/_local/http,os` during health checks
30+
- Capacity model dynamically recalculated on each discovery cycle from minimum `allocatedProcessors` across all nodes
31+
- TLS-aware rate limiting prevents overwhelming recovering servers during outages
32+
- Three-input timeout formula: `max(healthTimeout, rateLimitedTimeout, minimumFloor) + jitter`
33+
- Shuffle ready connection list on add/resurrect to prevent round-robin hot-spotting
34+
- Two-phase readiness health check: `GET /` then `GET /_cluster/health?local=true` with `initializing_shards` gate to prevent routing to recovering nodes
35+
- Store cluster health metrics (`ClusterHealthLocal`) on each connection for observability
36+
- Periodic cluster health refresh for ready connections keeps `ClusterHealthLocal` data current for load-shedding and routing decisions
37+
- Refresh interval scales with cluster size: `clamp(liveNodes × clientsPerServer / healthCheckRate, 5s, 5min)`
38+
- Single-node clusters skip refresh entirely (no routing benefit)
39+
- Node stats polling with load shedding via `NodeStatsInterval` configuration
40+
- Polls `GET /_nodes/_local/stats/jvm,breaker` to detect overloaded nodes
41+
- Overloaded nodes are demoted from the ready list to the dead list
42+
- Overload detection: JVM heap threshold (`OverloadedHeapThreshold`, default 85%), circuit breaker size ratio (`OverloadedBreakerRatio`, default 0.90), breaker trip delta, and cluster status red
43+
- Add heterogeneous Docker cluster targets for integration-testing weighted routing and role-based request routing
44+
- `cluster.heterogeneous.cpu.1` and `cluster.heterogeneous.cpu.2` set per-node CPU limits via Docker Compose overrides
45+
- `cluster.heterogeneous.roles` assigns distinct node roles (cluster_manager+ingest, data+ingest, data)
46+
- `cluster.homogeneous` removes all overrides to reset to default configuration
47+
- `cluster.status` now shows per-node roles and allocated processors via `_nodes/http,os`
1048

1149
### Changed
1250

51+
- Consolidate test utilities into two canonical packages: opensearchtransport/testutil (env helpers, polling, version comparison) and opensearchapi/testutil (client-dependent helpers, test suite, JSON comparison)
1352
- Refactor Client struct to use embedded mutex pattern for improved thread safety ([#775](https://github.com/opensearch-project/opensearch-go/pull/775))
1453
- Refactor metrics struct to use atomic counters for lock-free request/failure tracking ([#776](https://github.com/opensearch-project/opensearch-go/pull/776))
1554
- Test against Opensearch 2.19.4, 3.1, 3.3, and 3.4 ([#782](https://github.com/opensearch-project/opensearch-go/pull/782))
55+
- Migrate all test files to context-aware API calls for proper timeout and cancellation support
56+
- Add cluster readiness validation and improve cluster error diagnostics
57+
- Update Docker cluster management to add version-aware role detection (cluster_manager vs master)
58+
- Generate unique document IDs in tests for parallel test execution and eliminate known test flakes
59+
- Reduce integration test timeout from 1h to 10m per package with parallel execution support
60+
- Refactor transport code for improved maintainability (rename ErrInvalidRole -> InvalidRoleError, add response body cleanup, simplify initialization)
1661
- **BREAKING**: Enhanced node discovery to match OpenSearch server behavior ([#765](https://github.com/opensearch-project/opensearch-go/issues/765))
1762
- Dedicated cluster manager nodes are now excluded from client request routing by default (best practice)
1863
- Node selection logic now matches Java client `NodeSelector.SKIP_DEDICATED_CLUSTER_MASTERS` behavior
64+
- **BREAKING**: Add context support to discovery and client lifecycle management
65+
- `opensearchtransport.Discoverable` interface now requires `context.Context` parameter: `DiscoverNodes(ctx context.Context) error`
66+
- `opensearch.Client.DiscoverNodes()` and `opensearchtransport.Client.DiscoverNodes()` now require `context.Context` parameter
67+
- `opensearch.Config` and `opensearchtransport.Config` now accept optional `Context` and `CancelFunc` fields
68+
- `opensearchutil.BulkIndexerConfig` now accepts optional `Context` and `CancelFunc` fields
69+
- Enables proper context propagation for timeouts, cancellation, and graceful shutdown
70+
- Role compatibility validation prevents conflicting role assignments (master+cluster_manager, warm+search)
71+
- OpenSearch 3.0+ searchable snapshots now use `warm` role instead of deprecated `search` role
72+
- **BREAKING**: Migrate `signer/aws` package from AWS SDK v1 to AWS SDK v2 due to AWS SDK v1 reaching end-of-support on July 31, 2025
73+
- Constructor now takes `aws.Config` instead of `session.Options`
74+
- See USER_GUIDE.md for details required to migrate
75+
- Users who need access to the existing `signer/awsv2` API can still use it, however they are encouraged to migrate to `signer/aws`
1976

2077
### Deprecated
2178

2279
### Removed
2380

2481
### Fixed
2582

83+
- Fix connection lifecycle bug in statusConnectionPool.OnFailure where connections were scheduled for resurrection before being moved from ready to dead list, causing potential race conditions
2684
- Fix flaky connection integration test by replacing arbitrary sleep times with proper server readiness polling
85+
- Fix cluster readiness checks in integration tests to handle HTTPS cold start delays (increase timeout to 15s)
86+
- Fix GitHub Actions workflow authentication for OpenSearch 2.12.0+ password changes (admin -> myStrongPassword123!)
87+
- Fix Docker cluster management to properly handle version-specific configurations and clean stale images/volumes
88+
- Fix OpenSearch 2.8.0+ Tasks API compatibility by adding cancellation_time_millis field to TasksListTask struct
2789
- Fix OpenSearch 3.1.0+ API compatibility by adding phase_results_processors field to nodes API and time_in_execution fields to cluster pending tasks API
2890
- Fix OpenSearch 3.2.0+ API compatibility by adding max_last_index_request_timestamp and startree query fields across nodes stats, indices stats, and cat APIs, plus settings field to security plugin health API
2991
- Fix OpenSearch 3.3.0+ API compatibility by adding neural_search breaker, query_failed and startree_query_failed search fields, search pipeline system_generated fields across multiple APIs, plus ingestion_status field to cluster state API and jwks_uri field to security config API
3092
- Fix OpenSearch 3.4.0+ API compatibility by adding warmer fields to merges section, parallelism field to thread pool, and status_counter field across multiple APIs
31-
- Fix cat indices API field naming compatibility across OpenSearch versions by using forward-compatible field names (PrimarySearchStartreeQuery*) that match the corrected 3.3.0+ naming, with fallback support for the temporary 3.2.0 field names
93+
- Fix cat indices API field naming compatibility across OpenSearch versions by using forward-compatible field names (PrimarySearchStartreeQuery) that match the corrected 3.3.0+ naming, with fallback support for the temporary 3.2.0 field names
3294
- Fix cat APIs data type compatibility by changing byte fields from int to string to properly handle values like "0b"
3395
- Fix floating point precision loss in nodes stats concurrent_avg_slice_count field by changing from float32 to float64
3496

@@ -256,7 +318,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
256318
- Bumps codecov action version to v4 ([#517](https://github.com/opensearch-project/opensearch-go/pull/517))
257319
- Changes bulk error/reason field and some cat response fields to pointer as they can be nil ([#510](https://github.com/opensearch-project/opensearch-go/pull/510))
258320
- Adjust workflows to work with security plugin ([#507](https://github.com/opensearch-project/opensearch-go/pull/507))
259-
- Updates USER_GUIDE.md and /_samples/ ([#518](https://github.com/opensearch-project/opensearch-go/pull/518))
321+
- Updates USER_GUIDE.md and add samples ([#518](https://github.com/opensearch-project/opensearch-go/pull/518))
260322
- Updates opensearchtransport.Client to use pooled gzip writer and buffer ([#521](https://github.com/opensearch-project/opensearch-go/pull/521))
261323
- Use go:build tags for testing ([#52?](https://github.com/opensearch-project/opensearch-go/pull/52?))
262324

@@ -290,7 +352,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
290352

291353
- Updates workflow action versions ([#488](https://github.com/opensearch-project/opensearch-go/pull/488))
292354
- Changes integration tests to work with secure and unsecure OpenSearch ([#488](https://github.com/opensearch-project/opensearch-go/pull/488))
293-
- Moves functions from `opensearch/internal/test` to `internal/test` for more general test uses ([#488](https://github.com/opensearch-project/opensearch-go/pull/488))
355+
- Moves functions from `opensearch/internal/test` to `opensearchutil/testutil` for shared test utilities ([#488](https://github.com/opensearch-project/opensearch-go/pull/488))
294356
- Changes `custom_foldername` field to pointer as it can be `null` ([#488](https://github.com/opensearch-project/opensearch-go/pull/488))
295357
- Changs cat indices Primary and Replica field to pointer as it can be `null` ([#488](https://github.com/opensearch-project/opensearch-go/pull/488))
296358
- Replaces `ioutil` with `io` in examples and integration tests [#495](https://github.com/opensearch-project/opensearch-go/pull/495)

DEVELOPER_GUIDE.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
- [Integration Testing](#integration-testing)
1010
- [Composing an OpenSearch Docker Container](#composing-an-opensearch-docker-container)
1111
- [Execute integration tests from your terminal](#execute-integration-tests-from-your-terminal)
12+
- [Advanced Cluster Configuration](#advanced-cluster-configuration)
13+
- [Cluster Scaling](#cluster-scaling)
14+
- [Heterogeneous Clusters](#heterogeneous-clusters)
15+
- [CPU Limits](#cpu-limits)
16+
- [Node Roles](#node-roles)
17+
- [Combining Overrides](#combining-overrides)
18+
- [Resetting to Defaults](#resetting-to-defaults)
19+
- [Testing Specific OpenSearch Versions](#testing-specific-opensearch-versions)
20+
- [Cluster Status and Troubleshooting](#cluster-status-and-troubleshooting)
1221
- [Lint](#lint)
1322
- [Markdown lint](#markdown-lint)
1423
- [Go lint](#go-lint)
@@ -95,6 +104,110 @@ In order to differentiate unit tests from integration tests, Go has a built-in m
95104
make cluster.stop cluster.clean
96105
```
97106

107+
## Advanced Cluster Configuration
108+
109+
By default, `make cluster.start` launches a 3-node cluster where every node has the same resources and roles (`cluster_manager,data,ingest`). The targets below let you customize the cluster for testing weighted round-robin routing, role-based request routing, and other behavior that only surfaces with non-uniform nodes.
110+
111+
### Cluster Scaling
112+
113+
Scale the running cluster to a different number of nodes without rebuilding:
114+
115+
```
116+
make cluster.scale.1 # Single-node cluster
117+
make cluster.scale.2 # 2-node cluster
118+
make cluster.scale.3 # Full 3-node cluster (default)
119+
```
120+
121+
### Heterogeneous Clusters
122+
123+
Override files let you change CPU limits and node roles independently. Each target writes a Docker Compose override file under `.ci/opensearch/` that is automatically merged with `docker-compose.yml` on the next `cluster.build` or `cluster.start`. The override files are not checked into source control.
124+
125+
The override file paths are:
126+
127+
| Override | File |
128+
|----------|------|
129+
| CPU limits | `.ci/opensearch/docker-compose.cpu-override.yml` |
130+
| Node roles | `.ci/opensearch/docker-compose.roles-override.yml` |
131+
132+
These are standard Docker Compose files. You can hand-edit them for custom configurations (e.g., different CPU ratios or role combinations not covered by the Make targets), or remove individual files to selectively reset one dimension while keeping the other:
133+
134+
```
135+
# Remove only the CPU override, keep roles
136+
rm .ci/opensearch/docker-compose.cpu-override.yml
137+
138+
# Remove only the roles override, keep CPU limits
139+
rm .ci/opensearch/docker-compose.roles-override.yml
140+
```
141+
142+
#### CPU Limits
143+
144+
Set per-node CPU limits so the client's weighted round-robin allocates proportional traffic. The `deploy.resources.limits.cpus` value is reported by each node via `GET /_nodes/os` as `allocated_processors`, which the client uses to compute connection weights.
145+
146+
```
147+
# Balanced weights [1,1,2]
148+
make cluster.heterogeneous.cpu.1 # node1=2, node2=2, node3=4 CPUs
149+
150+
# Skewed weights [1,2,4]
151+
make cluster.heterogeneous.cpu.2 # node1=1, node2=2, node3=4 CPUs
152+
```
153+
154+
#### Node Roles
155+
156+
Assign different roles to each node so the client's role-based routing policy can direct requests to the correct nodes (e.g., bulk requests to ingest-capable nodes, search requests to data nodes):
157+
158+
```
159+
make cluster.heterogeneous.roles # node1=cluster_manager+ingest, node2=data+ingest, node3=data
160+
```
161+
162+
#### Combining Overrides
163+
164+
CPU and role overrides are independent files and can be combined. Set both, then rebuild:
165+
166+
```
167+
make cluster.heterogeneous.cpu.1 cluster.heterogeneous.roles
168+
make cluster.stop cluster.clean cluster.build cluster.start
169+
```
170+
171+
Verify the resulting configuration:
172+
173+
```
174+
curl -sk 'https://admin:myStrongPassword123%21@localhost:9200/_nodes/http,os?pretty' \
175+
| jq '.nodes[] | {name, roles: .roles, processors: .os.allocated_processors}'
176+
```
177+
178+
#### Resetting to Defaults
179+
180+
Remove all override files and return to the default homogeneous 3-node cluster:
181+
182+
```
183+
make cluster.homogeneous
184+
make cluster.stop cluster.clean cluster.build cluster.start
185+
```
186+
187+
### Testing Specific OpenSearch Versions
188+
189+
The cluster supports any published OpenSearch Docker image version. Always clean before switching versions to avoid stale data or cached images:
190+
191+
```
192+
make cluster.stop
193+
OPENSEARCH_VERSION=2.19.1 make cluster.clean cluster.build cluster.start
194+
make test-integ
195+
```
196+
197+
Set `SECURE_INTEGRATION=false` to disable TLS and basic auth:
198+
199+
```
200+
SECURE_INTEGRATION=false OPENSEARCH_VERSION=2.19.1 make cluster.clean cluster.build cluster.start
201+
```
202+
203+
### Cluster Status and Troubleshooting
204+
205+
Use `make cluster.status` to display cluster health, node info, Docker container state, and index/shard details. It auto-detects whether the cluster is running in secure or insecure mode.
206+
207+
```
208+
make cluster.status
209+
```
210+
98211
## Lint
99212

100213
To keep all the code in a certain uniform format, it was decided to use some writing rules. If you wrote something wrong, it's okay, you can simply run the script to check the necessary files, and optionally format the content. But keep in mind that all these checks are repeated on the pipeline, so it's better to check locally.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ OpenSearch Go Client
1414

1515
**opensearch-go** is [a community-driven, open source fork](https://aws.amazon.com/blogs/opensource/introducing-opensearch/) of go-elasticsearch licensed under the [Apache v2.0 License](LICENSE.txt). For more information, see [opensearch.org](https://opensearch.org/).
1616

17+
The client supports automatic node discovery, request-based connection routing, and role-aware node selection. See the [User Guide](USER_GUIDE.md) and [guides](guides/) for usage examples and configuration options.
18+
1719
## Project Resources
1820

1921
- [Project Website](https://opensearch.org/)

0 commit comments

Comments
 (0)