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

chore: improve rsources service table setup procedure #4165

Merged
merged 2 commits into from
Nov 27, 2023

Conversation

atzoum
Copy link
Contributor

@atzoum atzoum commented Nov 21, 2023

Description

  • Using an advisory lock during rsources table setup to avoid race conditions between pods.
  • Addressing rsources tests' flakiness while running in github actions:
    • isolating services/rsources tests on a separate runner
    • using BeforeEach/AfterEach instead of BeforeAll/AfterAll

Linear Ticket

resolves PIPE-488
resolves PIPE-561

Security

  • The code changed/added as part of this pull request won't create any security issues with how the software is being used.

Summary by CodeRabbit

  • CI/CD Pipeline Enhancements

    • Improved CI/CD pipeline to conditionally configure test runs and artifact naming for the "services" package.
  • Testing Infrastructure

    • Updated Makefile to refine test execution based on package inclusion or exclusion.
    • Enhanced test suites for better resource management and isolation during setup and teardown.
  • Service Reliability

    • Implemented advisory lock mechanism to ensure safe initialization of resources.
    • Adjusted JobService configuration to enforce a minimum connection pool size for stability.

Copy link

codecov bot commented Nov 21, 2023

Codecov Report

Attention: 16 lines in your changes are missing coverage. Please review.

Comparison is base (e803bf9) 72.48% compared to head (4440591) 72.47%.

Files Patch % Lines
services/rsources/handler.go 61.90% 11 Missing and 5 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4165      +/-   ##
==========================================
- Coverage   72.48%   72.47%   -0.02%     
==========================================
  Files         383      383              
  Lines       55802    55822      +20     
==========================================
+ Hits        40448    40455       +7     
- Misses      13008    13019      +11     
- Partials     2346     2348       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

services/rsources/handler.go Outdated Show resolved Hide resolved
services/rsources/handler.go Outdated Show resolved Hide resolved
services/rsources/handler.go Outdated Show resolved Hide resolved
services/rsources/handler.go Outdated Show resolved Hide resolved
Copy link
Contributor

coderabbitai bot commented Nov 27, 2023

Walkthrough

The changes involve updates to CI/CD configurations, Makefile test commands, and source handler logic. The CI/CD pipeline now has conditional configurations for tests, the Makefile adjusts test execution based on variables, and the source handler code introduces advisory locks and a minimum pool size requirement. Test suites are refactored for better resource management and consistency.

Changes

File Path Change Summary
.github/workflows/tests.yaml Updated CI/CD pipeline with conditional configurations and changes to test commands and artifact naming.
Makefile Modified test command execution based on package and exclude variables; updated test-warehouse-integration target.
services/.../handler.go Added lockID constant, refactored init function, moved migrateFailedKeysTable, and added withAdvisoryLock function.
services/.../handler_test.go
services/.../handler_v1_test.go
Replaced BeforeAll and AfterAll with BeforeEach and AfterEach; adjusted resource creation and cleanup.
services/.../rsources.go Modified NewJobService to enforce a minimum MaxPoolSize.

🐇✨
In the realm of code, where rabbits hop,
We tweak and change; we never stop.
Locks in place, tests refined,
With every commit, our goals aligned. 🌟
🐇✨


Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • If you reply to a review comment from CodeRabbit, the bot will automatically respond.
  • To engage with CodeRabbit bot directly around the specific lines of code in the PR, mention @coderabbitai in your review comment
  • Note: Review comments are made on code diffs or files, not on the PR overview.
  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Note: For conversation with the bot, please use the review comments on code diffs or files.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 7

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e803bf9 and 4440591.
Files selected for processing (6)
  • .github/workflows/tests.yaml (3 hunks)
  • Makefile (2 hunks)
  • services/rsources/handler.go (4 hunks)
  • services/rsources/handler_test.go (6 hunks)
  • services/rsources/handler_v1_test.go (4 hunks)
  • services/rsources/rsources.go (1 hunks)
Files skipped from review due to trivial changes (1)
  • services/rsources/handler_test.go
Additional comments: 15
.github/workflows/tests.yaml (3)
  • 76-82: The changes to the Warehouse Service Integration step correctly use matrix variables to run tests for specific packages. Ensure that the make test-warehouse command is correctly set up to accept and handle the package variable.

  • 123-131: The inclusion of services/rsources in the matrix and the conditional exclusion of services/rsources when testing the services package are well-implemented to avoid running redundant tests. Confirm that the make test command in the Package Unit step is correctly configured to handle these conditions.

  • 148-154: The use of exclude and package variables in the Package Unit step is a good practice for flexible test execution. Verify that the make test command is properly set up to handle these variables and that the exclude patterns are correctly defined to match the intended files or directories.

