Skip to content

Commit 7d3d6fa

Browse files
committed
Add request-aware connection routing and enhanced node selection
Introduces flexible connection routing system that allows intelligent node selection based on request characteristics and node roles. This enables performance optimizations by routing operations to the most appropriate nodes. Features: - RequestAwareSelector interface for operation-based node selection - Role-based selector with options pattern: WithRequiredRoles(), WithExcludedRoles(), WithStrictMode(), WithFallback() - SmartSelector with automatic operation detection for bulk and search requests - Automatic routing of bulk operations to ingest nodes and search to data nodes - Extend connection pool with RequestAwareConnectionPool interface - Add documentation and guides for node discovery and role management This new routing system is backward compatible and optional - existing clients continue to use round-robin selection unless explicitly configured. Fixes: opensearch-project#770 Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
1 parent a366807 commit 7d3d6fa

9 files changed

Lines changed: 1146 additions & 51 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
77
### Added
88
- Enhanced cluster readiness checking for improved test reliability: `ostest.NewClient()` now includes readiness validation (health + cluster state + nodes info)
99
- Configuration option `IncludeDedicatedClusterManagers` for controlling cluster manager node routing ([#765](https://github.com/opensearch-project/opensearch-go/issues/765))
10+
- Request-aware connection routing for improved performance and service availability ([#770](https://github.com/opensearch-project/opensearch-go/pull/770))
11+
- `RequestAwareSelector` interface for operation-based node selection
12+
- Role-based selector with flexible options pattern: `NewRoleBasedSelector()` with `WithRequiredRoles()`, `WithExcludedRoles()`, `WithStrictMode()`, `WithFallback()` options
13+
- `SmartSelector` with automatic operation detection for bulk and search requests
14+
- Automatic routing of bulk operations to ingest nodes and search operations to data nodes
1015

1116
### Changed
1217
- Refactor Client struct to use embedded mutex pattern for improved thread safety ([#775](https://github.com/opensearch-project/opensearch-go/pull/775))

USER_GUIDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,4 +396,5 @@ func getCredentialProvider(accessKey, secretAccessKey, token string) aws.Credent
396396
- [Advanced Index Actions](guides/advanced_index_actions.md)
397397
- [Index Templates](guides/index_template.md)
398398
- [Data Streams](guides/data_streams.md)
399+
- [Node Discovery and Role Management](guides/node_discovery_and_roles.md)
399400
- [Retry and Backoff](guides/retry_backoff.md)

guides/bulk.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ import (
1515
"fmt"
1616
"os"
1717
"strings"
18+
"time"
1819

20+
"github.com/opensearch-project/opensearch-go/v4"
1921
"github.com/opensearch-project/opensearch-go/v4/opensearchapi"
22+
"github.com/opensearch-project/opensearch-go/v4/opensearchtransport"
2023
)
2124

2225
func main() {
@@ -27,6 +30,7 @@ func main() {
2730
}
2831

2932
func example() error {
33+
// Basic client setup
3034
client, err := opensearchapi.NewDefaultClient()
3135
if err != nil {
3236
return err
@@ -35,6 +39,37 @@ func example() error {
3539
ctx := context.Background()
3640
```
3741
42+
### Advanced Setup: Optimized for Bulk Operations
43+
44+
For high-throughput bulk operations, you can configure the client to automatically route requests to appropriate nodes:
45+
46+
```go
47+
// Advanced client setup with smart routing for mixed workloads
48+
advancedClient, err := opensearch.NewClient(opensearch.Config{
49+
Addresses: []string{"http://localhost:9200"},
50+
51+
// Enable node discovery to find all cluster nodes
52+
DiscoverNodesOnStart: true,
53+
DiscoverNodesInterval: 5 * time.Minute,
54+
55+
// Configure smart routing: bulk operations go to ingest nodes, searches go to data nodes
56+
Transport: &opensearchtransport.Config{
57+
Selector: opensearchtransport.NewSmartSelector(
58+
opensearchtransport.NewRoundRobinSelector(),
59+
),
60+
},
61+
})
62+
if err != nil {
63+
return err
64+
}
65+
66+
// This client will automatically route operations to appropriate nodes:
67+
// - Bulk operations -> ingest nodes
68+
// - Search operations -> data nodes
69+
// - Other operations -> round-robin
70+
_ = advancedClient
71+
```
72+
3873
Next, create an index named `movies` and another named `books` with the default settings:
3974
4075
```go
@@ -217,6 +252,86 @@ The following code shows an example on how to look for errors in the response:
217252
}
218253
```
219254
255+
## Performance Optimization for Bulk Operations
256+
257+
### Automatic Ingest Node Routing
258+
259+
For production environments with dedicated ingest nodes, you can optimize bulk operation performance by routing requests to the most appropriate nodes:
260+
261+
```go
262+
// Create a client optimized for bulk operations
263+
bulkClient, err := opensearch.NewClient(opensearch.Config{
264+
Addresses: []string{"http://localhost:9200"},
265+
266+
// Enable node discovery
267+
DiscoverNodesOnStart: true,
268+
DiscoverNodesInterval: 5 * time.Minute,
269+
270+
// Use smart selector for automatic operation routing
271+
Transport: &opensearchtransport.Config{
272+
Selector: opensearchtransport.NewSmartSelector(
273+
opensearchtransport.NewRoundRobinSelector(),
274+
),
275+
},
276+
})
277+
if err != nil {
278+
return err
279+
}
280+
281+
// This bulk request will automatically route to ingest nodes
282+
bulkResp, err := bulkClient.Bulk(
283+
ctx,
284+
opensearchapi.BulkReq{
285+
Body: strings.NewReader(`{ "index": { "_index": "movies", "_id": "perf-1" } }
286+
{ "title": "High Performance Bulk", "year": 2024 }
287+
{ "index": { "_index": "movies", "_id": "perf-2" } }
288+
{ "title": "Optimized Ingest", "year": 2024 }
289+
`),
290+
},
291+
)
292+
if err != nil {
293+
return err
294+
}
295+
fmt.Printf("Optimized bulk completed with %d items\n", len(bulkResp.Items))
296+
```
297+
298+
### Choosing the Right Selector
299+
300+
You can choose different routing strategies based on your cluster setup:
301+
302+
```go
303+
// Create a fallback selector (round-robin)
304+
fallbackSelector := opensearchtransport.NewRoundRobinSelector()
305+
306+
// Option 1: Prefer ingest nodes, fallback to any available node
307+
ingestPreferred := opensearchtransport.NewRoleBasedSelector(
308+
opensearchtransport.WithRequiredRoles(opensearchtransport.RoleIngest),
309+
opensearchtransport.WithFallback(fallbackSelector),
310+
)
311+
312+
// Option 2: Only use ingest nodes, fail if none available (strict mode)
313+
ingestOnly := opensearchtransport.NewRoleBasedSelector(
314+
opensearchtransport.WithRequiredRoles(opensearchtransport.RoleIngest),
315+
opensearchtransport.WithStrictMode(),
316+
)
317+
318+
// Option 3: Automatically detect operation type and route appropriately
319+
smartSelector := opensearchtransport.NewSmartSelector(fallbackSelector)
320+
321+
// Option 4: Use the generic selector for custom role combinations
322+
customSelector := opensearchtransport.NewRoleBasedSelector(
323+
opensearchtransport.WithRequiredRoles(opensearchtransport.RoleIngest),
324+
opensearchtransport.WithExcludedRoles(opensearchtransport.RoleClusterManager),
325+
opensearchtransport.WithFallback(fallbackSelector),
326+
)
327+
```
328+
329+
The smart selector automatically detects different operation types:
330+
- **Bulk operations** (`/_bulk`) -> Routes to ingest nodes
331+
- **Ingest pipeline operations** (`/_ingest/`) -> Routes to ingest nodes
332+
- **Search operations** (`/_search`) -> Routes to data nodes
333+
- **Other operations** -> Uses default routing
334+
220335
## Cleanup
221336
222337
To clean up the resources created in this guide, delete the `movies` and `books` indices:

0 commit comments

Comments
 (0)