Transport classes and utilities shared among .NET Elastic client libraries
This library was lifted from elasticsearch-net and then transformed to be used across all Elastic services rather than only Elasticsearch.
Provides the clients connectivity components, exposes a (potentially) cluster aware request pipeline that can be resilient to nodes dropping in & out of rotation.
This package is heavily optimized for the Elastic (elastic.co) product suite and Elastic Cloud (cloud.elastic.co) SAAS offering.
The transport is designed to fail over fast and in constant time.
If a Node
is considered bad it will fail-over only immediately given the overall request timeout allows for it.
It's an explicit non-goal to introduce full (incremental) retry mechanisms. This library is too generic to be making
these decisions and should be pushed on to the products/libraries making use of Elastic.Transport
This library can be used on its own but is typically used as the heart of a facade client that models all the API endpoints.
In its most direct and terse form you can use the following to create requests.
var settings = new TransportConfiguration(new Uri("http://localhost:9200"));
var transport = new Transport(settings);
var response = transport.Get<StringResponse>("/");
var headResponse = transport.Head("/");
Get
and Head
are extension methods to the only method HttpTransport
dictates namely Request()
and its async variant.
Wrapping clients most likely will list out all components
explicitly and use Transport<TConfiguration>
var pool = new StaticConnectionPool(new[] {new Node(new Uri("http://localhost:9200"))});
var connection = new HttpConnection();
var serializer = LowLevelRequestResponseSerializer.Instance;
var product = ElasticsearchProductRegistration.Default;
var settings = new TransportConfiguration(pool, connection, serializer, product);
var transport = new Transport<TransportConfiguration>(settings);
var response = transport.Request<StringResponse>(HttpMethod.GET, "/");
This allows implementers to extend TransportConfiguration
with product/service specific configuration.
HttpTransport
itself only defines Request()
and RequestAsync()
and all wrapping clients accept an HttpTransport
.
The HttpTransport
implementation that this library ships models a request pipeline that can deal with a large variety of topologies
Whilst complex every effort is made to only walk paths if we are certain they provide value.
It introduces two special API calls
sniff
a request to the service/product that should inform the client about the active topology.ping
the fastest request the transport can do to validate aNode
is alive.
If you instantiate Transport
you can pass an instance of IProductRegistation
to provide implementations
for sniff
and ping
.
var settings = new TransportConfiguration(new Uri("http://localhost:9200"));
var transport = new Transport(settings);
Will use the DefaultProductRegistration
wich opts out of sniff
and ping
However this library ship with a default implementation to fill in the blanks for Elasticsearch
so we can create a transport for Elasticsearch that support sniff
and ping
as followed
var uri = new Uri("http://localhost:9200");
var settings = new TransportConfiguration(uri, ElasticsearchProductRegistration.Default);
var transport = new Transport(settings);
All components are optional and ship with sane defaults. Typically client users only provide
the IConnectionPool
to the transport configuration
IConnectionPool
a registry ofNodes
the transport will ask for a view it can iterate over.
ONLY if a connection pool indicates it supports receiving new nodes will the transport sniff.IConnection
Abstraction for the actual IO the transport needs to perform.HttpTransportSerializer
Allows you to inject your own serializer, the default usesSystem.Text.Json
IProductRegistration
Product specific implementations and metadata provider
ITransportConfigurationValues
A transport configuration instance, explictly designed for clients to introduce subclasses ofRequestPipelineFactory
A factory creatingRequestPipeline
instancesDateTimeProvider
Abstraction around the staticDateTime.Now
so we can test algorithms without waiting on the clock on the wall.MemoryStreamFactory
A factory creatingMemoryStream
instances.
The default HttpTransport
implementation ships with various DiagnosticSources
to make the whole
flow through the request pipeline auditable and debuggable.
Every response returned by Transport
has to implement TransportResponse
which has one property ApiCall
of
type ApiCallDetails
which in turns holds all information relevant to the request and response.
NOTE:
it also exposes response.ApiCallDetails.DebugInformation
always holds a human readable string to indicate
what happened.
Further more DiagnosticSources
exists for various purposes e.g (de)serialization times, time to first byte & various counters
TestableResponseFactory
can be used to create response objects for use in unit tests.
Example code using the Moq library:
var response = TestableResponseFactory.CreateSuccessfulResponse<SearchResponse<Document>>(new(), 200);
var mock = new Mock<ElasticsearchClient>();
mock
.Setup(m => m.SearchAsync<Document>(It.IsAny<SearchRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(response);