What is Monotonic read and how it's application in real world
Monotonic reads is a consistency guarantee in distributed systems where, if a client performs multiple read operations on the same data, the client will never see older versions of the data .
In other words, the sequence of reads by a single client will always reflect a timeline where each subsequent read is at least as up-to-date as the previous one.
This guarantee ensures that the client observes a consistent progression of data changes without regressing to stale versions.
Example of Monotonic Reads
Imagine a distributed database with multiple replicas, and a client reading from different replicas in a load-balanced setup:
Without Monotonic Reads:
At
t1
, the client reads data from replicaA
and gets the valuex = 10
.At
t2
, the client reads data from replicaB
and gets the older valuex = 5
(due to replication delay).The client observes non-monotonic behavior, which can lead to inconsistencies in the application logic.
With Monotonic Reads:
Once the client sees the value
x = 10
, any subsequent read from any replica will guarantee a value at least as recent asx = 10
.The client observes monotonic behavior, ensuring data consistency in the sequence of reads.
How Monotonic Reads are Ensured
Monotonic reads can be implemented using various techniques, including:
Versioning or Timestamps:
Each update to the data is assigned a monotonically increasing version or timestamp (e.g., Lamport timestamps or TrueTime in Spanner).
During reads, the client tracks the highest version or timestamp it has seen and ensures that future reads fetch data with at least that version.
Session Stickiness:
Clients are "sticky" to a specific replica for the duration of their session. This ensures they only observe data from one replica, which avoids inconsistencies caused by replication delays.
Read Repair:
If a client reads stale data from a replica, the system ensures that this replica fetches and serves the latest version of the data before returning it.
Linearizable Systems:
Systems providing stronger guarantees like linearizability inherently ensure monotonic reads, as they provide a globally consistent order of operations.
How Monotonic Reads are Useful
Monotonic reads are particularly important in applications that depend on data consistency across time for correctness or user experience. Here’s why they are useful:
1. User-facing Consistency
In applications with user-facing data, monotonic reads prevent confusing scenarios like:
A user sending a message and seeing it disappear momentarily due to reading from a stale replica.
Example: In chat systems, once a message is sent and displayed, the client should always observe it in subsequent reads.
2. Correctness in Business Logic
Applications often rely on consistent state progression for correctness. Monotonic reads prevent:
Errors caused by regressing to older values.
Example: In banking or financial applications, if a user transfers money and sees their updated balance, they should never see an earlier balance again.
3. Data Causality
Monotonic reads respect the natural causality of events. For example:
If
Event A
happens beforeEvent B
, a client will not observeEvent B
without having first observedEvent A
.
This is important for collaborative applications like Google Docs, where edits must appear in the order they were made.
4. Improved Developer Experience
Developers can rely on monotonic reads to simplify application logic, knowing the system guarantees consistency within a single client session.
Monotonic Reads in Google Spanner
Google Spanner inherently provides monotonic reads, thanks to its TrueTime-based timestamps and strong consistency guarantees:
Spanner assigns globally consistent timestamps to all writes.
A client tracks the timestamp of the last read and ensures future reads only return data with timestamps ≥ the last seen timestamp.
This guarantees monotonic reads even in geographically distributed setups.
Applications where monotonic reads are used:
Social Media Platforms (e.g., Twitter, Facebook, Instagram):
Use Case: A user sees a post, likes it, and then refreshes the feed. The post with the like should still appear in subsequent reads (i.e., the user should not see an older state of the post).
Reason: Users expect consistent interaction with their data, even if it's eventually consistent across the entire system.
Online Banking Systems:
Use Case: A user views their account balance, performs a transaction, and checks the balance again. The balance should reflect the transaction and not show an earlier state.
Reason: Financial data consistency for individual clients is critical to maintain trust and accuracy.
E-commerce Platforms:
Use Case: A user adds an item to their cart. When they revisit the cart, they expect to see the updated state (with the added item) and not an earlier snapshot.
Reason: Shopping cart consistency is necessary for a smooth user experience.
Collaborative Document Editing (e.g., Google Docs):
Use Case: A user edits a document and then reopens it. They expect to see their latest edits reflected rather than an older version.
Reason: Maintaining the correct order of updates ensures a seamless editing experience.
Messaging Applications (e.g., WhatsApp, Slack):
Use Case: A user sends or receives messages. When reloading the chat, they expect the messages to appear in the same order and with no missing updates.
Reason: Maintaining monotonic reads avoids confusing scenarios where users see messages out of sequence.
Version Control Systems (e.g., Git, SVN):
Use Case: When pulling or fetching changes, a developer should always see the most recent updates after a previous fetch.
Reason: Ensures that developers do not encounter older versions of code after seeing a newer one.
Leaderboard Systems:
Use Case: A player sees their ranking on the leaderboard. When they check again, they should not see an earlier ranking position after seeing an updated one.
Reason: Players expect the ranking to reflect consistent progress.
Benefits of Monotonic Reads:
Enhances user experience by ensuring consistent and logical data interactions.
Simplifies application logic since developers do not have to handle regressions in data state for the same user.
Prevents confusion caused by "time travel" effects (i.e., data appearing to move backward in time).
In distributed systems, achieving monotonic reads often involves techniques like:
Ensuring a session-based consistency model.
Using a consistent hashing mechanism to route a client's requests to the same replica.
Implementing vector clocks or Lamport timestamps to track and ensure ordering.
Image Source: DDIA book
Image Source:- Wikipedia