Zero Downtime Migration with Dual Write Pattern in Python
Implement a dual-write pattern that writes user data to both legacy and new systems simultaneously to enable zero-downtime migration.
Python code
55 linesfrom datetime import datetime
import json
class UserService:
def __init__(self):
self.legacy_db = {}
self.new_db = {}
self.migration_log = []
def write_user(self, user_id, name, email):
# Write to new system first
user_record = {
"id": user_id,
"name": name,
"email": email,
"updated_at": datetime.utcnow().isoformat()
}
self.new_db[user_id] = user_record
# Dual-write to legacy system
self.legacy_db[user_id] = user_record.copy()
self.migration_log.append(f"Wrote user {user_id} to both systems at {user_record['updated_at']}")
def read_from_legacy(self, user_id):
return self.legacy_db.get(user_id)
def read_from_new(self, user_id):
return self.new_db.get(user_id)
def verify_consistency(self):
mismatches = []
for user_id in set(self.legacy_db) | set(self.new_db):
if self.legacy_db.get(user_id) != self.new_db.get(user_id):
mismatches.append(user_id)
return len(mismatches) == 0, mismatches
if __name__ == "__main__":
service = UserService()
# Simulate migration traffic
service.write_user("user-001", "Alice", "alice@example.com")
service.write_user("user-002", "Bob", "bob@example.com")
service.write_user("user-003", "Carol", "carol@example.com")
# Simulate reads during migration
print("Legacy read:", service.read_from_legacy("user-002"))
print("New read:", service.read_from_new("user-002"))
# Verify data consistency between both stores
is_consistent, conflicts = service.verify_consistency()
print(f"\nMigration log entries: {len(service.migration_log)}")
print(f"All systems consistent: {is_consistent}")
print(f"Conflict IDs: {conflicts}")
Output
Legacy read: {'id': 'user-002', 'name': 'Bob', 'email': 'bob@example.com', 'updated_at': '2025-01-15T10:30:00.123456'}
New read: {'id': 'user-002', 'name': 'Bob', 'email': 'bob@example.com', 'updated_at': '2025-01-15T10:30:00.123456'}
Migration log entries: 3
All systems consistent: True
Conflict IDs: []
How it works
This dual-write pattern writes to the new system first, then copies the same record to the legacy store, ensuring both databases stay in sync during migration. The verify_consistency method compares all records across both systems to detect data drift or partial failures. Timestamps are captured once and shared between stores to guarantee identical records. The migration log provides an audit trail for tracking every write operation during the transition window, which is critical for rollback decisions. This approach allows reads to continue from either system without downtime while the new infrastructure is validated in production.
Common mistakes
- Writing to only one system when a write fails, causing divergence
- Not using the same timestamp for both writes, creating false inconsistencies
- Ignoring the migration log for audit and rollback analysis
- Forgetting to handle backfill of existing legacy data before going live
Variations
- Use a message queue to async dual-write, decoupling the write from the DB operation
- Implement a read-repair strategy where inconsistent reads trigger background correction
Real-world use cases
- Migrating a user database from a legacy monolith to a new microservice while keeping both available for reads.
- Rolling out a new analytics store alongside the existing production database to validate query performance before switching.
- Transitioning from a self-hosted PostgreSQL cluster to a managed cloud database without a service interruption.
Sponsored
More from Production deployment patterns
- Auto Rollback on Error Rate Exceeded in Python medium
- Automate Semantic Versioning with Conventional Commits in Python medium
- Design a Data Helper for Beginners in Python easy
- Docker healthcheck CMD mock in Python easy
- Generate a Mock Artifact Version Tag in Python easy
- Generate a docker-compose.yml with mock services in Python easy
Keep learning
Related tutorials and quizzes for this topic.