dynamodb

package
v1.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

README

dynamodb — DynamoDB client for TinyGo

aws-sdk-go-v2 does not build with TinyGo: its transport layer imports net/http/httputil, and it reaches for the full net/http.Transport API, which TinyGo declares as an empty struct. The dependency closure of the DynamoDB service alone is 244 packages. This package speaks the JSON protocol directly instead, over the SigV4 signer in cloud/aws.

import "github.com/shibukawa/tinygodriver/nosql/dynamodb"

client, err := dynamodb.New(dynamodb.WithRegion("ap-northeast-1"))
defer client.Close()

item, err := client.GetItem(ctx, "users", dynamodb.Key{"pk": dynamodb.S("u#1")})
name, _ := item["name"].AsString()

Implementation selection

Request building, signing, and JSON decoding are shared code. The builds differ only in how a request reaches the network:

Build HTTP stack (aws.Backend)
Standard Go net/http with crypto/tls
TinyGo, or -tags force_tinygo_logic https, TLS through the host OS

API

Method DynamoDB operation
GetItem, PutItem, UpdateItem, DeleteItem the item API, with condition expressions
Query, Scan one page per call
BatchGetItem, BatchWriteItem up to 100 reads or 25 writes per request
CreateTable, DeleteTable, DescribeTable, ListTables table administration

Transactions, PartiQL, Streams and DAX are out of scope.

Attribute values

AttributeValue is one attribute in its wire form. The codec is written out rather than derived by reflection, so the supported types are visible in the type declaration:

item := dynamodb.Item{
	"pk":      dynamodb.S("u#1"),
	"age":     dynamodb.N(42),
	"active":  dynamodb.Bool(true),
	"tags":    dynamodb.SS("a", "b"),
	"profile": dynamodb.Map(map[string]dynamodb.AttributeValue{
		"city": dynamodb.S("東京"),
	}),
}

Numbers are held as text. DynamoDB numbers carry up to 38 significant digits, which float64 cannot represent, so the conversion happens where the caller picks what to lose:

n, ok := item["age"].AsInt()       // int64
f, ok := item["ratio"].AsFloat()   // float64, 15 digits
s, ok := item["big"].AsNumber()    // the stored text, lossless

Structs map through dynamodbav tags, the spelling aws-sdk-go-v2 uses, so an example written against the SDK ports over:

type User struct {
	PK      string    `dynamodbav:"pk"`
	Age     int       `dynamodbav:"age"`
	Created time.Time `dynamodbav:"created"`
	Note    string    `dynamodbav:"note,omitempty"`
	Ignored string    `dynamodbav:"-"`
}

item, err := dynamodb.MarshalItem(user)
err = dynamodb.UnmarshalItem(item, &user)

Only the field names port over from the SDK's tag: options such as ,stringset and ,unixtime are not read, so []string becomes a list (L), not a string set. Use dynamodb.SS(...) when you want a set.

MarshalItem and UnmarshalItem are the only reflection this package does, and a program that does not call them does not link them — 24 KB and about 0.8 µs per item, measured under TinyGo 0.41.1.

Building items by hand is not, however, a reflection-free path. The request body goes through encoding/json either way, and that is where the reflection and the time actually are:

time allocations
MarshalItem (reflection) 0.8 µs 21
json.Marshal of the item (either path) 2.2 µs 42
by hand, end to end 2.7 µs 59
through MarshalItem, end to end 3.1 µs 63

encoding/json and reflect are about 151 KB of a 1.45 MB TinyGo binary, and they are linked whichever way you build items. Removing them would mean replacing encoding/json for the whole request and response path, not just for item mapping.

Pagination

Query and Scan return one page. The loop stays in your code:

var startKey dynamodb.Key
for {
	opts := []dynamodb.QueryOption{
		dynamodb.WithExpressionValues(map[string]dynamodb.AttributeValue{
			":pk": dynamodb.S("u#1"),
		}),
	}
	if startKey != nil {
		opts = append(opts, dynamodb.WithExclusiveStartKey(startKey))
	}
	page, err := client.Query(ctx, "events", "pk = :pk", opts...)
	if err != nil {
		return err
	}
	use(page.Items)
	if !page.HasMore() {
		break
	}
	startKey = page.LastEvaluatedKey
}

An empty page with a continuation key is normal: a filter drops items after the page has been read, and paid for.

The batch calls work the same way. What DynamoDB declined comes back in UnprocessedItems, in a shape that can be sent again:

result, err := client.BatchWriteItem(ctx, writes)
for err == nil && result.HasUnprocessed() {
	result, err = client.BatchWriteItem(ctx, result.UnprocessedItems)
}