Makefile (1)
  • 37-41: The test-warehouse-integration target correctly runs integration tests for the specified package. Ensure that the package variable is always defined when this target is invoked to avoid running tests on unintended directories.
services/rsources/handler.go (5)
  • 480-480: The introduction of a constant lockID is a good practice for maintainability and readability.

  • 482-514: The use of withAdvisoryLock in the init function to manage concurrent setup operations is a good approach to prevent race conditions.

  • 603-609: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [528-609]

Refactoring migrateFailedKeysTable to accept a *sql.Tx ensures that the migration process is transactional, which is crucial for data integrity.

  • 813-826: The implementation of withAdvisoryLock to acquire an advisory lock and execute a function within a transaction is a robust pattern for ensuring that critical sections of code are executed safely in a concurrent environment.

  • 482-514: Verify that the lockID used in withAdvisoryLock is unique and not conflicting with other advisory locks in the system to avoid potential deadlocks or unintended behavior.

services/rsources/handler_v1_test.go (4)
  • 45-51: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [34-50]

The use of BeforeEach and AfterEach instead of BeforeAll and AfterAll is a good practice for ensuring that each test case is run with a fresh environment, which can help prevent tests from affecting each other and improve test reliability.

  • 45-45: In the test setup, the createService function is called without explicit error handling. Ensure that this function has internal error handling or is designed to panic in case of failure, which is acceptable in test environments.

  • 49-49: The purgeResources function is called in the AfterEach hook without explicit error handling. Confirm that this function handles errors internally or that errors during resource cleanup do not impact the test outcomes.

  • 382-388: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [340-387]

The BeforeEach and AfterEach hooks in the multitenant setup context are correctly used to ensure test isolation and a clean environment for each test case. This is consistent with the changes made in the single-tenant setup context and follows best practices.

services/rsources/rsources.go (2)
  • 202-203: The change to enforce a minimum MaxPoolSize of 2 is a good safeguard to ensure there are always enough connections available for the service to function correctly upon startup.

  • 199-201: Setting a default logger if none is provided is a good practice for ensuring that logging is always available.

Comment on lines 20 to 31
$(eval TEST_OPTIONS = -p=1 -v -failfast -shuffle=on -coverprofile=profile.out -covermode=atomic -coverpkg=./... -vet=all --timeout=15m)
endif
ifdef package
$(TEST_CMD) $(TEST_OPTIONS) $(package) && touch $(TESTFILE) || true
ifdef exclude
$(eval FILES = `go list ./$(package)/... | egrep -iv '$(exclude)'`)
$(TEST_CMD) -count=1 $(TEST_OPTIONS) $(FILES) && touch $(TESTFILE) || true
else
$(TEST_CMD) $(TEST_OPTIONS) ./$(package)/... && touch $(TESTFILE) || true
endif
else ifdef exclude
$(eval FILES = `go list ./... | egrep -iv '$(exclude)'`)
$(TEST_CMD) -count=1 $(TEST_OPTIONS) $(FILES) && touch $(TESTFILE) || true
Copy link
Contributor

Choose a reason for hiding this comment

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

The test command logic for handling package and exclude variables is correctly implemented. However, consider using $(shell ...) instead of backticks for command substitution to improve readability and maintainability in the Makefile.

