CrackedRuby CrackedRuby

Overview

Consistency models establish the rules that govern how and when updates made by one process become visible to other processes in a distributed system. Each model represents a different set of trade-offs between data consistency, system availability, and performance. The choice of consistency model fundamentally shapes application behavior, data integrity guarantees, and operational characteristics.

In a single-node system, consistency is straightforward: operations execute in program order, and all reads see the most recent write. Distributed systems complicate this picture. Multiple nodes maintain copies of data, network partitions can isolate nodes, and operations may execute concurrently across different locations. Consistency models formalize what guarantees the system provides despite these challenges.

The spectrum of consistency models ranges from strong consistency, where all nodes see the same data at the same time, to weak consistency, where different nodes may temporarily diverge. Stronger models provide simpler programming semantics but typically sacrifice availability and performance. Weaker models enable higher availability and better performance but require applications to handle data conflicts and eventual convergence.

# Strong consistency example: All readers see the write immediately
database.write(key: "user:123", value: {name: "Alice"})
database.read(key: "user:123")  
# => Always returns {name: "Alice"} across all nodes

# Eventual consistency example: Readers may see stale data temporarily
cache.set("session:456", {logged_in: true})
cache.get("session:456")  
# => May return nil briefly on different nodes before propagation

Understanding consistency models requires distinguishing between the logical ordering of operations and their physical execution timing. A consistency model doesn't dictate when operations actually execute; it defines what ordering guarantees the system must maintain from the client's perspective.

Key Principles

Consistency models build on several foundational concepts that define their behavior and guarantees.

Operation Visibility determines when writes become observable to read operations. In a strongly consistent system, a write must be visible to all subsequent reads immediately. In a weakly consistent system, there may be a window of time during which some reads return old values while the update propagates.

Operation Ordering specifies the sequence in which operations appear to execute. Sequential consistency requires all operations to appear in some global order consistent with the program order of each individual process. Causal consistency only preserves the order of causally related operations, allowing concurrent operations to be observed in different orders by different processes.

Linearizability represents the strongest single-object consistency guarantee. A linearizable system ensures that operations appear to execute atomically at some point between their invocation and response, with all operations respecting a single global order. This creates the illusion of a single, non-replicated data store.

# Linearizable operation semantics
class LinearizableRegister
  def write(value)
    # Write must take effect at some instant during this method
    timestamp = Time.now.to_f
    @value = {data: value, timestamp: timestamp}
  end
  
  def read
    # Read must return value from some instant during this method
    @value[:data]
  end
end

Serializability applies to transactions involving multiple objects. A serializable execution of concurrent transactions produces the same result as some serial execution of those transactions. This guarantee applies to the outcome but doesn't specify the actual order or timing.

Causal Consistency maintains the ordering of operations that have a cause-and-effect relationship. If operation A happens before operation B in a way that B could observe A's effects, all processes must see A before B. Concurrent operations that don't have a causal relationship can be observed in any order.

Eventual Consistency guarantees that if no new updates occur, all replicas will eventually converge to the same value. This model provides no bounds on convergence time and no ordering guarantees during the convergence period. The system may temporarily violate consistency constraints.

# Causal consistency example with version vectors
class CausalStore
  def initialize
    @vector_clock = Hash.new(0)
    @data = {}
  end
  
  def write(key, value, observed_version)
    # Increment local clock
    @vector_clock[node_id] += 1
    
    # Store value with version that includes causal history
    @data[key] = {
      value: value,
      version: @vector_clock.dup,
      depends_on: observed_version
    }
  end
  
  def read(key)
    entry = @data[key]
    {value: entry[:value], version: entry[:version]}
  end
end

Monotonic Reads ensure that if a process reads a value v1, subsequent reads by that process never return older values. This prevents time from appearing to flow backward from a client's perspective.

Monotonic Writes guarantee that write operations from a single process are applied in the order they were issued. If a process writes v1 then v2, all processes that observe these writes see them in that order.

Read-Your-Writes consistency ensures that a process always sees its own previous writes. After writing a value, subsequent reads by that process reflect the written value, even if other processes might not yet see the update.

Design Considerations

Selecting an appropriate consistency model requires analyzing application requirements against system constraints and trade-offs.

CAP Theorem Implications fundamentally constrain distributed system design. The theorem states that in the presence of network partitions, a system must choose between consistency and availability. Strong consistency models sacrifice availability during partitions, while weak consistency models sacrifice immediate consistency to maintain availability. The practical question becomes: how does the application behave when nodes cannot communicate?