Errors

Failures are *dynamodb.Error with Op, Table, StatusCode, Type, Message and RequestID, wrapping a sentinel:

if errors.Is(err, dynamodb.ErrConditionalCheck) {
	// the condition refused the write, which is an answer, not a fault
}

ErrItemNotFound, ErrResourceNotFound, ErrConditionalCheck, ErrThroughputExceeded, ErrThrottled, ErrValidation, ErrTableInUse, ErrTableNotFound, ErrRequestTooLarge, ErrTransactionConflict, ErrBadCredentials, ErrChecksumMismatch, ErrServerFailure.

A GetItem that matches nothing is ErrItemNotFound rather than an empty item: DynamoDB answers a miss with a 200 and no Item member, which is too easy to read as an item with no attributes.

Errors are matched on the exception name after the #, because both namespaces appear on the wire: com.amazonaws.dynamodb.v20120810# for service errors and com.amazon.coral.service# for authentication failures.

Every reply is checked against its x-amz-crc32 header before it is decoded. A mismatch is retried, then reported as ErrChecksumMismatch.

Retries and idempotency

Throttling is a normal operating condition on a provisioned table, so retrying is on by default: 3 attempts, exponential backoff with jitter, bounded by the request context.

Retried Not retried
ThrottlingException, ProvisionedThroughputExceededException ValidationException
RequestLimitExceeded, TransactionConflictException ConditionalCheckFailedException
InternalServerError, ServiceUnavailable, 5xx ResourceNotFoundException
a checksum mismatch, a connection failure anything else the server named

Retrying has a consequence worth stating plainly: a write can be delivered twice. A request can reach DynamoDB and have its reply lost, and on TinyGo builds the transport also replays a request once when a pooled connection turns out to have been closed by the peer. The bound is attempts × 2 deliveries.

PutItem and an UpdateItem that only SETs are idempotent, so this changes nothing for them. An UpdateItem with ADD is not:

// Not idempotent: a replay increments twice.
client.UpdateItem(ctx, "counters", key, "ADD hits :one", ...)

// Guarded: the second delivery fails its condition instead.
client.UpdateItem(ctx, "counters", key, "ADD hits :one",
	dynamodb.WithCondition("attribute_not_exists(seen_token)"), ...)

WithRetry(1, 0) disables client-level retrying. It does not make delivery exactly-once — nothing over HTTP does — and on TinyGo builds the transport replay remains until Transport.DisableKeepAlives is set.

Limits

Exported so a caller batching work can chunk against them rather than copying numbers out of AWS documentation into its own source, where they drift silently.

Constant Value
MaxBatchGet 100 items per BatchGetItem, across tables
MaxBatchWrite 25 put/delete requests per BatchWriteItem, across tables
MaxItemBytes 400 KiB
MaxRequestBytes 16 MiB

Connections

Connections are pooled, which matters more here than for object storage. A TLS handshake to a regional endpoint measures around 90 ms while the round trip itself is around 10 ms, so an unpooled client spends nine tenths of its time on setup.

Every request goes to one host, so WithMaxIdleConns is effectively the whole pool. The default is 4; set it to the number of operations you run at once, since a call that finds no pooled connection pays the handshake.

Close releases the pooled connections, and with them the TLS handles held by the host OS. See the https connection reuse notes for the idle timeout and the replay rule underneath all this.

Configuration

client, err := dynamodb.New(
	dynamodb.WithEndpoint("http://127.0.0.1:8000"),
	dynamodb.WithRegion("ap-northeast-1"),
	dynamodb.WithCredentials(aws.Credentials{AccessKeyID: id, SecretAccessKey: secret}),
	dynamodb.WithMaxIdleConns(8),
	dynamodb.WithTimeout(10*time.Second),
	dynamodb.WithRetry(3, 25*time.Millisecond),
)

Defaults come from the environment: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_REGION then AWS_DEFAULT_REGION, and AWS_ENDPOINT_URL_DYNAMODB then AWS_ENDPOINT_URL. There is no shared credentials file, no SSO, and no IMDS lookup.

Testing

Unit tests run on both build paths and need nothing external:

go test ./nosql/dynamodb/ && go test -tags force_tinygo_logic ./nosql/dynamodb/

The integration tests need a server. DynamoDB Local is the one this suite targets:

docker run -d -p 8000:8000 amazon/dynamodb-local \
	-jar DynamoDBLocal.jar -inMemory -sharedDb

-sharedDb is required: without it the server partitions data by access key and region, so a table created with one credential is invisible to another.

