Class DynamoDbClientOptions
- Namespace
- DynaSharp
- Assembly
- DynaSharp.dll
Configures a DynamoDbClient or a DynamoDbStreamsClient. Every setting is optional; unset settings are resolved from the environment the same way the AWS SDKs do.
public sealed record DynamoDbClientOptions : IEquatable<DynamoDbClientOptions>
- Inheritance
-
DynamoDbClientOptions
- Implements
- Inherited Members
Examples
using var client = new DynamoDbClient(new DynamoDbClientOptions
{
Region = AwsRegion.FromName("eu-central-2"),
MaxAttempts = 1,
});
Remarks
The options are a record, so a variation of a shared configuration is a with expression, for example
options with { MaxAttempts = 1 } for a client that must not retry its updates. The copy is shallow: it shares the credentials
provider, the HTTP handler and the logger factory of the original. Options gives these options back as a client resolved them. ToString() names the type of the
provider and nothing it holds, whatever the provider prints, so options are safe to log. What the client does with the settings while an operation runs,
that is retries, timeouts, errors and telemetry, is described on DynamoDbRequests.
Constructors
DynamoDbClientOptions()
public DynamoDbClientOptions()
Properties
AttemptTimeout
Gets the timeout of a single attempt, from looking up the credentials to reading the whole response, so a slow credentials provider counts against it. Retries get a fresh timeout each. Defaults to 30 seconds. An attempt that times out surfaces as TimeoutException, never as OperationCanceledException, and so does a timeout of the HttpHandler, such as its connect timeout. Use InfiniteTimeSpan for no timeout; otherwise the value must be positive and at most 4294967294 milliseconds (about 49.7 days).
public TimeSpan AttemptTimeout { get; init; }
Property Value
Credentials
Gets the credentials provider. null, the default, uses the default chain of the AWS SDKs (DefaultCredentialsProvider): environment variables, web identity token, shared profile, container endpoint, instance metadata, in that order.
public ICredentialsProvider? Credentials { get; init; }
Property Value
Remarks
The default chain takes the first source that has credentials: the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN environment variables; the web identity
token file named by AWS_WEB_IDENTITY_TOKEN_FILE, which is how IAM roles for service accounts work on EKS; the profile selected by AWS_PROFILE in the shared credentials and
config files; the container credentials endpoint of ECS and EKS; the instance metadata service of EC2. Nothing is looked up before the first request, and a source that applies but
is broken fails the request instead of falling through to the next one.
One source only. To use one source, or to turn off the network lookups, pass an EnvironmentCredentialsProvider, WebIdentityCredentialsProvider, ProfileCredentialsProvider, ContainerCredentialsProvider, InstanceMetadataCredentialsProvider or StaticCredentialsProvider.
Share one provider. Every client that is created without this setting makes its own DefaultCredentialsProvider, with its own cache of credentials. On Amazon EC2, ECS and EKS that means one set of requests to the metadata service, the container endpoint or STS, and one refresh before expiry, per client; a DynamoDbClient and a DynamoDbStreamsClient already make two. An application that creates more than one client there should create one provider and pass it in the options of all of them: providers are thread-safe, and the clients then share the cached credentials. A provider that the application creates does not log, whereas the one a client makes logs to LoggerFactory. It also judges the expiry of credentials on the local clock, whereas the one a client makes uses the clock that the client corrects from the responses of DynamoDB (see MaxAttempts), so that a local clock that is behind does not delay a refresh. The client never disposes a provider it was given.
HttpHandler
Gets the HTTP handler that sends the requests, which is the seam for tests and for wire inspection. null, the default, makes the client create and own a SocketsHttpHandler.
public HttpMessageHandler? HttpHandler { get; init; }
Property Value
Remarks
Only the requests to DynamoDB. The client does not dispose a handler it was given. It carries the requests to DynamoDB, and no others. The providers of credentials have HTTP clients of their own, for the token of WebIdentityCredentialsProvider and the keys of the container and metadata endpoints, so a client with a stub here still calls the real STS or metadata endpoint when the environment selects that provider. A test sets Credentials as well.
Read the body before SendAsync returns. The content of a request is valid only until SendAsync(HttpRequestMessage, CancellationToken)
returns; the client reuses its memory for the next call. A handler reads the content before it returns, never after, and sends a request once, never twice in parallel, so a
hedging handler does not work here. A body read late belongs to another call and fails the signature check of DynamoDB. A retry handler is not needed: the client retries by
itself (see MaxAttempts).
LoggerFactory
Gets the logger factory. When null, nothing is logged.
public ILoggerFactory? LoggerFactory { get; init; }
Property Value
MaxAttempts
Gets the maximum number of attempts per operation, including the first one. Defaults to 4: three retries, so that a throttled call waits for the table to recover (at most 50, 100 and 200 ms before the retries) instead of giving up after the first short pause. Use 1 to send every request once and never retry. The value must be at least 1; the client rejects anything else when it is created.
public int MaxAttempts { get; init; }
Property Value
Remarks
The client retries with the "standard" retry mode of the AWS SDKs, tuned for DynamoDB. It repeats a request when an attempt
fails with a throttling error (ProvisionedThroughputExceededException, ThrottlingException,
RequestLimitExceededException, LimitExceededException, TransactionInProgressException),
with ReplicatedWriteConflictException or RequestTimeoutException, with HTTP 500, 502, 503 or 504 (including
InternalServerErrorException and a proxy answering 503 with a page that is not a DynamoDB response), with a network
failure (HttpRequestException), or with a TimeoutException: the attempt exceeded AttemptTimeout, or a timeout of the
HttpHandler, such as ConnectTimeout, cancelled it. It never retries a
cancellation by the caller's token, CredentialsUnavailableException, ValidationException,
ConditionalCheckFailedException, TransactionCanceledException,
TransactionConflictException, AccessDeniedException, any other 4xx response or a response body above
64 MB. After the last attempt the error of that attempt surfaces unchanged.
Before a retry the client waits a random time between zero and min(20 s, base * 2^(attempt - 1)), with a base of 50 ms
for throttling errors and 100 ms for everything else. Each attempt has its own AttemptTimeout and looks up its
credentials again, so an operation can take up to MaxAttempts * AttemptTimeout plus the waits. Cancelling the call
ends a wait at once.
A failure that comes back at once and is not throttling, such as a refused connection, uses up the default of 4 attempts within about a second, because the three waits are at most 100, 200 and 400 ms: a window of 700 ms at most and 350 ms on average, against 350 ms at most for throttling. That is the time a dependency has to come up. A test fixture that waits on a TCP port can still fail inside that window when a proxy opens the port before the service listens: wait on an HTTP response.
Every client has a retry quota of 500 tokens that is shared by all its operations. A retry takes 5 tokens (10 after a timeout); a successful request gives back 1, or what its last retry took. When the quota is empty, failed requests are no longer retried and their errors surface at once, until successful requests have refilled it. This keeps a client from adding load to a service that is struggling.
A request is sent more than once, so it can take effect more than once: when an attempt reached DynamoDB but its response was
lost (a timeout, a dropped connection), the retry repeats a write that was already applied. Idempotent writes such as
PutItem or DeleteItem are safe to repeat; updates that depend on the current value (ADD on a number,
list_append, SET x = x + :n) are applied twice. Guard such updates with a condition expression, for example on
a version attribute. Setting MaxAttempts to 1 makes them at most once, not exact: a lost response then reports a failure after the update was applied.
If DynamoDB rejects a request as signed with a wrong time (the local clock is more than 4 minutes off) and its Date
header shows the difference, the client remembers the offset, signs all later requests with the corrected time and repeats
the request at once. That repeat counts as an attempt but uses no wait and no quota.
If DynamoDB rejects the credentials of a request as expired (ExpiredTokenException) and the provider can replace them, which
is the case for an IRefreshableCredentialsProvider such as ContainerCredentialsProvider and
InstanceMetadataCredentialsProvider, the client lets it drop them and repeats the request at once. That repeat follows
the same rules: it counts as an attempt but uses no wait and no quota. It happens once per call, so if the next credentials are rejected
as well, the error surfaces. Credentials are dropped even when no attempt is left, so that the next call does not use them. Concurrent
requests that fail like this share one fetch. If the fetch fails, its CredentialsUnavailableException surfaces.
With a provider that cannot replace credentials, such as StaticCredentialsProvider,
EnvironmentCredentialsProvider or ProfileCredentialsProvider, the error surfaces unchanged after one attempt.
Region
Gets the region. null, the default, takes AWS_REGION, then AWS_DEFAULT_REGION, then the region of the profile in the shared AWS config file.
public AwsRegion? Region { get; init; }
Property Value
Remarks
The profile is the one AWS_PROFILE selects (default default) in ~/.aws/config or AWS_CONFIG_FILE. A missing, unreadable or malformed file does not fail the client
unless no region can be found at all. The region is required even with ServiceUrl, because it is part of the request signature.
ServiceUrl
Gets an explicit endpoint, for example http://localhost:8000 for DynamoDB Local. null, the default, takes AWS_ENDPOINT_URL_DYNAMODB, then
AWS_ENDPOINT_URL, and otherwise the public endpoint of the region.
public Uri? ServiceUrl { get; init; }
Property Value
Remarks
Shape. An absolute http or https URL without user info, path, query and fragment; the client rejects anything else when it is created. The public endpoint of the region is DynamoDbEndpoint.
Plain http. Over http the requests, their signatures and the session token travel unencrypted, so for an http endpoint whose host is not localhost or a loopback address,
such as http://dynamodb-local:8000 in docker-compose, the client logs a warning once when it is created.
Streams. A DynamoDbStreamsClient created from the same options uses the same URL (an emulator such as DynamoDB Local serves both APIs on one address);
without it, it reads AWS_ENDPOINT_URL_DYNAMODB_STREAMS, then AWS_ENDPOINT_URL, and otherwise uses DynamoDbStreamsEndpoint.
TableNaming
Gets the function that turns the table an entity declares into the table the client opens, such as name => $"{environment}-{name}". When null, a declared name is used as it is.
public Func<string, string>? TableNaming { get; init; }
Property Value
Remarks
Only declared names. Table<T>() passes the name of Table, or of Table, through it on every call. An explicit name, as in Table<T>(string) and Table(string, string, string?), is used exactly as given, so it can be an ARN, or a table of a test.
A name or an exception. The result may be a name or an ARN, which the client does not check: DynamoDB rejects one it does not know with the first command. A result that is null or empty makes Table<T>() throw InvalidOperationException, and an exception of the function reaches the caller as it is.
TimeProvider
Gets the local clock, used for request signatures, timeouts and retry delays. Signatures and the expiry of the credentials of the default chain use it plus the correction that the client learned, see MaxAttempts. Defaults to System. It also runs AttemptTimeout, so a fake clock that a test moves can time out a request in flight: such a test sets AttemptTimeout to InfiniteTimeSpan.
public TimeProvider TimeProvider { get; init; }
Property Value
TokenSigner
Gets the keys that sign the ContinuationToken of the paged reads of the client, for a web API whose clients must not choose their position. When null, tokens are plain, as ContinuationToken describes.
public ContinuationTokenSigner? TokenSigner { get; init; }
Property Value
Remarks
With a signer, Next of every Query, Scan and fan-out query carries a signature over the token and over the table, the index and the key condition of the
read, with the partitions of a fan-out. PageAsync checks it before a request goes out, and a token that does not hold fails with a FormatException that quotes nothing of it:
one a client made up or altered, one of another query, one signed with a key the signer no longer holds, and one without a signature that came through Parse(string?).
A token of FromKey(IReadOnlyDictionary<string, AttributeValue>) is code of the service, not input of a client, and goes through unsigned.
Without a signer, a signed token fails in the same way, because nothing can check it. So switching signing on or off makes the clients start their reads again, once. Every instance of a service that hands out tokens and takes them back needs the same keys; ContinuationTokenSigner says how to rotate them. A DynamoDbStreamsClient has no paged reads and ignores this.
Methods
Equals(DynamoDbClientOptions?)
public bool Equals(DynamoDbClientOptions? other)
Parameters
otherDynamoDbClientOptions
Returns
Equals(object?)
public override bool Equals(object? obj)
Parameters
objobject
Returns
ForEmulator(Uri, string)
Creates options for an emulator such as DynamoDB Local or Dynoxide: serviceUrl as ServiceUrl, the region us-east-1 unless
region names another, and the static credentials local and local, so that nothing is read from the environment.
public static DynamoDbClientOptions ForEmulator(Uri serviceUrl, string region = "us-east-1")
Parameters
serviceUrlUriThe address of the emulator, for example
http://localhost:8000, with the rules of ServiceUrl, which the client checks when it is created.regionstringThe name of the region, for example
eu-central-2, with the rules of FromName(string). It only says which database of DynamoDB Local the requests reach.
Returns
- DynamoDbClientOptions
The options.
Examples
using var client = new DynamoDbClient(DynamoDbClientOptions.ForEmulator(new Uri("http://localhost:8000")));
Remarks
Any region and keys do. Every request is signed, and an emulator checks neither the region nor the keys. The options are a record, so the rest is a with: DynamoDbClientOptions.ForEmulator(url) with { MaxAttempts = 1 }. A DynamoDbStreamsClient made from these options reads the
streams of the same emulator. An http URL whose host is not localhost or a loopback address logs the warning that ServiceUrl describes. For real DynamoDB, leave this out and let the environment decide.
The region and the keys pick the tables. DynamoDB Local started without -sharedDb keeps one database per access key and region, so a program or a tool that uses another region or other keys
sees other tables, and none of those this client created: the AWS CLI with the region of the profile, for one, or a second program with its own values. Two programs that both leave the region out meet;
a tool takes us-east-1 and the keys local and local too, or the client takes the region of the tool: DynamoDbClientOptions.ForEmulator(url, "eu-central-2"). The keys stay
local and local; other keys are a with of Credentials. DynamoDB Local with -sharedDb, as the compose file of the repository starts it, keeps one database for all of them.
Exceptions
- ArgumentNullException
serviceUrlorregionis null.- ArgumentException
regionis empty or no region name: anything but lowercase ASCII letters, digits and inner hyphens.
GetHashCode()
public override int GetHashCode()
Returns
ToString()
public override string ToString()
Returns
Operators
operator ==(DynamoDbClientOptions?, DynamoDbClientOptions?)
public static bool operator ==(DynamoDbClientOptions? left, DynamoDbClientOptions? right)
Parameters
leftDynamoDbClientOptionsrightDynamoDbClientOptions
Returns
operator !=(DynamoDbClientOptions?, DynamoDbClientOptions?)
public static bool operator !=(DynamoDbClientOptions? left, DynamoDbClientOptions? right)
Parameters
leftDynamoDbClientOptionsrightDynamoDbClientOptions