Applications that require correctness guarantees often mandate strong consistency despite availability costs. Financial transactions, inventory management, and seat reservations cannot tolerate conflicting states. The cost of inconsistency exceeds the cost of occasional unavailability.

# Strong consistency for inventory management
class InventorySystem
  def reserve_item(item_id, quantity)
    # Must use distributed lock or consensus
    transaction do |tx|
      current = tx.read(item_id, isolation: :serializable)
      
      if current.quantity >= quantity
        tx.write(item_id, {quantity: current.quantity - quantity})
        :success
      else
        :insufficient_inventory
      end
    end
  end
end

Applications that prioritize availability often accept eventual consistency. Social media feeds, content caching, and metrics collection can tolerate temporary inconsistency. Users accept that likes and follower counts may briefly differ across views.

Application-Level Conflict Resolution becomes necessary with weak consistency models. When concurrent updates create conflicts, the application must define merge semantics. Last-write-wins strategies are simple but can lose data. Custom merge functions preserve all updates but increase complexity.

# Eventual consistency with conflict resolution
class EventuallyConsistentCounter
  def increment(amount)
    # Increment is commutative - conflicts resolve automatically
    @replicas.each do |replica|
      replica.merge_update(node_id, amount)
    end
  end
  
  def value
    # Sum all increments from all replicas
    @replicas.sum { |r| r.local_value }
  end
end

Session Guarantees provide middle-ground consistency appropriate for many applications. Read-your-writes, monotonic reads, and monotonic writes can be implemented without full strong consistency, giving clients coherent views without global coordination.

Geographic Distribution amplifies consistency trade-offs. Synchronous replication across continents adds hundreds of milliseconds to every write. Asynchronous replication provides low latency but creates windows of inconsistency. Multi-datacenter applications often partition data by geographic region, accepting weaker cross-region consistency for stronger within-region guarantees.

Write Patterns influence model selection. Write-heavy workloads benefit from weak consistency because strong consistency requires coordinating every write. Read-heavy workloads often tolerate eventual consistency since reads outnumber writes, and cached values remain valid for reasonable periods.

Data Relationships complicate consistency requirements. Operations spanning multiple objects require transaction semantics or application-level coordination. A weak consistency model may suffice for independent objects but prove inadequate when maintaining invariants across objects.

Implementation Approaches

Different mechanisms achieve various consistency guarantees in distributed systems.

Primary-Backup Replication designates one replica as primary, handling all writes. The primary synchronously or asynchronously propagates updates to backup replicas. Synchronous propagation provides strong consistency at the cost of write latency and availability during primary failure. Asynchronous propagation reduces latency but creates consistency windows.

class PrimaryBackupStore
  def initialize(primary:, backups:)
    @primary = primary
    @backups = backups
  end
  
  def write(key, value)
    # Write to primary first
    @primary.set(key, value)
    
    # Synchronously replicate to quorum of backups
    required_acks = (@backups.size / 2) + 1
    acks = @backups.count { |backup| backup.replicate(key, value) }
    
    raise ReplicationError unless acks >= required_acks
  end
  
  def read(key)
    # Can read from any replica
    (@backups + [@primary]).sample.get(key)
  end
end

Quorum Systems achieve consistency without a single primary. Each operation must be acknowledged by a majority of replicas. For N replicas, writes require W acknowledgments and reads require R acknowledgments where W + R > N. This overlap ensures reads see the most recent write.

Read quorums and write quorums can be adjusted to optimize for different access patterns. Higher write quorums (W = N) provide read-one-write-all semantics. Lower write quorums (W = 1, R = N) optimize for write latency. The key constraint is W + R > N for consistency.

class QuorumStore
  def initialize(replicas:, write_quorum:, read_quorum:)
    @replicas = replicas
    @write_quorum = write_quorum
    @read_quorum = read_quorum
  end
  
  def write(key, value)
    version = generate_version
    writes = @replicas.map { |r| 
      Thread.new { r.write(key, value, version) } 
    }
    
    successes = writes.count { |t| t.value == :success }
    raise QuorumError unless successes >= @write_quorum
  end
  
  def read(key)
    reads = @replicas.map { |r| 
      Thread.new { r.read(key) } 
    }
    
    responses = reads.map(&:value).compact
    raise QuorumError unless responses.size >= @read_quorum
    
    # Return value with highest version
    responses.max_by { |r| r[:version] }[:value]
  end
