Skip to content

Commit

Permalink
CBG-4263 create single actor tests
Browse files Browse the repository at this point in the history
  • Loading branch information
torcolvin committed Nov 8, 2024
1 parent 656b84d commit aa1957d
Show file tree
Hide file tree
Showing 16 changed files with 402 additions and 164 deletions.
16 changes: 8 additions & 8 deletions db/crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -1797,7 +1797,7 @@ func (db *DatabaseCollectionWithUser) storeOldBodyInRevTreeAndUpdateCurrent(ctx
// Store the new revision body into the doc:
doc.setRevisionBody(ctx, newRevID, newDoc, db.AllowExternalRevBodyStorage(), newDocHasAttachments)
doc.SyncData.Attachments = newDoc.DocAttachments
doc.metadataOnlyUpdate = newDoc.metadataOnlyUpdate
doc.MetadataOnlyUpdate = newDoc.MetadataOnlyUpdate

if doc.CurrentRev == newRevID {
doc.NewestRev = ""
Expand All @@ -1808,7 +1808,7 @@ func (db *DatabaseCollectionWithUser) storeOldBodyInRevTreeAndUpdateCurrent(ctx
if doc.CurrentRev != prevCurrentRev {
doc.promoteNonWinningRevisionBody(ctx, doc.CurrentRev, db.RevisionBodyLoader)
// If the update resulted in promoting a previous non-winning revision body to winning, this isn't a metadata only update.
doc.metadataOnlyUpdate = nil
doc.MetadataOnlyUpdate = nil
}
}
}
Expand Down Expand Up @@ -2088,8 +2088,8 @@ func (col *DatabaseCollectionWithUser) documentUpdateFunc(
return
}

// compute mouMatch before the callback modifies doc.metadataOnlyUpdate
mouMatch := doc.metadataOnlyUpdate != nil && base.HexCasToUint64(doc.metadataOnlyUpdate.CAS) == doc.Cas
// compute mouMatch before the callback modifies doc.MetadataOnlyUpdate
mouMatch := doc.MetadataOnlyUpdate != nil && base.HexCasToUint64(doc.MetadataOnlyUpdate.CAS) == doc.Cas
// Invoke the callback to update the document and with a new revision body to be used by the Sync Function:
newDoc, newAttachments, createNewRevIDSkipped, updatedExpiry, err := callback(doc)
if err != nil {
Expand Down Expand Up @@ -2322,8 +2322,8 @@ func (db *DatabaseCollectionWithUser) updateAndReturnDoc(ctx context.Context, do
updatedDoc.Spec = appendRevocationMacroExpansions(updatedDoc.Spec, revokedChannelsRequiringExpansion)

updatedDoc.IsTombstone = currentRevFromHistory.Deleted
if doc.metadataOnlyUpdate != nil {
if doc.metadataOnlyUpdate.CAS != "" {
if doc.MetadataOnlyUpdate != nil {
if doc.MetadataOnlyUpdate.CAS != "" {
updatedDoc.Spec = append(updatedDoc.Spec, sgbucket.NewMacroExpansionSpec(XattrMouCasPath(), sgbucket.MacroCas))
}
} else {
Expand Down Expand Up @@ -2386,8 +2386,8 @@ func (db *DatabaseCollectionWithUser) updateAndReturnDoc(ctx context.Context, do
} else if doc != nil {
// Update the in-memory CAS values to match macro-expanded values
doc.Cas = casOut
if doc.metadataOnlyUpdate != nil && doc.metadataOnlyUpdate.CAS == expandMacroCASValueString {
doc.metadataOnlyUpdate.CAS = base.CasToString(casOut)
if doc.MetadataOnlyUpdate != nil && doc.MetadataOnlyUpdate.CAS == expandMacroCASValueString {
doc.MetadataOnlyUpdate.CAS = base.CasToString(casOut)
}
// update the doc's HLV defined post macro expansion
doc = postWriteUpdateHLV(doc, casOut)
Expand Down
4 changes: 2 additions & 2 deletions db/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -1865,9 +1865,9 @@ func (db *DatabaseCollectionWithUser) resyncDocument(ctx context.Context, docid,
}
doc.SetCrc32cUserXattrHash()

// Update metadataOnlyUpdate based on previous Cas, metadataOnlyUpdate
// Update MetadataOnlyUpdate based on previous Cas, MetadataOnlyUpdate
if db.useMou() {
doc.metadataOnlyUpdate = computeMetadataOnlyUpdate(doc.Cas, doc.RevSeqNo, doc.metadataOnlyUpdate)
doc.MetadataOnlyUpdate = computeMetadataOnlyUpdate(doc.Cas, doc.RevSeqNo, doc.MetadataOnlyUpdate)
}

_, rawSyncXattr, rawVvXattr, rawMouXattr, rawGlobalXattr, err := updatedDoc.MarshalWithXattrs()
Expand Down
8 changes: 4 additions & 4 deletions db/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ type Document struct {
ID string `json:"-"` // Doc id. (We're already using a custom MarshalJSON for *document that's based on body, so the json:"-" probably isn't needed here)
Cas uint64 // Document cas
rawUserXattr []byte // Raw user xattr as retrieved from the bucket
metadataOnlyUpdate *MetadataOnlyUpdate // Contents of _mou xattr, marshalled/unmarshalled with document from xattrs
MetadataOnlyUpdate *MetadataOnlyUpdate // Contents of _mou xattr, marshalled/unmarshalled with document from xattrs

Deleted bool
DocExpiry uint32
Expand Down Expand Up @@ -433,7 +433,7 @@ func unmarshalDocumentWithXattrs(ctx context.Context, docid string, data, syncXa
}

if len(mouXattrData) > 0 {
if err := base.JSONUnmarshal(mouXattrData, &doc.metadataOnlyUpdate); err != nil {
if err := base.JSONUnmarshal(mouXattrData, &doc.MetadataOnlyUpdate); err != nil {
base.WarnfCtx(ctx, "Failed to unmarshal mouXattr for key %v, mou will be ignored. Err: %v mou:%s", base.UD(docid), err, mouXattrData)
}
}
Expand Down Expand Up @@ -1272,8 +1272,8 @@ func (doc *Document) MarshalWithXattrs() (data, syncXattr, vvXattr, mouXattr, gl
return nil, nil, nil, nil, nil, pkgerrors.WithStack(base.RedactErrorf("Failed to MarshalWithXattrs() doc SyncData with id: %s. Error: %v", base.UD(doc.ID), err))
}

if doc.metadataOnlyUpdate != nil {
mouXattr, err = base.JSONMarshal(doc.metadataOnlyUpdate)
if doc.MetadataOnlyUpdate != nil {
mouXattr, err = base.JSONMarshal(doc.MetadataOnlyUpdate)
if err != nil {
return nil, nil, nil, nil, nil, pkgerrors.WithStack(base.RedactErrorf("Failed to MarshalWithXattrs() doc MouData with id: %s. Error: %v", base.UD(doc.ID), err))
}
Expand Down
6 changes: 3 additions & 3 deletions db/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ func (db *DatabaseCollectionWithUser) ImportDoc(ctx context.Context, docid strin
} else {
if existingDoc.Deleted {
existingBucketDoc.Xattrs[base.SyncXattrName], err = base.JSONMarshal(existingDoc.SyncData)
if err == nil && existingDoc.metadataOnlyUpdate != nil && db.useMou() {
existingBucketDoc.Xattrs[base.MouXattrName], err = base.JSONMarshal(existingDoc.metadataOnlyUpdate)
if err == nil && existingDoc.MetadataOnlyUpdate != nil && db.useMou() {
existingBucketDoc.Xattrs[base.MouXattrName], err = base.JSONMarshal(existingDoc.MetadataOnlyUpdate)
}
} else {
existingBucketDoc.Body, existingBucketDoc.Xattrs[base.SyncXattrName], existingBucketDoc.Xattrs[base.VvXattrName], existingBucketDoc.Xattrs[base.MouXattrName], existingBucketDoc.Xattrs[base.GlobalXattrName], err = existingDoc.MarshalWithXattrs()
Expand Down Expand Up @@ -337,7 +337,7 @@ func (db *DatabaseCollectionWithUser) importDoc(ctx context.Context, docid strin

// If this is a metadata-only update, set metadataOnlyUpdate based on old doc's cas and mou
if metadataOnlyUpdate && db.useMou() {
newDoc.metadataOnlyUpdate = computeMetadataOnlyUpdate(doc.Cas, revNo, doc.metadataOnlyUpdate)
newDoc.MetadataOnlyUpdate = computeMetadataOnlyUpdate(doc.Cas, revNo, doc.MetadataOnlyUpdate)
}

return newDoc, nil, !shouldGenerateNewRev, updatedExpiry, nil
Expand Down
8 changes: 4 additions & 4 deletions db/import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ func TestOnDemandImportMou(t *testing.T) {
require.NoError(t, err)

if db.UseMou() {
require.NotNil(t, doc.metadataOnlyUpdate)
require.Equal(t, base.CasToString(writeCas), doc.metadataOnlyUpdate.PreviousCAS)
require.Equal(t, base.CasToString(doc.Cas), doc.metadataOnlyUpdate.CAS)
require.NotNil(t, doc.MetadataOnlyUpdate)
require.Equal(t, base.CasToString(writeCas), doc.MetadataOnlyUpdate.PreviousCAS)
require.Equal(t, base.CasToString(doc.Cas), doc.MetadataOnlyUpdate.CAS)
} else {
require.Nil(t, doc.metadataOnlyUpdate)
require.Nil(t, doc.MetadataOnlyUpdate)
}
})

Expand Down
2 changes: 1 addition & 1 deletion rest/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2834,7 +2834,7 @@ func TestPvDeltaReadAndWrite(t *testing.T) {

// assert that we have a prev CV drop to pv and a new CV pair, assert pv values are as expected after delta conversions
assert.Equal(t, testSource, newDoc.HLV.SourceID)
assert.Equal(t, version2.CV.Value, newDoc.HLV.Version)
assert.Equal(t, version2.HLV.Version, newDoc.HLV.Version)
assert.Len(t, newDoc.HLV.PreviousVersions, 1)
assert.Equal(t, casV1, newDoc.HLV.PreviousVersions[encodedSourceV1])

Expand Down
10 changes: 2 additions & 8 deletions rest/blip_api_crud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2173,21 +2173,15 @@ func TestPullReplicationUpdateOnOtherHLVAwarePeer(t *testing.T) {
otherSource := "otherSource"
hlvHelper := db.NewHLVAgent(t, rt.GetSingleDataStore(), otherSource, "_vv")
existingHLVKey := "doc1"
cas := hlvHelper.InsertWithHLV(ctx, existingHLVKey)
_ = hlvHelper.InsertWithHLV(ctx, existingHLVKey)

// force import of this write
_, _ = rt.GetDoc(docID)
bucketDoc, _, err := collection.GetDocWithXattrs(ctx, docID, db.DocUnmarshalAll)
require.NoError(t, err)

// create doc version of the above doc write
version1 := DocVersion{
RevTreeID: bucketDoc.CurrentRev,
CV: db.Version{
SourceID: hlvHelper.Source,
Value: cas,
},
}
version1 := DocVersionFromDocument(bucketDoc)

_ = btcRunner.WaitForVersion(client.id, docID, version1)

Expand Down
31 changes: 24 additions & 7 deletions rest/utilities_testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -1485,7 +1485,7 @@ func createBlipTesterWithSpec(tb testing.TB, spec BlipTesterSpec, rt *RestTester
if err != nil {
return nil, err
}
log.Printf("Creating user: %v", userDocBody)
log.Printf("db:%s Creating user: %v", bt.restTester.GetDatabase().Name, userDocBody)

// Create a user. NOTE: this must come *after* the bt.rt.TestPublicHandler() call, otherwise it will end up getting ignored
_ = bt.restTester.SendAdminRequest(
Expand Down Expand Up @@ -2448,12 +2448,15 @@ func WaitAndAssertBackgroundManagerExpiredHeartbeat(t testing.TB, bm *db.Backgro

// DocVersion represents a specific version of a document in an revID/HLV agnostic manner.
type DocVersion struct {
RevTreeID string
CV db.Version
RevTreeID string // RevTreeID is the rev treee ID of a document, may be empty not present
HLV *db.HybridLogicalVector // HLV is the hybrid logical vector of the document, may not be present
Mou *db.MetadataOnlyUpdate // Mou is the metadata only update of the document, may not be present
Cas uint64 // Cas is the cas value of the document
HasImplicitCV bool // If true, the HLV was constructed from cas@sourceID instead of from _vv, used for documents written to Couchbase Server without Sync Gateway
}

func (v *DocVersion) String() string {
return fmt.Sprintf("RevTreeID: %s", v.RevTreeID)
func (v DocVersion) String() string {
return fmt.Sprintf("Cas:%d RevTreeID:%s HLV:%+v Mou:%+v HasImplicitCV:%t", v.Cas, v.RevTreeID, v.HLV, v.Mou, v.HasImplicitCV)
}

func (v DocVersion) Equal(o DocVersion) bool {
Expand All @@ -2465,15 +2468,19 @@ func (v DocVersion) Equal(o DocVersion) bool {

func (v DocVersion) GetRev(useHLV bool) string {
if useHLV {
if v.CV.SourceID == "" {
if v.HLV == nil {
return ""
}
return v.CV.String()
return v.HLV.GetCurrentVersionString()
} else {
return v.RevTreeID
}
}

func (v DocVersion) CV() string {
return v.GetRev(true)
}

// Digest returns the digest for the current version
func (v DocVersion) Digest() string {
return strings.Split(v.RevTreeID, "-")[1]
Expand Down Expand Up @@ -2504,6 +2511,16 @@ func NewDocVersionFromFakeRev(fakeRev string) DocVersion {
return DocVersion{RevTreeID: fakeRev}
}

// DocVersionFromDocument returns a DocVersion from the given document.
func DocVersionFromDocument(doc *db.Document) DocVersion {
return DocVersion{
RevTreeID: doc.CurrentRev,
Mou: doc.MetadataOnlyUpdate,
Cas: doc.Cas,
HLV: doc.HLV,
}
}

// DocVersionFromPutResponse returns a DocRevisionID from the given response to PUT /{, or fails the given test if a rev ID was not found.
func DocVersionFromPutResponse(t testing.TB, response *TestResponse) DocVersion {
var r struct {
Expand Down
23 changes: 8 additions & 15 deletions rest/utilities_testing_blip_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ func (btc *BlipTesterCollectionClient) PushRev(docID string, parentVersion DocVe

func (btc *BlipTesterCollectionClient) requireRevID(expected DocVersion, revID string) {
if btc.UseHLV() {
require.Equal(btc.parent.rt.TB(), expected.CV.String(), revID)
require.Equal(btc.parent.rt.TB(), expected.CV(), revID)
} else {
require.Equal(btc.parent.rt.TB(), expected.RevTreeID, revID)
}
Expand All @@ -948,7 +948,7 @@ func (btc *BlipTesterClient) GetDocVersion(docID string) DocVersion {
if !btc.UseHLV() || doc.HLV == nil {
return DocVersion{RevTreeID: doc.CurrentRev}
}
return DocVersion{RevTreeID: doc.CurrentRev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}}
return DocVersion{RevTreeID: doc.CurrentRev, HLV: doc.HLV}
}

// PushRevWithHistory creates a revision on the client with history, and immediately sends a changes request for it.
Expand Down Expand Up @@ -1193,7 +1193,7 @@ func (btc *BlipTesterClient) AssertOnBlipHistory(t *testing.T, msg *blip.Message
require.NoError(t, err)
if subProtocol >= db.CBMobileReplicationV4 { // history could be empty a lot of the time in HLV messages as updates from the same source won't populate previous versions
if msg.Properties[db.RevMessageHistory] != "" {
assert.Equal(t, docVersion.CV.String(), msg.Properties[db.RevMessageHistory])
assert.Equal(t, docVersion.CV(), msg.Properties[db.RevMessageHistory])
}
} else {
assert.Equal(t, docVersion.RevTreeID, msg.Properties[db.RevMessageHistory])
Expand All @@ -1207,7 +1207,7 @@ func (btc *BlipTesterCollectionClient) GetVersion(docID string, docVersion DocVe

if doc, ok := btc.docs[docID]; ok {
if doc.revMode == revModeHLV {
if doc.getCurrentRevID() == docVersion.CV.String() {
if doc.getCurrentRevID() == docVersion.CV() {
return doc.body, true
}
} else {
Expand Down Expand Up @@ -1306,7 +1306,7 @@ func (btr *BlipTesterReplicator) storeMessage(msg *blip.Message) {
func (btc *BlipTesterCollectionClient) WaitForBlipRevMessage(docID string, version DocVersion) (msg *blip.Message) {
var revID string
if btc.UseHLV() {
revID = version.CV.String()
revID = version.CV()
} else {
revID = version.RevTreeID
}
Expand Down Expand Up @@ -1455,16 +1455,9 @@ func (btc *BlipTesterCollectionClient) sendPushMsg(msg *blip.Message) error {
// PutDoc will upsert the document with a given contents.
func (btc *BlipTesterClient) PutDoc(docID string, body string) DocVersion {
rt := btc.rt
version := rt.PutDoc(docID, body)
if btc.UseHLV() {
collection, _ := rt.GetSingleTestDatabaseCollection()
source, value := collection.GetDocumentCurrentVersion(rt.TB(), docID)
version.CV = db.Version{
SourceID: source,
Value: value,
}
}
return version
var unmarshalledBody db.Body
require.NoError(rt.TB(), base.JSONUnmarshal([]byte(body), &unmarshalledBody))
return rt.PutDocDirectly(docID, unmarshalledBody)
}

// RequireRev checks the current rev for the specified docID on the backend the BTC is replicating
Expand Down
16 changes: 8 additions & 8 deletions rest/utilities_testing_resttester.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,35 +419,35 @@ func (rt *RestTester) RequireDbOnline() {
// TEMPORARY HELPER METHODS FOR BLIP TEST CLIENT RUNNER
func (rt *RestTester) PutDocDirectly(docID string, body db.Body) DocVersion {
collection, ctx := rt.GetSingleTestDatabaseCollectionWithUser()
rev, doc, err := collection.Put(ctx, docID, body)
_, doc, err := collection.Put(ctx, docID, body)
require.NoError(rt.TB(), err)
return DocVersion{RevTreeID: rev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}}
return DocVersionFromDocument(doc)
}

func (rt *RestTester) UpdateDocDirectly(docID string, version DocVersion, body db.Body) DocVersion {
collection, ctx := rt.GetSingleTestDatabaseCollectionWithUser()
body[db.BodyId] = docID
body[db.BodyRev] = version.RevTreeID
rev, doc, err := collection.Put(ctx, docID, body)
_, doc, err := collection.Put(ctx, docID, body)
require.NoError(rt.TB(), err)
return DocVersion{RevTreeID: rev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}}
return DocVersionFromDocument(doc)
}

func (rt *RestTester) DeleteDocDirectly(docID string, version DocVersion) DocVersion {
collection, ctx := rt.GetSingleTestDatabaseCollectionWithUser()
rev, doc, err := collection.DeleteDoc(ctx, docID, version.RevTreeID)
_, doc, err := collection.DeleteDoc(ctx, docID, version.RevTreeID)
require.NoError(rt.TB(), err)
return DocVersion{RevTreeID: rev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}}
return DocVersionFromDocument(doc)
}

func (rt *RestTester) PutDocDirectlyInCollection(collection *db.DatabaseCollection, docID string, body db.Body) DocVersion {
dbUser := &db.DatabaseCollectionWithUser{
DatabaseCollection: collection,
}
ctx := base.UserLogCtx(collection.AddCollectionContext(rt.Context()), "gotest", base.UserDomainBuiltin, nil)
rev, doc, err := dbUser.Put(ctx, docID, body)
_, doc, err := dbUser.Put(ctx, docID, body)
require.NoError(rt.TB(), err)
return DocVersion{RevTreeID: rev, CV: db.Version{SourceID: doc.HLV.SourceID, Value: doc.HLV.Version}}
return DocVersionFromDocument(doc)
}

// PutDocWithAttachment will upsert the document with a given contents and attachments.
Expand Down
Loading

0 comments on commit aa1957d

Please sign in to comment.