@@ -3958,6 +3958,20 @@ func TestTombstoneCompaction(t *testing.T) {
39583958 t .Skip ("If running with no xattrs compact acts as a no-op" )
39593959 }
39603960
3961+ tests := []struct {
3962+ numDocs int
3963+ runAsScheduledBackgroundTask bool
3964+ }{
3965+ // Multiples of Batch Size
3966+ {numDocs : db .QueryTombstoneBatch },
3967+ {numDocs : db .QueryTombstoneBatch * 4 },
3968+ // Smaller Than Batch Size
3969+ {numDocs : 2 },
3970+ {numDocs : db .QueryTombstoneBatch / 4 },
3971+ // Larger than Batch Size
3972+ {numDocs : db .QueryTombstoneBatch + 20 },
3973+ }
3974+
39613975 var rt * rest.RestTester
39623976 numCollections := 1
39633977
@@ -3968,64 +3982,84 @@ func TestTombstoneCompaction(t *testing.T) {
39683982 rt = rest .NewRestTester (t , nil )
39693983 }
39703984 defer rt .Close ()
3971- zero := time .Duration (0 )
3972- rt .GetDatabase ().Options .PurgeInterval = & zero
3973-
3974- compactionTotal := 0
3975- expectedBatches := 0
3985+ rt .GetDatabase ().Options .PurgeInterval = base .Ptr (time .Duration (0 ))
39763986
3977- TestCompact := func (numDocs int ) {
3978-
3979- count := 0
3987+ for _ , test := range tests {
3988+ for _ , runAsScheduledBackgroundTask := range []bool {false , true } {
3989+ t .Run (fmt .Sprintf ("numDocs:%d asBackgroundTask:%v" , test .numDocs , runAsScheduledBackgroundTask ), func (t * testing.T ) {
3990+
3991+ // seed with tombstones
3992+ for count := 0 ; count < test .numDocs ; count ++ {
3993+ for _ , keyspace := range rt .GetKeyspaces () {
3994+ response := rt .SendAdminRequest ("POST" , fmt .Sprintf ("/%s/" , keyspace ), `{"foo":"bar"}` )
3995+ assert .Equal (t , http .StatusOK , response .Code )
3996+ var body db.Body
3997+ err := base .JSONUnmarshal (response .Body .Bytes (), & body )
3998+ assert .NoError (t , err )
3999+ revID := body ["rev" ].(string )
4000+ docID := body ["id" ].(string )
4001+
4002+ response = rt .SendAdminRequest ("DELETE" , fmt .Sprintf ("/%s/%s?rev=%s" , keyspace , docID , revID ), "" )
4003+ assert .Equal (t , http .StatusOK , response .Code )
4004+ }
4005+ }
39804006
3981- for count < numDocs {
3982- count ++
3983- for _ , keyspace := range rt .GetKeyspaces () {
3984- response := rt .SendAdminRequest ("POST" , fmt .Sprintf ("/%s/" , keyspace ), `{"foo":"bar"}` )
3985- assert .Equal (t , 200 , response .Code )
3986- var body db.Body
3987- err := base .JSONUnmarshal (response .Body .Bytes (), & body )
3988- assert .NoError (t , err )
3989- revId := body ["rev" ].(string )
3990- docId := body ["id" ].(string )
4007+ expectedCompactions := test .numDocs * numCollections
4008+ expectedBatches := (test .numDocs / db .QueryTombstoneBatch + 1 ) * numCollections
39914009
3992- response = rt .SendAdminRequest ("DELETE" , fmt .Sprintf ("/%s/%s?rev=%s" , keyspace , docId , revId ), "" )
3993- assert .Equal (t , 200 , response .Code )
3994- }
3995- }
3996- resp := rt .SendAdminRequest ("POST" , "/{{.db}}/_compact" , "" )
3997- rest .RequireStatus (t , resp , http .StatusOK )
4010+ numCompactionsBefore := int (rt .GetDatabase ().DbStats .Database ().NumTombstonesCompacted .Value ())
4011+ var numBatchesBefore int
4012+ if base .TestsDisableGSI () {
4013+ numBatchesBefore = int (rt .GetDatabase ().DbStats .Query (fmt .Sprintf (base .StatViewFormat , db .DesignDocSyncHousekeeping (), db .ViewTombstones )).QueryCount .Value ())
4014+ } else {
4015+ numBatchesBefore = int (rt .GetDatabase ().DbStats .Query (db .QueryTypeTombstones ).QueryCount .Value ())
4016+ }
39984017
3999- err := rt .WaitForCondition (func () bool {
4000- time .Sleep (1 * time .Second )
4001- return rt .GetDatabase ().TombstoneCompactionManager .GetRunState () == db .BackgroundProcessStateCompleted
4002- })
4003- assert .NoError (t , err )
4018+ numIdleKvOpsBefore := int (base .SyncGatewayStats .GlobalStats .ResourceUtilizationStats ().NumIdleKvOps .Value ())
4019+ numIdleQueryOpsBefore := int (base .SyncGatewayStats .GlobalStats .ResourceUtilizationStats ().NumIdleQueryOps .Value ())
4020+
4021+ if runAsScheduledBackgroundTask {
4022+ database , err := db .CreateDatabase (rt .GetDatabase ())
4023+ require .NoError (t , err )
4024+ purgedCount , err := database .Compact (base .TestCtx (t ), false , nil , base .NewSafeTerminator (), true )
4025+ require .NoError (t , err )
4026+ require .Equal (t , expectedCompactions , purgedCount )
4027+
4028+ numIdleKvOpsAfter := int (base .SyncGatewayStats .GlobalStats .ResourceUtilizationStats ().NumIdleKvOps .Value ())
4029+ numIdleQueryOpsAfter := int (base .SyncGatewayStats .GlobalStats .ResourceUtilizationStats ().NumIdleQueryOps .Value ())
4030+
4031+ // cannot do equal here because there are other idle kv ops unrelated to compaction
4032+ assert .GreaterOrEqual (t , numIdleKvOpsAfter - numIdleKvOpsBefore , expectedCompactions )
4033+ assert .Equal (t , numIdleQueryOpsAfter - numIdleQueryOpsBefore , expectedBatches )
4034+ } else {
4035+ resp := rt .SendAdminRequest ("POST" , "/{{.db}}/_compact" , "" )
4036+ rest .RequireStatus (t , resp , http .StatusOK )
4037+ err := rt .WaitForCondition (func () bool {
4038+ return rt .GetDatabase ().TombstoneCompactionManager .GetRunState () == db .BackgroundProcessStateCompleted
4039+ })
4040+ assert .NoError (t , err )
4041+
4042+ numIdleKvOpsAfter := int (base .SyncGatewayStats .GlobalStats .ResourceUtilizationStats ().NumIdleKvOps .Value ())
4043+ numIdleQueryOpsAfter := int (base .SyncGatewayStats .GlobalStats .ResourceUtilizationStats ().NumIdleQueryOps .Value ())
4044+
4045+ // ad-hoc compactions don't invoke idle ops - but we do have other idle kv ops so can't ensure it stays zero
4046+ assert .GreaterOrEqual (t , numIdleKvOpsAfter - numIdleKvOpsBefore , 0 )
4047+ assert .Equal (t , numIdleQueryOpsAfter - numIdleQueryOpsBefore , 0 )
4048+ }
40044049
4005- compactionTotal += ( numDocs * numCollections )
4006- require .Equal (t , compactionTotal , int ( rt . GetDatabase (). DbStats . Database (). NumTombstonesCompacted . Value ()) )
4050+ actualCompactions := int ( rt . GetDatabase (). DbStats . Database (). NumTombstonesCompacted . Value ()) - numCompactionsBefore
4051+ require .Equal (t , expectedCompactions , actualCompactions )
40074052
4008- var actualBatches int64
4009- if base .TestsDisableGSI () {
4010- actualBatches = rt .GetDatabase ().DbStats .Query (fmt .Sprintf (base .StatViewFormat , db .DesignDocSyncHousekeeping (), db .ViewTombstones )).QueryCount .Value ()
4011- } else {
4012- actualBatches = rt .GetDatabase ().DbStats .Query (db .QueryTypeTombstones ).QueryCount .Value ()
4053+ var actualBatches int
4054+ if base .TestsDisableGSI () {
4055+ actualBatches = int (rt .GetDatabase ().DbStats .Query (fmt .Sprintf (base .StatViewFormat , db .DesignDocSyncHousekeeping (), db .ViewTombstones )).QueryCount .Value ()) - numBatchesBefore
4056+ } else {
4057+ actualBatches = int (rt .GetDatabase ().DbStats .Query (db .QueryTypeTombstones ).QueryCount .Value ()) - numBatchesBefore
4058+ }
4059+ require .Equal (t , expectedBatches , actualBatches )
4060+ })
40134061 }
4014-
4015- expectedBatches += (numDocs / db .QueryTombstoneBatch + 1 ) * numCollections
4016- require .Equal (t , expectedBatches , int (actualBatches ))
40174062 }
4018-
4019- // Multiples of Batch Size
4020- TestCompact (db .QueryTombstoneBatch )
4021- TestCompact (db .QueryTombstoneBatch * 4 )
4022-
4023- // Smaller Than Batch Size
4024- TestCompact (2 )
4025- TestCompact (db .QueryTombstoneBatch / 4 )
4026-
4027- // Larger than Batch Size
4028- TestCompact (db .QueryTombstoneBatch + 20 )
40294063}
40304064
40314065// TestOneShotGrantTiming simulates a one-shot changes feed returning before a previously issued grant has been
0 commit comments