feat(workspace): implement Yjs CRDT real-time collaborative editing (resolves FLAW-01)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 29s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 29s
This commit is contained in:
1 parent
0bdeb5f320
commit
ef8cf8f022
13 files changed
+1028
-85
No files matched your search
+35
-25
@@ -64,43 +64,53 @@ Alle WebSocket-Nachrichten werden als JSON mit einheitlicher Hülle übertragen:
|
||||
|
||||
---
|
||||
|
||||
## 🔒 4. Dokument-Sperrsystem (Document Locking)
|
||||
---
|
||||
|
||||
Um Schreibkonflikte bei Notizen im Workspace zu vermeiden, verwendet MindSift ein **serverseitiges Lease-Locking mit Heartbeat**:
|
||||
## ⚡ 4. CRDT-Echtzeitprotokoll (Yjs Collaborative Editing)
|
||||
|
||||
Seit Version 2.1 unterstützt MindSift **echte, konfliktfreie Mehrbenutzer-Kollaboration** für Notizen via Yjs CRDT. Clients tauschen binäre Delta-Updates und Live-Awareness-Informationen über dedizierte WebSocket-Nachrichten aus:
|
||||
|
||||
### CRDT-Nachrichtentypen
|
||||
| Event | Richtung | Zweck / Payload |
|
||||
|---|---|---|
|
||||
| `CRDT_JOIN` | Client ➔ Server | Tritt dem Dokumentenraum (`doc:{docId}`) bei. |
|
||||
| `CRDT_INIT` | Server ➔ Client | Sendet den aktuellen Basiszustand (`stateBlob`) und offene Delta-Updates (`updates[]`) an den Client. |
|
||||
| `CRDT_UPDATE` | Bidirektional | Überträgt inkrementelle binäre Yjs-Updates (Base64-kodiert). Server speichert in `document_crdt_updates` und verteilt an alle anderen Raumnutzer. |
|
||||
| `CRDT_COMPACT` | Client ➔ Server | Komprimiert mehrere Updates zu einem konsolidierten Snapshot in `document_crdt_state`. |
|
||||
| `CRDT_AWARENESS` | Bidirektional | Überträgt Live-Präsenz (Fokus auf `blockId`, Benutzername, Farbe). |
|
||||
| `CRDT_AWARENESS_LEAVE` | Bidirektional | Signalisiert, dass ein Nutzer den Fokus verloren hat oder das Dokument verlassen hat. |
|
||||
| `CRDT_LEAVE` | Client ➔ Server | Verlässt den Dokumentenraum explizit. |
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor UserA as Benutzer A
|
||||
participant WS as WebSocket Hub
|
||||
participant WS as WebSocket Hub & SQLite
|
||||
actor UserB as Benutzer B
|
||||
|
||||
UserA->>WS: LOCK_DOC (docId: "doc_123")
|
||||
WS-->>UserA: Lock bestätigt (30s TTL)
|
||||
WS->>UserB: DOC_LOCKED (docId: "doc_123", lockedBy: "Benutzer A")
|
||||
Note over UserB: Editor gesperrt (Schreibschutz aktiv)
|
||||
UserA->>WS: CRDT_JOIN (docId: "doc_123")
|
||||
WS-->>UserA: CRDT_INIT (stateBlob, updates)
|
||||
UserB->>WS: CRDT_JOIN (docId: "doc_123")
|
||||
WS-->>UserB: CRDT_INIT (stateBlob, updates)
|
||||
|
||||
loop Alle 15 Sekunden
|
||||
UserA->>WS: HEARTBEAT_DOC (docId: "doc_123")
|
||||
WS-->>UserA: TTL um 30s verlängert
|
||||
end
|
||||
UserA->>WS: CRDT_UPDATE (docId, updateBlob)
|
||||
WS->>WS: Append in document_crdt_updates
|
||||
WS->>UserB: CRDT_UPDATE (docId, updateBlob)
|
||||
Note over UserB: Yjs wendet Delta atomar an (0 Konflikte)
|
||||
|
||||
alt Reguläres Schließen
|
||||
UserA->>WS: UNLOCK_DOC (docId: "doc_123")
|
||||
WS->>UserB: DOC_UNLOCKED (docId: "doc_123")
|
||||
Note over UserB: Editor wieder freigegeben
|
||||
else Verbindungsabbruch / Tab geschlossen
|
||||
Note over WS: Heartbeat bleibt aus (TTL läuft ab)
|
||||
WS->>UserB: DOC_UNLOCKED (Automatisch nach Ablauf)
|
||||
end
|
||||
UserA->>WS: CRDT_AWARENESS (docId, focusedBlockId)
|
||||
WS->>UserB: CRDT_AWARENESS (docId, user, focusedBlockId)
|
||||
Note over UserB: Avatar-Badge erscheint am Block
|
||||
```
|
||||
|
||||
### Locking-Events
|
||||
- **`LOCK_DOC` (Client ➔ Server):** Fordert eine 30-Sekunden-Sperre für ein Dokument an.
|
||||
- **`HEARTBEAT_DOC` (Client ➔ Server):** Verlängert die Sperre alle 15 Sekunden, solange der Benutzer das Dokument geöffnet hat.
|
||||
- **`UNLOCK_DOC` (Client ➔ Server):** Gibt die Sperre beim Verlassen des Dokuments frei.
|
||||
- **`DOC_LOCKED` (Server ➔ Clients):** Informiert andere Benutzer, dass das Dokument schreibgeschützt ist.
|
||||
- **`DOC_UNLOCKED` (Server ➔ Clients):** Hebt den Schreibschutz auf.
|
||||
---
|
||||
|
||||
## 🔒 5. Abwärtskompatibles Document-Lease-Locking
|
||||
|
||||
Vor Version 2.1 nutzte MindSift ein serverseitiges Lease-Locking mit 30-Sekunden-Heartbeats. Durch die CRDT-Engine wurde die blockierende Bearbeitungssperre (`isLockedByOther`) abgelöst:
|
||||
- **Aktuelles Verhalten:** Mehrere Benutzer mit Bearbeitungsrechten können zeitgleich dasselbe Dokument bearbeiten.
|
||||
- **Presence & Locks:** Die Events `LOCK_DOC` und `HEARTBEAT_DOC` bleiben abwärtskompatibel erhalten und dienen nun der reinen Präsenzanzeige (z.B. gelber Statusindikator „wird gerade von X angesehen").
|
||||
- **Schreibschutz:** Ein echter Schreibschutz (`isReadOnly`) greift ausschließlich noch dann, wenn der Nutzer nur über Leserechte (`permission === 'read'`) für die Notiz oder den Ordner verfügt.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user