end

Vector Clocks track causality in distributed systems. Each node maintains a counter for every other node, creating a vector of logical timestamps. When a node performs an operation, it increments its own counter. When nodes exchange messages, they merge vector clocks, taking the maximum of each counter. This allows detecting concurrent operations and preserving causal ordering.

Vector clocks grow with the number of nodes, creating scaling challenges. Dotted version vectors optimize this by tracking only active writers. Version vectors trade space overhead for precise conflict detection.

class VectorClock
  def initialize(node_id)
    @node_id = node_id
    @clocks = Hash.new(0)
  end
  
  def increment
    @clocks[@node_id] += 1
  end
  
  def merge(other_clock)
    @clocks.merge!(other_clock.clocks) { |_, v1, v2| [v1, v2].max }
  end
  
  def happens_before?(other)
    @clocks.all? { |node, count| count <= other.clocks[node] } &&
      @clocks != other.clocks
  end
  
  def concurrent?(other)
    !happens_before?(other) && !other.happens_before?(self)
  end
  
  attr_reader :clocks
end

Consensus Algorithms like Raft and Paxos provide strong consistency by ensuring all replicas agree on operation order. Consensus protocols elect a leader that sequences operations. Followers replicate the leader's log. The system maintains consistency despite failures as long as a majority of nodes remain available.

Consensus protocols pay latency costs for coordination. Each operation requires at least one round-trip to a majority of nodes. This makes them unsuitable for geographically distributed systems spanning continents but appropriate for regional clusters.

Conflict-Free Replicated Data Types (CRDTs) provide eventual consistency without coordination. CRDTs define merge operations that are commutative, associative, and idempotent. Replicas can accept updates independently and merge states later, guaranteed to converge to the same value.

Different CRDT designs suit different data types. G-Counters only increment. PN-Counters support both increment and decrement. OR-Sets handle add and remove operations. The constraint is that merge operations must resolve conflicts deterministically without coordination.

class GCounter
  def initialize(node_id)
    @node_id = node_id
    @counts = Hash.new(0)
  end
  
  def increment(amount = 1)
    @counts[@node_id] += amount
  end
  
  def value
    @counts.values.sum
  end
  
  def merge(other)
    @counts.merge!(other.counts) { |_, v1, v2| [v1, v2].max }
  end
  
  attr_reader :counts
end

Ruby Implementation

Ruby applications interact with consistency models primarily through database clients and distributed data stores.

ActiveRecord with PostgreSQL provides serializable isolation through snapshot isolation. PostgreSQL's serializable snapshot isolation detects conflicts by tracking read and write dependencies between concurrent transactions. When conflicts occur, one transaction aborts with a serialization failure.

class Account < ActiveRecord::Base
  def transfer_to(other_account, amount)
    Account.transaction(isolation: :serializable) do
      current_balance = balance
      raise InsufficientFunds if current_balance < amount
      
      update!(balance: current_balance - amount)
      other_account.update!(balance: other_account.balance + amount)
    end
  rescue ActiveRecord::SerializationFailure
    # Retry transaction on conflict
    retry
  end
end

Redis offers eventual consistency by default with optional strong consistency through Redis Sentinel or Redis Cluster. Single Redis instances provide linearizable operations within that instance. Replication between primary and replicas is asynchronous, creating brief consistency windows.

require 'redis'

class SessionStore
  def initialize
    @redis = Redis.new(
      cluster: ['redis://node1:6379', 'redis://node2:6379'],
      reconnect_attempts: 3
    )
  end
  
  def store_session(session_id, data)
    # Eventual consistency - writes may not be immediately visible
    @redis.setex("session:#{session_id}", 3600, data.to_json)
  end
  
  def get_session(session_id)
    json = @redis.get("session:#{session_id}")
    json ? JSON.parse(json) : nil
  end
end

Elasticsearch provides eventual consistency with tunable replication. The write consistency parameter controls how many replicas must acknowledge a write. Setting it to "quorum" requires a majority of replicas, providing stronger consistency at the cost of write availability.

require 'elasticsearch'