Comment on lines 199 to 207
if config.Log == nil {
config.Log = logger.NewLogger().Child("rsources")
}
if config.MaxPoolSize <= 2 {
config.MaxPoolSize = 2 // minimum 2 connections in the pool for proper startup
}
var (
localDB, sharedDB *sql.DB
err error
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider checking the error returned by sql.Open before setting MaxOpenConns on localDB and sharedDB to ensure that the database connections were successfully opened.

Comment on lines +202 to +204
if config.MaxPoolSize <= 2 {
config.MaxPoolSize = 2 // minimum 2 connections in the pool for proper startup
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure that MaxPoolSize is not only at least 2, but also that it is not a negative value, which could cause unexpected behavior.

Comment on lines 476 to 514
return time.After(config.GetDuration("Rsources.stats.cleanup.interval", 1, time.Hour))
}
}
sh.log.Debugf("setting up rsources tables in %s", sh.config.LocalHostname)
if err := setupTables(ctx, sh.localDB, sh.config.LocalHostname, sh.log); err != nil {

const lockID = 100020001

if err := withAdvisoryLock(ctx, sh.localDB, lockID, func(tx *sql.Tx) error {
sh.log.Debugf("setting up rsources tables in %s", sh.config.LocalHostname)
if err := setupTables(ctx, sh.localDB, sh.config.LocalHostname, sh.log); err != nil {
return err
}
if err := migrateFailedKeysTable(ctx, tx); err != nil {
return fmt.Errorf("migrating rsources_failed_keys table: %w", err)
}
sh.log.Debugf("rsources tables setup successfully in %s", sh.config.LocalHostname)
return nil
}); err != nil {
return err
}
if err := migrateFailedKeysTable(ctx, sh.localDB); err != nil {
return fmt.Errorf("failed to migrate rsources_failed_keys table: %w", err)
}
sh.log.Debugf("rsources tables setup successfully in %s", sh.config.LocalHostname)

if sh.sharedDB != nil {
sh.log.Debugf("setting up rsources tables for shared db %s", sh.config.SharedConn)
if err := setupTables(ctx, sh.sharedDB, "shared", sh.log); err != nil {
return err
}
sh.log.Debugf("rsources tables for shared db %s setup successfully", sh.config.SharedConn)
if err := withAdvisoryLock(ctx, sh.sharedDB, lockID, func(_ *sql.Tx) error {
sh.log.Debugf("setting up rsources tables for shared db %s", sh.config.SharedConn)
if err := setupTables(ctx, sh.sharedDB, "shared", sh.log); err != nil {
return err
}
sh.log.Debugf("rsources tables for shared db %s setup successfully", sh.config.SharedConn)

sh.log.Debugf("setting up rsources logical replication in %s", sh.config.LocalHostname)
if err := sh.setupLogicalReplication(ctx); err != nil {
return fmt.Errorf("failed to setup rsources logical replication in %s: %w", sh.config.LocalHostname, err)
sh.log.Debugf("setting up rsources logical replication in %s", sh.config.LocalHostname)
if err := sh.setupLogicalReplication(ctx); err != nil {
return fmt.Errorf("logical replication in %q: %w", sh.config.LocalHostname, err)
}
sh.log.Debugf("rsources logical replication setup successfully in %s", sh.config.LocalHostname)
return nil
}); err != nil {
return err
}
sh.log.Debugf("rsources logical replication setup successfully in %s", sh.config.LocalHostname)
}
return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider refactoring the init function to break down the setup of local and shared databases into separate, smaller functions for improved readability and maintainability.

if err != nil {
return fmt.Errorf("failed to start transaction: %w", err)
}
defer func() { _ = tx.Rollback() }()
Copy link
Contributor

Choose a reason for hiding this comment

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

The deferred rollback in withAdvisoryLock should be conditionally executed only if the commit has not been successful to avoid unnecessary rollbacks.

- defer func() { _ = tx.Rollback() }()
+ defer func() {
+   if r := recover(); r != nil || err != nil {
+     _ = tx.Rollback()
+   }
+ }()

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
defer func() { _ = tx.Rollback() }()
defer func() {
if r := recover(); r != nil || err != nil {
_ = tx.Rollback()
}
}()

Comment on lines 31 to 37
Out: 4,
Failed: 6,
}
BeforeAll(func() {
BeforeEach(func() {
var err error
pool, err = dockertest.NewPool("")
Expect(err).NotTo(HaveOccurred())
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: This review was outside the patches, and no patch overlapping with it was found. Original lines [40-40]

The MaxPoolSize is set to 1 in the JobServiceConfig, which might not be sufficient for concurrent test execution. Consider increasing this value to ensure that there are enough connections available in the pool for the service to function correctly, especially during parallel test runs.


Note: This review was outside the patches, and no patch overlapping with it was found. Original lines [376-376]

The MaxPoolSize is set to 1 in the JobServiceConfig for the multitenant setup, which could lead to insufficient database connections during concurrent test execution. Consider increasing this value to ensure that there are enough connections available in the pool for the service to function correctly, especially during parallel test runs.

@atzoum atzoum merged commit 42d5130 into master Nov 27, 2023
43 checks passed
@atzoum atzoum deleted the chore.rsourcesStartup branch November 27, 2023 12:34
@atzoum atzoum restored the chore.rsourcesStartup branch November 27, 2023 13:39
@atzoum atzoum deleted the chore.rsourcesStartup branch November 27, 2023 13:40
This was referenced Dec 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants