Architecture
This document explains the core architecture, components, and design principles behind HarmonyLite. Understanding these concepts will help you better implement, configure, and troubleshoot your HarmonyLite deployment.
HarmonyLite is an AP system (Availability + Partition Tolerance) that uses SQLite triggers to capture changes, NATS JetStream to distribute them, and a last-writer-wins strategy for conflict resolution. Any node can accept writes, and all nodes eventually converge to the same state.
This is the recommended starting point for understanding HarmonyLite. After this, read Replication for details on how changes propagate, then Snapshots for recovery mechanisms.
Architectural Overview
HarmonyLite implements a leaderless, eventually consistent replication system for SQLite databases. The architecture consists of four main components working together:
- Change Data Capture (CDC): Monitors and records database changes
- Message Distribution: Publishes and subscribes to change events
- Change Application: Applies changes to local databases
- State Management: Handles snapshots and recovery
The following diagram illustrates the high-level architecture:
Core Components
1. Change Data Capture (CDC)
HarmonyLite uses SQLite triggers to capture all database changes:
- Triggers: Automatically installed on all tables to detect INSERT, UPDATE, and DELETE operations
- Change Log Tables: Each monitored table has a corresponding
__harmonylite__<table_name>_change_logtable - Global Change Log: A master table (
__harmonylite___change_log_global) tracks the sequence of operations
When a change occurs:
- The trigger fires and captures the change details
- Information is stored in the change log table
- A reference is added to the global change log
Table Structure
Database changes are tracked in specialized tables with this structure:
2. Message Distribution
HarmonyLite uses NATS JetStream for reliable message distribution:
- Change Detection: Monitors the database for modifications
- Change Collection: Retrieves pending records from change log tables
- Hash Calculation: Computes a hash from table name and primary keys
- Stream Selection: Routes changes to specific streams based on the hash
- Publishing: Sends changes to NATS JetStream
- Confirmation: Marks changes as published after acknowledgment
This approach ensures changes to the same row are always handled in order, while allowing parallel processing of changes to different rows.
3. Change Application
When a node receives a change message:
- It checks if the change was originated locally (to avoid cycles)
- It verifies the change hasn't been applied before
- It parses the change details (table, operation type, values)
- It applies the change to the local database
- It records the message sequence for recovery tracking
4. State Management
HarmonyLite maintains system state through:
- Sequence Map: Tracks the last processed message for each stream
- Snapshots: Periodic database snapshots for efficient recovery
- CBOR Serialization: Efficient binary encoding for change records
- Schema Registry: NATS KV-backed registry for cluster-wide schema visibility
5. Component Design
The internal package structure follows this design:
Sequence Map & Idempotency
The Sequence Map is the "brain" of HarmonyLite's reliability. It is a local file (default: seq-map.cbor) that maintains the state of consumption for every stream.
Why is it critical?
-
Idempotency (Exactly-Once Processing):
- In distributed systems, messages may be delivered more than once.
- The Sequence Map filters out duplicates by ensuring we only process
Sequence > StartSequence. - This guarantees that a database change (like an INSERT) is never applied twice, which would corrupt data.
-
Crash Recovery:
- If a node restarts, it reads the Sequence Map to know exactly where it left off.
- It resumes consumption from
LastSequence + 1.
How it works
The Sequence Map is a simple Key-Value store serialized in CBOR (Concise Binary Object Representation) for performance and compactness.
- Key: Stream Name (e.g.,
harmonylite-changes-1) - Value: Last successfully applied Sequence Number (e.g.,
1042)
Key Mechanisms
Leaderless Replication
Unlike leader-follower systems, HarmonyLite operates without a designated leader:
- Any node can accept writes
- Changes propagate to all nodes
- No single point of failure
- Higher write availability
Eventual Consistency
HarmonyLite prioritizes availability over immediate consistency:
- Changes eventually reach all nodes
- Last-writer-wins conflict resolution
- No global locking mechanism
- Non-blocking operations
Sharding
Change streams can be sharded to improve performance:
- Each shard handles a subset of rows
- Determined by hashing table name and primary keys
- Enables parallel processing
- Configurable via
replication_log.shards
Message Flow
The complete message flow looks like this:
Snapshot and Recovery
Snapshot Creation
Snapshots provide efficient node recovery:
Node Recovery
When a node starts or needs to catch up:
Schema Versioning
HarmonyLite includes schema versioning to handle rolling upgrades where nodes may temporarily have different database schemas.
How It Works
- Schema Hash Computation: Each table's schema is hashed using Atlas for introspection
- Message Tagging: Every replication message includes the sender's schema hash
- Validation: Receivers compare the message hash against their local schema
- Pause on Mismatch: If schemas differ, replication pauses (NAK with delay)
- Automatic Resume: After local schema upgrade and restart, replication resumes
Schema Registry
HarmonyLite maintains a NATS KeyValue registry (harmonylite-schema-registry) that tracks each node's schema state:
{
"node_id": 1,
"schema_hash": "a1b2c3d4e5f6...",
"previous_hash": "x9y8z7w6v5u4...",
"harmonylite_version": "1.2.0",
"updated_at": "2025-01-20T10:30:00Z"
}
The previous_hash field enables smooth rolling upgrades: a node accepts events matching either its current hash or its previous hash. This prevents unnecessary pauses when upgrading nodes one at a time in a multi-publisher cluster.
This enables operators to monitor schema rollout progress across the cluster.
Rolling Upgrade Flow
For detailed design documentation, see Schema Versioning Design.
Understanding Trade-offs
CAP Theorem Positioning
HarmonyLite makes specific trade-offs according to the CAP theorem:
- Consistency: Eventual (not strong)
- Availability: High (prioritized)
- Partition Tolerance: Maintained
This positions HarmonyLite as an AP system (Availability and Partition Tolerance) rather than a CP system.
Suitable Use Cases
HarmonyLite is ideal for:
- Read-heavy workloads
- Systems that can tolerate eventual consistency
- Applications needing high write availability
- Edge computing and distributed systems
Less Suitable Use Cases
HarmonyLite may not be the best choice for:
- Strong consistency requirements
- Complex transactional workloads
- Financial systems requiring immediate consistency
- Systems with strict ordering requirements
Performance Characteristics
Scalability
- Read Scalability: Excellent (horizontal)
- Write Scalability: Good (limited by conflict resolution)
- Node Count: Practical up to dozens of nodes
Latency
- Local Operations: Minimal impact (~1-5ms overhead)
- Replication Delay: Typically 50-500ms depending on network
- Recovery Time: Proportional to changes since last snapshot
Resource Usage
- Memory: Moderate (configurable)
- CPU: Low to moderate
- Disk: Additional space for change logs and snapshots
- Network: Proportional to change volume and compression settings
Next Steps
- Replication Details - Deep dive into the replication process
- Snapshots - How snapshots and recovery work
- Configuration Reference - Complete configuration options