class SearchIndex
  def initialize
    @client = Elasticsearch::Client.new(
      hosts: ['es1:9200', 'es2:9200', 'es3:9200']
    )
  end
  
  def index_document(id, document)
    @client.index(
      index: 'documents',
      id: id,
      body: document,
      consistency: 'quorum',  # Require majority ack
      refresh: false           # Don't wait for refresh
    )
  end
  
  def search(query)
    @client.search(
      index: 'documents',
      body: {query: {match: {content: query}}}
    )
  end
end

Cassandra through the cassandra-driver gem exposes consistency levels explicitly. Applications specify consistency requirements per operation. ONE requires only one replica to acknowledge, QUORUM requires a majority, and ALL requires all replicas. LOCAL_QUORUM operates within a single datacenter.

require 'cassandra'

class DistributedCounter
  def initialize
    @cluster = Cassandra.cluster(
      hosts: ['cassandra1', 'cassandra2', 'cassandra3']
    )
    @session = @cluster.connect('analytics')
  end
  
  def increment_counter(metric_id, amount)
    @session.execute(
      "UPDATE counters SET count = count + ? WHERE metric_id = ?",
      arguments: [amount, metric_id],
      consistency: :quorum  # Require majority for consistency
    )
  end
  
  def read_counter(metric_id)
    rows = @session.execute(
      "SELECT count FROM counters WHERE metric_id = ?",
      arguments: [metric_id],
      consistency: :one  # Accept eventual consistency for reads
    )
    rows.first['count']
  end
end

DynamoDB through the aws-sdk gem defaults to eventual consistency with optional strong consistency for reads. Applications specify consistent_read when strong consistency is required. DynamoDB uses conditional updates to implement optimistic concurrency control.

require 'aws-sdk-dynamodb'

class DynamoStore
  def initialize
    @client = Aws::DynamoDB::Client.new(region: 'us-east-1')
    @table = 'user_data'
  end
  
  def get_item_strongly_consistent(key)
    @client.get_item(
      table_name: @table,
      key: {'user_id' => key},
      consistent_read: true  # Strong consistency
    ).item
  end
  
  def get_item_eventually_consistent(key)
    @client.get_item(
      table_name: @table,
      key: {'user_id' => key},
      consistent_read: false  # Default eventual consistency
    ).item
  end
  
  def conditional_update(key, expected_version, new_data)
    @client.update_item(
      table_name: @table,
      key: {'user_id' => key},
      update_expression: 'SET #data = :data, #version = :version',
      expression_attribute_names: {
        '#data' => 'data',
        '#version' => 'version'
      },
      expression_attribute_values: {
        ':data' => new_data,
        ':version' => expected_version + 1,
        ':expected' => expected_version
      },
      condition_expression: '#version = :expected'
    )
  rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
    :conflict
  end
end

Implementing Session Guarantees in Ruby applications requires tracking state on the client side. Monotonic reads need version tracking to reject stale responses. Read-your-writes requires routing reads to replicas that have received the client's writes.

class MonotonicReadClient
  def initialize(replicas)
    @replicas = replicas
    @last_version = 0
  end
  
  def read(key)
    # Try replicas until finding one with sufficient version
    @replicas.shuffle.each do |replica|
      result = replica.read(key)
      
      if result[:version] >= @last_version
        @last_version = result[:version]
        return result[:value]
      end
    end
    
    raise StaleReadError
  end
end

Performance Considerations

Consistency models directly impact system performance, latency, and throughput characteristics.

Strong Consistency Costs manifest primarily as increased latency and reduced availability. Each write operation must propagate to multiple replicas synchronously before acknowledging success. This serialization point becomes a bottleneck. Network latency between replicas adds directly to operation latency. Geographic distribution amplifies this effect—synchronous replication across continents adds 100-300ms per operation.

Linearizability requires coordination that prevents concurrent operations from proceeding independently. This serialization limits throughput to the rate at which a single coordination point can process operations. Distributed consensus protocols like Raft require at least one round trip to a majority of nodes for each operation.

# Performance comparison: local vs distributed
require 'benchmark'

# Local strong consistency (single PostgreSQL server)
local_time = Benchmark.measure do
  1000.times do |i|
    User.transaction { User.create(name: "user_#{i}") }
  end
end

# Distributed strong consistency (multi-region replicas)
distributed_time = Benchmark.measure do
  1000.times do |i|
    User.transaction(isolation: :serializable) do
      User.create(name: "user_#{i}")
      # Waits for replication to all regions
    end
  end
end

# local_time.real: ~2.5 seconds
# distributed_time.real: ~150 seconds (60x slower)

