Class CreateTableCommand
- Namespace
- DynaSharp
- Assembly
- DynaSharp.dll
Creates a table from what its entities declare: await orders.CreateTable().Including<Customer>().Including<Product>().SendAsync(ct);. CreateTable() starts one.
public sealed class CreateTableCommand
- Inheritance
-
CreateTableCommand
- Inherited Members
Remarks
Key names, index names, key schemas and attribute types come from the metadata of the entities. Billing is on demand unless Provisioned(long, long) says otherwise, and every index projects all attributes.
Local and global indexes are both created. A local index exists only if the table is created with it, so an entity left out here means an index that cannot be added later; a global one can, through
UpdateTable in DynaSharp.Requests.
One entity does not describe a single table, whose indexes are the union over its entities, so Including<TOther>() names the others. A table without an entity starts from the key names it was opened with, as strings, unless an included entity stores them otherwise.
A key template is a string, and a plain key is what its converter writes: a number, a binary value, or a string for anything else. A custom converter says what it writes only by implementing
IAddableConverter, which makes the key a number; any other counts as a string, so a custom converter that writes binary needs ToRequest(). Stream(StreamViewType) and
TimeToLive(string) cover those two. For tags, a storage class or a projection of fewer attributes, take ToRequest() and send it through DynaSharp.Requests.
Unlike the commands on items, this is an ordinary class: a clause changes the command and returns it, it can be kept in a field and sent again, and it is not thread-safe. A retry repeats the request: when a response was lost, the retry of a table this very call created fails with ResourceInUseException, which IfNotExists() turns into success.
Methods
IfNotExists()
Treats a table that exists as success: DescribeTable goes first, and a table it finds ends the create there, with its description and without a CreateTable, which is what makes a create at the start
of a program safe to repeat. The table it finds is checked against what the command declares. TimeToLive(string) on the chain still goes on, and can change the table.
public CreateTableCommand IfNotExists()
Returns
- CreateTableCommand
This command.
Remarks
What it checks. The description it already has holds the schema, so the check takes no call. A key of the table that differs from the key of the command throws an InvalidOperationException
that names the table, the key it expects and the key it found, before any wait or time to live: another partition or sort key attribute, a sort key the command has and the table lacks or the other way round,
or a key attribute of another type where both sides know it. That table fails every read and write of the entities with a ValidationException, and DynamoDB cannot change the key of a table,
so a deploy should stop there. A table opened without an entity, client.Table(name, "pk", "sk"), knows its key names and not their types, so only the names count.
What lets part of the code work is no exception, but Drift, which the command also logs once as a warning: an index the entities declare that the table lacks, or has with other keys
or as the other kind, time to live on another attribute than the one asked for, and, with Stream(StreamViewType) on the chain, a stream that is off or of another view. The command changes none of it, as the indexes
and the stream of a command reach only a table it creates: a global index goes in with UpdateTable, and a local one only with a new table. What the table has beyond the command, such as an index or
time to live it does not ask for, is no drift, since other code may use it. A table that is still being created is success with that status. A WaitUntilActive() still fails over a table that
lacks a global index this command declares. Created says whether the table was found.
Permissions. A table that exists takes dynamodb:DescribeTable alone, unless TimeToLive(string) adds its own, so a role that may not create tables gets through, and no failed call shows in CloudTrail or in error metrics. Creating a table takes
dynamodb:CreateTable as well, and dynamodb:DescribeTable is needed for that too: the look comes first, and any error of it other than a missing table surfaces, so a role without it gets
AccessDeniedException and creates nothing. With WaitUntilActive(), a first look that already shows the table and its indexes active returns without another call.
A table that is going away is no success, with or without a wait, because no table is left afterwards: the command goes on to CreateTable, and when DescribeTable says DELETING again, the
ResourceInUseException of the create surfaces, and sending the command again creates the table once the delete is through.
When DescribeTable finds no table, the command creates it. That can still find a table, because DescribeTable is eventually consistent, and instances of a program that start together create at the same
time: ResourceInUseException then ends in the description of the table it found. When DescribeTable finds nothing after such an answer, either a delete just ended or another process just created
the table, so the command creates again, up to 5 times over 1.5 seconds, which ends in a new table or in the one that turned up. The ResourceInUseException surfaces only if that settles nothing.
Including<TOther>()
Adds what another entity on this table declares, so that its indexes exist too. The result is the union: an index two entities declare exists once, and several templates over the same attributes, an overloaded index, are one index. Including an entity twice changes nothing.
public CreateTableCommand Including<TOther>() where TOther : IDynamoDbEntity<TOther>
Returns
- CreateTableCommand
This command.
Type Parameters
TOtherAny entity on this table.
Exceptions
- ArgumentException
The entity disagrees with the table or with an entity before it, which the message names both of: the key of the table has other names, it stores an attribute as another type, it declares an index of that name over other attributes, or as local where the other is global, or its TimeToLiveAttribute is on another attribute, since a table has time to live on one.
Provisioned(long, long)
Gives the table provisioned capacity instead of on-demand billing. DynamoDB needs capacity for each global index as well, and each gets these numbers; a local index shares that of the table. For other numbers per index, take ToRequest().
public CreateTableCommand Provisioned(long read, long write)
Parameters
readlongThe read capacity units per second, at least 1.
writelongThe write capacity units per second, at least 1.
Returns
- CreateTableCommand
This command.
SendAsync(CancellationToken)
Creates the table (CreateTable). Creation is asynchronous: without WaitUntilActive() or time to live, of TimeToLive(string) or of a TimeToLiveAttribute of an entity, which implies it, the table is normally CREATING when this returns.
public ValueTask<CreateTableResult> SendAsync(CancellationToken cancellationToken = default)
Parameters
cancellationTokenCancellationTokenCancels the call, and the wait. A table DynamoDB accepted stays accepted, so a cancelled wait leaves a table behind.
Returns
- ValueTask<CreateTableResult>
The table and what this call did: Table is the new description, the active one with WaitUntilActive(), or the existing one with IfNotExists(), which is what the first look found; Created and TimeToLiveEnabled say whether this call created the table and turned time to live on, and Drift how a table it found differs from what it declares.
Exceptions
- ObjectDisposedException
The client is disposed.
- ResourceInUseException
A table of that name exists and IfNotExists() was not called, or it is being deleted, which IfNotExists() does not cover.
- AccessDeniedException
The role lacks a permission of the calls this makes, which IfNotExists() lists.
- LimitExceededException
The account has too many tables, or creates or deletes tables too fast.
- ValidationException
DynamoDB rejects the request, for example because the name is malformed or the table has too many indexes, or time to live cannot be turned on, as within the hour after a change of it.
- InvalidOperationException
IfNotExists() found a table whose key differs from the key of the command, in attribute names, in having a sort key or in a type that both know. The message names the table, the expected and the found key. Nothing waits and nothing changes then.
- TableNotActiveException
The table or an index of it was not active in time, with WaitUntilActive() or TimeToLive(string), or the table did not list a global index this command declares, which ends the wait early.
- TimeoutException
The command ran out of Timeout(TimeSpan), with a message that names the table, the timeout and the step, and the OperationCanceledException of the call in flight inside it. Or a call ran out of AttemptTimeout on its last attempt.
- DynamoDbException
DynamoDB returns any other error; a throttling or server error only after the last attempt.
Stream(StreamViewType)
Gives the table a stream, which ToRequest() shows. It applies to a table this command creates: with IfNotExists(), the stream of a table that exists stays untouched, and one that is off or
of another view is a StreamDiffers in Drift. Change it with UpdateTable in DynaSharp.Requests.
public CreateTableCommand Stream(StreamViewType view)
Parameters
viewStreamViewTypeWhat a record of the stream holds.
Returns
- CreateTableCommand
This command.
Exceptions
- ArgumentOutOfRangeException
viewis no view, which DynamoDB would refuse, and this call finds out before a table is created.
TimeToLive(Attr)
Makes sure that the table has time to live on the attribute of a handle: .TimeToLive(Order.Attrs.ExpiresAt). See TimeToLive(string).
public CreateTableCommand TimeToLive(Attr attribute)
Parameters
Returns
- CreateTableCommand
This command.
Exceptions
- ArgumentException
attributehas no path, or a nested one, because time to live reads an attribute at the top of an item.
TimeToLive(string)
Makes sure that the table has time to live on this attribute, on a table this command creates and on one IfNotExists() finds. CreateTable does not take it, so it is no part of
ToRequest(): it happens after the create, or after finding the table. An entity of the command with TimeToLiveAttribute asks for the same without this call, and this call
replaces what the entities declare.
public CreateTableCommand TimeToLive(string attributeName)
Parameters
attributeNamestringThe name of the attribute that holds the expiry time.
Returns
- CreateTableCommand
This command.
Remarks
It needs an active table, so it implies WaitUntilActive(), with the five minutes of that method unless WaitUntilActive(TimeSpan) gave another timeout, and with all its rules: a wait that runs out throws TableNotActiveException and leaves time to live as it was, also when a table IfNotExists() found lacks a global index this command declares. So a program that adds an index to a table that exists calls this after it added the index.
Then DescribeTimeToLive looks, and ENABLED or ENABLING on this attribute ends it. ENABLED or ENABLING on another attribute ends it too, and changes nothing: it is a
TimeToLiveDiffers in Drift, since turning it off and on again with the new name is a decision this command does not take, and one that DynamoDB
spreads over two changes. Anything else, time to live off for one, sends UpdateTimeToLive, which turns it on, on a table that exists too. DynamoDB answers that with a ValidationException
for time to live that is on already, and for a second change within the hour that a change takes. Instances of a program start together, so that can mean that another one got there first: the command looks
again and takes it for success if time to live is now on, for this attribute or, as drift, for another. Otherwise that exception surfaces. TimeToLiveEnabled is
true only after an update DynamoDB accepted.
Permissions. It takes dynamodb:DescribeTimeToLive on every call, dynamodb:UpdateTimeToLive when time to live is not on yet, and dynamodb:DescribeTable for the wait.
The attribute of an item must be a number that holds a Unix time in seconds, which [DynamoDbConverter(typeof(EpochSecondsConverter))] on a DateTimeOffset writes, and which TimeToLiveAttribute checks at build time. See UpdateTimeToLiveRequest for when DynamoDB deletes.
Exceptions
- ArgumentException
attributeNameis null or empty.
Timeout(TimeSpan)
Bounds the whole of SendAsync(CancellationToken), every call it makes and every wait, for a program that creates its table at the start and must not hang there. When the time runs out, SendAsync(CancellationToken) throws a TimeoutException that names the table, the timeout and the step it was at.
public CreateTableCommand Timeout(TimeSpan timeout)
Parameters
timeoutTimeSpanThe time for the whole command, more than zero, or InfiniteTimeSpan for no bound.
Returns
- CreateTableCommand
This command.
Remarks
The bound covers the look of IfNotExists(), the CreateTable with its retries, the wait of WaitUntilActive(), and turning time to live on. The call in flight is
cancelled, where a token of the caller on a timer gives a bare OperationCanceledException.
Exceptions
- ArgumentOutOfRangeException
timeoutis zero, negative other than infinite, or above 4294967294 milliseconds, which this call finds out before a table is created.
ToRequest()
Builds the request that SendAsync(CancellationToken) would send, and sends nothing. It is the escape hatch for what this command does not model: change it with with, as in
request with { Tags = tags }, and send it with client.CreateTableAsync(request). Stream(StreamViewType) is in it. WaitUntilActive(), IfNotExists() and TimeToLive(string)
happen around the request and are not.
public CreateTableRequest ToRequest()
Returns
- CreateTableRequest
The request.
ToRequestJson()
Renders the body of the CreateTable request that SendAsync(CancellationToken) would send, as DynamoDB JSON, and sends nothing: the JSON of ToRequest(). It is meant for tests and debugging.
IfNotExists(), WaitUntilActive() and TimeToLive(string) make calls of their own around the request, which are not in it.
public string ToRequestJson()
Returns
- string
The request body.
Remarks
Unlike ToRequestJson of the commands of items, it does not end the command, which stays this ordinary class: it can be rendered and then sent.
WaitUntilActive()
Makes SendAsync(CancellationToken) wait until the table and its global secondary indexes, those the entities of this command declare among them, are ACTIVE, for at most five minutes. See WaitUntilActive(TimeSpan).
public CreateTableCommand WaitUntilActive()
Returns
- CreateTableCommand
This command.
WaitUntilActive(TimeSpan)
Makes SendAsync(CancellationToken) wait until the table and its global secondary indexes are ACTIVE, as WaitUntilActiveAsync(TimeSpan, CancellationToken) does.
Without this, the table is CREATING when the call returns, and a read or write of it fails until it is active.
public CreateTableCommand WaitUntilActive(TimeSpan timeout)
Parameters
timeoutTimeSpanHow long to wait after the table was created. InfiniteTimeSpan is not a bound and is rejected.
Returns
- CreateTableCommand
This command.
Remarks
An index added later. With IfNotExists() over a table that exists, an index that UpdateTable added can still be CREATING, which takes minutes even on an empty
table and often longer than the default of five, so give that wait half an hour or more.
Every global index that this command declares, by its entity and by Including<TOther>(), has to be listed and active, and a local one needs no wait. With IfNotExists() over an older table that lacks
a declared index, nothing adds the index, so this fails instead of returning, with a TableNotActiveException that names it. It does not wait out the timeout for that: an index still not listed at
the fifth look in a row, 1.5 seconds in, ends the wait, because a create lists all its indexes and only an UpdateTable sent before this command adds one. Add the index with UpdateTable in DynaSharp.Requests first.
That needs an active table, which a table opened without an entity waits for, because it declares nothing: client.Table(name, "pk", "sk").WaitUntilActiveAsync(). Then this wait follows the backfill.
The timeout bounds the wait and nothing else, so the calls before it and time to live after it take their own time. Timeout(TimeSpan) bounds all of it and names the table when it runs out, where a CancellationToken on a timer gives a bare TaskCanceledException or OperationCanceledException, with no table and no index named.
Exceptions
- ArgumentOutOfRangeException
timeoutis negative, which this call finds out, so that no table is created for it.