Skip to content

Add database provider abstraction layer#41

Merged
renecannao merged 1 commit intomasterfrom
issue32-db-abstraction
Mar 24, 2026
Merged

Add database provider abstraction layer#41
renecannao merged 1 commit intomasterfrom
issue32-db-abstraction

Conversation

@renecannao
Copy link
Copy Markdown

Summary

  • Introduces a DatabaseProvider interface that abstracts database-specific operations (replication status discovery, read-only control, replication start/stop) behind a common contract
  • Implements MySQLProvider as the default provider, delegating to existing DAO functions with zero behavior change
  • Adds a thread-safe provider registry with MySQL registered at init time
  • Documents the provider system and how to implement new providers

This is the architectural foundation for multi-database support — MySQL becomes the first provider, and future engines (e.g., PostgreSQL) can be added by implementing the same interface.

Closes #32

Test plan

  • go build succeeds with no new errors
  • All new provider tests pass (TestMySQLProviderName, TestDefaultProviderIsMySQL, TestMySQLProviderImplementsInterface, TestSetAndGetProvider, TestReplicationStatusDefaults)
  • Compile-time interface satisfaction check ensures MySQLProvider implements DatabaseProvider
  • Verify no regressions in existing go/inst tests

Introduce a DatabaseProvider interface that abstracts database-specific
operations (replication status, read-only control, start/stop replication)
behind a common contract. This is the architectural foundation for
multi-database support.

- Define DatabaseProvider interface and ReplicationStatus type (provider.go)
- Implement MySQLProvider that delegates to existing DAO functions (provider_mysql.go)
- Add provider registry with MySQL as default provider (provider_registry.go)
- Add unit tests for interface satisfaction and registry (provider_test.go)
- Add documentation for the provider system (docs/database-providers.md)

Closes #32
Copilot AI review requested due to automatic review settings March 24, 2026 01:07
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

Warning

Rate limit exceeded

@renecannao has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4900820e-9144-4ec5-a29b-015150173f71

📥 Commits

Reviewing files that changed from the base of the PR and between e08f4b6 and e9d41f4.

📒 Files selected for processing (5)
  • docs/database-providers.md
  • go/inst/provider.go
  • go/inst/provider_mysql.go
  • go/inst/provider_registry.go
  • go/inst/provider_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue32-db-abstraction

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request establishes a foundational database provider abstraction layer within Orchestrator. By introducing a DatabaseProvider interface and a configurable registry, it decouples core orchestration logic from specific database engine operations. This architectural enhancement paves the way for future multi-database support, starting with a fully compatible MySQL implementation, without altering current MySQL-related behaviors.

Highlights

  • Database Provider Interface: Introduced a DatabaseProvider interface to abstract database-specific operations like replication status discovery, read-only control, and replication start/stop.
  • MySQL Provider Implementation: Implemented MySQLProvider as the default provider, which delegates to existing DAO functions, ensuring no behavioral changes for MySQL.
  • Thread-Safe Provider Registry: Added a thread-safe provider registry (provider_registry.go) that manages the active DatabaseProvider instance and registers MySQLProvider at initialization.
  • Comprehensive Documentation: Provided comprehensive documentation (docs/database-providers.md) explaining the provider system, its architecture, usage, and guidelines for implementing new providers.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@renecannao renecannao merged commit 00af557 into master Mar 24, 2026
3 of 7 checks passed
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a well-designed database provider abstraction layer, decoupling core logic from database-specific operations. The changes include a new DatabaseProvider interface, a default MySQLProvider implementation, a thread-safe registry, and comprehensive documentation. The code is clean and the approach is solid. I have one suggestion to improve maintainability by reducing code duplication in the MySQLProvider implementation.

