Skip to main content

OpenSearch Provider

Full parity with the Elasticsearch provider, built on OpenSearch.Client. Covers projections, index lifecycle management (ISM), resilient client, health monitoring, dead letter handling, materialized views, and tenant sharding.

Before You Start

  • .NET 8.0+ (or .NET 9/10 for latest features)
  • An OpenSearch cluster (local, AWS OpenSearch Service, or self-hosted)
  • Familiarity with data access and projections

Installation

dotnet add package Excalibur.Data.OpenSearch

Dependencies: Excalibur.Data.Abstractions, OpenSearch.Client

Quick Start

// Register OpenSearch client + projection store
services.AddOpenSearchServices("https://opensearch.example.com:9200");

services.AddOpenSearchProjectionStore<OrderSummary>(opts =>
{
opts.NodeUri = "https://opensearch.example.com:9200";
opts.IndexName = "order-summaries";
});

Registration Options

Client Registration

// Single node
services.AddOpenSearchServices("https://opensearch.example.com:9200");

// Multi-node cluster
services.AddOpenSearchServices(new[]
{
new Uri("https://node1.example.com:9200"),
new Uri("https://node2.example.com:9200"),
new Uri("https://node3.example.com:9200"),
});

// With custom connection settings
services.AddOpenSearchServices("https://opensearch.example.com:9200",
configureSettings: settings =>
{
settings.BasicAuthentication("admin", "password");
settings.DisableDirectStreaming();
});

// With preconfigured client
var client = new OpenSearchClient(new ConnectionSettings(new Uri("https://...")));
services.AddOpenSearchServices(client);

Projection Store

// Per-projection registration
services.AddOpenSearchProjectionStore<OrderSummary>(opts =>
{
opts.NodeUri = "https://opensearch.example.com:9200";
opts.IndexName = "order-summaries";
});

// With node URI shorthand
services.AddOpenSearchProjectionStore<OrderSummary>(
"https://opensearch.example.com:9200");

// With shared client factory
services.AddOpenSearchProjectionStore<OrderSummary>(
clientFactory: sp => sp.GetRequiredService<OpenSearchClient>(),
configureOptions: opts => opts.IndexName = "order-summaries");

// Batch registration (multiple projections, shared node)
services.AddOpenSearchProjections("https://opensearch.example.com:9200", projections =>
{
projections.Add<OrderSummary>();
projections.Add<CustomerProfile>(opts => opts.IndexName = "customers");
projections.Add<ProductCatalog>(opts => opts.IndexName = "products");
});

Resilient Client

Adds retry and circuit breaker policies around OpenSearch operations:

services.Configure<OpenSearchResilienceOptions>(opts =>
{
opts.MaxRetries = 3;
opts.CircuitBreakerThreshold = 5;
});
Option TypeKey Settings
OpenSearchResilienceOptionsMaxRetries, CircuitBreakerThreshold
OpenSearchRetryPolicyOptionsMaxRetries, BaseDelay, MaxDelay
CircuitBreakerOptionsFailureThreshold, ResetTimeout, HalfOpenMaxAttempts
OpenSearchTimeoutOptionsRequestTimeout, ConnectionTimeout

Monitoring

services.Configure<OpenSearchMonitoringOptions>(opts =>
{
// Enable health monitoring, metrics, request logging
});

Includes:

  • Health monitoring via OpenSearchHealthMonitor (cluster green/yellow/red)
  • OTel metrics via OpenSearchMetrics (operations, latency, errors)
  • Activity tracing via OpenSearchActivitySource
  • Request logging via OpenSearchRequestLogger
  • Performance diagnostics via OpenSearchPerformanceDiagnostics

Health Checks

services.AddHealthChecks()
.AddOpenSearchHealthCheck();

Index Lifecycle Management (ISM)

OpenSearch uses ISM (Index State Management) instead of Elasticsearch's ILM:

// Interfaces available via DI
// IIndexLifecycleManager -- ISM policy management
// IIndexTemplateManager -- Index template management
// IIndexOperationsManager -- Index CRUD, aliases, rollover
// IIndexAliasManager -- Alias management

Materialized Views

services.AddOpenSearchMaterializedViews(opts =>
{
opts.NodeUri = "https://opensearch.example.com:9200";
});

Dead Letter Handling

Failed documents are captured in a dead letter index:

services.Configure<OpenSearchDeadLetterOptions>(opts =>
{
opts.IndexName = "dead-letters";
opts.MaxRetries = 3;
});

Tenant Sharding

services.AddExcaliburEventSourcing(builder =>
{
builder.EnableTenantSharding(opts => opts.EnableTenantSharding = true);
builder.UseOpenSearchTenantProjectionStore<OrderSummary>();
});

Persistence Provider

services.AddOpenSearchPersistence(opts =>
{
opts.RefreshPolicy = OpenSearchRefreshPolicy.WaitFor;
});

Host Extensions

Initialize indexes at application startup:

var host = builder.Build();
await host.InitializeOpenSearchIndexesAsync();
await host.RunAsync();

Elasticsearch vs OpenSearch Comparison

FeatureElasticsearch PackageOpenSearch Package
Client libraryElastic.Clients.ElasticsearchOpenSearch.Client (NEST-based)
Index lifecycleILM (Index Lifecycle Management)ISM (Index State Management)
Projection storeAddElasticSearchProjectionStore<T>AddOpenSearchProjectionStore<T>
Batch registrationAddElasticSearchProjections()AddOpenSearchProjections()
Health checkAddElasticsearchHealthCheck()AddOpenSearchHealthCheck()
Resilient clientAddResilientElasticsearchServices()IResilientOpenSearchClient + options
Tenant shardingUseElasticSearchTenantProjectionStore<T>UseOpenSearchTenantProjectionStore<T>
Event ID range106000-106999108000-108999

The two packages provide feature parity. Choose based on your search engine.

See Also