Skip to content

Commit 5503c1e

Browse files
authored
CBG-5467 when resuming via action=start preserve stats (#8370)
1 parent 1e77c14 commit 5503c1e

10 files changed

Lines changed: 49 additions & 39 deletions

db/background_mgr.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ var errBackgroundManagerProcessAlreadyRunning = base.HTTPErrorf(http.StatusServi
5252
// errBackgroundManagerProcessAlreadyStopped is returned when an action to Stop a process occurs but it is already stopped.
5353
var errBackgroundManagerProcessAlreadyStopped = base.HTTPErrorf(http.StatusServiceUnavailable, "Process already stopped")
5454

55+
// backgroundManagerInitMode is the return type of BackgroundManagerProcessI.Init, indicating whether
56+
// the process is starting fresh or resuming a prior run.
57+
type backgroundManagerInitMode int
58+
59+
const (
60+
// backgroundManagerInitReset means Init started a new run, discarding any previous state.
61+
backgroundManagerInitReset backgroundManagerInitMode = iota
62+
// backgroundManagerInitResume means Init found a previous run and will continue from where it left off.
63+
backgroundManagerInitResume
64+
)
65+
5566
// backgroundManagerUpdateClusterStatusMode controls whether updateMultiNodeClusterAwareStatus enforces
5667
// consistency between the local and cluster states.
5768
type backgroundManagerUpdateClusterStatusMode int
@@ -130,7 +141,8 @@ type BackgroundManagerStatus struct {
130141
// Examples of this: ReSync, Compaction, Attachment Migration
131142
type BackgroundManagerProcessI[O any] interface {
132143
// Init is called before Run for setup purposes. If Init errors, Run will not happen.
133-
Init(ctx context.Context, options O, clusterStatus []byte) error
144+
// Returns backgroundManagerInitResume if resuming an existing (stopped) run, backgroundManagerInitReset if starting a new one.
145+
Init(ctx context.Context, options O, clusterStatus []byte) (backgroundManagerInitMode, error)
134146
// Run implements all of the work of the process.
135147
Run(ctx context.Context, options O, persistClusterStatusCallback updateStatusCallbackFunc, terminator *base.SafeTerminator) error
136148
// GetProcessStatus accepts the current BackgroundManagerStatus and the previousStatus that was serialized. previousStatus is
@@ -252,7 +264,7 @@ func (b *BackgroundManager[O]) start(ctx context.Context, options O, processClus
252264
b.setStartTime(previousStatus.StartTime)
253265
}
254266

255-
err = b.Process.Init(ctx, options, processClusterStatus)
267+
initMode, err := b.Process.Init(ctx, options, processClusterStatus)
256268
if err != nil {
257269
return err
258270
}
@@ -317,7 +329,7 @@ func (b *BackgroundManager[O]) start(ctx context.Context, options O, processClus
317329
var err error
318330
if b.mode() == backgroundManagerModeMultiNode {
319331
mode := backgroundManagerStatusStart
320-
if isResume {
332+
if isResume || initMode == backgroundManagerInitResume {
321333
mode = backgroundManagerStatusResume
322334
}
323335
err = b.updateMultiNodeClusterAwareStatus(ctx, mode)

db/background_mgr_async_index_init.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ type AsyncIndexInitManager struct {
2525
}
2626

2727
// Init is called synchronously to set up a run for the background manager process. See Run() for the async part.
28-
func (a *AsyncIndexInitManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) error {
28+
func (a *AsyncIndexInitManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) (backgroundManagerInitMode, error) {
2929
a.lock.Lock()
3030
defer a.lock.Unlock()
3131
a._statusMap = options["statusMap"].(*IndexStatusByCollection)
3232
a._doneChan = options["doneChan"].(chan error)
33-
return nil
33+
return backgroundManagerInitReset, nil
3434
}
3535

3636
// Run is called inside a goroutine to perform the job of the job. This function should block until the job is complete.

db/background_mgr_attachment_compaction.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func NewAttachmentCompactionManager(metadataStore base.DataStore, metaKeys *base
5353
}
5454
}
5555

56-
func (a *AttachmentCompactionManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) error {
56+
func (a *AttachmentCompactionManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) (backgroundManagerInitMode, error) {
5757
database := options["database"].(*Database)
5858
database.DbStats.Database().CompactionAttachmentStartTime.Set(uint64(time.Now().UTC().Unix()))
5959

@@ -88,7 +88,7 @@ func (a *AttachmentCompactionManager) Init(ctx context.Context, options map[stri
8888
// process from scratch with a new compaction ID. Otherwise, we should resume with the compact ID, phase and
8989
// stats specified in the doc.
9090
if statusDoc.State == BackgroundProcessStateCompleted || err != nil || (reset && ok) {
91-
return newRunInit()
91+
return backgroundManagerInitReset, newRunInit()
9292
} else {
9393
a.CompactID = statusDoc.CompactID
9494
a.Phase = statusDoc.Phase
@@ -100,11 +100,11 @@ func (a *AttachmentCompactionManager) Init(ctx context.Context, options map[stri
100100
base.InfofCtx(ctx, base.KeyAll, "Attachment Compaction: Attempting to resume compaction with compact ID: %q phase %q", a.CompactID, a.Phase)
101101
}
102102

103-
return nil
103+
return backgroundManagerInitResume, nil
104104

105105
}
106106

107-
return newRunInit()
107+
return backgroundManagerInitReset, newRunInit()
108108
}
109109

110110
func (a *AttachmentCompactionManager) Run(ctx context.Context, options map[string]any, persistClusterStatusCallback updateStatusCallbackFunc, terminator *base.SafeTerminator) error {
@@ -222,7 +222,7 @@ func (a *AttachmentCompactionManager) handleAttachmentCompactionRollbackError(ct
222222
}
223223
if phase == MarkPhase {
224224
// initialise new compaction run as we want to start the phase mark again in event of rollback
225-
err = a.Init(ctx, options, nil)
225+
_, err = a.Init(ctx, options, nil)
226226
if err != nil {
227227
base.WarnfCtx(ctx, "error on initialization of new run after rollback has been indicated: %s", err)
228228
return false, err

db/background_mgr_attachment_migration.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func NewAttachmentMigrationManager(database *DatabaseContext) *BackgroundManager
5252
}
5353
}
5454

55-
func (a *AttachmentMigrationManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) error {
55+
func (a *AttachmentMigrationManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) (backgroundManagerInitMode, error) {
5656
newRunInit := func() error {
5757
uniqueUUID, err := uuid.NewRandom()
5858
if err != nil {
@@ -76,7 +76,7 @@ func (a *AttachmentMigrationManager) Init(ctx context.Context, options map[strin
7676
// If the previous run completed, or there was an error during unmarshalling the status we will start the
7777
// process from scratch with a new migration ID. Otherwise, we should resume with the migration ID, stats specified in the doc.
7878
if statusDoc.State == BackgroundProcessStateCompleted || err != nil || reset {
79-
return newRunInit()
79+
return backgroundManagerInitReset, newRunInit()
8080
}
8181
a.MigrationID = statusDoc.MigrationID
8282
a.docsProcessed.Store(statusDoc.DocsProcessed)
@@ -86,10 +86,10 @@ func (a *AttachmentMigrationManager) Init(ctx context.Context, options map[strin
8686

8787
base.InfofCtx(ctx, base.KeyAll, "Attachment Migration: Resuming migration with migration ID: %s, %d already processed", a.MigrationID, a.docsProcessed.Load())
8888

89-
return nil
89+
return backgroundManagerInitResume, nil
9090
}
9191

92-
return newRunInit()
92+
return backgroundManagerInitReset, newRunInit()
9393
}
9494

9595
func (a *AttachmentMigrationManager) Run(ctx context.Context, options map[string]any, persistClusterStatusCallback updateStatusCallbackFunc, terminator *base.SafeTerminator) error {

db/background_mgr_metadata_migration.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type MigrationManagerStatusDoc struct {
6868
MigrationManagerResponse `json:"status"`
6969
}
7070

71-
func (m *MetadataMigrationManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) error {
71+
func (m *MetadataMigrationManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) (backgroundManagerInitMode, error) {
7272
newRunInit := func() error {
7373
uniqueUUID, err := uuid.NewRandom()
7474
if err != nil {
@@ -92,17 +92,17 @@ func (m *MetadataMigrationManager) Init(ctx context.Context, options map[string]
9292
// If the previous run completed, there was an error during unmarshalling the status, or
9393
// the caller requested a reset, start again with a fresh migration ID and zeroed counters.
9494
if status.State == BackgroundProcessStateCompleted || err != nil || reset {
95-
return newRunInit()
95+
return backgroundManagerInitReset, newRunInit()
9696
}
9797
m.docsProcessed.Store(status.DocsProcessed)
9898
m.docsFailed.Store(status.DocsFailed)
9999
m.docsOutOfScope.Store(status.DocsOutOfScope)
100100
m.passes.Store(status.Passes)
101101
m.MigrationID = status.MigrationID
102102
base.InfofCtx(ctx, base.KeyAll, "Metadata Migration: Resuming migration run with migration ID: %s, docs processed: %d, docs failed: %d, passes: %d", m.MigrationID, status.DocsProcessed, status.DocsFailed, status.Passes)
103-
return nil
103+
return backgroundManagerInitResume, nil
104104
}
105-
return newRunInit()
105+
return backgroundManagerInitReset, newRunInit()
106106
}
107107

108108
func (m *MetadataMigrationManager) Run(ctx context.Context, options map[string]any, persistClusterStatusCallback updateStatusCallbackFunc, terminator *base.SafeTerminator) error {

db/background_mgr_resync_dcp.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,15 @@ func NewResyncManagerDCP(db *DatabaseContext, distributed bool) *BackgroundManag
137137
}
138138

139139
// Init processes the options to start a resync process and sets them as struct members.
140-
func (r *ResyncManagerDCP) Init(ctx context.Context, options ResyncOptions, clusterStatus []byte) error {
140+
func (r *ResyncManagerDCP) Init(ctx context.Context, options ResyncOptions, clusterStatus []byte) (backgroundManagerInitMode, error) {
141141
r.setStartOptions(options)
142142

143143
var collections DatabaseCollections
144144
if len(options.Collections) > 0 {
145145
var err error
146146
collections, err = r.db.collections(options.Collections)
147147
if err != nil {
148-
return err
148+
return backgroundManagerInitReset, err
149149
}
150150
} else {
151151
collections = slices.Collect(maps.Values(r.db.CollectionByID))
@@ -171,7 +171,7 @@ func (r *ResyncManagerDCP) Init(ctx context.Context, options ResyncOptions, clus
171171
} else {
172172
r.initializeFromPreviousStatus(statusDoc)
173173
base.InfofCtx(ctx, base.KeyAll, "Resuming resync with ID: %q", r.ResyncID)
174-
return nil
174+
return backgroundManagerInitResume, nil
175175
}
176176

177177
if statusDoc.ResyncID != "" {
@@ -183,7 +183,7 @@ func (r *ResyncManagerDCP) Init(ctx context.Context, options ResyncOptions, clus
183183

184184
newID, err := uuid.NewRandom()
185185
if err != nil {
186-
return err
186+
return backgroundManagerInitReset, err
187187
}
188188

189189
docsTargeted, err := totalResyncDocs(ctx, collections)
@@ -198,13 +198,13 @@ func (r *ResyncManagerDCP) Init(ctx context.Context, options ResyncOptions, clus
198198
if r.Distributed {
199199
endSeqNosMap, err := base.GetHighSeqNos(ctx, r.db.Bucket)
200200
if err != nil {
201-
return err
201+
return backgroundManagerInitReset, err
202202
}
203203
r.setEndSeqNosFromMap(endSeqNosMap)
204204
}
205205

206206
base.InfofCtx(ctx, base.KeyAll, "Running new resync process with ID: %q - %s", r.ResyncID, resetMsg)
207-
return nil
207+
return backgroundManagerInitReset, nil
208208
}
209209

210210
// totalResyncDocs returns an estimate of the number of documents processed for resync.

db/background_mgr_resync_dcp_test.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func TestResyncDCPInit(t *testing.T) {
162162
require.NoError(t, err)
163163
}
164164

165-
err = db.ResyncManager.Process.Init(ctx, options, clusterData)
165+
_, err = db.ResyncManager.Process.Init(ctx, options, clusterData)
166166
require.NoError(t, err)
167167

168168
response := getResyncStats(t, db)
@@ -404,12 +404,9 @@ func TestResyncManagerDCPResumeStoppedProcess(t *testing.T) {
404404
require.NoError(t, err)
405405

406406
stats = waitForResyncState(t, db, BackgroundProcessStateCompleted)
407-
if !db.useShardedDCP() {
408-
// CBG-5467 these stats are too low for distributed resync
409-
assert.GreaterOrEqual(t, stats.DocsProcessed, int64(docsToCreate))
410-
assert.Equal(t, int64(docsToCreate), stats.DocsChanged)
411-
// DocsTargeted is preserved from the original run start, even after resume
412-
}
407+
assert.GreaterOrEqual(t, stats.DocsProcessed, int64(docsToCreate))
408+
assert.Equal(t, int64(docsToCreate), stats.DocsChanged)
409+
// DocsTargeted is preserved from the original run start, even after resume
413410
assert.Equal(t, initialDocsTargeted, stats.DocsTargeted)
414411
assert.Equal(t, int64(0), db.DbStats.Database().ResyncDocsTargeted.Value()) // resync finished so reset back to 0
415412

@@ -1128,7 +1125,8 @@ func TestResyncDCPInitStoresOptionsInMeta(t *testing.T) {
11281125
Collections: base.NewCollectionNames(),
11291126
}
11301127

1131-
require.NoError(t, db.ResyncManager.Process.Init(ctx, options, nil))
1128+
_, initErr := db.ResyncManager.Process.Init(ctx, options, nil)
1129+
require.NoError(t, initErr)
11321130

11331131
_, metaBytes, err := db.ResyncManager.Process.GetProcessStatus(BackgroundManagerStatus{State: BackgroundProcessStateStopped}, nil)
11341132
require.NoError(t, err)

db/background_mgr_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ type MockProcess struct {
3131
lock sync.Mutex
3232
}
3333

34-
func (m *MockProcess) Init(ctx context.Context, options map[string]any, clusterStatus []byte) error {
34+
func (m *MockProcess) Init(ctx context.Context, options map[string]any, clusterStatus []byte) (backgroundManagerInitMode, error) {
3535
m.lock.Lock()
3636
defer m.lock.Unlock()
3737
m.InitCalled = true
38-
return nil
38+
return backgroundManagerInitReset, nil
3939
}
4040

4141
func (m *MockProcess) Run(ctx context.Context, options map[string]any, persistClusterStatusCallback updateStatusCallbackFunc, terminator *base.SafeTerminator) error {
@@ -472,7 +472,7 @@ type ResumableMockProcess struct {
472472
optionsLock sync.RWMutex
473473
}
474474

475-
func (r *ResumableMockProcess) Init(ctx context.Context, options map[string]any, clusterStatus []byte) error {
475+
func (r *ResumableMockProcess) Init(ctx context.Context, options map[string]any, clusterStatus []byte) (backgroundManagerInitMode, error) {
476476
r.optionsLock.Lock()
477477
r.receivedOptions = options
478478
r.optionsLock.Unlock()

db/background_mgr_tombstone_compaction.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ func NewTombstoneCompactionManager() *BackgroundManager[map[string]any] {
3434
}
3535
}
3636

37-
func (t *TombstoneCompactionManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) error {
37+
func (t *TombstoneCompactionManager) Init(ctx context.Context, options map[string]any, clusterStatus []byte) (backgroundManagerInitMode, error) {
3838
database := options["database"].(*Database)
3939
database.DbStats.Database().CompactionTombstoneStartTime.Set(uint64(time.Now().UTC().Unix()))
4040

41-
return nil
41+
return backgroundManagerInitReset, nil
4242
}
4343

4444
func (t *TombstoneCompactionManager) Run(ctx context.Context, options map[string]any, persistClusterStatusCallback updateStatusCallbackFunc, terminator *base.SafeTerminator) error {

db/database_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4521,8 +4521,8 @@ type testBackgroundProcess[O any] struct {
45214521
isStoppable bool
45224522
}
45234523

4524-
func (i *testBackgroundProcess[O]) Init(_ context.Context, _ O, _ []byte) error {
4525-
return nil
4524+
func (i *testBackgroundProcess[O]) Init(_ context.Context, _ O, _ []byte) (backgroundManagerInitMode, error) {
4525+
return backgroundManagerInitReset, nil
45264526
}
45274527

45284528
func (i *testBackgroundProcess[O]) Run(_ context.Context, _ O, _ updateStatusCallbackFunc, terminator *base.SafeTerminator) error {

0 commit comments

Comments
 (0)