docs: update Design Principles section with enhanced lock strategy and event handling guidelines

This commit is contained in:
bsiggel
2026-03-08 20:28:57 +00:00
parent d69801ed97
commit d71b5665b6

View File

@@ -69,35 +69,55 @@ Webhook (HTTP) → Queue Event → Event Handler → External APIs
1. **Fire events liberally** - 10 redundant events are cheaper than complex coordination
2. **Make handlers idempotent** - Early returns when nothing to do
3. **Sequential per entity, parallel across entities** - Lock prevents collisions, not updates
4. **Accept event storms** - Handlers queue up, process one by one
4. **Accept event storms** - Handlers queue up, Motia retries automatically
**Lock Strategy: Sequential Processing per Entity**
**Lock Strategy: Step + Input Values**
```
Event Storm für Document A:
├─ Event 1: update → Handler starts → Lock acquired
├─ Event 2: update → Queued (waits for lock)
├─ Event 3: update → Queued (after Event 2)
└─ Event 4: update → Queued (after Event 3)
Lock Key Format: {step_name}:{entity_type}:{entity_id}
Document B (parallel):
└─ Event 1: update → Own lock, processes in parallel!
Examples:
- document_sync:CDokumente:doc123
- beteiligte_sync:CBeteiligte:bet456
- collection_create:Account:acc789
Result:
- Same entity: Sequential (prevents file upload collisions)
- Different entities: Parallel (independent locks)
- Lost events: Zero (all queued and processed)
- Duplicate work: Prevented by idempotency checks
Rules:
✅ Parallel: sync document:A + sync document:B (different IDs)
❌ Blocked: sync document:A + sync document:A (same ID)
Lock Acquisition:
├─ Try acquire lock
├─ Success → Process → Release in finally block
└─ Fail (already locked) → Return early → Motia retries later
```
**Example Flow:**
**Handler Pattern:**
```python
async def handler(event_data, ctx):
entity_id = event_data['entity_id']
# 1. Try acquire lock
lock_key = f"document_sync:{entity_type}:{entity_id}"
lock_acquired = await acquire_lock(lock_key)
if not lock_acquired:
ctx.logger.info(f"⏸️ Lock busy, skipping (Motia will retry)")
return # Early return - no error, just skip
try:
# 2. Do work (only if lock acquired)
await sync_document(entity_id)
finally:
# 3. ALWAYS release lock (robust cleanup)
await release_lock(lock_key)
```
t=0: User ändert Document A (fileStatus → "changed")
t=1: Event 1 fired → Lock acquired → Sync starts
t=2: User ändert Document A again (fileStatus → "changed")
t=3: Event 2 fired → Queued (lock busy)
t=4: Event 1 completes → fileStatus="unchanged", xaiSyncStatus="clean"
t=5: Event 2 starts → Lock acquired
**Retry Behavior:**
- Lock fail → Handler returns without error
- Motia Queue sees: No error, no completion marker
- Motia automatically retries after configured delay
- Eventually lock available → Handler processes
Check: fileStatus="unchanged", xaiSyncStatus="clean"
→ Early return (nothing to do) ✅