DYNAMODB_TEST_ENDPOINT=http://127.0.0.1:8000 go test ./nosql/dynamodb/

The local server accepts any well-formed credentials without verifying the signature, so it proves the request shapes and the decoding, not the signing. Signing is covered by known-answer tests in cloud/aws, whose expected values came from aws-sdk-go-v2's own signer.

examples/dynamodbdemo runs the whole lifecycle under either compiler.

Documentation

Overview

Package dynamodb is a DynamoDB client that builds with TinyGo.

The maintained Go client cannot be used here: aws-sdk-go-v2 reaches for the full net/http.Transport API through smithy-go, which TinyGo declares as an empty struct, and its transport layer imports net/http/httputil, which does not compile under TinyGo at all. This package therefore speaks the DynamoDB JSON protocol directly, over the SigV4 signer in cloud/aws and whichever HTTP stack the build selects.

client, err := dynamodb.New(dynamodb.WithRegion("ap-northeast-1"))
defer client.Close()

item, err := client.GetItem(ctx, "users", dynamodb.Key{
	"pk": dynamodb.S("u#1"),
})
name, _ := item["name"].AsString()

Attribute values

AttributeValue carries one attribute in its wire form, built with S, N, B, Bool, Null, List, Map and the set constructors, and read back with the As accessors. The codec is hand-written rather than derived by reflection, so the supported type set is visible in the type declaration.

Building items by hand is not a reflection-free path, and it is worth being precise about that: the request body still goes through encoding/json, which is reflection-based, so reflect is linked either way. Measured on tinygo 0.41.1, encoding/json and reflect are about 151 KB of a 1.45 MB binary whatever this package does.

Numbers are held as text, because DynamoDB numbers carry up to 38 significant digits and float64 does not. AsInt and AsFloat convert where the caller has chosen what to lose; AsNumber returns the stored text.

MarshalItem and UnmarshalItem map Go structs through dynamodbav tags, the same spelling aws-sdk-go-v2 uses. Only the field names port over: tag options beyond omitempty, such as the SDK's stringset and unixtime, are not read.

They are the only reflection this package itself does, and a program that does not call them does not link them: 24 KB and around 0.8 us per item on tinygo 0.41.1. That is the whole saving from building items by hand, against the 2.2 us that json.Marshal costs either way.

Pagination and batches

Query and Scan return one page. A truncated page carries LastEvaluatedKey, which feeds WithExclusiveStartKey, so the request loop stays in the caller's code rather than hidden inside a paginator.

The batch calls return what they could not do, in UnprocessedKeys and UnprocessedItems, rather than retrying inside the call: a partial success is a result, and whether the rest still matters is the caller's decision. UnprocessedItems can be passed straight back to BatchWriteItem.

Retries and delivery

Throttling is a normal operating condition on a provisioned table, not a fault, so retrying is on by default: three attempts with exponential backoff and jitter, bounded by the request context. Errors the server has already judged, such as a validation failure or a refused condition, are not retried.

Retrying has a consequence worth stating plainly. A request can be delivered and its reply lost, so a retried write can be applied twice. On TinyGo builds the transport also replays a request once when a pooled connection turns out to have been closed by the peer, which multiplies the bound to attempts x 2. PutItem and an UpdateItem that only SETs are idempotent and unaffected; an UpdateItem with ADD is not, and should carry a condition expression. See WithRetry.

Connections

Connections are pooled, which matters more here than for object storage: a TLS handshake is around 90 ms against a regional endpoint while the round trip itself is around 10 ms, so an unpooled client spends nine tenths of its time on setup. Every request goes to one host, so WithMaxIdleConns is effectively the whole pool: set it to the number of operations run at once.

Close releases the pooled connections, and with them the TLS handles the host OS holds.

Index

Constants

View Source
const (
	// MaxBatchGet is the most items one BatchGetItem accepts, across tables.
	MaxBatchGet = 100

	// MaxBatchWrite is the most put and delete requests one BatchWriteItem
	// accepts, across tables.
	MaxBatchWrite = 25

	// MaxItemBytes is the largest a single item may be.
	MaxItemBytes = 400 << 10

	// MaxRequestBytes bounds one API request.
	MaxRequestBytes = 16 << 20
)

Service limits, from the published quotas. They are exported because a caller batching work has to chunk against them, and a number copied out of the documentation into every consumer drifts silently when the service changes it.

View Source
const (
	// DefaultTimeout bounds one logical operation, including retries, backoff,
	// and reading the reply. It is short because these are small round trips: a
	// call that has not finished in ten seconds is not going to.
	DefaultTimeout = 10 * time.Second

	// DefaultMaxIdleConns is how many connections stay pooled. Every request
	// goes to one host, so this is the whole pool; a caller running more
	// operations at once should raise it with WithMaxIdleConns.
	DefaultMaxIdleConns = 4

	// DefaultAttempts is how many times a retryable failure is sent, the first
	// try included.
	DefaultAttempts = 3

	// DefaultRetryBase is the first backoff delay, doubled per attempt and
	// capped at retryCap.
	DefaultRetryBase = 25 * time.Millisecond
)

Defaults for a client built without the matching option.

Variables

View Source
var (
	// ErrEmptyAttribute is an AttributeValue with no field set. DynamoDB has no
	// encoding for "no type", so this is a programming error rather than a NULL.
	ErrEmptyAttribute = errors.New("dynamodb: attribute value has no type set")

	// ErrAmbiguousAttribute is an AttributeValue with more than one field set.
	// The wire form carries exactly one member, so there is no correct choice
	// to make here.
	ErrAmbiguousAttribute = errors.New("dynamodb: attribute value has more than one type set")
)

Errors from the attribute codec itself, as opposed to a DynamoDB reply.

View Source
var (
	ErrItemNotFound        = errors.New("dynamodb: item not found")
	ErrResourceNotFound    = errors.New("dynamodb: resource not found")
	ErrConditionalCheck    = errors.New("dynamodb: conditional check failed")
	ErrThroughputExceeded  = errors.New("dynamodb: provisioned throughput exceeded")
	ErrThrottled           = errors.New("dynamodb: request throttled")
	ErrValidation          = errors.New("dynamodb: validation failed")
	ErrTableInUse          = errors.New("dynamodb: table in use")
	ErrTableNotFound       = errors.New("dynamodb: table not found")
	ErrRequestTooLarge     = errors.New("dynamodb: request too large")
	ErrTransactionConflict = errors.New("dynamodb: transaction conflict")
	ErrBadCredentials      = errors.New("dynamodb: credentials rejected")
	ErrChecksumMismatch    = errors.New("dynamodb: response checksum mismatch")
	ErrServerFailure       = errors.New("dynamodb: server failure")
	ErrHTTPClientOwnership = errors.New("dynamodb: WithHTTPClient cannot be combined with WithMaxIdleConns")

	// ErrNoCredentials and ErrNoRegion are the shared configuration errors, so
	// errors.Is matches whether the caller compares against this package or
	// cloud/aws.
	ErrNoCredentials = aws.ErrNoCredentials
	ErrNoRegion      = aws.ErrNoRegion
)

Sentinel errors. DynamoDB exception names are mapped onto them so application code can branch with errors.Is without matching strings.

Functions

func UnmarshalItem

func UnmarshalItem(item Item, out any) error

UnmarshalItem fills a struct, or a *map[string]AttributeValue, from an item. Attributes with no matching field are ignored; fields with no matching attribute are left alone.

func WithCondition

func WithCondition(expr string) writeOnlyOption

WithCondition guards a write with a condition expression, such as "attribute_not_exists(pk)". A failed condition is ErrConditionalCheck, which is the intended outcome rather than a fault.

func WithConsistentRead

func WithConsistentRead(consistent bool) readOption

WithConsistentRead asks for a strongly consistent read, which costs twice the read units and cannot be used against a global secondary index.

func WithExclusiveStartKey

func WithExclusiveStartKey(key Key) scanRangeOption

WithExclusiveStartKey continues from where a page stopped, using the LastEvaluatedKey of the previous Page.

func WithExpressionNames

func WithExpressionNames(names map[string]string) nameOption

WithExpressionNames supplies the #name placeholders an expression uses.

func WithExpressionValues

func WithExpressionValues(values map[string]AttributeValue) valueOption

WithExpressionValues supplies the :value placeholders an expression uses.

func WithFilter

func WithFilter(expr string) scanRangeOption

WithFilter drops items after they are read, as a filter expression. It saves bandwidth, not read capacity: the items are read and charged first.

func WithIndex

func WithIndex(name string) scanRangeOption

WithIndex reads through a secondary index instead of the table itself.

func WithLimit

func WithLimit(n int) pageOption

WithLimit bounds how many items one page evaluates. DynamoDB may return fewer, and a short page with a LastEvaluatedKey is not the end of the result.

func WithProjection

func WithProjection(expr string) readOption

WithProjection limits the attributes returned, as a projection expression: "pk, #n, profile.city". Names that collide with reserved words go through WithExpressionNames.

func WithReturnValues

func WithReturnValues(which string) writeOnlyOption

WithReturnValues asks for attributes back from a write: "ALL_OLD", "ALL_NEW", "UPDATED_OLD", "UPDATED_NEW", or "NONE".

func WithScanForward

func WithScanForward(forward bool) queryOnlyOption

WithScanForward reads the sort key in ascending order when true, which is the default, and descending when false.

func WithStartTable

func WithStartTable(name string) listOnlyOption

WithStartTable continues a table listing from the LastEvaluatedName of the previous TableList.

Types

type AttributeType

type AttributeType string

AttributeType is the type of a key attribute. Only these three can be keys.

const (
	TypeString AttributeType = "S"
	TypeNumber AttributeType = "N"
	TypeBinary AttributeType = "B"
)

The key attribute types.

type AttributeValue

type AttributeValue struct {
	S    *string                   // string
	N    *string                   // number, as text
	B    []byte                    // binary
	BOOL *bool                     // boolean
	NULL bool                      // the null attribute
	L    []AttributeValue          // list
	M    map[string]AttributeValue // map
	SS   []string                  // string set
	NS   []string                  // number set
	BS   [][]byte                  // binary set
}

AttributeValue is one attribute in its wire form. Exactly one field is set; which one is the attribute's type.

The codec is written out rather than derived by reflection, so the supported type set is visible in this declaration and an unsupported type is a compile error rather than a runtime surprise. MarshalItem converts Go structs for callers who want that instead.

Numbers are held as text. DynamoDB numbers carry up to 38 significant digits, which float64 cannot represent, so conversion happens in AsInt and AsFloat where the caller has chosen the Go type that loses the precision.

func B

func B(v []byte) AttributeValue

B returns a binary attribute.

func BS

func BS(vs ...[]byte) AttributeValue

BS returns a binary set.

func Bool

func Bool(v bool) AttributeValue

Bool returns a boolean attribute.

func List

func List(vs ...AttributeValue) AttributeValue

List returns a list attribute.

func Map

Map returns a map attribute.

func N

func N[T Number](v T) AttributeValue

N returns a number attribute, formatted without loss for the Go type given.

func NS

func NS(vs ...string) AttributeValue

NS returns a number set from values already in DynamoDB's number format.

func NString

func NString(v string) AttributeValue

NString returns a number attribute from text already in DynamoDB's format, which is how a value with more precision than float64 gets in.

func Null

func Null() AttributeValue

Null returns the null attribute.

func S

S returns a string attribute. The empty string is a valid value and is stored as one.

func SS

func SS(vs ...string) AttributeValue

SS returns a string set. DynamoDB rejects an empty set.

func (AttributeValue) AsBool

func (a AttributeValue) AsBool() (bool, bool)

AsBool returns the boolean value.

func (AttributeValue) AsBytes

func (a AttributeValue) AsBytes() ([]byte, bool)

AsBytes returns the binary value.

func (AttributeValue) AsFloat

func (a AttributeValue) AsFloat() (float64, bool)

AsFloat parses the number as a float64, losing precision beyond 15 digits.

func (AttributeValue) AsInt

func (a AttributeValue) AsInt() (int64, bool)

AsInt parses the number as an int64. It reports false for a non-number and for a number that does not fit, which includes any value with a fractional part.

func (AttributeValue) AsList

func (a AttributeValue) AsList() ([]AttributeValue, bool)

AsList returns the list value.

func (AttributeValue) AsMap

func (a AttributeValue) AsMap() (map[string]AttributeValue, bool)

AsMap returns the map value.

func (AttributeValue) AsNumber

func (a AttributeValue) AsNumber() (string, bool)

AsNumber returns the number as the text DynamoDB stored, which is the only lossless form.

func (AttributeValue) AsString

func (a AttributeValue) AsString() (string, bool)

AsString returns the string value, and whether the attribute held one.

func (AttributeValue) IsNull

func (a AttributeValue) IsNull() bool

IsNull reports whether the attribute is the null attribute, which is not the same as being absent from an item.

func (AttributeValue) Kind

func (a AttributeValue) Kind() Kind

Kind reports which type a carries.

func (AttributeValue) MarshalJSON

func (a AttributeValue) MarshalJSON() ([]byte, error)

MarshalJSON writes the single-member object DynamoDB expects.

The member is assembled by hand rather than through a one-entry map: a map per attribute made this the allocation hot spot of every Put-shaped call. json.Marshal still renders the inner value, so escaping stays the standard library's.

func (*AttributeValue) UnmarshalJSON

func (a *AttributeValue) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the single-member object DynamoDB sends. An unknown member is an error rather than an ignored field: a type this package does not know about would otherwise decode to an empty attribute.

The member is located by a direct scan rather than by decoding into a one-entry map, which was the allocation hot spot of reading any reply. The scan handles exactly the well-formed single-member object; any input it is not sure about — extra members, escaped member names, malformed framing — falls through to the map path, which reports it precisely as before.

type BatchGetResult

type BatchGetResult struct {
	Items           map[string][]Item
	UnprocessedKeys map[string][]Key
}

BatchGetResult is what BatchGetItem returned, per table.

UnprocessedKeys are the keys DynamoDB declined to read this time, usually because the batch hit a throughput or size limit. They are returned rather than retried inside the call: a partial success is a result, and whether the rest still matters is the caller's decision.

func (*BatchGetResult) HasUnprocessed

func (r *BatchGetResult) HasUnprocessed() bool

HasUnprocessed reports whether any key went unread.

type BatchOption

type BatchOption interface {
	// contains filtered or unexported methods
}

BatchOption configures BatchGetItem.

type BatchWriteResult

type BatchWriteResult struct {
	UnprocessedItems map[string][]WriteRequest
}

BatchWriteResult is what BatchWriteItem returned.

UnprocessedItems carries the writes that did not happen, in a form that can be passed straight back to BatchWriteItem.

func (*BatchWriteResult) HasUnprocessed

func (r *BatchWriteResult) HasUnprocessed() bool

HasUnprocessed reports whether any write was declined.

type BillingMode

type BillingMode string

BillingMode is how a table is charged.

const (
	PayPerRequest BillingMode = "PAY_PER_REQUEST"
	Provisioned   BillingMode = "PROVISIONED"
)

The billing modes. PayPerRequest needs no capacity planning and is the default here, because a table this client creates is usually a test table or a small one.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to one DynamoDB endpoint. It is safe for concurrent use.

func New

func New(opts ...Option) (*Client, error)

New builds a Client. Region, endpoint, and credentials fall back to the environment, so a configured shell needs no options at all.

func (*Client) BatchGetItem

func (c *Client) BatchGetItem(ctx context.Context, keys map[string][]Key, opts ...BatchOption) (*BatchGetResult, error)

BatchGetItem reads up to MaxBatchGet items across tables in one request.

One handshake and one round trip serve the whole batch, which is what makes it worth more than the sum of its GetItems.

func (*Client) BatchWriteItem

func (c *Client) BatchWriteItem(ctx context.Context, writes map[string][]WriteRequest) (*BatchWriteResult, error)

BatchWriteItem puts and deletes up to MaxBatchWrite items across tables in one request.

It is not a transaction: the writes succeed or fail one by one, and the ones that did not happen come back in UnprocessedItems. Conditions are not available here, which is the price of the batching.

func (*Client) Close

func (c *Client) Close() error

Close releases the pooled connections held for the endpoint.

It matters more than it looks: a pooled TLS connection holds a handle in the TLS stack of the host OS, which outlives the last request until the idle timeout expires. A client supplied through WithHTTPClient is left alone, since its owner may still be using it.

func (*Client) CreateTable

func (c *Client) CreateTable(ctx context.Context, def TableDefinition) error

CreateTable creates a table and returns as soon as DynamoDB accepts the request, which is before the table is usable. Poll DescribeTable for Active.

func (*Client) DeleteItem

func (c *Client) DeleteItem(ctx context.Context, table string, key Key, opts ...WriteOption) (*WriteResult, error)

DeleteItem removes one item by primary key. Deleting an item that is not there succeeds.

func (*Client) DeleteTable

func (c *Client) DeleteTable(ctx context.Context, table string) error

DeleteTable removes a table and everything in it.

func (*Client) DescribeTable

func (c *Client) DescribeTable(ctx context.Context, table string) (*TableDescription, error)

DescribeTable reports a table's state.

func (*Client) Endpoint

func (c *Client) Endpoint() string

Endpoint reports the endpoint URL in use.

func (*Client) GetItem

func (c *Client) GetItem(ctx context.Context, table string, key Key, opts ...GetOption) (Item, error)

GetItem reads one item by primary key.

A key that matches nothing is ErrItemNotFound, not an empty item: DynamoDB answers a miss with a 200 and no Item member, which is too easy to read as an item with no attributes.

func (*Client) ListTables

func (c *Client) ListTables(ctx context.Context, opts ...ListOption) (*TableList, error)

ListTables lists table names, one page per call.

func (*Client) PutItem

func (c *Client) PutItem(ctx context.Context, table string, item Item, opts ...WriteOption) (*WriteResult, error)

PutItem writes one item, replacing any item with the same key.

WithCondition("attribute_not_exists(pk)") turns it into an insert that fails with ErrConditionalCheck instead of overwriting.

func (*Client) Query

func (c *Client) Query(ctx context.Context, table, keyCond string, opts ...QueryOption) (*Page, error)

Query reads items sharing a partition key, as one page.

keyCond is a key condition expression: "pk = :pk", or "pk = :pk AND begins_with(sk, :prefix)". Its placeholders come from WithExpressionValues.

There is no paginator: a truncated page carries LastEvaluatedKey, which feeds WithExclusiveStartKey, so the request loop stays where the caller can see it.

func (*Client) Region

func (c *Client) Region() string

Region reports the signing region.

func (*Client) Scan

func (c *Client) Scan(ctx context.Context, table string, opts ...ScanOption) (*Page, error)

Scan reads every item in a table or index, as one page. It reads and charges for everything it touches, so a filter is not a substitute for a Query.

func (*Client) UpdateItem

func (c *Client) UpdateItem(ctx context.Context, table string, key Key, update string, opts ...WriteOption) (*WriteResult, error)

UpdateItem applies an update expression to one item, creating it when it does not exist. The expression is the DynamoDB syntax: "SET #n = :name", "ADD score :delta", "REMOVE obsolete".

An ADD expression is not idempotent, so pair it with WithCondition when the caller cannot tolerate the request arriving twice. See WithRetry.

type Error

type Error struct {
	Op         string // "GetItem", "Query", ...
	Table      string
	StatusCode int
	Type       string // the exception name, without its namespace
	Message    string
	RequestID  string
	// contains filtered or unexported fields
}

Error is a failed DynamoDB operation.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Retryable

func (e *Error) Retryable() bool

Retryable reports whether sending the same request again could succeed.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the sentinel this failure maps onto, so errors.Is works.

type GetOption

type GetOption interface {
	// contains filtered or unexported methods
}

GetOption configures GetItem.

type Item

type Item = map[string]AttributeValue

Item is a DynamoDB item: attribute names to values.

func MarshalItem

func MarshalItem(v any) (Item, error)

MarshalItem converts a struct, or a map[string]AttributeValue, into an item.

Fields are named by their dynamodbav tag, or by the field name when there is none. A tag of "-" skips the field, and ",omitempty" skips it when it holds the zero value.

The supported types are string, the integer and float kinds, bool, []byte, time.Time (RFC 3339 text), slices, maps with string keys, structs, and pointers to any of those. A nil pointer becomes the null attribute.

type Key

type Key = map[string]AttributeValue

Key is the primary key of one item: the partition attribute, and the sort attribute when the table has one.

type KeyAttribute

type KeyAttribute struct {
	Name string
	Type AttributeType
}

KeyAttribute is one attribute used as a key.

type Kind

type Kind int

Kind is which of the DynamoDB types an AttributeValue carries.

const (
	KindNone Kind = iota
	KindString
	KindNumber
	KindBinary
	KindBool
	KindNull
	KindList
	KindMap
	KindStringSet
	KindNumberSet
	KindBinarySet
)

The DynamoDB attribute types. KindNone is the zero AttributeValue, which is not a valid wire value.

type ListOption

type ListOption interface {
	// contains filtered or unexported methods
}

ListOption configures ListTables.

type Number

type Number interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
		~float32 | ~float64
}

Number is what N accepts.

type Option

type Option func(*config)

Option configures a Client.

func WithCredentials

func WithCredentials(creds aws.Credentials) Option

WithCredentials sets static credentials.

func WithCredentialsFromEnv

func WithCredentialsFromEnv() Option

WithCredentialsFromEnv reads credentials from the environment. New does this already when no credentials option is given; the option states it explicitly.

func WithEndpoint

func WithEndpoint(endpoint string) Option

WithEndpoint overrides the endpoint URL, for DynamoDB Local or a compatible server. It defaults to AWS_ENDPOINT_URL_DYNAMODB, AWS_ENDPOINT_URL, or the regional AWS endpoint.

func WithHTTPClient

func WithHTTPClient(client *http.Client) Option

WithHTTPClient supplies the http.Client to use. Close does not shut down a client supplied this way.

func WithMaxIdleConns

func WithMaxIdleConns(n int) Option

WithMaxIdleConns sets how many connections stay pooled for the endpoint.

