How the Zakirullin Files Sync Algorithm Handles Conflicts: Timestamp Detection and LCS-Based Merging
The Zakirullin Files sync algorithm detects conflicts by comparing server and client modification timestamps, resolving them through an LCS-based three-way merge when both sides have divergent edits, or returning the server version unmodified when only the server has changed.
The zakirullin/files.md repository implements a distributed file synchronization system designed to handle concurrent modifications across multiple clients. The conflict resolution logic resides in the server/sync package, which orchestrates detection through temporal comparisons and resolution through intelligent text merging algorithms.
Conflict Detection via Timestamp Comparison
The algorithm's first phase determines whether the server's version of a file is newer than the version the client currently holds. This detection mechanism operates identically across both bulk and single-file synchronization endpoints.
The Modification Time Check
In server/sync/sync.go, the server retrieves the current modification time for the requested file and compares it against the timestamp provided by the client. A conflict is flagged when the server's timestamp is strictly greater:
fileWasModifiedOnServer := serverModifiedTime > clientFile.LastModified
This check appears in SyncFilenames at lines 34-38 for bulk operations, where the server iterates through multiple files and assesses each one independently. The same logic surfaces in SyncFile at lines 14-16 for single-file synchronization, using the variable serverLastModified instead:
fileWasModifiedOnServer = serverLastModified > clientFile.LastModified
Detecting Client State with Sync Markers
Beyond timestamp comparison, the algorithm determines whether the client has actually modified its local copy since the last successful synchronization. The server examines clientFile.ClientLastSynced and clientFile.ClientLastModified to identify if the client version remains untouched:
wasNotModifiedOnClient := clientFile.ClientLastSynced != 0 && clientFile.ClientLastModified == clientFile.ClientLastSynced
This distinction proves critical for deciding whether to overwrite client data or preserve local changes.
Conflict Resolution Strategies
Once the server identifies that its version is newer, it employs different resolution strategies depending on the client's modification state.
Server-Only Changes
When fileWasModifiedOnServer evaluates to true but wasNotModifiedOnClient also holds true, the client has not edited the file locally. In this scenario, the server simply transmits its current version back to the client without writing to the filesystem, as implemented in SyncFile at lines 15-19:
if fileWasModifiedOnServer && wasNotModifiedOnClient {
content = serverContent
shouldUpdateOnServer = false
}
The response status remains "ok" or transitions to "updatedOnServer", indicating that the client should replace its local copy with the server's version.
Bidirectional Modifications and Text Merging
When both the server and client have modified the file (fileWasModifiedOnServer && !wasNotModifiedOnClient), the algorithm invokes the Merge function to reconcile the divergent content. This occurs in SyncFile at lines 20-25:
} else if fileWasModifiedOnServer {
content = Merge(serverContent, clientFile.Content)
status = StatusMerged
}
The merged result is written back to the server's filesystem and returned to the client with a "merged" status code.
Implementation of the LCS-Based Merge Algorithm
The core merging logic resides in server/sync/merge.go, where the Merge function implements a longest common subsequence (LCS) algorithm to perform three-way text reconciliation.
Algorithm Structure
The function first splits both input strings into lines, then constructs a dynamic programming table to calculate LCS lengths:
func Merge(s1, s2 string) string {
lines1 := strings.Split(s1, "\n")
lines2 := strings.Split(s2, "\n")
lcsLength := make([][]int, len(lines1)+1)
for i := range lcsLength {
lcsLength[i] = make([]int, len(lines2)+1)
}
for i := 1; i <= len(lines1); i++ {
for j := 1; j <= len(lines2); j++ {
if lines1[i-1] == lines2[j-1] {
lcsLength[i][j] = lcsLength[i-1][j-1] + 1
} else {
lcsLength[i][j] = max(lcsLength[i-1][j], lcsLength[i][j-1])
}
}
}
result := backtrack(lines1, lines2, lcsLength, len(lines1), len(lines2))
result = mergeEmojisInJournalHeaders(result)
return strings.Join(result, "\n")
}
(lines 24-55 in server/sync/merge.go)
Post-Processing Journal Headers
After backtracking through the LCS table to assemble the merged line list, the algorithm performs a specialized post-processing step via mergeEmojisInJournalHeaders. This function collapses repeated journal headers containing emojis to maintain document consistency in markdown files.
Sync Response Protocol and Status Codes
The synchronization endpoints return structured responses that inform the client how to update its local state. According to the implementation in server/sync/sync.go at lines 66-71, the response payload contains a status field with three possible values:
"ok"– No conflict detected; the server accepted the client's version"updatedOnServer"– The server had a newer version and the client had not modified the file locally; the client should replace its copy"merged"– Both sides modified the file; the client should update its local copy with the merged content returned in the response
The client uses this status flag to determine whether to overwrite its local file, preserving the synchronization state for subsequent operations.
Summary
- The Zakirullin Files sync algorithm detects conflicts by comparing
serverModifiedTimeagainstclientFile.LastModifiedinserver/sync/sync.go - Server-only changes result in the server returning its version without filesystem writes, identified when
ClientLastModified == ClientLastSynced - Bidirectional modifications trigger the
Mergefunction inserver/sync/merge.go, which implements an LCS-based algorithm to reconcile divergent text - The merge process includes post-processing for emoji-aware journal header collapsing via
mergeEmojisInJournalHeaders - Response status codes (
"ok","updatedOnServer","merged") direct the client on how to update its local state
Frequently Asked Questions
How does the Zakirullin Files sync algorithm determine if a conflict exists?
The algorithm detects conflicts by comparing timestamps in the server/sync package. Specifically, it evaluates whether serverModifiedTime > clientFile.LastModified in both the SyncFilenames and SyncFile functions. If the server's modification time is newer than the timestamp the client last saw, the server flags the file as having been modified on the server, triggering conflict resolution logic.
What happens when both the client and server modify the same file?
When both sides have divergent changes, the server invokes the Merge function from server/sync/merge.go. This function implements a longest common subsequence (LCS) algorithm that builds a dynamic programming table to identify common lines between both versions, then backtracks to assemble a merged text. The result is written to the server and returned to the client with a "merged" status code.
How does the algorithm handle cases where only the server modified the file?
If the server detects a newer modification time but the client has not made local changes (determined by checking clientFile.ClientLastModified == clientFile.ClientLastSynced), the algorithm treats this as a server-only change. The server returns its current content to the client with an "updatedOnServer" status without writing to the filesystem, instructing the client to replace its local copy.
What is the purpose of the mergeEmojisInJournalHeaders function in the merging process?
The mergeEmojisInJournalHeaders function performs post-processing on the merged text after the LCS algorithm assembles the combined line list. As implemented in server/sync/merge.go, this function specifically handles markdown journal headers containing emojis, collapsing repeated headers to maintain document consistency and prevent duplicate dated entries in synchronized markdown files.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →