Struct object_store::aws::DynamoCommit
source · pub struct DynamoCommit { /* private fields */ }
Expand description
A DynamoDB-based commit protocol, used to provide conditional write support for S3
§Limitations
Only conditional operations, e.g. copy_if_not_exists
will be synchronized, and can
therefore race with non-conditional operations, e.g. put
, copy
, delete
, or
conditional operations performed by writers not configured to synchronize with DynamoDB.
Workloads making use of this mechanism must ensure:
- Conditional and non-conditional operations are not performed on the same paths
- Conditional operations are only performed via similarly configured clients
Additionally as the locking mechanism relies on timeouts to detect stale locks, performance will be poor for systems that frequently delete and then create objects at the same path, instead being optimised for systems that primarily create files with paths never used before, or perform conditional updates to existing files
§Commit Protocol
The DynamoDB schema is as follows:
- A string partition key named
"path"
- A string sort key named
"etag"
- A numeric TTL attribute named
"ttl"
- A numeric attribute named
"generation"
- A numeric attribute named
"timeout"
An appropriate DynamoDB table can be created with the CLI as follows:
$ aws dynamodb create-table --table-name <TABLE_NAME> --key-schema AttributeName=path,KeyType=HASH AttributeName=etag,KeyType=RANGE --attribute-definitions AttributeName=path,AttributeType=S AttributeName=etag,AttributeType=S
$ aws dynamodb update-time-to-live --table-name <TABLE_NAME> --time-to-live-specification Enabled=true,AttributeName=ttl
To perform a conditional operation on an object with a given path
and etag
(*
if creating),
the commit protocol is as follows:
- Perform HEAD request on
path
and error on precondition mismatch - Create record in DynamoDB with given
path
andetag
with the configured timeout- On Success: Perform operation with the configured timeout
- On Conflict:
- Periodically re-perform HEAD request on
path
and error on precondition mismatch - If
timeout * max_skew_rate
passed, replace the record incrementing the"generation"
- On Success: GOTO 2.1
- On Conflict: GOTO 2.2
- Periodically re-perform HEAD request on
Provided no writer modifies an object with a given path
and etag
without first adding a
corresponding record to DynamoDB, we are guaranteed that only one writer will ever commit.
This is inspired by the DynamoDB Lock Client but simplified for the more limited requirements of synchronizing object storage. The major changes are:
- Uses a monotonic generation count instead of a UUID rvn, as this is:
- Cheaper to generate, serialize and compare
- Cannot collide
- More human readable / interpretable
- Relies on TTL to eventually clean up old locks
It also draws inspiration from the DeltaLake S3 Multi-Cluster commit protocol, but generalised to not make assumptions about the workload and not rely on first writing to a temporary path.
Implementations§
source§impl DynamoCommit
impl DynamoCommit
sourcepub fn new(table_name: String) -> Self
pub fn new(table_name: String) -> Self
Create a new DynamoCommit
with a given table name
sourcepub fn with_timeout(self, millis: u64) -> Self
pub fn with_timeout(self, millis: u64) -> Self
Overrides the lock timeout.
A longer lock timeout reduces the probability of spurious commit failures and multi-writer races, but will increase the time that writers must wait to reclaim a lock lost. The default value of 20 seconds should be appropriate for must use-cases.
sourcepub fn with_max_clock_skew_rate(self, rate: u32) -> Self
pub fn with_max_clock_skew_rate(self, rate: u32) -> Self
The maximum clock skew rate tolerated by the system.
An environment in which the clock on the fastest node ticks twice as fast as the slowest node, would have a clock skew rate of 2. The default value of 3 should be appropriate for most environments.
Trait Implementations§
source§impl Clone for DynamoCommit
impl Clone for DynamoCommit
source§fn clone(&self) -> DynamoCommit
fn clone(&self) -> DynamoCommit
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moresource§impl Debug for DynamoCommit
impl Debug for DynamoCommit
source§impl PartialEq for DynamoCommit
impl PartialEq for DynamoCommit
impl Eq for DynamoCommit
impl StructuralPartialEq for DynamoCommit
Auto Trait Implementations§
impl Freeze for DynamoCommit
impl RefUnwindSafe for DynamoCommit
impl Send for DynamoCommit
impl Sync for DynamoCommit
impl Unpin for DynamoCommit
impl UnwindSafe for DynamoCommit
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
source§unsafe fn clone_to_uninit(&self, dst: *mut T)
unsafe fn clone_to_uninit(&self, dst: *mut T)
clone_to_uninit
)source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key
and return true
if they are equal.source§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more