Skip to content

Commit

Permalink
CBG-4020 Add init_in_progress, change 'reason' to 'require_resync' fl…
Browse files Browse the repository at this point in the history
…ag (#6916)

Backports CBG-4019 to 3.1.8.
  • Loading branch information
adamcfraser authored Jun 24, 2024
1 parent 66e5b09 commit 0eb61e1
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 16 deletions.
13 changes: 8 additions & 5 deletions docs/api/components/schemas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2436,8 +2436,11 @@ CollectionNames:
- Starting
- Stopping
- Resyncing
reason:
description: Optional reason a database is in a particular state.
enum:
- require_resync
- ""
require_resync:
description: Indicates whether the database requires resync before it can be brought online.
type: boolean
example: true
init_in_progress:
description: Indicates whether database initialization is in progress.
type: boolean
example: true
3 changes: 3 additions & 0 deletions docs/api/paths/admin/db-.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ get:
description: Unique server identifier.
type: string
example: 995618a6a6cc9ac79731bd13240e19b5
init_in_progress:
description: Indicates whether database initialization is in progress.
type: boolean
scopes:
description: 'Scopes that are used by the database.'
type: object
Expand Down
8 changes: 4 additions & 4 deletions rest/adminapitest/collections_admin_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,10 @@ func TestRequireResync(t *testing.T) {
// databases sorted alphabetically
require.Equal(t, db1Name, allDBsSummary[0].DBName)
require.Equal(t, db.RunStateString[db.DBOnline], allDBsSummary[0].State)
require.Equal(t, "", allDBsSummary[0].Reason)
require.Equal(t, false, allDBsSummary[0].RequireResync)
require.Equal(t, db2Name, allDBsSummary[1].DBName)
require.Equal(t, db.RunStateString[db.DBOffline], allDBsSummary[1].State)
require.Equal(t, rest.OfflineReasonRequireResync, allDBsSummary[1].Reason)
require.Equal(t, true, allDBsSummary[1].RequireResync)

// Run resync for collection
resyncCollections := make(db.ResyncCollections, 0)
Expand Down Expand Up @@ -296,10 +296,10 @@ func TestRequireResync(t *testing.T) {
// databases sorted alphabetically
require.Equal(t, db1Name, allDBsSummary[0].DBName)
require.Equal(t, db.RunStateString[db.DBOnline], allDBsSummary[0].State)
require.Equal(t, "", allDBsSummary[0].Reason)
require.Equal(t, false, allDBsSummary[0].RequireResync)
require.Equal(t, db2Name, allDBsSummary[1].DBName)
require.Equal(t, db.RunStateString[db.DBOffline], allDBsSummary[1].State)
require.Equal(t, "", allDBsSummary[1].Reason)
require.Equal(t, false, allDBsSummary[1].RequireResync)

resp = rt.SendAdminRequest("GET", "/"+db2Name+"/", "")
rest.RequireStatus(t, resp, http.StatusOK)
Expand Down
11 changes: 7 additions & 4 deletions rest/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,14 +404,16 @@ type DatabaseRoot struct {
State string `json:"state"`
ServerUUID string `json:"server_uuid,omitempty"`
RequireResync []string `json:"require_resync,omitempty"`
InitializationActive bool `json:"init_in_progress,omitempty"`
Scopes map[string]databaseRootScope `json:"scopes,omitempty"` // stats for each scope/collection
}

type DbSummary struct {
DBName string `json:"db_name"`
Bucket string `json:"bucket"`
State string `json:"state"`
Reason string `json:"reason,omitempty"`
DBName string `json:"db_name"`
Bucket string `json:"bucket"`
State string `json:"state"`
InitializationActive bool `json:"init_in_progress,omitempty"`
RequireResync bool `json:"require_resync,omitempty"`
}

type databaseRootScope struct {
Expand Down Expand Up @@ -448,6 +450,7 @@ func (h *handler) handleGetDB() error {
State: runState,
ServerUUID: h.db.DatabaseContext.ServerUUID,
RequireResync: h.db.RequireResync.ScopeAndCollectionNames(),
InitializationActive: h.server.DatabaseInitManager.HasActiveInitialization(h.db.Name),

// TODO: If running with multiple scope/collections
// Scopes: map[string]databaseRootScope{
Expand Down
3 changes: 3 additions & 0 deletions rest/database_init_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ func (m *DatabaseInitManager) InitializeDatabase(ctx context.Context, startupCon
}

func (m *DatabaseInitManager) HasActiveInitialization(dbName string) bool {
if m == nil {
return false
}
m.workersLock.Lock()
defer m.workersLock.Unlock()
_, ok := m.workers[dbName]
Expand Down
30 changes: 30 additions & 0 deletions rest/indextest/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ func TestAsyncOnlineOffline(t *testing.T) {
rest.WaitForChannel(t, initStarted, "waiting for initialization to start")
log.Printf("initialization started")
waitAndRequireDBState(t, dbName, db.DBOffline)
verifyInitializationActive(t, dbName, true)

// Set up payloads for upserting db state
onlineConfigUpsert := rest.DbConfig{
Expand All @@ -444,11 +445,13 @@ func TestAsyncOnlineOffline(t *testing.T) {
resp = rest.BootstrapAdminRequest(t, http.MethodPost, "/"+dbName+"/_config", string(dbOnlineConfigPayload))
resp.RequireStatus(http.StatusCreated)
waitAndRequireDBState(t, dbName, db.DBStarting)
verifyInitializationActive(t, dbName, true)

// Take the database offline while async init is still in progress
resp = rest.BootstrapAdminRequest(t, http.MethodPost, "/"+dbName+"/_config", string(dbOfflineConfigPayload))
resp.RequireStatus(http.StatusCreated)
waitAndRequireDBState(t, dbName, db.DBOffline)
verifyInitializationActive(t, dbName, true)

// Verify offline changes can still be made
resp = rest.BootstrapAdminRequest(t, http.MethodGet, "/"+keyspace+"/_config/sync", "")
Expand All @@ -458,10 +461,12 @@ func TestAsyncOnlineOffline(t *testing.T) {
resp = rest.BootstrapAdminRequest(t, http.MethodPost, "/"+dbName+"/_config", string(dbOnlineConfigPayload))
resp.RequireStatus(http.StatusCreated)
waitAndRequireDBState(t, dbName, db.DBStarting)
verifyInitializationActive(t, dbName, true)

// Unblock initialization, verify status goes to Online
close(unblockInit)
waitAndRequireDBState(t, dbName, db.DBOnline)
verifyInitializationActive(t, dbName, true)

// Verify only four collections were initialized (offline/online didn't trigger duplicate initialization)
totalCount := atomic.LoadInt64(&collectionCount)
Expand All @@ -471,11 +476,13 @@ func TestAsyncOnlineOffline(t *testing.T) {
resp = rest.BootstrapAdminRequest(t, http.MethodPost, "/"+dbName+"/_config", string(dbOfflineConfigPayload))
resp.RequireStatus(http.StatusCreated)
waitAndRequireDBState(t, dbName, db.DBOffline)
verifyInitializationActive(t, dbName, false)

// Take database back online after init complete, verify successful
resp = rest.BootstrapAdminRequest(t, http.MethodPost, "/"+dbName+"/_config", string(dbOnlineConfigPayload))
resp.RequireStatus(http.StatusCreated)
waitAndRequireDBState(t, dbName, db.DBOnline)
verifyInitializationActive(t, dbName, false)

}

Expand Down Expand Up @@ -888,6 +895,29 @@ func TestAsyncInitRemoteConfigUpdates(t *testing.T) {
waitAndRequireDBState(t, dbName, db.DBOnline)
}

// verifyInitializationActive verifies the expected value of InitializationActive on db and verbose all_dbs responses
func verifyInitializationActive(t *testing.T, dbName string, expectedValue bool) {

var dbResult rest.DatabaseRoot
resp := rest.BootstrapAdminRequest(t, http.MethodGet, "/"+dbName+"/", "")
resp.RequireStatus(http.StatusOK)
require.NoError(t, base.JSONUnmarshal([]byte(resp.Body), &dbResult))
require.Equal(t, expectedValue, dbResult.InitializationActive)

var allDbResult []rest.DbSummary
allDbResp := rest.BootstrapAdminRequest(t, http.MethodGet, "/_all_dbs?verbose=true", "")
allDbResp.RequireStatus(http.StatusOK)
require.NoError(t, base.JSONUnmarshal([]byte(allDbResp.Body), &allDbResult))
dbFound := false
for _, dbSummary := range allDbResult {
if dbSummary.DBName == dbName {
dbFound = true
require.Equal(t, expectedValue, dbSummary.InitializationActive)
}
}
require.True(t, dbFound, "Database not found in _all_dbs response")
}

func makeDbConfig(t *testing.T, tb *base.TestBucket, syncFunction string, importFilter string) rest.DbConfig {

scopesConfig := rest.GetCollectionsConfig(t, tb, 1)
Expand Down
7 changes: 4 additions & 3 deletions rest/server_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ const kStatsReportInterval = time.Hour
const kDefaultSlowQueryWarningThreshold = 500 // ms
const KDefaultNumShards = 16

const OfflineReasonRequireResync = "require_resync"

var errCollectionsUnsupported = base.HTTPErrorf(http.StatusBadRequest, "Named collections specified in database config, but not supported by connected Couchbase Server.")

var ErrSuspendingDisallowed = errors.New("database does not allow suspending")
Expand Down Expand Up @@ -338,9 +336,12 @@ func (sc *ServerContext) allDatabaseSummaries() []DbSummary {
}
if state == db.RunStateString[db.DBOffline] {
if len(dbctx.RequireResync.ScopeAndCollectionNames()) > 0 {
summary.Reason = OfflineReasonRequireResync
summary.RequireResync = true
}
}
if sc.DatabaseInitManager.HasActiveInitialization(name) {
summary.InitializationActive = true
}
dbs = append(dbs, summary)
}
sort.Slice(dbs, func(i, j int) bool {
Expand Down

0 comments on commit 0eb61e1

Please sign in to comment.