Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CBG-3700 fix race conditions #6633

Merged
merged 11 commits into from
Jan 10, 2024
Merged

CBG-3700 fix race conditions #6633

merged 11 commits into from
Jan 10, 2024

Conversation

torcolvin
Copy link
Collaborator

@torcolvin torcolvin commented Jan 8, 2024

Addresses a race in go test -tags cb_sg_enterprise ./rest/replicatortest -race -run TestGroupIDReplications

  • each ServerContext will reset the global logger. Provide a way to avoid the logging setup, assuming that the test logging framework will already be configured. The logger can get reset while a different server (database) is logging, such as writing checkpoint documents.
  • dbReplicatorStats map can be accessed simultaneously by SGReplicateMgr and when it is explicitly called in the test.

I don't really care for the way I avoid re-initialization of the global logger, but I don't have a better alternative in mind.

Pre-review checklist

  • Removed debug logging (fmt.Print, log.Print, ...)
  • Logging sensitive data? Make sure it's tagged (e.g. base.UD(docID), base.MD(dbName))
  • Updated relevant information in the API specifications (such as endpoint descriptions, schemas, ...) in docs/api

Integration Tests

- each ServerContext will reset the global logger. Provide a way to
  avoid the logging setup, assuming that the test logging framework will
  already be configured. The logger can get reset while a different
  server (database) is logging, such as writing checkpoint documents.
- dbReplicatorStats map can be accessed simultaneously by SGReplicateMgr
  and when it is explicitly called in the test.
base/stats.go Outdated Show resolved Hide resolved
@@ -85,6 +85,8 @@ type StartupConfig struct {
CouchbaseKeepaliveInterval *int `json:"couchbase_keepalive_interval,omitempty" help:"TCP keep-alive interval between SG and Couchbase server"`

DeprecatedConfig *DeprecatedConfig `json:"-,omitempty" help:"Deprecated options that can be set from a legacy config upgrade, but cannot be set from a 3.0 config."`

avoidLoggingSetup bool `json:"-"` // Used to avoid logging setup so as to not modify globals. This only used for testing multiple ServerContexts simultaneously. This will use the same logging configuration as is already set up.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we could build this into initLogging, so that callers don't need to worry about setting this flag? (i.e. have InitLogging run within a mutex and set a global flag when complete, and check that flag when run)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a single test TestConfigsIncludeDefaults that requires initialization of a logger, to test whether it will pass the DCP base key to the database level. I'm not convinced this part of this test needs the workaround defer RunBootstrapLoggerInitialization(t)().

I don't know if I like this solution better because it relies on a global but I think it makes the part of the code to be modified very small, which I like.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the previous commit, I had the impression that we should only ever be calling SetupAndValidateLogging once per process. (and that unit tests with multiple server contexts should reuse the same logging config). Is that accurate? If yes, I think we should be encapsulate the 'run once' logic inside SetupAndValidateLogging.

I looked at TestConfigsIncludeDefaults and I think it can just use SetUpTestLogging to initialize the DCP log key - I don't think it needs a custom call to SetupAndValidateLogging.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to add config.Logging.Console.Enabled = base.BoolPtr(true) to this test, which is normally enabled as a side effect of InitLogging but not normally needed in a JSON for server config. I think this is fine for this test, which keeps the guts of the inheritance.

@adamcfraser adamcfraser assigned torcolvin and unassigned adamcfraser Jan 8, 2024
@torcolvin torcolvin assigned adamcfraser and unassigned torcolvin Jan 8, 2024
@adamcfraser adamcfraser assigned torcolvin and unassigned adamcfraser Jan 9, 2024
torcolvin and others added 3 commits January 8, 2024 20:25
This causes a race condition in the instance that two databases with
the same config group are created simultaneously. An example of how this
can happen is:

- config polling to pick up a db config A
- GET /dbA/

The second will independently attempt to load the configuration while
the first is running. In theory, this could happen for any different
database in step 2, since all databases will be in the same
group id.
@torcolvin torcolvin assigned adamcfraser and unassigned torcolvin Jan 9, 2024
rest/config.go Outdated
log.Printf("[ERR] Error setting up logging: %v", err)
return nil, fmt.Errorf("error setting up logging: %v", err)
// logger will be initialized only when running tests
if !loggerInitialized {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't feel like quite the right place to be using this variable - wouldn't we'd want to encapsulate it in the logging handling (inside SetupAndValidateLogging)?

We'd also still need some sort of synchronization to avoid races.

@@ -85,6 +85,8 @@ type StartupConfig struct {
CouchbaseKeepaliveInterval *int `json:"couchbase_keepalive_interval,omitempty" help:"TCP keep-alive interval between SG and Couchbase server"`

DeprecatedConfig *DeprecatedConfig `json:"-,omitempty" help:"Deprecated options that can be set from a legacy config upgrade, but cannot be set from a 3.0 config."`

avoidLoggingSetup bool `json:"-"` // Used to avoid logging setup so as to not modify globals. This only used for testing multiple ServerContexts simultaneously. This will use the same logging configuration as is already set up.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the previous commit, I had the impression that we should only ever be calling SetupAndValidateLogging once per process. (and that unit tests with multiple server contexts should reuse the same logging config). Is that accurate? If yes, I think we should be encapsulate the 'run once' logic inside SetupAndValidateLogging.

I looked at TestConfigsIncludeDefaults and I think it can just use SetUpTestLogging to initialize the DCP log key - I don't think it needs a custom call to SetupAndValidateLogging.

@adamcfraser adamcfraser assigned torcolvin and unassigned adamcfraser Jan 9, 2024
@torcolvin torcolvin assigned adamcfraser and unassigned torcolvin Jan 9, 2024
@torcolvin torcolvin enabled auto-merge (squash) January 10, 2024 20:13
@torcolvin torcolvin merged commit 2e37dac into master Jan 10, 2024
29 of 30 checks passed
@torcolvin torcolvin deleted the CBG-3700 branch January 10, 2024 21:40
// If SetupServerContext is called while any other go routines that might use logging are running, it will
// cause a data race, therefore only initialize logging and other globals on the first call. From a main
// program, there is only one ServerContext.
if serverContextGlobalsInitialized.CompareAndSwap(false, true) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how this is intended to work for a suite of tests that only invoke SetupServerContext once per test.

Is it expected that only the first test gets to set global config, and all others can't?

I'd have expected this flag to be reset between tests maybe (t.Cleanup?), if the intention is to only allow this to be run once per test to avoid races

Comment on lines +1351 to +1352
// If SetupServerContext is called while any other go routines that might use logging are running, it will
// cause a data race, therefore only initialize logging and other globals on the first call. From a main
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't protect against tests that have a mix of RestTester and full Bootstrap/ServerContext (e.g. TestGroupIDReplications)

bbrks pushed a commit that referenced this pull request Mar 28, 2024
- each ServerContext will reset the global logger. Only reset the global logging once per execution of the program, to simulate what happens in mane.
- dbReplicatorStats map can be accessed simultaneously by SGReplicateMgr and when it is explicitly called in the test.
- Mutex RegisterImportPindexImpl which was unsynchronized when multiple calls to NewDatabaseContext happen simultaneously.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants