Was this page helpful?
Caution
You're viewing documentation for an unstable version of ScyllaDB Manual. Switch to the latest stable version.
Alternator: DynamoDB API in ScyllaDB¶
Introduction¶
Alternator is a ScyllaDB feature adding compatibility with Amazon DynamoDB(TM). DynamoDB’s API uses JSON-encoded requests and responses which are sent over an HTTP or HTTPS transport. It is described in detail in Amazon’s DynamoDB API Reference.
Our goal is that any application written to use Amazon DynamoDB could be run, unmodified, against ScyllaDB with Alternator enabled. Alternator’s compatibility with DynamoDB is fairly complete, but users should be aware of some differences and some unimplemented features. The extent of Alternator’s compatibility with DynamoDB is described in the ScyllaDB Alternator for DynamoDB users document, which is updated as the work on Alternator progresses and compatibility continues to improve.
Alternator also adds several features and APIs that are not available in DynamoDB. These are described in Alternator-specific APIs.
Running Alternator¶
By default, ScyllaDB does not listen for DynamoDB API requests. To enable this API in ScyllaDB you must set at least two configuration options, alternator_port and alternator_write_isolation. For example in the YAML configuration file:
alternator_port: 8000
alternator_write_isolation: only_rmw_uses_lwt # or always, forbid or unsafe
or, equivalently, via command-line arguments: `–alternator-port=8000 –alternator-write-isolation=only_rmw_uses_lwt.
the alternator_port option determines on which port ScyllaDB listens for DynamoDB API requests. By default, it listens on this port on all network interfaces. To listen only on a specific interface, configure also the alternator_address option.
The meaning of the alternator_write_isolation option is explained in the “Write isolation policies” section of Alternator-specific APIs. Alternator has four different choices for the implementation of writes, each with different advantages. You should carefully consider which of the options makes more sense for your intended use case and configure alternator_write_isolation accordingly. There is currently no default for this option: Trying to run ScyllaDB with an Alternator port selected but without configuring write isolation will result in an error message, asking you to set it.
In addition to (or instead of) serving HTTP requests on alternator_port,
ScyllaDB can accept DynamoDB API requests over HTTPS (encrypted), on the port
specified by alternator_https_port. As usual for HTTPS servers, the
operator must specify certificate and key files. By default these should
be placed in /etc/scylla/scylla.crt and /etc/scylla/scylla.key, but
these default locations can overridden by specifying
--alternator-encryption-options keyfile="..." and
--alternator-encryption-options certificate="...".
In addition to alternator_port and alternator_https_port, the two options alternator_port_proxy_protocol (for HTTP) and alternator_https_port_proxy_protocol (for HTTPS) allow running Alternator behind a reverse proxy, such as HAProxy or AWS PrivateLink, and still report the correct client address. The reverse proxy must be configured to use Proxy Protocol v2 (the binary header format).
By default, ScyllaDB saves a snapshot of deleted tables. But Alternator does
not offer an API to restore these snapshots, so these snapshots are not useful
and waste disk space - deleting a table does not recover any disk space.
It is therefore recommended to disable this automatic-snapshotting feature
by configuring the auto_snapshot option to false.
See also https://github.com/scylladb/scylladb/issues/5283.
DynamoDB applications specify a single “endpoint” address, e.g.,
dynamodb.us-east-1.amazonaws.com. Behind the scenes, a DNS server and/or
load balancers distribute the connections to many different backend nodes.
Alternator does not provide such a load-balancing setup, so you should
either set one up, or set up the client library to do the load balancing
itself. Instructions, code and examples for doing this can be found in the
Alternator Load Balancing project.
Mutual TLS (mTLS) for Alternator¶
In addition to the usual SigV4 signature authentication, Alternator also
supports mutual TLS (mTLS) authentication, where the client presents a
certificate that the server validates. By default, the CN (Common Name) of
the certificate’s Subject DN is used as the role name. This mapping is
configurable via the auth_certificate_role_queries option (described
below), which also supports matching Subject Alternative Names (SANs).
To configure mTLS, use the alternator-encryption-options configuration.
Two settings control the behavior:
truststore: Path to a CA certificate file. The server uses this to validate client certificates.require_client_auth: Controls how strictly client certificates are enforced. There are three modes:require_client_auth=true— The server requires a valid client certificate. Clients without one (or with an untrusted certificate) are rejected at the TLS handshake level. The certificate is mapped to a role viaauth_certificate_role_queries; if the mapping fails or the role does not exist withLOGIN=true, the request is rejected. SigV4 authentication is not used. Example configuration:--alternator-https-port 8043 --alternator-encryption-options certificate=/path/to/server.crt --alternator-encryption-options keyfile=/path/to/server.key --alternator-encryption-options truststore=/path/to/ca.crt --alternator-encryption-options require_client_auth=true
require_client_auth=optional— The server requests a client certificate but does not require one. Clients that present no certificate fall back to SigV4 authentication. This allows both mTLS and SigV4 clients to connect to the same HTTPS port. Clients that present a valid (CA-signed) certificate must authenticate via mTLS: their certificate is mapped to a role, and if the mapping fails or the role does not exist withLOGIN=true, the request is rejected — there is no SigV4 fallback for certificate-bearing clients. Example configuration:--alternator-https-port 8043 --alternator-encryption-options certificate=/path/to/server.crt --alternator-encryption-options keyfile=/path/to/server.key --alternator-encryption-options truststore=/path/to/ca.crt --alternator-encryption-options require_client_auth=optional
require_client_auth=false(default) — The server does not request a client certificate at all. Only SigV4 authentication is used.
When a client certificate is presented on a connection, SigV4 authentication is not needed, and if a SigV4 signature appears on any request sent over this connection, it is silently ignored. This means that when a connection uses mTLS, the same role applies to all requests on that connection and cannot be overridden on a per-request basis.
Mapping a certificate to a role is controlled by the
auth_certificate_role_queries configuration option (documented in detail in
Certificate-based Authentication).
Each entry in the list has a source and a query (a regular expression
with exactly one capture group). The first matching entry determines the
role name.
source: SUBJECT— matches against the certificate’s Subject DN string (e.g."CN=alice,O=MyOrg"). The default configuration uses this source with the patternCN=([^,]+)to extract the Common Name.source: ALTNAME— matches against the certificate’s Subject Alternative Names (SANs), formatted as a comma-separated list ofTYPE=valueentries (e.g."EMAIL=alice@example.com,DNS=host.example.com"). EMAIL SANs are the standard way to carry user identity in enterprise PKI.
Example: to authenticate using an email SAN of the form user@example.com
and map the local part to a role:
auth_certificate_role_queries:
- source: ALTNAME
query: 'EMAIL=([^@,]+)@'
When using mTLS, set alternator_enforce_authorization: true and configure
authorizer: CassandraAuthorizer so that the certificate-derived username is
looked up and authorized against the role system (see the Authentication and
Authorization section in compatibility.md). If SigV4 authentication is also
used (i.e., require_client_auth=optional), additionally configure
authenticator: PasswordAuthenticator.
Alternator design and implementation¶
This section provides only a very brief introduction to Alternator’s design. A much more detailed document about the features of the DynamoDB API and how they are, or could be, implemented in ScyllaDB can be found in: https://docs.google.com/document/d/1i4yjF5OSAazAY_-T8CBce9-2ykW4twx_E_Nt2zDoOVs
Almost all of Alternator’s source code (except some initialization code) can be found in the alternator/ subdirectory of ScyllaDB’s source code. Extensive functional tests can be found in the test/alternator subdirectory. These tests are written in Python, and can be run against both Alternator and Amazon’s DynamoDB; This allows verifying that Alternator’s behavior matches the one observed on DynamoDB. See test/alternator/README.md for more information about the tests and how to run them.
With Alternator enabled on port 8000 (for example), every ScyllaDB node listens for DynamoDB API requests on this port. These requests, in JSON format over HTTP, are parsed and result in calls to internal Scylla C++ functions - there is no CQL generation or parsing involved. In ScyllaDB terminology, the node receiving the request acts as the coordinator, and often passes the request on to one or more other nodes - replicas which hold copies of the requested data.
Alternator tables are stored as ScyllaDB tables, each in a separate keyspace. Each keyspace is initialized when the corresponding Alternator table is created (with a CreateTable request). The replication factor (RF) for this keyspace is chosen at that point, depending on the size of the cluster: RF=3 is used on clusters with three or more nodes, and RF=1 is used for smaller clusters. Such smaller clusters are, of course, only recommended for tests because of the risk of data loss.
Each table in Alternator is stored as a ScyllaDB table in a separate keyspace. The DynamoDB key columns (hash and sort key) have known types, and become partition and clustering key columns of the ScyllaDB table. All other attributes may be different for each row, so are stored in one map column in ScyllaDB, and not as separate columns.
DynamoDB supports two consistency levels for reads, “eventual consistency”
and “strong consistency”. These two modes are implemented using ScyllaDB’s CL
(consistency level) feature: All writes are done using the LOCAL_QUORUM
consistency level, then strongly-consistent reads are done with
LOCAL_QUORUM, while eventually-consistent reads are with just LOCAL_ONE.
In ScyllaDB (and its inspiration, Cassandra), high write performance is achieved by ensuring that writes do not require reads from disk. The DynamoDB API, however, provides many types of requests that need a read before the write (a.k.a. RMW requests - read-modify-write). For example, a request may copy an existing attribute, increment an attribute, be conditional on some expression involving existing values of attribute, or request that the previous values of attributes be returned. These read-modify-write transactions should be isolated from each other, so by default Alternator implements every write operation using ScyllaDB’s LWT (lightweight transactions). This default can be overridden on a per-table basis, by tagging the table as explained above in the “write isolation policies” section.
DynamoDB allows attributes to be nested - a top-level attribute may be a list or a map, and each of its elements may further be lists or maps, etc. Alternator currently stores the entire content of a top-level attribute as one JSON object. This means that UpdateItem requests which want modify a non-top-level attribute directly (e.g., a.b[3].c) need RMW: Alternator implements such requests by reading the entire top-level attribute a, modifying only a.b[3].c, and then writing back a.