Eventual Consistency Performance enables higher throughput and lower latency by removing synchronization points. Writes complete after updating only the local replica. Asynchronous replication propagates updates in the background without blocking the client. This allows operations to proceed at local speeds regardless of replica count or geographic distribution.

The cost of eventual consistency appears as increased complexity rather than latency. Applications must handle conflicts, stale reads, and temporary inconsistency. Conflict resolution overhead occurs when merges require application logic. Large numbers of concurrent updates to the same object increase conflict frequency.

Read Optimization differs significantly across consistency models. Eventual consistency allows reads from any replica, enabling geographic read optimization by directing clients to nearby replicas. Strong consistency often requires reading from the primary or a quorum of replicas, preventing geographic optimization.

class GeographicallyOptimizedCache
  def initialize(local_replicas, remote_replicas)
    @local = local_replicas
    @remote = remote_replicas
  end
  
  def get(key, strong_consistency: false)
    if strong_consistency
      # Must read from quorum - includes remote replicas
      read_quorum(key, @local + @remote)  # 150ms avg
    else
      # Can read from local replica only
      @local.sample.get(key)  # 2ms avg
    end
  end
end

Write Amplification occurs when replication multiplies the cost of each write. Strong consistency requires synchronously writing to multiple replicas. A write to a system with 5 replicas performs 5x the I/O of a single-node write. Network bandwidth consumption scales similarly. Quorum systems allow tuning this trade-off by varying quorum size.

Conflict Resolution Overhead impacts eventually consistent systems. When concurrent updates create conflicts, merging states adds computation cost. Simple strategies like last-write-wins have minimal overhead. Custom merge functions for complex data structures increase CPU usage. CRDTs trade merge computation for coordination elimination.

Caching Effectiveness varies with consistency guarantees. Strong consistency invalidates caches on every write, reducing cache hit rates. Eventual consistency allows longer cache TTLs since stale data is acceptable. Applications must balance staleness tolerance against cache performance.

class ConsistencyAwareCache
  def initialize(consistency_model:)
    @cache = {}
    @ttl = consistency_model == :strong ? 0 : 60
  end
  
  def get(key)
    cached = @cache[key]
    
    if cached && Time.now - cached[:timestamp] < @ttl
      cached[:value]  # Cache hit
    else
      value = fetch_from_source(key)
      @cache[key] = {value: value, timestamp: Time.now}
      value  # Cache miss
    end
  end
end

Transaction Abort Rates significantly impact performance in systems using optimistic concurrency control. High contention on hot keys causes frequent transaction aborts and retries. Each retry multiplies the effective cost of operations. Serializable isolation in PostgreSQL shows this effect clearly under contention.

Network Partition Handling affects performance during failures. Strong consistency systems become unavailable when partitions prevent reaching a quorum. Operations block until partition heals. Eventual consistency systems remain available during partitions but accumulate divergence requiring later reconciliation.

Practical Examples

Real-world scenarios demonstrate consistency model selection and implementation.

Social Media News Feed tolerates eventual consistency. Posts propagating to follower feeds can lag by seconds without harming user experience. Users accept that like counts and comments may briefly differ across views. The scale benefits of eventual consistency—handling millions of concurrent updates—outweigh consistency costs.

class NewsFeedService
  def initialize
    @redis = Redis.new
    @fanout_queue = Sidekiq::Queue.new('feed_fanout')
  end
  
  def post_update(user_id, content)
    # Write to user's timeline immediately
    post_id = SecureRandom.uuid
    post = {id: post_id, user_id: user_id, content: content}
    @redis.zadd("timeline:#{user_id}", Time.now.to_i, post.to_json)
    
    # Asynchronously fanout to followers (eventual consistency)
    FanoutWorker.perform_async(post_id, user_id)
    
    post_id
  end
  
  def get_feed(user_id, count: 50)
    # Read from local cache - may see slightly stale data
    posts = @redis.zrevrange("feed:#{user_id}", 0, count - 1)
    posts.map { |p| JSON.parse(p) }
  end
end

class FanoutWorker
  include Sidekiq::Worker
  
  def perform(post_id, author_id)
    post = fetch_post(post_id)
    followers = fetch_followers(author_id)
    
    # Write to each follower's feed asynchronously
    followers.each do |follower_id|
      Redis.current.zadd(
        "feed:#{follower_id}",
        post[:timestamp],
        post.to_json
      )
    end
  end
end

