2019-03-13 16:39:10 +01:00
|
|
|
package diff
|
2018-05-02 21:26:11 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
2018-08-16 21:05:21 +02:00
|
|
|
|
2019-02-22 11:40:27 +01:00
|
|
|
. "github.com/zrepl/zrepl/replication/logic/pdu"
|
2018-05-02 21:26:11 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type ConflictNoCommonAncestor struct {
|
2018-06-20 20:20:37 +02:00
|
|
|
SortedSenderVersions, SortedReceiverVersions []*FilesystemVersion
|
2018-05-02 21:26:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConflictNoCommonAncestor) Error() string {
|
|
|
|
return "no common snapshot or suitable bookmark between sender and receiver"
|
|
|
|
}
|
|
|
|
|
|
|
|
type ConflictDiverged struct {
|
2018-06-20 20:20:37 +02:00
|
|
|
SortedSenderVersions, SortedReceiverVersions []*FilesystemVersion
|
|
|
|
CommonAncestor *FilesystemVersion
|
|
|
|
SenderOnly, ReceiverOnly []*FilesystemVersion
|
2018-05-02 21:26:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConflictDiverged) Error() string {
|
|
|
|
return "the receiver's latest snapshot is not present on sender"
|
|
|
|
}
|
|
|
|
|
2018-06-20 20:20:37 +02:00
|
|
|
func SortVersionListByCreateTXGThenBookmarkLTSnapshot(fsvslice []*FilesystemVersion) []*FilesystemVersion {
|
|
|
|
lesser := func(s []*FilesystemVersion) func(i, j int) bool {
|
2018-05-02 21:26:11 +02:00
|
|
|
return func(i, j int) bool {
|
|
|
|
if s[i].CreateTXG < s[j].CreateTXG {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if s[i].CreateTXG == s[j].CreateTXG {
|
|
|
|
// Bookmark < Snapshot
|
2018-06-20 20:20:37 +02:00
|
|
|
return s[i].Type == FilesystemVersion_Bookmark && s[j].Type == FilesystemVersion_Snapshot
|
2018-05-02 21:26:11 +02:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if sort.SliceIsSorted(fsvslice, lesser(fsvslice)) {
|
|
|
|
return fsvslice
|
|
|
|
}
|
2018-06-20 20:20:37 +02:00
|
|
|
sorted := make([]*FilesystemVersion, len(fsvslice))
|
2018-05-02 21:26:11 +02:00
|
|
|
copy(sorted, fsvslice)
|
|
|
|
sort.Slice(sorted, lesser(sorted))
|
|
|
|
return sorted
|
|
|
|
}
|
|
|
|
|
|
|
|
// conflict may be a *ConflictDiverged or a *ConflictNoCommonAncestor
|
2018-06-20 20:20:37 +02:00
|
|
|
func IncrementalPath(receiver, sender []*FilesystemVersion) (incPath []*FilesystemVersion, conflict error) {
|
2018-05-02 21:26:11 +02:00
|
|
|
|
|
|
|
receiver = SortVersionListByCreateTXGThenBookmarkLTSnapshot(receiver)
|
|
|
|
sender = SortVersionListByCreateTXGThenBookmarkLTSnapshot(sender)
|
|
|
|
|
|
|
|
// Find most recent common ancestor by name, preferring snapshots over bookmarks
|
|
|
|
|
|
|
|
mrcaRcv := len(receiver) - 1
|
|
|
|
mrcaSnd := len(sender) - 1
|
|
|
|
|
|
|
|
for mrcaRcv >= 0 && mrcaSnd >= 0 {
|
|
|
|
if receiver[mrcaRcv].Guid == sender[mrcaSnd].Guid {
|
2018-09-06 03:24:15 +02:00
|
|
|
// Since we arrive from the end of the array, and because we defined bookmark < snapshot,
|
|
|
|
// this condition will match snapshot first, which is what we want because it gives us
|
|
|
|
// size estimation
|
2018-05-02 21:26:11 +02:00
|
|
|
break
|
|
|
|
}
|
2018-09-04 22:32:19 +02:00
|
|
|
receiverCreation, err := receiver[mrcaRcv].CreationAsTime()
|
|
|
|
if err != nil {
|
|
|
|
panic(err) // FIXME move this to a sorting phase before
|
|
|
|
}
|
|
|
|
senderCreation, err := sender[mrcaSnd].CreationAsTime()
|
|
|
|
if err != nil {
|
|
|
|
panic(err) // FIXME move this to the sorting phase before
|
|
|
|
}
|
|
|
|
|
|
|
|
if receiverCreation.Before(senderCreation) {
|
2018-05-02 21:26:11 +02:00
|
|
|
mrcaSnd--
|
|
|
|
} else {
|
|
|
|
mrcaRcv--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mrcaRcv == -1 || mrcaSnd == -1 {
|
|
|
|
return nil, &ConflictNoCommonAncestor{
|
|
|
|
SortedSenderVersions: sender,
|
|
|
|
SortedReceiverVersions: receiver,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mrcaRcv != len(receiver)-1 {
|
|
|
|
return nil, &ConflictDiverged{
|
|
|
|
SortedSenderVersions: sender,
|
|
|
|
SortedReceiverVersions: receiver,
|
|
|
|
CommonAncestor: sender[mrcaSnd],
|
|
|
|
SenderOnly: sender[mrcaSnd+1:],
|
|
|
|
ReceiverOnly: receiver[mrcaRcv+1:],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// incPath must not contain bookmarks except initial one,
|
2018-06-20 20:20:37 +02:00
|
|
|
incPath = make([]*FilesystemVersion, 0, len(sender))
|
2018-05-02 21:26:11 +02:00
|
|
|
incPath = append(incPath, sender[mrcaSnd])
|
|
|
|
// it's ok if incPath[0] is a bookmark, but not the subsequent ones in the incPath
|
|
|
|
for i := mrcaSnd + 1; i < len(sender); i++ {
|
2018-06-20 20:20:37 +02:00
|
|
|
if sender[i].Type == FilesystemVersion_Snapshot && incPath[len(incPath)-1].Guid != sender[i].Guid {
|
2018-05-02 21:26:11 +02:00
|
|
|
incPath = append(incPath, sender[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(incPath) == 1 {
|
|
|
|
// nothing to do
|
|
|
|
incPath = incPath[1:]
|
|
|
|
}
|
|
|
|
return incPath, nil
|
|
|
|
}
|