Skip to content

Commit

Permalink
CBG-4019 Add init_in_progress property to db and verbose all_dbs resp…
Browse files Browse the repository at this point in the history
…onses (#6910)
  • Loading branch information
adamcfraser authored Jun 24, 2024
1 parent 1e534ff commit c85d5c0
Show file tree
Hide file tree
Showing 7 changed files with 60 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 @@ -2523,11 +2523,14 @@ 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
all_user_channels:
description: |-
All user channels split by how they were assigned to the user and by keyspace.
Expand Down
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
'404':
$ref: ../../components/responses.yaml#/Not-found
tags:
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,13 +404,15 @@ 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"`
}

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"`
}

func (h *handler) handleGetDB() error {
Expand All @@ -436,6 +438,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),
}

h.writeJSON(response)
Expand Down
4 changes: 4 additions & 0 deletions rest/database_init_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ func (m *DatabaseInitManager) InitializeDatabase(ctx context.Context, startupCon
}

func (m *DatabaseInitManager) HasActiveInitialization(dbName string) bool {
if m == nil {
// When not using persistent config, DatabaseInitManager will be 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 @@ -389,6 +389,7 @@ func TestAsyncOnlineOffline(t *testing.T) {
rest.WaitForChannel(t, initStarted, "waiting for initialization to start")
log.Printf("initialization started")
waitAndRequireDBState(t, sc, dbName, db.DBOffline)
verifyInitializationActive(t, sc, dbName, true)

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

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

// Verify offline changes can still be made
resp = rest.BootstrapAdminRequest(t, sc, http.MethodGet, "/"+keyspace+"/_config/sync", "")
resp.RequireResponse(http.StatusOK, syncFunc)
verifyInitializationActive(t, sc, dbName, true)

// Take the database back online while async init is still in progress, verify state goes to Starting
resp = rest.BootstrapAdminRequest(t, sc, http.MethodPost, "/"+dbName+"/_config", string(dbOnlineConfigPayload))
resp.RequireStatus(http.StatusCreated)
waitAndRequireDBState(t, sc, dbName, db.DBStarting)
verifyInitializationActive(t, sc, dbName, true)

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

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

}

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

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

var dbResult rest.DatabaseRoot
resp := rest.BootstrapAdminRequest(t, sc, 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, sc, 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 @@ -45,8 +45,6 @@ const defaultBytesStatsReportingInterval = 30 * time.Second

const dbLoadedStateChangeMsg = "DB loaded from config"

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 @@ -363,9 +361,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 c85d5c0

Please sign in to comment.