E-commerce Inventory requires strong consistency to prevent overselling. When stock quantity reaches zero, the system must prevent further purchases across all concurrent sessions. Eventual consistency would allow race conditions where multiple customers purchase the last item.

class InventoryManager
  def initialize
    @db = ActiveRecord::Base.connection
  end
  
  def purchase_item(item_id, quantity)
    # Use serializable transaction for strong consistency
    ActiveRecord::Base.transaction(isolation: :serializable) do
      item = Item.lock.find(item_id)
      
      if item.stock >= quantity
        item.update!(stock: item.stock - quantity)
        create_order(item_id, quantity)
        :success
      else
        :insufficient_stock
      end
    end
  rescue ActiveRecord::SerializationFailure
    # Retry on concurrent modification
    retry
  end
  
  private
  
  def create_order(item_id, quantity)
    Order.create!(
      item_id: item_id,
      quantity: quantity,
      timestamp: Time.now
    )
  end
end

Distributed Session Storage balances consistency and availability using session-specific guarantees. Read-your-writes ensures users see their own actions immediately. Other sessions can tolerate brief staleness for session data like preferences and recently viewed items.

class SessionStore
  def initialize(redis_cluster)
    @redis = redis_cluster
    @local_cache = {}
  end
  
  def write(session_id, key, value)
    # Write to primary for read-your-writes consistency
    @redis.set("session:#{session_id}:#{key}", value.to_json)
    
    # Update local cache for immediate read
    @local_cache["#{session_id}:#{key}"] = value
    
    # Asynchronously replicate to other regions
    ReplicationWorker.perform_async(session_id, key, value)
  end
  
  def read(session_id, key, strict: false)
    cache_key = "#{session_id}:#{key}"
    
    # Check local cache first (read-your-writes)
    return @local_cache[cache_key] if @local_cache.key?(cache_key)
    
    if strict
      # Strong consistency - read from primary
      json = @redis.get("session:#{session_id}:#{key}")
    else
      # Eventual consistency - read from any replica
      json = @redis.get("session:#{session_id}:#{key}", replicate: false)
    end
    
    json ? JSON.parse(json) : nil
  end
end

Real-Time Collaboration uses causal consistency to preserve operation ordering while allowing concurrent edits. Operational transformation or CRDTs merge concurrent changes without coordination. Users see their own changes immediately while other users' changes arrive asynchronously.

class CollaborativeDocument
  def initialize(document_id)
    @document_id = document_id
    @vector_clock = VectorClock.new(node_id)
    @pending_ops = []
  end
  
  def insert_text(position, text)
    # Create operation with causal context
    @vector_clock.increment
    
    operation = {
      type: :insert,
      position: position,
      text: text,
      clock: @vector_clock.dup
    }
    
    # Apply locally immediately
    apply_operation(operation)
    
    # Broadcast to other clients asynchronously
    broadcast_operation(operation)
  end
  
  def receive_remote_operation(operation)
    # Check causal dependencies
    if satisfies_dependencies?(operation)
      apply_operation(operation)
      @vector_clock.merge(operation[:clock])
    else
      # Buffer until dependencies satisfied
      @pending_ops << operation
    end
    
    # Process any buffered operations now satisfied
    process_pending_operations
  end
  
  private
  
  def satisfies_dependencies?(operation)
    operation[:clock].happens_before?(@vector_clock) ||
      operation[:clock].concurrent?(@vector_clock)
  end
end

Metrics Collection System uses eventual consistency for high-throughput ingestion. Individual data points can arrive out of order or with slight delays. Aggregated metrics converge to correct values as all data arrives. Accepting eventual consistency enables horizontal scaling of ingestion nodes.

class MetricsCollector
  def initialize
    @cassandra = Cassandra.cluster.connect('metrics')
  end
  
  def record_metric(metric_name, value, timestamp = Time.now)
    # Write with eventual consistency for maximum throughput
    @cassandra.execute_async(
      "INSERT INTO metrics (name, timestamp, value) VALUES (?, ?, ?)",
      arguments: [metric_name, timestamp, value],
      consistency: :one  # Single replica for speed
    )
  end
  
  def get_aggregated_metric(metric_name, window_minutes)
    # Read aggregates with quorum for reasonable accuracy
    rows = @cassandra.execute(
      "SELECT sum(value) as total FROM metrics 
       WHERE name = ? AND timestamp > ?",
      arguments: [metric_name, window_minutes.minutes.ago],
      consistency: :quorum
    )
    
    rows.first['total']
  end