Comment thread go/inst/provider_mysql.go
Comment on lines +69 to +75
func (p *MySQLProvider) IsReplicaRunning(key InstanceKey) (bool, error) {
instance, err := ReadTopologyInstance(&key)
if err != nil {
return false, log.Errore(err)
}
return instance.ReplicaRunning(), nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The IsReplicaRunning method duplicates the logic for fetching the instance topology from GetReplicationStatus. To improve maintainability and reduce code duplication, consider implementing IsReplicaRunning by calling GetReplicationStatus and returning the ReplicaRunning field from the resulting status object. This makes it clear that IsReplicaRunning is a convenience helper for GetReplicationStatus.

Suggested change
func (p *MySQLProvider) IsReplicaRunning(key InstanceKey) (bool, error) {
instance, err := ReadTopologyInstance(&key)
if err != nil {
return false, log.Errore(err)
}
return instance.ReplicaRunning(), nil
}
func (p *MySQLProvider) IsReplicaRunning(key InstanceKey) (bool, error) {
status, err := p.GetReplicationStatus(key)
if err != nil {
return false, err
}
return status.ReplicaRunning, nil
}

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Introduces an initial database-provider abstraction in go/inst to decouple engine-specific operations (starting with MySQL) behind a common DatabaseProvider interface, enabling future multi-database support.

Changes:

  • Added DatabaseProvider interface and a database-agnostic ReplicationStatus model.
  • Implemented MySQLProvider and set it as the default via a thread-safe global provider accessor.
  • Added unit tests for provider wiring and documentation describing how to add new providers.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
go/inst/provider.go Defines DatabaseProvider interface and ReplicationStatus model.
go/inst/provider_mysql.go Implements the MySQL provider by delegating to existing instance/DAO operations.
go/inst/provider_registry.go Adds global provider storage with RWMutex and MySQL default initialization.
go/inst/provider_test.go Adds tests for default provider and basic provider behaviors.
docs/database-providers.md Documents the provider abstraction and how to implement/register providers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// manages. The default provider is MySQL.
func SetProvider(p DatabaseProvider) {
providerMu.Lock()
defer providerMu.Unlock()
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

SetProvider currently allows setting a nil provider, which would make GetProvider() return nil and can lead to panics at call sites (e.g., GetProvider().ProviderName()). Consider rejecting nil (return an error or panic with a clear message) or keeping the existing provider when p is nil, so the global provider is always non-nil after init.

Suggested change
defer providerMu.Unlock()
defer providerMu.Unlock()
if p == nil {
// Ignore attempts to set a nil provider to avoid panics from GetProvider().
// The existing provider (including the default set in init) is preserved.
return
}

Copilot uses AI. Check for mistakes.
Comment thread go/inst/provider_mysql.go

position := instance.ExecutedGtidSet
if position == "" {
position = instance.SelfBinlogCoordinates.DisplayString()
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

If the instance has neither ExecutedGtidSet nor a valid binlog coordinate (SelfBinlogCoordinates.LogFile is empty), DisplayString() will produce the sentinel ":0", which looks like a real position but is not meaningful. Consider returning an empty Position (or another explicit "unknown" sentinel) when coordinates are empty, to avoid consumers misinterpreting ":0" as a valid replication position.

Suggested change
position = instance.SelfBinlogCoordinates.DisplayString()
// Only derive a position from binlog coordinates if they are valid.
// When LogFile is empty, DisplayString() may return a sentinel like ":0",
// which looks like a real position but is not meaningful. In that case,
// leave position empty to clearly indicate that it is unknown.
if instance.SelfBinlogCoordinates.LogFile != "" {
position = instance.SelfBinlogCoordinates.DisplayString()
}

Copilot uses AI. Check for mistakes.
Comment thread go/inst/provider_mysql.go
Comment on lines +70 to +74
instance, err := ReadTopologyInstance(&key)
if err != nil {
return false, log.Errore(err)
}
return instance.ReplicaRunning(), nil
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

IsReplicaRunning duplicates the same ReadTopologyInstance call already done by GetReplicationStatus. To reduce duplication and keep semantics consistent if the logic changes, consider implementing IsReplicaRunning by calling GetReplicationStatus and returning status.ReplicaRunning.

Suggested change
instance, err := ReadTopologyInstance(&key)
if err != nil {
return false, log.Errore(err)
}
return instance.ReplicaRunning(), nil
status, err := p.GetReplicationStatus(key)
if err != nil {
return false, err
}
return status.ReplicaRunning, nil

Copilot uses AI. Check for mistakes.
Comment thread go/inst/provider_test.go
Comment on lines +63 to +65
if status.Lag != 0 {
t.Error("expected Lag to be 0 by default")
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

This test asserts Go zero-value behavior (Lag == 0) but the documented contract for ReplicationStatus says "Lag of -1 means unknown" (also repeated in docs/database-providers.md). This makes the test misleading and likely to become a blocker if you later introduce a constructor/default that sets Lag to -1. Consider removing this test, or changing it to validate the documented semantics instead of zero-values.

Suggested change
if status.Lag != 0 {
t.Error("expected Lag to be 0 by default")
}

Copilot uses AI. Check for mistakes.
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.

Phase 3.3: Database abstraction layer (multi-database support)

2 participants