fix: Pull-Wasserzeichen sprang über fremde Ereignisse + schärfere Push-Kollisionskontrolle
EventStore.Pull gab bisher den globalen ServerSeq-Höchststand als neuen Cursor zurück statt den höchsten unter den tatsächlich gelieferten Ereignissen - hatte ein Gerät selbst kurz zuvor etwas gepusht, sprang sein Pull-Cursor über noch nicht abgeholte Ereignisse anderer Geräte hinweg und verpasste sie dauerhaft, ohne jeden Fehler. Ersetzt außerdem die bisherige 30-Sekunden-Heuristik zur Konflikterkennung beim Push durch exakte BasedOnServerSeq-Prüfung: jedes SyncEvent trägt die ServerSeq, auf der es aufbaut: der Server lehnt ab, wenn der aktuelle Stand nicht mehr passt. Bei Ablehnung lädt der Client sofort den neuen Server-Stand nach, löst den Konflikt nach der bestehenden Desktop-vs-Companion/ Timestamp-Politik auf und macht ihn immer in der Konflikt-Review-UI sichtbar, statt die verworfene Änderung stillschweigend zu verlieren. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ public class EventQueue : IDisposable
|
||||
private readonly ILiteCollection<SyncMeta> _meta;
|
||||
private readonly ILiteCollection<ConflictEntry> _conflicts;
|
||||
private readonly ILiteCollection<PendingAttachmentUpload> _attachmentUploads;
|
||||
private readonly ILiteCollection<EntityVersion> _entityVersions;
|
||||
private long _currentSeq;
|
||||
|
||||
public EventQueue(string path)
|
||||
@@ -23,6 +24,7 @@ public class EventQueue : IDisposable
|
||||
_meta = _db.GetCollection<SyncMeta>("meta");
|
||||
_conflicts = _db.GetCollection<ConflictEntry>("conflicts");
|
||||
_attachmentUploads = _db.GetCollection<PendingAttachmentUpload>("attachment_uploads");
|
||||
_entityVersions = _db.GetCollection<EntityVersion>("entity_versions");
|
||||
_attachmentUploads.EnsureIndex(x => x.StorageId, unique: true);
|
||||
_queue.EnsureIndex(x => x.SequenceNr);
|
||||
_currentSeq = _meta.FindById("seq")?.Value ?? 0;
|
||||
@@ -67,6 +69,20 @@ public class EventQueue : IDisposable
|
||||
_conflicts.Update(conflict);
|
||||
}
|
||||
|
||||
// ── Lokale Versionsverfolgung je Entität (optimistische Nebenläufigkeitskontrolle) ──────
|
||||
// Merkt sich pro Entität die zuletzt bekannte ServerSeq — Grundlage für SyncEvent.
|
||||
// BasedOnServerSeq beim Push (siehe SyncEngine.PushAsync) und dafür, wie ein abgelehnter
|
||||
// Push nach dem Nachladen des aktuellen Server-Stands aufgelöst wird.
|
||||
|
||||
public long? GetKnownServerSeq(string entityType, string entityId) =>
|
||||
_entityVersions.FindById(EntityVersionKey(entityType, entityId))?.ServerSeq;
|
||||
|
||||
public void SetKnownServerSeq(string entityType, string entityId, long serverSeq) =>
|
||||
_entityVersions.Upsert(new EntityVersion
|
||||
{ Key = EntityVersionKey(entityType, entityId), ServerSeq = serverSeq });
|
||||
|
||||
private static string EntityVersionKey(string entityType, string entityId) => $"{entityType}:{entityId}";
|
||||
|
||||
// ── Anhang-Warteliste (getrennt von der JSON-Ereignis-Outbox, siehe AttachmentSyncer) ────
|
||||
public void QueueAttachmentUpload(string storageId)
|
||||
{
|
||||
@@ -103,3 +119,10 @@ internal class PendingAttachmentUpload
|
||||
public ObjectId Id { get; set; } = ObjectId.NewObjectId();
|
||||
public string StorageId { get; set; } = "";
|
||||
}
|
||||
|
||||
internal class EntityVersion
|
||||
{
|
||||
[BsonId]
|
||||
public string Key { get; set; } = "";
|
||||
public long ServerSeq { get; set; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user