Skip to content

Timestamp-based conflict resolution for structural ops #10

Description

@FuJacob

Context

Structural operations (node create/move/delete, edge create/delete) currently use simple last-write-wins with no formal ordering. The server relays messages in arrival order without conflict detection. Sim uses client-generated timestamps with deterministic tiebreaking for structural conflict resolution.

Text content already has proper CRDT conflict resolution (RGA). This ticket is about the structural layer only.

What to build

Client-side: Timestamp every operation

  • Every outbound structural operation includes a timestamp field (high-resolution client clock, e.g., performance.now() + wall clock)
  • Format: ISO 8601 with millisecond precision, or monotonic counter

Server-side: Timestamp-based ordering

  • Position conflicts: When two users simultaneously move the same node, persist the one with the latest timestamp. Broadcast both in sequence — clients converge to the latest.
  • Create/delete conflicts: If a delete arrives for an entity that was modified after the delete's timestamp, the modification wins (the entity survives). If the delete is newer, it wins.

Deterministic tiebreaking

  • When timestamps are identical (within precision), use userId lexicographic ordering as tiebreaker
  • Ensures all clients converge to the same state given the same set of ops

Server-side coalescing for position

  • Position updates within a ~25ms window for the same node: only persist the latest timestamp
  • Reduces DB churn during collaborative dragging

Current code references

  • frontend/src/shared/events.ts — outbound event types (no timestamp field today)
  • services/collab/src/main/java/com/leetdoodle/collab/handler/CanvasWebSocketHandler.java — relay logic (no conflict detection)

Acceptance criteria

  • All structural ops carry client-generated timestamps
  • Server resolves position conflicts using latest timestamp
  • Deterministic tiebreaker (userId) for equal timestamps
  • All clients converge to identical state given same operations
  • Server coalesces rapid position updates before persisting

Metadata

Metadata

Assignees

No one assigned

    Labels

    Full-stackFrontend and backend work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions