Skip to content

Commit

Permalink
CBG-3209: Add cv index and retrieval for revision cache (#6491)
Browse files Browse the repository at this point in the history
* CBG-3209: changes for retreival of a doc from the rev cache via CV with backwards compatability in mind

* fix failing test, add commnets

* fix lint

* updated to address comments

* rebase chnages needed

* updated to tests that call Get on revision cache

* updates based of new direction with PR + addressing comments

* updated to fix panic

* updated to fix another panic

* address comments

* updates based off commnets

* remove commnented out line

* updates to skip test relying on import and update PutExistingRev doc update type to update HLV

* updates to remove code adding rev id to value inside addToRevMapPostLoad. Added code to assign this inside value.store

* remove redundent code
  • Loading branch information
gregns1 authored and torcolvin committed Nov 1, 2023
1 parent af0ee77 commit 1de3947
Show file tree
Hide file tree
Showing 22 changed files with 999 additions and 318 deletions.
8 changes: 4 additions & 4 deletions db/access_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestDynamicChannelGrant(t *testing.T) {

// Create a document in channel chan1
doc1Body := Body{"channel": "chan1", "greeting": "hello"}
_, _, err = dbCollection.PutExistingRevWithBody(ctx, "doc1", doc1Body, []string{"1-a"}, false)
_, _, err = dbCollection.PutExistingRevWithBody(ctx, "doc1", doc1Body, []string{"1-a"}, false, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)

// Verify user cannot access document
Expand All @@ -54,7 +54,7 @@ func TestDynamicChannelGrant(t *testing.T) {

// Write access granting document
grantingBody := Body{"type": "setaccess", "owner": "user1", "channel": "chan1"}
_, _, err = dbCollection.PutExistingRevWithBody(ctx, "grant1", grantingBody, []string{"1-a"}, false)
_, _, err = dbCollection.PutExistingRevWithBody(ctx, "grant1", grantingBody, []string{"1-a"}, false, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)

// Verify reloaded user can access document
Expand All @@ -66,12 +66,12 @@ func TestDynamicChannelGrant(t *testing.T) {

// Create a document in channel chan2
doc2Body := Body{"channel": "chan2", "greeting": "hello"}
_, _, err = dbCollection.PutExistingRevWithBody(ctx, "doc2", doc2Body, []string{"1-a"}, false)
_, _, err = dbCollection.PutExistingRevWithBody(ctx, "doc2", doc2Body, []string{"1-a"}, false, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)

// Write access granting document for chan2 (tests invalidation when channels/inval_seq exists)
grantingBody = Body{"type": "setaccess", "owner": "user1", "channel": "chan2"}
_, _, err = dbCollection.PutExistingRevWithBody(ctx, "grant2", grantingBody, []string{"1-a"}, false)
_, _, err = dbCollection.PutExistingRevWithBody(ctx, "grant2", grantingBody, []string{"1-a"}, false, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)

// Verify user can now access both documents
Expand Down
28 changes: 14 additions & 14 deletions db/attachment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestBackupOldRevisionWithAttachments(t *testing.T) {
var rev2Body Body
rev2Data := `{"test": true, "updated": true, "_attachments": {"hello.txt": {"stub": true, "revpos": 1}}}`
require.NoError(t, base.JSONUnmarshal([]byte(rev2Data), &rev2Body))
_, _, err = collection.PutExistingRevWithBody(ctx, docID, rev2Body, []string{"2-abc", rev1ID}, true)
_, _, err = collection.PutExistingRevWithBody(ctx, docID, rev2Body, []string{"2-abc", rev1ID}, true, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)
rev2ID := "2-abc"

Expand Down Expand Up @@ -201,7 +201,7 @@ func TestAttachments(t *testing.T) {
rev2Bstr := `{"_attachments": {"bye.txt": {"stub":true,"revpos":1,"digest":"sha1-gwwPApfQR9bzBKpqoEYwFmKp98A="}}, "_rev": "2-f000"}`
var body2B Body
assert.NoError(t, base.JSONUnmarshal([]byte(rev2Bstr), &body2B))
_, _, err = collection.PutExistingRevWithBody(ctx, "doc1", body2B, []string{"2-f000", rev1id}, false)
_, _, err = collection.PutExistingRevWithBody(ctx, "doc1", body2B, []string{"2-f000", rev1id}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't update document")
}

Expand Down Expand Up @@ -285,7 +285,7 @@ func TestAttachmentCASRetryAfterNewAttachment(t *testing.T) {
rev2Data := `{"prop1":"value2", "_attachments": {"hello.txt": {"data":"aGVsbG8gd29ybGQ="}}}`
require.NoError(t, base.JSONUnmarshal([]byte(rev2Data), &rev2Body))
collection := GetSingleDatabaseCollectionWithUser(t, db)
_, _, err := collection.PutExistingRevWithBody(ctx, "doc1", rev2Body, []string{"2-abc", rev1ID}, true)
_, _, err := collection.PutExistingRevWithBody(ctx, "doc1", rev2Body, []string{"2-abc", rev1ID}, true, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)

log.Printf("Done creating rev 2 for key %s", key)
Expand Down Expand Up @@ -316,7 +316,7 @@ func TestAttachmentCASRetryAfterNewAttachment(t *testing.T) {
var rev3Body Body
rev3Data := `{"prop1":"value3", "_attachments": {"hello.txt": {"revpos":2,"stub":true,"digest":"sha1-Kq5sNclPz7QV2+lfQIuc6R7oRu0="}}}`
require.NoError(t, base.JSONUnmarshal([]byte(rev3Data), &rev3Body))
_, _, err = collection.PutExistingRevWithBody(ctx, "doc1", rev3Body, []string{"3-abc", "2-abc", rev1ID}, true)
_, _, err = collection.PutExistingRevWithBody(ctx, "doc1", rev3Body, []string{"3-abc", "2-abc", rev1ID}, true, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)

log.Printf("rev 3 done")
Expand Down Expand Up @@ -348,7 +348,7 @@ func TestAttachmentCASRetryDuringNewAttachment(t *testing.T) {
rev2Data := `{"prop1":"value2"}`
require.NoError(t, base.JSONUnmarshal([]byte(rev2Data), &rev2Body))
collection := GetSingleDatabaseCollectionWithUser(t, db)
_, _, err := collection.PutExistingRevWithBody(ctx, "doc1", rev2Body, []string{"2-abc", rev1ID}, true)
_, _, err := collection.PutExistingRevWithBody(ctx, "doc1", rev2Body, []string{"2-abc", rev1ID}, true, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)

log.Printf("Done creating rev 2 for key %s", key)
Expand Down Expand Up @@ -379,7 +379,7 @@ func TestAttachmentCASRetryDuringNewAttachment(t *testing.T) {
var rev3Body Body
rev3Data := `{"prop1":"value3", "_attachments": {"hello.txt": {"data":"aGVsbG8gd29ybGQ="}}}`
require.NoError(t, base.JSONUnmarshal([]byte(rev3Data), &rev3Body))
_, _, err = collection.PutExistingRevWithBody(ctx, "doc1", rev3Body, []string{"3-abc", "2-abc", rev1ID}, true)
_, _, err = collection.PutExistingRevWithBody(ctx, "doc1", rev3Body, []string{"3-abc", "2-abc", rev1ID}, true, ExistingVersionWithUpdateToHLV)
require.NoError(t, err)

log.Printf("rev 3 done")
Expand Down Expand Up @@ -568,57 +568,57 @@ func TestRetrieveAncestorAttachments(t *testing.T) {
// Create document (rev 1)
text := `{"key": "value", "version": "1a"}`
assert.NoError(t, base.JSONUnmarshal([]byte(text), &body))
doc, revID, err := collection.PutExistingRevWithBody(ctx, "doc", body, []string{"1-a"}, false)
doc, revID, err := collection.PutExistingRevWithBody(ctx, "doc", body, []string{"1-a"}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't create document")
log.Printf("doc: %v", doc)

// Add an attachment to a document (rev 2)
text = `{"key": "value", "version": "2a", "_attachments": {"att1.txt": {"data": "YXR0MS50eHQ="}}}`
assert.NoError(t, base.JSONUnmarshal([]byte(text), &body))
body[BodyRev] = revID
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"2-a", "1-a"}, false)
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"2-a", "1-a"}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't create document")
log.Printf("doc: %v", doc)

text = `{"key": "value", "version": "3a", "_attachments": {"att1.txt": {"stub":true,"revpos":2,"digest":"sha1-gwwPApfQR9bzBKpqoEYwFmKp98A="}}}`
assert.NoError(t, base.JSONUnmarshal([]byte(text), &body))
body[BodyRev] = revID
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"3-a", "2-a"}, false)
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"3-a", "2-a"}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't create document")
log.Printf("doc: %v", doc)

text = `{"key": "value", "version": "4a", "_attachments": {"att1.txt": {"stub":true,"revpos":2,"digest":"sha1-gwwPApfQR9bzBKpqoEYwFmKp98A="}}}`
assert.NoError(t, base.JSONUnmarshal([]byte(text), &body))
body[BodyRev] = revID
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"4-a", "3-a"}, false)
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"4-a", "3-a"}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't create document")
log.Printf("doc: %v", doc)

text = `{"key": "value", "version": "5a", "_attachments": {"att1.txt": {"stub":true,"revpos":2,"digest":"sha1-gwwPApfQR9bzBKpqoEYwFmKp98A="}}}`
assert.NoError(t, base.JSONUnmarshal([]byte(text), &body))
body[BodyRev] = revID
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"5-a", "4-a"}, false)
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"5-a", "4-a"}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't create document")
log.Printf("doc: %v", doc)

text = `{"key": "value", "version": "6a", "_attachments": {"att1.txt": {"stub":true,"revpos":2,"digest":"sha1-gwwPApfQR9bzBKpqoEYwFmKp98A="}}}`
assert.NoError(t, base.JSONUnmarshal([]byte(text), &body))
body[BodyRev] = revID
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"6-a", "5-a"}, false)
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"6-a", "5-a"}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't create document")
log.Printf("doc: %v", doc)

text = `{"key": "value", "version": "3b", "type": "pruned"}`
assert.NoError(t, base.JSONUnmarshal([]byte(text), &body))
body[BodyRev] = revID
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"3-b", "2-a"}, false)
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"3-b", "2-a"}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't create document")
log.Printf("doc: %v", doc)

text = `{"key": "value", "version": "3b", "_attachments": {"att1.txt": {"stub":true,"revpos":2,"digest":"sha1-gwwPApfQR9bzBKpqoEYwFmKp98A="}}}`
assert.NoError(t, base.JSONUnmarshal([]byte(text), &body))
body[BodyRev] = revID
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"3-b", "2-a"}, false)
doc, _, err = collection.PutExistingRevWithBody(ctx, "doc", body, []string{"3-b", "2-a"}, false, ExistingVersionWithUpdateToHLV)
assert.NoError(t, err, "Couldn't create document")
log.Printf("doc: %v", doc)
}
Expand Down
4 changes: 2 additions & 2 deletions db/blip_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1183,9 +1183,9 @@ func (bh *blipHandler) processRev(rq *blip.Message, stats *processRevStats) (err
// bh.conflictResolver != nil represents an active SGR2 and BLIPClientTypeSGR2 represents a passive SGR2
forceAllowConflictingTombstone := newDoc.Deleted && (bh.conflictResolver != nil || bh.clientType == BLIPClientTypeSGR2)
if bh.conflictResolver != nil {
_, _, err = bh.collection.PutExistingRevWithConflictResolution(bh.loggingCtx, newDoc, history, true, bh.conflictResolver, forceAllowConflictingTombstone, rawBucketDoc)
_, _, err = bh.collection.PutExistingRevWithConflictResolution(bh.loggingCtx, newDoc, history, true, bh.conflictResolver, forceAllowConflictingTombstone, rawBucketDoc, ExistingVersionWithUpdateToHLV)
} else {
_, _, err = bh.collection.PutExistingRev(bh.loggingCtx, newDoc, history, revNoConflicts, forceAllowConflictingTombstone, rawBucketDoc)
_, _, err = bh.collection.PutExistingRev(bh.loggingCtx, newDoc, history, revNoConflicts, forceAllowConflictingTombstone, rawBucketDoc, ExistingVersionWithUpdateToHLV)
}
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion db/change_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ func (c *changeCache) DocChanged(event sgbucket.FeedEvent) {

// Now add the entry for the new doc revision:
if len(rawUserXattr) > 0 {
collection.revisionCache.Remove(docID, syncData.CurrentRev)
collection.revisionCache.RemoveWithRev(docID, syncData.CurrentRev)
}
change := &LogEntry{
Sequence: syncData.Sequence,
Expand Down
4 changes: 2 additions & 2 deletions db/changes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,14 +478,14 @@ func BenchmarkChangesFeedDocUnmarshalling(b *testing.B) {

// Create child rev 1
docBody["child"] = "A"
_, _, err = collection.PutExistingRevWithBody(ctx, docid, docBody, []string{"2-A", revId}, false)
_, _, err = collection.PutExistingRevWithBody(ctx, docid, docBody, []string{"2-A", revId}, false, ExistingVersionWithUpdateToHLV)
if err != nil {
b.Fatalf("Error creating child1 rev: %v", err)
}

// Create child rev 2
docBody["child"] = "B"
_, _, err = collection.PutExistingRevWithBody(ctx, docid, docBody, []string{"2-B", revId}, false)
_, _, err = collection.PutExistingRevWithBody(ctx, docid, docBody, []string{"2-B", revId}, false, ExistingVersionWithUpdateToHLV)
if err != nil {
b.Fatalf("Error creating child2 rev: %v", err)
}
Expand Down
37 changes: 26 additions & 11 deletions db/crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func (db *DatabaseCollectionWithUser) getRev(ctx context.Context, docid, revid s
if revid != "" {
// Get a specific revision body and history from the revision cache
// (which will load them if necessary, by calling revCacheLoader, above)
revision, err = db.revisionCache.Get(ctx, docid, revid, includeBody, RevCacheOmitDelta)
revision, err = db.revisionCache.GetWithRev(ctx, docid, revid, includeBody, RevCacheOmitDelta)
} else {
// No rev ID given, so load active revision
revision, err = db.revisionCache.GetActive(ctx, docid, includeBody)
Expand Down Expand Up @@ -381,7 +381,7 @@ func (db *DatabaseCollectionWithUser) GetDelta(ctx context.Context, docID, fromR
return nil, nil, nil
}

fromRevision, err := db.revisionCache.Get(ctx, docID, fromRevID, RevCacheOmitBody, RevCacheIncludeDelta)
fromRevision, err := db.revisionCache.GetWithRev(ctx, docID, fromRevID, RevCacheOmitBody, RevCacheIncludeDelta)

// If the fromRevision is a removal cache entry (no body), but the user has access to that removal, then just
// return 404 missing to indicate that the body of the revision is no longer available.
Expand Down Expand Up @@ -424,7 +424,7 @@ func (db *DatabaseCollectionWithUser) GetDelta(ctx context.Context, docID, fromR

// db.DbStats.StatsDeltaSync().Add(base.StatKeyDeltaCacheMisses, 1)
db.dbStats().DeltaSync().DeltaCacheMiss.Add(1)
toRevision, err := db.revisionCache.Get(ctx, docID, toRevID, RevCacheOmitBody, RevCacheIncludeDelta)
toRevision, err := db.revisionCache.GetWithRev(ctx, docID, toRevID, RevCacheOmitBody, RevCacheIncludeDelta)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -881,7 +881,7 @@ func (db *DatabaseCollectionWithUser) updateHLV(d *Document, docUpdateEvent DocU
d.HLV.CurrentVersionCAS = hlvExpandMacroCASValue
case Import:
// work to be done to decide if the VV needs updating here, pending CBG-3503
case NewVersion:
case NewVersion, ExistingVersionWithUpdateToHLV:
// add a new entry to the version vector
newVVEntry := CurrentVersionVector{}
newVVEntry.SourceID = db.dbCtx.BucketUUID
Expand Down Expand Up @@ -1045,23 +1045,22 @@ func (db *DatabaseCollectionWithUser) Put(ctx context.Context, docid string, bod
}

// Adds an existing revision to a document along with its history (list of rev IDs.)
func (db *DatabaseCollectionWithUser) PutExistingRev(ctx context.Context, newDoc *Document, docHistory []string, noConflicts bool, forceAllConflicts bool, existingDoc *sgbucket.BucketDocument) (doc *Document, newRevID string, err error) {
return db.PutExistingRevWithConflictResolution(ctx, newDoc, docHistory, noConflicts, nil, forceAllConflicts, existingDoc)
func (db *DatabaseCollectionWithUser) PutExistingRev(ctx context.Context, newDoc *Document, docHistory []string, noConflicts bool, forceAllConflicts bool, existingDoc *sgbucket.BucketDocument, docUpdateEvent DocUpdateType) (doc *Document, newRevID string, err error) {
return db.PutExistingRevWithConflictResolution(ctx, newDoc, docHistory, noConflicts, nil, forceAllConflicts, existingDoc, docUpdateEvent)
}

// PutExistingRevWithConflictResolution Adds an existing revision to a document along with its history (list of rev IDs.)
// If this new revision would result in a conflict:
// 1. If noConflicts == false, the revision will be added to the rev tree as a conflict
// 2. If noConflicts == true and a conflictResolverFunc is not provided, a 409 conflict error will be returned
// 3. If noConflicts == true and a conflictResolverFunc is provided, conflicts will be resolved and the result added to the document.
func (db *DatabaseCollectionWithUser) PutExistingRevWithConflictResolution(ctx context.Context, newDoc *Document, docHistory []string, noConflicts bool, conflictResolver *ConflictResolver, forceAllowConflictingTombstone bool, existingDoc *sgbucket.BucketDocument) (doc *Document, newRevID string, err error) {
func (db *DatabaseCollectionWithUser) PutExistingRevWithConflictResolution(ctx context.Context, newDoc *Document, docHistory []string, noConflicts bool, conflictResolver *ConflictResolver, forceAllowConflictingTombstone bool, existingDoc *sgbucket.BucketDocument, docUpdateEvent DocUpdateType) (doc *Document, newRevID string, err error) {
newRev := docHistory[0]
generation, _ := ParseRevID(ctx, newRev)
if generation < 0 {
return nil, "", base.HTTPErrorf(http.StatusBadRequest, "Invalid revision ID")
}

docUpdateEvent := ExistingVersion
allowImport := db.UseXattrs()
doc, _, err = db.updateAndReturnDoc(ctx, newDoc.ID, allowImport, newDoc.DocExpiry, nil, docUpdateEvent, existingDoc, func(doc *Document) (resultDoc *Document, resultAttachmentData AttachmentData, createNewRevIDSkipped bool, updatedExpiry *uint32, resultErr error) {
// (Be careful: this block can be invoked multiple times if there are races!)
Expand Down Expand Up @@ -1162,7 +1161,7 @@ func (db *DatabaseCollectionWithUser) PutExistingRevWithConflictResolution(ctx c
return doc, newRev, err
}

func (db *DatabaseCollectionWithUser) PutExistingRevWithBody(ctx context.Context, docid string, body Body, docHistory []string, noConflicts bool) (doc *Document, newRev string, err error) {
func (db *DatabaseCollectionWithUser) PutExistingRevWithBody(ctx context.Context, docid string, body Body, docHistory []string, noConflicts bool, docUpdateEvent DocUpdateType) (doc *Document, newRev string, err error) {
err = validateAPIDocUpdate(body)
if err != nil {
return nil, "", err
Expand All @@ -1187,7 +1186,7 @@ func (db *DatabaseCollectionWithUser) PutExistingRevWithBody(ctx context.Context

newDoc.UpdateBody(body)

doc, newRevID, putExistingRevErr := db.PutExistingRev(ctx, newDoc, docHistory, noConflicts, false, nil)
doc, newRevID, putExistingRevErr := db.PutExistingRev(ctx, newDoc, docHistory, noConflicts, false, nil, docUpdateEvent)

if putExistingRevErr != nil {
return nil, "", putExistingRevErr
Expand Down Expand Up @@ -1990,7 +1989,7 @@ func (db *DatabaseCollectionWithUser) updateAndReturnDoc(ctx context.Context, do

// Prior to saving doc, remove the revision in cache
if createNewRevIDSkipped {
db.revisionCache.Remove(doc.ID, doc.CurrentRev)
db.revisionCache.RemoveWithRev(doc.ID, doc.CurrentRev)
}

base.DebugfCtx(ctx, base.KeyCRUD, "Saving doc (seq: #%d, id: %v rev: %v)", doc.Sequence, base.UD(doc.ID), doc.CurrentRev)
Expand All @@ -2004,6 +2003,8 @@ func (db *DatabaseCollectionWithUser) updateAndReturnDoc(ctx context.Context, do
}
} else if doc != nil {
doc.Cas = casOut
// update the doc's HLV defined post macro expansion
doc = postWriteUpdateHLV(doc, casOut)
}
}

Expand Down Expand Up @@ -2061,6 +2062,7 @@ func (db *DatabaseCollectionWithUser) updateAndReturnDoc(ctx context.Context, do
Expiry: doc.Expiry,
Deleted: doc.History[newRevID].Deleted,
_shallowCopyBody: storedDoc.Body(ctx),
CV: &CurrentVersionVector{VersionCAS: doc.HLV.Version, SourceID: doc.HLV.SourceID},
}

if createNewRevIDSkipped {
Expand Down Expand Up @@ -2123,6 +2125,19 @@ func (db *DatabaseCollectionWithUser) updateAndReturnDoc(ctx context.Context, do
return doc, newRevID, nil
}

func postWriteUpdateHLV(doc *Document, casOut uint64) *Document {
if doc.HLV == nil {
return doc
}
if doc.HLV.Version == hlvExpandMacroCASValue {
doc.HLV.Version = casOut
}
if doc.HLV.CurrentVersionCAS == hlvExpandMacroCASValue {
doc.HLV.CurrentVersionCAS = casOut
}
return doc
}

func getAttachmentIDsForLeafRevisions(ctx context.Context, db *DatabaseCollectionWithUser, doc *Document, newRevID string) (map[string]struct{}, error) {
leafAttachments := make(map[string]struct{})

Expand Down
Loading

0 comments on commit 1de3947

Please sign in to comment.