This document compares emerge with alternative approaches for coordination and synchronization. Some are genuine alternatives with different trade-offs, while others might seem similar but fundamentally differ in their approach or capabilities. See also Algorithm for emerge's specific approach.
How it works: A central master distributes work to workers and collects results.
graph TD
M[Master] --> W1[Worker 1]
M --> W2[Worker 2]
M --> W3[Worker 3]
W1 --> M
W2 --> M
W3 --> M
Example Implementation:
type Master struct {
workQueue chan Task
results chan Result
workers []*Worker
}
func (m *Master) Distribute() {
for task := range m.tasks {
m.workQueue <- task // Master assigns work
}
}When to use instead of emerge:
- ✅ Need strict task assignment control
- ✅ Have reliable central infrastructure
- ✅ Tasks are independent (no coordination needed)
- ✅ Need simple debugging and monitoring
Why emerge is different:
- Emerge has no master - all agents are equal (see Decentralization)
- Emerge handles coordination, not just distribution
- Emerge survives master failure (no SPOF) - see Disruption
- Emerge scales without bottleneck (see Scales)
How it works: Nodes vote to agree on values through leader election and log replication.
sequenceDiagram
participant L as Leader
participant F1 as Follower 1
participant F2 as Follower 2
Note over L,F2: Phase 1: Leader Election
F1->>L: Vote Request
F2->>L: Vote Request
L->>F1: I'm Leader
L->>F2: I'm Leader
Note over L,F2: Phase 2: Log Replication
L->>F1: Append Entry
L->>F2: Append Entry
F1->>L: ACK
F2->>L: ACK
Example Implementation:
type RaftNode struct {
state State // Leader, Follower, Candidate
currentTerm int
votedFor string
log []LogEntry
}
func (r *RaftNode) RequestVote(term int, candidateId string) bool {
// Voting logic for discrete decisions
if term > r.currentTerm {
r.currentTerm = term
r.votedFor = candidateId
return true
}
return false
}When to use instead of emerge:
- ✅ Need distributed database consistency
- ✅ Require discrete decision making
- ✅ Need strong consistency guarantees
- ✅ Must maintain ordered log of events
Why emerge is different:
- Emerge does continuous synchronization, not discrete consensus
- Emerge needs no leader election (see Decentralization)
- Emerge handles dynamic values, not fixed decisions
- Emerge optimizes for coordination, not consistency (see Goals)
How it works: Producers send messages to queues, consumers process them.
graph LR
P1[Producer 1] --> Q[Queue/Topic]
P2[Producer 2] --> Q
Q --> C1[Consumer 1]
Q --> C2[Consumer 2]
Q --> C3[Consumer 3]
Example Implementation:
type MessageQueue struct {
broker *Broker
producer *Producer
consumer *Consumer
}
func (mq *MessageQueue) PublishBatch(messages []Message) {
// Broker handles batching
mq.broker.BatchAndSend(messages)
}When to use instead of emerge:
- ✅ Need persistent message delivery
- ✅ Want decoupled producers/consumers
- ✅ Require message replay capability
- ✅ Need guaranteed delivery semantics
Why emerge is different:
- Emerge coordinates timing, not message passing (see Protocol)
- Emerge agents interact directly, no broker needed
- Emerge focuses on when to act, not what to communicate
- Emerge provides synchronization, not messaging
How it works: Distributed lock managers coordinate access to shared resources.
graph TD
subgraph "Lock Manager"
LM[Lock Service]
L1[Lock: /api/batch]
end
C1[Client 1] -->|acquire| LM
C2[Client 2] -->|wait| LM
C3[Client 3] -->|wait| LM
LM -->|grant| C1
Example Implementation:
type DistributedLock struct {
redis *Redis
key string
ttl time.Duration
}
func (dl *DistributedLock) TryBatch() error {
if dl.Acquire() {
defer dl.Release()
performBatchOperation()
}
return nil
}When to use instead of emerge:
- ✅ Need exclusive access to resources
- ✅ Require strict mutual exclusion
- ✅ Have shared mutable state
- ✅ Need simple coordination primitive
Why emerge is different:
- Emerge needs no locks - agents coordinate naturally through the protocol
- Emerge allows parallel action when synchronized (see Concurrency)
- Emerge is lock-free and wait-free (using atomic operations)
- Emerge handles timing, not exclusion
Why it seems similar: Both can coordinate timing of operations.
gantt
title Cron vs Emerge Timing
dateFormat HH:mm
axisFormat %H:%M
section Cron
Task A :00:00, 1m
Task B :00:00, 1m
Task C :00:00, 1m
Note: All fixed at same time
section Emerge
Agent A converges :00:00, 5s
Agent B converges :00:01, 4s
Agent C converges :00:02, 3s
All synchronized :00:05, 1m
Example Comparison:
// Cron: Static, predetermined
cronJob := "0 * * * *" // Every hour, exactly
// Emerge: Dynamic, emergent
emerge.MinimizeAPICalls() // Synchronize when ready - see [Goals](../concepts/goals.md)Why it's not an alternative:
- ❌ Cron is static scheduling, emerge is goal-directed and dynamic
- ❌ Cron can't adapt to system conditions
- ❌ Cron can't handle distributed coordination
- ❌ Cron causes thundering herd, emerge prevents it (see Use Cases)
Why it seems similar: Both can distribute work across multiple nodes.
graph TD
subgraph "Load Balancer"
LB[Load Balancer]
LB -->|route| S1[Server 1]
LB -->|route| S2[Server 2]
LB -->|route| S3[Server 3]
end
subgraph "Emerge"
A1[Agent 1] <--> A2[Agent 2]
A2 <--> A3[Agent 3]
A1 <--> A3
end
Example Comparison:
// Load Balancer: External distribution
lb.Route(request) // Balancer decides
// Emerge: Self-organizing distribution
emerge.DistributeLoad() // Agents coordinate themselves - see [Goals](../concepts/goals.md)Why it's not an alternative:
- ❌ Load balancers route requests, emerge coordinates agents
- ❌ Load balancers are centralized, emerge is decentralized
- ❌ Load balancers don't synchronize, they distribute
- ❌ Load balancers need external configuration
Why it seems similar: Both can coordinate system behavior based on conditions.
stateDiagram-v2
[*] --> Closed
Closed --> Open: Failure threshold
Open --> HalfOpen: After timeout
HalfOpen --> Closed: Success
HalfOpen --> Open: Failure
note right of Open: Circuit Breaker stops calls
note right of Closed: Emerge synchronizes calls
Example Comparison:
// Circuit Breaker: Failure protection
if circuitBreaker.IsOpen() {
return ErrCircuitOpen // Stop all calls
}
// Emerge: Coordinated action
if emerge.IsConverged() {
batchAPICalls() // Optimize calls
}Why it's not an alternative:
- ❌ Circuit breakers prevent actions, emerge coordinates them
- ❌ Circuit breakers react to failure, emerge optimizes success
- ❌ Circuit breakers are binary, emerge is continuous
- ❌ Circuit breakers protect, emerge enhances
Why it seems similar: Both involve multiple components interacting.
graph LR
subgraph "Event Bus"
P1[Publisher] -->|event| EB[Event Bus]
EB -->|notify| S1[Subscriber 1]
EB -->|notify| S2[Subscriber 2]
end
subgraph "Emerge"
E1[Agent 1] <-->|sync| E2[Agent 2]
E2 <-->|sync| E3[Agent 3]
end
Example Comparison:
// Event Bus: Message broadcasting
eventBus.Publish("batch.ready", data)
// Subscribers react independently
// Emerge: Phase synchronization
emerge.Synchronize()
// Agents coordinate timingWhy it's not an alternative:
- ❌ Event bus broadcasts messages, emerge synchronizes timing
- ❌ Event bus is about what happened, emerge is about when to act
- ❌ Subscribers are independent, emerge agents coordinate
- ❌ Event bus needs infrastructure, emerge is self-contained
Best of both worlds: Use emerge for timing, queue for communication.
graph TD
subgraph "Coordination Layer"
E1[Emerge Agent 1]
E2[Emerge Agent 2]
E3[Emerge Agent 3]
E1 <--> E2
E2 <--> E3
end
subgraph "Messaging Layer"
Q[Message Queue]
E1 -->|synchronized batch| Q
E2 -->|synchronized batch| Q
E3 -->|synchronized batch| Q
end
Example:
// Emerge coordinates when
if emerge.IsConverged() {
// Queue handles what
messages := collectMessages()
queue.PublishBatch(messages)
}Coordinated consensus: Use emerge for timing, consensus for decisions.
sequenceDiagram
Note over A1,A3: Emerge: Synchronize first
A1->>A2: Phase sync
A2->>A3: Phase sync
A3->>A1: Phase sync
Note over A1,A3: Then: Consensus vote
A1->>A2: Propose value
A2->>A3: Propose value
A3->>A1: Vote
Example:
// First synchronize with emerge
emerge.ReachConsensus() // See [Goals](../concepts/goals.md)
// Then make decision with Raft
if emerge.IsConverged() { // See [Coherence](../concepts/coherence.md)
raft.ProposeValue(value)
}| Need | Best Choice | Why |
|---|---|---|
| Coordinate timing across distributed agents | Emerge | Designed for distributed synchronization |
| Distribute independent tasks | Master-Worker | Simple, effective for independent work |
| Agree on discrete values | Raft/Paxos | Proven consensus algorithms |
| Pass messages between services | Message Queue | Reliable, persistent messaging |
| Protect shared resources | Distributed Lock | Simple mutual exclusion |
| Schedule at fixed times | Cron | Simple, predictable |
| Route HTTP requests | Load Balancer | Purpose-built for request routing |
| Batch operations dynamically | Emerge | Adaptive, goal-directed batching |
| Prevent cascade failures | Circuit Breaker | Fail-fast protection |
| Broadcast events | Event Bus | Decoupled event propagation |
Misconception: Database locks can coordinate distributed operations.
Reality:
// Database lock: Exclusive access
tx.Lock("batch_lock")
// Only ONE process can batch
// Emerge: Coordinated parallel access
emerge.Synchronize()
// ALL agents batch togetherWhy emerge is better for coordination:
- Allows parallel execution when synchronized (see Concurrency)
- No lock contention or deadlocks
- Scales without database bottleneck (see Scales)
Misconception: Container orchestration provides application-level coordination.
graph TD
subgraph "K8s: Where to run"
K[Kubernetes]
K -->|schedules| P1[Pod 1]
K -->|schedules| P2[Pod 2]
end
subgraph "Emerge: When to act"
P1 -->|contains| E1[Emerge Agent]
P2 -->|contains| E2[Emerge Agent]
E1 <-->|synchronize| E2
end
Why they're complementary:
- K8s handles deployment and scaling
- Emerge handles runtime coordination (see Protocol)
- K8s is infrastructure, emerge is application logic
Misconception: Webhooks can coordinate distributed systems.
Reality:
// Webhooks: Notification after the fact
onEvent := func() {
callWebhook(url) // Tell others something happened
}
// Emerge: Coordination before action
beforeAction := func() {
emerge.Synchronize() // Coordinate when to act
}Why emerge is different:
- Webhooks notify, emerge coordinates (see Synchronization)
- Webhooks are reactive, emerge is proactive (see Goal-Directed)
- Webhooks need endpoints, emerge is peer-to-peer (see Decentralization)
| Approach | Latency | Throughput | Scalability | Fault Tolerance |
|---|---|---|---|---|
| Emerge | Low | High | Excellent | Excellent |
| Master-Worker | Medium | Medium | Limited | Poor (SPOF) |
| Raft/Paxos | High | Low | Good | Good |
| Message Queue | Medium | High | Good | Good |
| Distributed Lock | High | Low | Poor | Medium |
| Cron | N/A | N/A | Excellent | Good |
| Load Balancer | Low | High | Good | Medium |
Be honest about emerge's limitations:
- Need ACID transactions → Use database
- Need message persistence → Use message queue
- Need discrete consensus → Use Raft/Paxos
- Need simple task distribution → Use master-worker
- Need HTTP routing → Use load balancer
- Need fixed scheduling → Use cron
- Need exclusive access → Use locks
- Algorithm - How emerge works
- Decentralization - Why emerge has no center
- Use Cases - When to use emerge
- Architecture - System design
- FAQ - Common questions
- Glossary - Term definitions