Events & Sync

Mistakes when handling events and the distributed nature of CRDTs.

Ignoring Local vs Remote

// WRONG: runs twice (local + sync confirmation)
content.observe(event => {
  saveToLocalStorage(content.toJSON())
  sendAnalytics('changed')
})

Observers fire for both local and remote changes.

Fix: Check transaction.local:

content.observe((event, transaction) => {
  updateUI()  // Always
  if (transaction.local) {
    saveToLocalStorage(content.toJSON())  // Only local
  }
})

Flooding Awareness Updates

// WRONG: 60+ messages/second
document.addEventListener('mousemove', e => {
  awareness.setLocalStateField('cursor', { x: e.clientX, y: e.clientY })
})

Floods network, overwhelms clients.

Fix: Throttle to 10-20 updates/second:

const updateCursor = throttle((x, y) => {
  awareness.setLocalStateField('cursor', { x, y })
}, 50)

Expecting Sync Order

// WRONG: assumes remote sees operations in order
yDoc.transact(() => items.set('config', { init: false }))
yDoc.transact(() => items.set('config', { init: true }))

CRDT sync order isn’t guaranteed across clients.

Fix: Design for eventual consistency. Use timestamps if order matters.

Checklist

  • Observers check transaction.local for side effects
  • Awareness updates throttled (10-20/sec max)
  • No assumptions about sync order

See Also