|
9 | 9 | package db
|
10 | 10 |
|
11 | 11 | import (
|
| 12 | + "context" |
12 | 13 | "encoding/base64"
|
| 14 | + "errors" |
13 | 15 | "fmt"
|
14 | 16 | "maps"
|
15 | 17 | "sort"
|
@@ -689,3 +691,50 @@ func (hlv *HybridLogicalVector) UnmarshalJSON(inputjson []byte) error {
|
689 | 691 | func (hlv HybridLogicalVector) GoString() string {
|
690 | 692 | return fmt.Sprintf("HybridLogicalVector{CurrentVersionCAS:%d, SourceID:%s, Version:%d, PreviousVersions:%#+v, MergeVersions:%#+v}", hlv.CurrentVersionCAS, hlv.SourceID, hlv.Version, hlv.PreviousVersions, hlv.MergeVersions)
|
691 | 693 | }
|
| 694 | + |
| 695 | +// ErrNoNewVersionsToAdd will be thrown when there are no new versions from incoming HLV to be added to HLV that is local |
| 696 | +var ErrNoNewVersionsToAdd = errors.New("no new versions to add to HLV") |
| 697 | + |
| 698 | +// IsInConflict is used to identify if two HLV's are in conflict or not. Will return boolean to indicate if in conflict |
| 699 | +// or not and will error for the following cases: |
| 700 | +// - Local HLV dominates incoming HLV (meaning local version is a newer version that the incoming one) |
| 701 | +// - Local CV matches incoming CV, so no new versions to add |
| 702 | +func IsInConflict(ctx context.Context, localHLV, incomingHLV *HybridLogicalVector) (bool, error) { |
| 703 | + incomingCV := incomingHLV.ExtractCurrentVersionFromHLV() |
| 704 | + localCV := localHLV.ExtractCurrentVersionFromHLV() |
| 705 | + |
| 706 | + // check if incoming CV and local CV are the same. This is needed here given that if both CV's are the same the |
| 707 | + // below check to check local revision is newer than incoming revision will pass given the check and will add the |
| 708 | + // incoming versions even if CV's are the same |
| 709 | + if localCV.Equal(*incomingCV) { |
| 710 | + base.DebugfCtx(ctx, base.KeyCRUD, "incoming CV %#+v is equal to local revision %#+v", incomingCV, localCV) |
| 711 | + return false, ErrNoNewVersionsToAdd |
| 712 | + } |
| 713 | + |
| 714 | + // standard no conflict case. In the simple case, this happens when: |
| 715 | + // - SG writes document 1@cbs1 |
| 716 | + // - CBL pulls document 1@cbs1 |
| 717 | + // - SG writes document 2@cbs1 |
| 718 | + if incomingHLV.DominatesSource(*localCV) { |
| 719 | + return false, nil |
| 720 | + } |
| 721 | + |
| 722 | + // local revision is newer than incoming revision. Common case: |
| 723 | + // - CBL writes document 1@cbl1 |
| 724 | + // - CBL pushes to SG as 1@cbl1 |
| 725 | + // - CBL pulls document 1@cbl1 |
| 726 | + // |
| 727 | + // NOTE: without P2P replication, this should not be the case and we would not get this revision, since CBL |
| 728 | + // would respond to a SG changes message that CBL does not need this revision |
| 729 | + if localHLV.DominatesSource(*incomingCV) { |
| 730 | + return false, ErrNoNewVersionsToAdd |
| 731 | + } |
| 732 | + // Check if conflict has been previously resolved. |
| 733 | + // - If merge versions are empty, then it has not be resolved. |
| 734 | + // - If merge versions do not match, then it has not been resolved. |
| 735 | + if len(incomingHLV.MergeVersions) != 0 && len(localHLV.MergeVersions) != 0 && maps.Equal(incomingHLV.MergeVersions, localHLV.MergeVersions) { |
| 736 | + base.DebugfCtx(ctx, base.KeyVV, "merge versions match between local HLV %#v and incoming HLV %#v, conflict previously resolved", localHLV, incomingCV) |
| 737 | + return false, nil |
| 738 | + } |
| 739 | + return true, nil |
| 740 | +} |
0 commit comments