Skip to content

Commit

Permalink
Handle local changes correctly when receiving snapshot (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
humdrum authored Jul 12, 2024
1 parent 023f1d7 commit 62bd08f
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Sources/Document/Change/ChangePack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ struct ChangePack {
return self.snapshot
}

/**
* `hasSnapshot` returns the whether this pack has snapshot or not.
*/
func hasSnapshot() -> Bool {
self.snapshot != nil
}

/**
* `getMinSyncedTicket` returns the minimum synced ticket of this pack.
*/
Expand Down
8 changes: 8 additions & 0 deletions Sources/Document/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ public actor Document {
// 02. Update the checkpoint.
self.checkpoint.forward(other: pack.getCheckpoint())

// NOTE(hackerwins): If the document has local changes, we need to apply
// them after applying the snapshot. We need to treat the local changes
// as remote changes because the application should apply the local
// changes to their own document.
if pack.hasSnapshot() {
try self.applyChanges(self.localChanges)
}

// 03. Do Garbage collection.
if let ticket = pack.getMinSyncedTicket() {
self.garbageCollect(ticket)
Expand Down
35 changes: 35 additions & 0 deletions Tests/Integration/ClientIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -778,4 +778,39 @@ final class ClientIntegrationTests: XCTestCase {
try await c2.deactivate()
}
}

func test_should_handle_local_changes_correctly_when_receiving_snapshot() async throws {
try await withTwoClientsAndDocuments(self.description, detachDocuments: false) { c1, d1, c2, d2 in
try await d1.update { root, _ in
root.counter = JSONCounter(value: Int64(0))
}

try await c1.sync()
try await c2.sync()

// 01. c1 increases the counter for creating snapshot.
for _ in 0 ..< 500 {
try await d1.update { root, _ in
(root.counter as? JSONCounter<Int64>)?.increase(value: 1)
}
}

try await c1.sync()

// 02. c2 receives the snapshot and increases the counter simultaneously.
Task {
try await c2.sync()
}
try await d2.update { root, _ in
(root.counter as? JSONCounter<Int64>)?.increase(value: 1)
}

try await c2.sync()
try await c1.sync()

let d1JSON = await d1.toSortedJSON()
let d2JSON = await d2.toSortedJSON()
XCTAssertEqual(d1JSON, d2JSON)
}
}
}

0 comments on commit 62bd08f

Please sign in to comment.