Set it to the number of operations the program runs at once: every request goes to the same host, so this cap is the whole pool. A call that finds no pooled connection pays a TLS handshake, which is roughly ten times the cost of the request itself.

func WithRegion

func WithRegion(region string) Option

WithRegion sets the signing region. It defaults to AWS_REGION, then AWS_DEFAULT_REGION.

func WithRetry

func WithRetry(attempts int, base time.Duration) Option

WithRetry sets how many times a retryable failure is sent, the first try included, and the first backoff delay. Attempts of 0 or 1 disables retrying; a zero base means DefaultRetryBase.

Retries are not free of consequence. A request can be delivered and its reply lost, and the transport underneath replays a request once when a pooled connection turns out to be dead, so a write can reach the table up to attempts x 2 times. PutItem and a plain UpdateItem SET are idempotent; an UpdateItem with ADD is not, and should carry a condition expression.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout bounds one logical operation, including retries, backoff, and reading the reply. Zero means DefaultTimeout.

type Page

type Page struct {
	Items            []Item
	LastEvaluatedKey Key
	Count            int
	ScannedCount     int
}

Page is one page of a Query or Scan.

LastEvaluatedKey is the continuation: non-nil means there is more, whatever Count says, and it feeds WithExclusiveStartKey. An empty page with a continuation key is normal, and happens when a filter dropped everything the page read.

func (*Page) HasMore

func (p *Page) HasMore() bool

HasMore reports whether another page follows this one.

type QueryOption

type QueryOption interface {
	// contains filtered or unexported methods
}

QueryOption configures Query.

type ScanOption

type ScanOption interface {
	// contains filtered or unexported methods
}

ScanOption configures Scan.

type SecondaryIndex

type SecondaryIndex struct {
	Name         string
	PartitionKey KeyAttribute
	SortKey      *KeyAttribute

	// Projection is "ALL", "KEYS_ONLY", or "INCLUDE" with Include listing the
	// extra attributes. Empty means "ALL".
	Projection string
	Include    []string

	// ReadCapacity and WriteCapacity apply to a global index on a provisioned
	// table, and are ignored otherwise.
	ReadCapacity  int64
	WriteCapacity int64
}

SecondaryIndex defines a global or local secondary index at table creation.

type TableDefinition

type TableDefinition struct {
	Name         string
	PartitionKey KeyAttribute
	SortKey      *KeyAttribute

	// BillingMode defaults to PayPerRequest, where the capacities are ignored.
	BillingMode   BillingMode
	ReadCapacity  int64
	WriteCapacity int64

	GlobalIndexes []SecondaryIndex
	LocalIndexes  []SecondaryIndex
}

TableDefinition is what CreateTable creates.

type TableDescription

type TableDescription struct {
	Name      string
	Status    string // "CREATING", "ACTIVE", "DELETING", ...
	ItemCount int64
	SizeBytes int64
	CreatedAt time.Time
	Keys      []KeyAttribute
}

TableDescription is what DescribeTable reports.

ItemCount and SizeBytes are updated by DynamoDB about every six hours, so they describe the table as of some time ago, not as of the call.

func (*TableDescription) Active

func (d *TableDescription) Active() bool

Active reports whether the table is ready for reads and writes.

type TableList

type TableList struct {
	Names []string

	// LastEvaluatedName continues the listing through WithStartTable, and is
	// empty on the last page.
	LastEvaluatedName string
}

TableList is one page of ListTables.

type WriteOption

type WriteOption interface {
	// contains filtered or unexported methods
}

WriteOption configures PutItem, UpdateItem and DeleteItem.

type WriteRequest

type WriteRequest struct {
	Put    Item
	Delete Key
}

WriteRequest is one member of a BatchWriteItem: either a put or a delete. Build it with PutRequest or DeleteRequest.

func DeleteRequest

func DeleteRequest(key Key) WriteRequest

DeleteRequest removes key as part of a batch.

func PutRequest

func PutRequest(item Item) WriteRequest

PutRequest stores item as part of a batch.

func (WriteRequest) MarshalJSON

func (w WriteRequest) MarshalJSON() ([]byte, error)

MarshalJSON writes the PutRequest or DeleteRequest wrapper the batch API expects.

func (*WriteRequest) UnmarshalJSON

func (w *WriteRequest) UnmarshalJSON(data []byte) error

UnmarshalJSON reads the same wrapper, which is how unprocessed items come back in a shape that can be sent again.

type WriteResult

type WriteResult struct {
	Attributes Item
}

WriteResult is what a write reports back. Attributes is populated only when the call asked for it with WithReturnValues.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL