Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CBG-4442 proposeChanges fix for VV proposed version and legacy previousRev #7253

Merged
merged 1 commit into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion db/blip_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,6 @@ func (bh *blipHandler) handleProposeChanges(rq *blip.Message) error {

changeIsVector := false
if versionVectorProtocol {
// only check if rev is vector in VV replication mode
changeIsVector = strings.Contains(rev, "@")
}
if versionVectorProtocol && changeIsVector {
Expand Down
18 changes: 13 additions & 5 deletions db/crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -3211,20 +3211,25 @@ func (db *DatabaseCollectionWithUser) CheckProposedRev(ctx context.Context, doci
}

// CheckProposedVersion - given DocID and a version in string form, check whether it can be added without conflict.
func (db *DatabaseCollectionWithUser) CheckProposedVersion(ctx context.Context, docid, proposedVersionStr string, previousVersionStr string) (status ProposedRevStatus, currentVersion string) {
func (db *DatabaseCollectionWithUser) CheckProposedVersion(ctx context.Context, docid, proposedVersionStr string, previousRev string) (status ProposedRevStatus, currentVersion string) {

proposedVersion, err := ParseVersion(proposedVersionStr)
if err != nil {
base.WarnfCtx(ctx, "Couldn't parse proposed version for doc %q / %q: %v", base.UD(docid), proposedVersionStr, err)
return ProposedRev_Error, ""
}

// previousRev may be revTreeID or version
var previousVersion Version
if previousVersionStr != "" {
previousRevFormat := "version"
if !strings.Contains(previousRev, "@") {
previousRevFormat = "revTreeID"
}
if previousRev != "" && previousRevFormat == "version" {
var err error
previousVersion, err = ParseVersion(previousVersionStr)
previousVersion, err = ParseVersion(previousRev)
if err != nil {
base.WarnfCtx(ctx, "Couldn't parse previous version for doc %q / %q: %v", base.UD(docid), previousVersionStr, err)
base.WarnfCtx(ctx, "Couldn't parse previous version for doc %q / %q: %v", base.UD(docid), previousRev, err)
return ProposedRev_Error, ""
}
}
Expand All @@ -3241,7 +3246,10 @@ func (db *DatabaseCollectionWithUser) CheckProposedVersion(ctx context.Context,
}
// New document not found on server
return ProposedRev_OK_IsNew, ""
} else if localDocCV == previousVersion {
} else if previousRevFormat == "revTreeID" && doc.CurrentRev == previousRev {
// Non-conflicting update, client's previous legacy revTreeID is server's currentRev
return ProposedRev_OK, ""
} else if previousRevFormat == "version" && localDocCV == previousVersion {
// Non-conflicting update, client's previous version is server's CV
return ProposedRev_OK, ""
} else if doc.HLV.DominatesSource(proposedVersion) {
Expand Down
32 changes: 31 additions & 1 deletion rest/blip_legacy_revid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,16 @@ func TestProposeChangesHandlingWithExistingRevs(t *testing.T) {
assert.NoError(t, err, "Error creating BlipTester")
defer bt.Close()
rt := bt.restTester
collection, _ := rt.GetSingleTestDatabaseCollection()

resp := rt.PutDoc("conflictingInsert", `{"version":1}`)
conflictingInsertRev := resp.RevTreeID

resp = rt.PutDoc("conflictingUpdate", `{"version":1}`)
conflictingUpdateRev1 := resp.RevTreeID
conflictingUpdateRev2 := rt.UpdateDocRev("conflictingUpdate", resp.RevTreeID, `{"version":2}`)
source, value := collection.GetDocumentCurrentVersion(t, "conflictingUpdate")
conflictingUpdateVersion2 := db.Version{SourceID: source, Value: value}

resp = rt.PutDoc("newUpdate", `{"version":1}`)
newUpdateRev1 := resp.RevTreeID
Expand All @@ -90,6 +93,7 @@ func TestProposeChangesHandlingWithExistingRevs(t *testing.T) {
existingDocRev := resp.RevTreeID

type proposeChangesCase struct {
description string
key string
revID string
parentRevID string
Expand All @@ -98,35 +102,61 @@ func TestProposeChangesHandlingWithExistingRevs(t *testing.T) {

proposeChangesCases := []proposeChangesCase{
proposeChangesCase{
description: "conflicting insert, legacy rev",
key: "conflictingInsert",
revID: "1-abc",
parentRevID: "",
expectedValue: map[string]interface{}{"status": float64(db.ProposedRev_Conflict), "rev": conflictingInsertRev},
},
proposeChangesCase{
description: "successful insert, legacy rev",
key: "newInsert",
revID: "1-abc",
parentRevID: "",
expectedValue: float64(db.ProposedRev_OK),
},
proposeChangesCase{
description: "conflicting update, legacy rev",
key: "conflictingUpdate",
revID: "2-abc",
parentRevID: conflictingUpdateRev1,
expectedValue: map[string]interface{}{"status": float64(db.ProposedRev_Conflict), "rev": conflictingUpdateRev2},
},
proposeChangesCase{
description: "successful update, legacy rev",
key: "newUpdate",
revID: "2-abc",
parentRevID: newUpdateRev1,
expectedValue: float64(db.ProposedRev_OK),
},
proposeChangesCase{
description: "insert, existing doc, legacy rev",
key: "existingDoc",
revID: existingDocRev,
parentRevID: "",
expectedValue: float64(db.ProposedRev_Exists),
},
proposeChangesCase{
description: "successful update, new version, legacy parent",
key: "newUpdate",
revID: "1000@CBL1",
parentRevID: newUpdateRev1,
expectedValue: float64(db.ProposedRev_OK),
},
proposeChangesCase{
description: "conflicting update, new version, legacy parent",
key: "conflictingUpdate",
revID: "1000@CBL1",
parentRevID: conflictingUpdateRev1,
expectedValue: map[string]interface{}{"status": float64(db.ProposedRev_Conflict), "rev": conflictingUpdateVersion2.String()},
},
proposeChangesCase{
description: "already known, existing version, legacy parent is ancestor",
key: "conflictingUpdate",
revID: conflictingUpdateVersion2.String(),
parentRevID: conflictingUpdateRev1,
expectedValue: float64(db.ProposedRev_Exists),
},
}

proposeChangesRequest := bt.newRequest()
Expand Down Expand Up @@ -161,7 +191,7 @@ func TestProposeChangesHandlingWithExistingRevs(t *testing.T) {
require.NoError(t, decodeErr)

for i, entry := range changeList {
assert.Equal(t, proposeChangesCases[i].expectedValue, entry)
assert.Equal(t, proposeChangesCases[i].expectedValue, entry, "mismatch in expected value for case %q", proposeChangesCases[i].description)
}
}

Expand Down
Loading