end

Reference

Consistency Model Comparison

Model Guarantees Use Cases Availability Performance
Linearizability Operations appear atomic in real-time order Financial transactions, seat reservations Low during partitions High latency, low throughput
Sequential Consistency Operations appear in some total order Shared memory, distributed locks Low during partitions Medium latency
Causal Consistency Causally related operations ordered Collaborative editing, social feeds Medium Medium latency
Eventual Consistency All replicas converge eventually Content distribution, caching High Low latency, high throughput
Read-Your-Writes Clients see own writes User sessions, preferences Medium Medium latency
Monotonic Reads No reading old values after new News feeds, notifications Medium Medium latency
Monotonic Writes Writes applied in order Logging, append-only data Medium Medium latency

Database Consistency Guarantees

Database Default Model Available Options Configuration
PostgreSQL Serializable Read Uncommitted, Read Committed, Repeatable Read, Serializable Transaction isolation level
MySQL Repeatable Read Read Uncommitted, Read Committed, Repeatable Read, Serializable Transaction isolation level
MongoDB Eventual Eventual, Linearizable Read concern and write concern
Cassandra Eventual ONE, QUORUM, ALL, LOCAL_QUORUM Consistency level per operation
Redis Linearizable (single instance) Eventual (with replication) Replication configuration
DynamoDB Eventual Eventual, Strong consistent_read parameter
Elasticsearch Eventual Eventual, Quorum Write consistency parameter

Ruby Client Consistency Configuration

Client Consistency Parameter Values Example
ActiveRecord isolation read_uncommitted, read_committed, repeatable_read, serializable transaction(isolation: :serializable)
cassandra-driver consistency one, quorum, all, local_quorum execute(query, consistency: :quorum)
redis-rb wait replicas, timeout wait(replicas: 2, timeout: 1000)
aws-sdk-dynamodb consistent_read true, false get_item(consistent_read: true)
mongo read_concern local, majority, linearizable find(read: {mode: :majority})

CAP Theorem Trade-offs

Choice Optimizes For Sacrifices Example Systems
CP (Consistency + Partition Tolerance) Data correctness during partitions Availability when partitioned HBase, MongoDB (strong), Redis (Sentinel)
AP (Availability + Partition Tolerance) Service availability during partitions Consistency during partitions Cassandra, DynamoDB, Riak
CA (Consistency + Availability) Consistency and availability Partition tolerance (single site only) PostgreSQL (single instance), MySQL (single instance)

Quorum Configuration Formulas

Configuration Write Quorum (W) Read Quorum (R) Consistency Guarantee Use Case
Strong Consistency Any Any where W + R > N Latest write visible Financial transactions
Read Optimized N 1 Eventual Read-heavy workloads
Write Optimized 1 N Eventual Write-heavy logging
Balanced (N/2) + 1 (N/2) + 1 Strong General purpose
Sloppy Quorum W < (N/2) + 1 R < (N/2) + 1 Eventual High availability priority

Common Consistency Patterns

Pattern Description Implementation Trade-off
Primary-Backup Single primary handles writes, backups replicate Synchronous or asynchronous replication Simplicity vs consistency strength
Multi-Master Multiple nodes accept writes Conflict detection and resolution Availability vs complexity
Quorum Replication Majority agreement required Overlapping read/write sets Consistency vs latency
CRDT Conflict-free merge operations Commutative merge functions No coordination vs limited operations
Consensus Distributed agreement protocol Raft, Paxos, ZAB Strong consistency vs latency
Vector Clocks Track causality with version vectors Increment and merge clocks Causal ordering vs space overhead

Version Vector Operations

Operation Description Complexity Use Case
Increment Advance local clock O(1) Record local event
Merge Take maximum of each component O(N) where N is node count Incorporate remote state
Compare Determine happens-before relationship O(N) Detect conflicts
Prune Remove inactive nodes O(N) Control space growth

Performance Characteristics

Operation Strong Consistency Causal Consistency Eventual Consistency
Write Latency Network RTT × replicas Local write + async replication Local write only
Read Latency Network RTT to quorum Local read Local read
Throughput Limited by coordination Medium High
Conflict Frequency Zero (prevented) Low (causally ordered) High (concurrent updates)
Network Overhead High (synchronous) Medium Low (asynchronous)
Availability During Partition Low Medium High