AntidoteDB / AntidoteDB/antidote
RFC 100: Public API for information on synchronization
- Dominant language
- Erlang
- Stars
- 892
- Forks
- 92
- PR merge metrics
- No merged PRs in 30d
Description
# RFC 100: Public API for information on synchronization
_Authors:_ Nuno Preguiça, Gonçalo Cabrita, Jean Araújo
_Status:_ Under discussion
(Under discussion / Rejected / Approved: assigned to X / Implemented: pull request X)
## Context and Goal
### Context
In AntidoteDB, updates are propagated asynchronously among DCs. A transaction accesses a snapshot of the stable updates known at the local DC (with stable meaning that atomicity of updates in a transaction and causal dependencies across transactions are satisfied).
Thus, when a transaction reads a value in a DC, it is possible some updates to that value have already been committed in some other DCs. Additionally, during the execution of a transaction, some other transaction may modify the read value in the same (or other) DC -- these updates are also not visible to the running transaction.
### Goal
Provide an API to allow applications to have information about the up-to-date'ness of read data. This allows the application to reason about the staleness of read data, based on information available in the local DC.
We can define _potential staleness_ as the time period for which the read data may be stale. This can be computed from the last synchronization with remote DCs, which establish the earliest time when an unseen update may have been issued (at some other DC).
## Use Case
### Synchronization status
A user-facing application may want to provide information to the user on the last time the local replica has been synchronized with the remote DCs.
### Information on known changes
An application may want to prevent executing transactions on data that it knows has been modified concurrently.
## API changes
### External: AntidoteDB + protobuf
- `last_sync([bound_object()], TxId) -> {ok, [clock_time()]}`.
For each object, it returns the earliest time it synchronized for the last time with some DC replicating the object -- let _t1_ be the last time it synchronized with DC 1, the result for object _o_ is _min(ti)_, with i the DCs where _o_ is replicated.
- `last_sync_detail([bound_object()], TxId) -> {ok, [vectorclock()]}`.
The same as before, but detailing the information of last synchronization with each DC.
- `known_stale([bound_object()], TxId) -> {ok, [boolean()]}`.
For each data item, it returns whether the local DC has received an update that has not been reflected in the transaction snapshot.
### Internal: transaction manager
- `last_sync([bound_object()], TxId) -> {ok, [clock_time()]}`.
- `last_sync_detail([bound_object()], TxId) -> {ok, [vectorclock()]}`.
- `known_stale([bound_object()], TxId) -> {ok, [boolean()]}`.
As in the external API.
### Internal: log
- `has_new_updates([bound_object(),vectorclock()]) -> {ok, [integer()]}`.
Returns the number of known updates not reflects in the given vectorclock.
## Design
- `last_sync([bound_object()], TxId) -> {ok, [clock_time()]}`.
The return value can be computed from the snapshot time of the transaction. It is only necessary to process the operation in the component that records the snapshot time for the transaction.
```
API.last_sync([bound_object()], TxId) ->
TM.last_sync([bound_object()], TxId).
TM.last_sync( list, TxId) ->
snapshot_time = get_snapshot_time( TxId)
return {ok, list.map(key ->
replicas = get_replica_set( key)
min( snapshot_time.filter( replicas)) ) }
```
NOTE: this implementation supports partial replication.
- `last_sync_detail([bound_object()], TxId) -> {ok, [vectorclock()]}`.
The return value can be computed from the snapshot time of the transaction. It is only necessary to process the operation in the component that records the snapshot time for the transaction.
```
API.last_sync_detail([bound_object()], TxId) ->
TM.last_sync_detail([bound_object()], TxId).
TM.last_sync_detail( list, TxId) ->
snapshot_time = get_snapshot_time( TxId)
return {ok, list.map(key ->
replicas = get_replica_set( key)
snapshot_time.filter( replicas) ) }
```
- `known_stale([bound_object()], TxId) -> {ok, [boolean()]}`.
```
API.known_stale([bound_object()], TxId) ->
TM.known_stale([bound_object()], TxId).
TM.known_stale([bound_object()], TxId) ->
snapshot_time = get_snapshot_time( TxId)
return {ok, list.map(key ->
LOG.has_new_updates([key,snapshot_time] > 0) ) }
```
- `LOG.has_new_updates([bound_object(),vectorclock()]) -> {ok, [integer()]}`.
Return value based on the log information.
## Implementation
Straightforward from the design.
Propagate calls to `clocksi_readitem_server` (the same way a read is done), where the snapshot time of the transaction is known.
## Prior discussion
This feature has been discussed in the past in:
- Syncfree deliverable D3.2 [https://syncfree.lip6.fr/attachments/article/46/d3.2.pdf]
- Deepthi Devaki Akkoorath, Viktória Fördós, Annette Bieniusa: Observing the consistency of distributed systems. Erlang Workshop 2016: 54-55.
## Record of changes
04-10-2017: first complete version
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.