Use ACID when you need strong consistency and can't tolerate stale reads (e.g., financial transactions). Use BASE when you need high availability and partition tolerance, and can tolerate temporary inconsistency (e.g., social media feeds, caching layers).
✦ Definition~90s read
What is ACID vs BASE?
ACID (Atomicity, Consistency, Isolation, Durability) and BASE (Basically Available, Soft state, Eventual consistency) are two sets of database design principles. ACID guarantees strict consistency for transactions, while BASE prioritizes availability and partition tolerance in distributed systems, accepting temporary inconsistency.
★
Think of ACID like a bank vault: every deposit and withdrawal is recorded instantly and exactly once.
Plain-English First
Think of ACID like a bank vault: every deposit and withdrawal is recorded instantly and exactly once. If the power goes out mid-transaction, the vault locks and nothing is lost. BASE is like a whiteboard in a busy office: someone writes a number, someone else erases it, and eventually everyone agrees on the final count. It's fast and works even if people are in different rooms, but you might see the wrong number for a moment.
Everyone says ACID is for banks and BASE is for everything else. That's a lie that'll cost you a pager alert at 3 AM. I've seen a social media feed built on Cassandra lose a user's post for 47 seconds because the read repair didn't trigger in time. I've also seen a payment service on PostgreSQLdeadlock because the isolation level was too strict for the concurrency pattern. The real question isn't which one is better—it's which one hurts less when it fails. By the end of this, you'll know exactly which consistency model your system needs, how to configure it without cargo-culting, and the exact failure modes that'll wake you up.
Why ACID Exists: The Problem of Partial Writes
Before ACID, databases could leave your data in a half-baked state. Imagine transferring $100 from account A to B: if the power fails after debiting A but before crediting B, the money vanishes. ACID's Atomicity ensures the entire operation succeeds or fails as one unit. Consistency guarantees that any transaction brings the database from one valid state to another—no orphaned rows, no violated constraints. Isolation prevents concurrent transactions from seeing each other's partial work. Durability means once a transaction commits, it survives a crash. Without these, you can't build reliable financial systems, inventory management, or any application where data integrity is non-negotiable.
TransactionExample.systemdesignSYSTEMDESIGN
1
2
3
4
5
6
7
8
// io.thecodeforge — SystemDesign tutorial
BEGINTRANSACTION;
UPDATE accounts SET balance = balance - 100WHERE account_id = 'A';
UPDATE accounts SET balance = balance + 100WHERE account_id = 'B';
COMMIT;
-- If the server crashes between the two updates, the transaction is rolled back.
-- Atomicity ensures both happen or neither.
Output
Transaction committed successfully.
⚠ Production Trap:
Don't assume ACID means zero data loss. If you use READ UNCOMMITTED isolation, you can still read dirty data. Always verify your isolation level in production—PostgreSQL defaults to READ COMMITTED, MySQL to REPEATABLE READ.
thecodeforge.io
Acid Vs Base
thecodeforge.io
Acid Vs Base
BASE: Trading Consistency for Availability
BASE emerged from the CAP theorem: when a network partition occurs, you must choose between consistency and availability. ACID systems choose consistency—they'll refuse to serve reads if they can't guarantee freshness. BASE systems choose availability—they'll serve whatever data they have, even if it's stale. This is why DynamoDB, Cassandra, and Couchbase are popular for high-traffic web apps. The trade-off is eventual consistency: after a write, reads may return old data for a window of time. For a social media 'like' counter, that's fine. For a medical records system, it's a lawsuit waiting to happen.
CassandraWriteExample.systemdesignSYSTEMDESIGN
1
2
3
4
5
6
7
8
9
10
11
12
// io.thecodeforge — SystemDesign tutorial
// Cassandra write with eventual consistency
// This write succeeds even if only one replica acknowledges it.
// Reads may not see this update until the gossip protocol propagates.
INSERTINTOuser_likes (user_id, post_id, liked_at)
VALUES ('user123', 'post456', toTimestamp(now()))
USINGCONSISTENCYONE;
// To force strong consistency, use QUORUM:
// INSERTINTO ... USINGCONSISTENCYQUORUM;
Output
Write successful (1 replica acknowledged).
💡Senior Shortcut:
When using BASE stores, always set your read consistency to the same level as write consistency for critical paths. For example, if you write with QUORUM, read with QUORUM. This gives you strong consistency without sacrificing all availability.
When ACID Breaks: The Isolation Level Trap
ACID's Isolation comes in levels: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE. Each level prevents certain anomalies but adds locking overhead. The classic rookie mistake is using SERIALIZABLE everywhere 'for safety'. I've seen this bring down a payments service when the thread pool was exhausted at 3am because every transaction waited for locks held by other transactions. The fix? Use READ COMMITTED for most operations, and only escalate to SERIALIZABLE for operations that truly need it—like checking account balances before a withdrawal. Even then, consider optimistic locking with version numbers instead.
OptimisticLocking.systemdesignSYSTEMDESIGN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// io.thecodeforge — SystemDesign tutorial
-- Optimistic locking using a version column
-- No locks held, but transaction may fail on commit
BEGIN;
SELECT balance, version FROM accounts WHERE account_id = 'A';
-- Application checks version and computes new balance
UPDATE accounts
SET balance = balance - 100, version = version + 1WHERE account_id = 'A'AND version = 5;
-- If version changed (another transaction updated), rows affected = 0
-- Retry the entire transaction
COMMIT;
Output
UPDATE 1 (if version matched) or UPDATE 0 (if version mismatch, retry needed).
⚠ Never Do This:
Don't use SERIALIZABLE isolation in a high-concurrency OLTP system without testing. The error you'll see: 'ERROR: could not serialize access due to read/write dependencies among transactions'. This causes massive retry storms and throughput collapse.
BASE Failure Modes: Stale Reads and Tombstones
BASE systems introduce two painful failure modes. First, stale reads: a client writes data, then immediately reads from a different replica that hasn't received the update yet. This breaks features like 'my profile shows my new name'. Mitigation: use read-after-write consistency by reading from the same replica that handled the write, or use quorum reads. Second, tombstones: in DynamoDB and Cassandra, deletes are just markers. If you delete a key and then immediately read, you might still get the old data because the tombstone hasn't propagated. This is a common source of bugs in session management. Always wait for a full gossip cycle (usually a few seconds) after a delete before relying on its absence.
DynamoDBReadAfterWrite.systemdesignSYSTEMDESIGN
1
2
3
4
5
6
7
8
9
10
11
12
// io.thecodeforge — SystemDesign tutorial
// DynamoDB: ensure read-after-write consistency
// Write with default eventual consistency
await dynamo.putItem({ TableName: 'Sessions', Item: session }).promise();
// Read with strong consistency to see the write immediately
const result = await dynamo.getItem({
TableName: 'Sessions',
Key: { sessionId: 'abc' },
ConsistentRead: true // This costs more RCUs but guarantees freshness
}).promise();
Output
Session object returned with latest data.
🔥Interview Gold:
Interviewers love asking: 'How do you handle read-after-write consistency in DynamoDB?' The answer: use ConsistentRead=true on the GetItem call, but be aware it consumes twice the read capacity units and has higher latency.
Choosing Between ACID and BASE: The Decision Framework
Here's the blunt rule: if you can't afford to lose a single write, use ACID. If you can tolerate temporary inconsistency for higher throughput and availability, use BASE. But it's not binary. Many systems use both: an ACID relational database for the core transactional data (orders, payments) and a BASE cache or search index for read-heavy workloads (product catalog, user feeds). The key is to define your consistency boundaries. For example, an e-commerce site might use PostgreSQL for the order table (ACID) and Elasticsearch for product search (BASE). If the search index is stale by 5 seconds, customers might not see the latest review, but they can still buy. That's acceptable.
HybridArchitecture.systemdesignSYSTEMDESIGN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// io.thecodeforge — SystemDesign tutorial
// Hybrid pattern: ACIDfor writes, BASEfor reads
// 1. Write to PostgreSQL (ACID)
BEGIN;
INSERTINTOorders (user_id, total) VALUES (123, 49.99);
COMMIT;
// 2. Asynchronously update Elasticsearch (BASE)
// This can fail without affecting the order
await elasticsearch.index({
index: 'orders',
body: { user_id: 123, total: 49.99 }
}).catch(err => console.error('Search index update failed', err));
// 3. Read from Elasticsearchfor fast search
// May be slightly stale, but that's acceptable
Output
Order committed in PostgreSQL. Search index update may be asynchronous.
💡Production Trap:
Don't assume eventual consistency means 'eventually within milliseconds'. In a partitioned network, it can take minutes. Always set a TTL or timeout on your reads to avoid serving data that's too stale.
thecodeforge.io
Acid Vs Base
When Not to Use ACID or BASE: The Overkill Zone
ACID is overkill when you're building a logging system or an append-only event stream. You don't need transactions to insert a log line—a simple BASE store with high write throughput is better. BASE is overkill when you have a single-node application with low concurrency. Using Cassandra for a blog with 100 visitors/day is absurd—SQLite with WAL mode gives you ACID with zero operational complexity. Also, don't use BASE for anything involving money, inventory, or user identity. I've seen startups try to use MongoDB (BASE-ish) for a payment ledger. The result: duplicate charges and angry customers. Use the right tool for the job.
OverkillExample.systemdesignSYSTEMDESIGN
1
2
3
4
5
6
7
8
9
10
11
// io.thecodeforge — SystemDesign tutorial
// Don't dothis: using ACIDfor a simple log
// Overkill and slow
BEGIN;
INSERTINTOlogs (message, timestamp) VALUES ('User logged in', NOW());
COMMIT;
// Better: just insert without transaction
INSERTINTOlogs (message, timestamp) VALUES ('User logged in', NOW());
// Or use a BASE store like Kafka or S3
Output
Log inserted.
⚠ The Classic Bug:
Using ACID for a high-volume audit log will kill your write throughput. Each transaction adds overhead. Instead, batch writes or use an append-only log like Kafka.
● Production incidentPOST-MORTEMseverity: high
The 4GB Container That Kept Dying
Symptom
A microservice handling order placement would crash with OOMKilled every 15 minutes under moderate load. No obvious memory leak in heap dumps.
Assumption
Team assumed a memory leak in the JVM or a misconfigured connection pool.
Root cause
The service used PostgreSQL with SERIALIZABLE isolation. Under concurrency, transactions retried exponentially, each retry holding a snapshot of the entire database in memory. The 4GB container limit was hit because each retry accumulated more MVCC snapshots.
Fix
Changed isolation to READ COMMITTED and added retry logic with jitter. Also increased container memory to 8GB as a buffer.
Key lesson
SERIALIZABLE isolation is not free—it trades memory for correctness.
Always benchmark your transaction retry memory footprint under peak concurrency.
Production debug guideSystematic recovery paths for the failure modes engineers actually hit.3 entries
Symptom · 01
Deadlock detected in PostgreSQL
→
Fix
1. Run SELECT * FROM pg_locks WHERE NOT granted; to see blocked transactions. 2. Identify the conflicting queries. 3. Ensure all transactions access tables in the same order. 4. Consider lowering isolation level to READ COMMITTED.
1. Check if idempotency keys are used. 2. Implement idempotency with a unique constraint or a TTL-based dedup table. 3. For DynamoDB, use conditional writes with attribute_not_exists.
★ ACID vs BASE Triage Cheat SheetFirst-response commands for when things go wrong — copy-paste ready.
Deadlock detected in PostgreSQL: `ERROR: deadlock detected`−
Immediate action
Check pg_locks to see blocked processes
Commands
SELECT * FROM pg_locks WHERE NOT granted;
SELECT pg_cancel_backend(pid) FROM pg_locks WHERE NOT granted;
Fix now
Ensure consistent lock ordering in application code.
Stale read in Cassandra: read returns old data after write+
Immediate action
Check consistency levels
Commands
nodetool getconsistency
nodetool repair
Fix now
Set read consistency to QUORUM or LOCAL_QUORUM.
Duplicate writes in DynamoDB: same item inserted twice+
Immediate action
Check if idempotency key is used
Commands
Scan for duplicate keys in application logs
Check conditional write logic
Fix now
Use ConditionExpression: 'attribute_not_exists(PK)' on writes.
Transaction retry storm in PostgreSQL: high CPU and OOM+
Immediate action
Check number of active transactions
Commands
SELECT count(*) FROM pg_stat_activity WHERE state = 'active';
SHOW max_connections;
Fix now
Reduce isolation level, add retry jitter, or increase container memory.
Feature / Aspect
ACID
BASE
Consistency model
Strong consistency (immediate)
Eventual consistency (temporary inconsistency)
Availability during partition
May become unavailable
Remains available
Performance
Lower write throughput due to locking
Higher write throughput, eventually consistent reads
Use cases
Financial transactions, inventory, user accounts
Social media feeds, caching, session stores
Database examples
PostgreSQL, MySQL, Oracle
Cassandra, DynamoDB, Couchbase
⚙ Quick Reference
6 commands from this guide
File
Command / Code
Purpose
TransactionExample.systemdesign
BEGIN TRANSACTION;
Why ACID Exists
CassandraWriteExample.systemdesign
INSERT INTO user_likes (user_id, post_id, liked_at)
ACID is not always safer—SERIALIZABLE isolation can cause retry storms that crash your service. Use the weakest isolation level that guarantees correctness.
2
BASE does not mean 'no consistency'—it means eventual consistency. You must design your application to handle stale reads, or use quorum for critical paths.
3
The best systems use both
ACID for the source of truth, BASE for read-optimized caches and indexes. Define clear consistency boundaries.
4
The counterintuitive truth
ACID databases can lose data too if you use READ UNCOMMITTED or don't fsync on commit. Always verify your durability settings.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
How does PostgreSQL handle concurrent transactions under SERIALIZABLE is...
Q02SENIOR
When would you choose DynamoDB over PostgreSQL for a user profile servic...
Q03SENIOR
What happens when you read from a Cassandra node that hasn't received a ...
Q04JUNIOR
What is the difference between READ COMMITTED and REPEATABLE READ isolat...
Q05SENIOR
You're debugging a production issue where a user's post appears after 10...
Q06SENIOR
How would you design a system that needs both ACID guarantees for paymen...
Q01 of 06SENIOR
How does PostgreSQL handle concurrent transactions under SERIALIZABLE isolation, and what happens when a conflict is detected?
ANSWER
PostgreSQL uses Serializable Snapshot Isolation (SSI). It tracks read-write conflicts using predicate locks. When a conflict is detected, one transaction is aborted with 'could not serialize access'. The application must retry. This is expensive—predicate locks consume memory and can cause performance degradation under high concurrency.
Q02 of 06SENIOR
When would you choose DynamoDB over PostgreSQL for a user profile service?
ANSWER
Choose DynamoDB when you need single-digit millisecond latency at any scale, can tolerate eventual consistency for profile reads, and want to offload operational overhead (no manual sharding). Choose PostgreSQL when you need complex queries (joins, aggregations), strong consistency, or transactional updates across multiple profile attributes.
Q03 of 06SENIOR
What happens when you read from a Cassandra node that hasn't received a recent write, and how do you mitigate it?
ANSWER
The read returns stale data. Mitigation: use read repair (triggered on read if consistency is not ALL), or use QUORUM consistency for both reads and writes. Also, run periodic nodetool repair to synchronize replicas.
Q04 of 06JUNIOR
What is the difference between READ COMMITTED and REPEATABLE READ isolation levels?
ANSWER
READ COMMITTED prevents dirty reads but allows non-repeatable reads (a row can change between two reads in the same transaction). REPEATABLE READ prevents non-repeatable reads by using a snapshot, but allows phantom reads (new rows inserted by other transactions). PostgreSQL's REPEATABLE READ also prevents phantom reads using snapshot isolation.
Q05 of 06SENIOR
You're debugging a production issue where a user's post appears after 10 seconds in a Cassandra-backed feed. What do you check?
ANSWER
First, check the consistency level used for writes and reads. If write consistency is ONE and read consistency is ONE, the read may hit a replica that hasn't received the write. Increase both to QUORUM. Also check if the write succeeded (no timeout). Finally, verify that the replication factor is >1 and that all replicas are up.
Q06 of 06SENIOR
How would you design a system that needs both ACID guarantees for payments and BASE scalability for product search?
ANSWER
Use a hybrid architecture: PostgreSQL for the payment ledger (ACID), and Elasticsearch for product search (BASE). Synchronize via a change data capture (CDC) pipeline: Debezium captures PostgreSQL changes and streams them to Kafka, then a consumer updates Elasticsearch. This ensures eventual consistency for search while maintaining strong consistency for payments.
01
How does PostgreSQL handle concurrent transactions under SERIALIZABLE isolation, and what happens when a conflict is detected?
SENIOR
02
When would you choose DynamoDB over PostgreSQL for a user profile service?
SENIOR
03
What happens when you read from a Cassandra node that hasn't received a recent write, and how do you mitigate it?
SENIOR
04
What is the difference between READ COMMITTED and REPEATABLE READ isolation levels?
JUNIOR
05
You're debugging a production issue where a user's post appears after 10 seconds in a Cassandra-backed feed. What do you check?
SENIOR
06
How would you design a system that needs both ACID guarantees for payments and BASE scalability for product search?
SENIOR
FAQ · 4 QUESTIONS
Frequently Asked Questions
01
What is the main difference between ACID and BASE in databases?
ACID guarantees strong consistency and isolation for transactions, while BASE prioritizes availability and partition tolerance, accepting temporary inconsistency. ACID is used for systems that cannot tolerate data loss (e.g., banking), BASE for systems that need high availability and can tolerate stale reads (e.g., social media).
Was this helpful?
02
When should I use ACID instead of BASE?
Use ACID when you need immediate consistency and cannot afford data loss or corruption. Examples: financial transactions, inventory management, user account balances. Use BASE when you need high write throughput and availability, and can tolerate temporary inconsistency. Examples: activity feeds, caching, session stores.
Was this helpful?
03
How do I ensure read-after-write consistency in a BASE system like DynamoDB?
Use the ConsistentRead=true parameter on GetItem calls. This consumes twice the read capacity units but guarantees you see the latest write. Alternatively, write to a single partition and read from the same partition using eventual consistency with a short delay.
Was this helpful?
04
Can a database be both ACID and BASE?
No, a single database cannot be both. However, you can architect a system that uses an ACID database for transactional data and a BASE database for read-heavy workloads, synchronizing them asynchronously. This hybrid approach is common in production.