| name | etcd-distributed-config |
| description | etcd distributed key-value store for centralized agent config and distributed locking. Watch API for live config reload, lease-based TTL keys, transactions, and distributed mutex patterns. Sources: etcd-io/etcd (Apache-2.0). |
/etcd-distributed-config
When to Use
- Centralized config for all agents: one write, all agents reload automatically
- Distributed mutex: only one agent executes a critical section at a time
- Service registry: agents register presence with TTL lease; expired = offline
- Consistent read: strong consistency required (vs eventual consistency of [[yjs-crdt-sync]])
Do NOT use for
- Local config (use [[cli-config-persistence]])
- High-frequency writes (etcd is optimized for reads; write throughput ~10k/s)
Connect and basic CRUD
import { Etcd3 } from 'etcd3'
const client = new Etcd3({
hosts: ['http://etcd-1:2379', 'http://etcd-2:2379', 'http://etcd-3:2379'],
auth: { username: 'yamtam', password: process.env.ETCD_PASSWORD! },
})
await client.put('/yamtam/config/model-tier').value('power')
const tier = await client.get('/yamtam/config/model-tier').string()
console.log('[etcd] tier:', tier)
await client.delete().key('/yamtam/config/deprecated').exec()
const entries = await client.getAll().prefix('/yamtam/config/').strings()
console.log('[etcd] config:', entries)
Watch API (live config reload)
const watcher = await client.watch()
.prefix('/yamtam/config/')
.create()
watcher
.on('put', (kv) => {
const key = kv.key.toString()
const value = kv.value.toString()
console.log(`[etcd] config changed: ${key} = ${value}`)
configCache.set(key, value)
})
.on('delete', (kv) => {
configCache.delete(kv.key.toString())
})
.on('error', (err) => {
console.error('[etcd] watch error:', err)
})
process.on('SIGTERM', () => watcher.cancel())
Lease-based TTL key (agent heartbeat / service registry)
const lease = client.lease(10)
await lease.put('/yamtam/agents/agent-1').value(JSON.stringify({
id: 'agent-1',
address: 'ws://agent-1:8080',
ts: Date.now(),
}))
lease.on('lost', async (err) => {
console.error('[etcd] lease lost:', err)
await lease.put('/yamtam/agents/agent-1').value(...)
})
Distributed mutex (only one agent at a time)
async function withLock(name: string, fn: () => Promise<void>) {
const lock = client.lock(name).ttl(15)
await lock.acquire()
try {
await fn()
} finally {
await lock.release()
}
}
await withLock('/yamtam/locks/schema-migration', async () => {
console.log('[etcd] running migration...')
await runMigration()
})
Anti-Fake-Pass Checklist
❌ TTL too short on lease → agent under load misses renewal, key deleted, false offline detection
❌ No watcher reconnect on error → stale config after etcd leader re-election
❌ Distributed lock without finally → lock never released if fn() throws
❌ Single etcd host → no HA; cluster needs ≥ 3 nodes for majority quorum
❌ Plain HTTP (not HTTPS) for etcd → credentials and config in cleartext
❌ getAll() without prefix on large cluster → returns all keys, unbounded memory