Skip to content

Multi-cluster support: MySQL and PostgreSQL simultaneously#75

Merged
renecannao merged 1 commit intomasterfrom
issue73-multi-cluster
Apr 8, 2026
Merged

Multi-cluster support: MySQL and PostgreSQL simultaneously#75
renecannao merged 1 commit intomasterfrom
issue73-multi-cluster

Conversation

@renecannao
Copy link
Copy Markdown

@renecannao renecannao commented Apr 8, 2026

Summary

  • Adds per-instance provider_type column to database_instance table, replacing the global config.Config.ProviderType as the sole routing mechanism for discovery, analysis, and recovery
  • Auto-detects database type via DB lookup, port heuristic (5432 = PostgreSQL), or falls back to global config for full backward compatibility
  • Merges MySQL and PostgreSQL replication analysis results when both provider types coexist in the backend

Details

Schema: New provider_type varchar(20) NOT NULL DEFAULT 'mysql' column added via migration patch.

Discovery: detectProviderType() checks the backend DB first, then uses port-based heuristic, then falls back to config.Config.ProviderType. This means existing MySQL-only and PG-only deployments work without config changes.

Analysis: GetReplicationAnalysis() now runs both MySQL and PostgreSQL analysis when instances of both types exist, merging results. When only one type is present, it runs the appropriate single analysis path.

Recovery: Already routes per-instance via AnalysisCode (e.g., DeadPrimary for PG, DeadMaster for MySQL) -- no changes needed.

Test plan

  • All existing unit tests pass (go test ./go/... -vet=off)
  • Build succeeds (go build -o /dev/null ./go/cmd/orchestrator)
  • New tests verify provider_type persistence and defaults
  • Manual: Verify MySQL-only deployment works with no config changes
  • Manual: Verify PG-only deployment (ProviderType="postgresql") still works
  • Manual: Verify mixed deployment discovers both MySQL and PG instances

Closes #73

Summary by CodeRabbit

  • New Features
    • Added full PostgreSQL database support alongside existing MySQL capabilities
    • Instances now track and persist their database engine provider type
    • Automatic provider type detection via connection port heuristics or stored metadata
    • Replication analysis now handles mixed MySQL and PostgreSQL clusters seamlessly

Store provider_type per instance in the database_instance table instead
of relying solely on the global config.Config.ProviderType setting.
This allows a single orchestrator instance to discover, analyze, and
recover both MySQL and PostgreSQL topologies at the same time.

Changes:
- Add provider_type column to database_instance (default 'mysql')
- Add ProviderType field to Instance struct
- Add detectProviderType() that checks DB, then port heuristic (5432=PG),
  then falls back to global config for backward compatibility
- Update ReadTopologyInstance/ReadTopologyInstanceBufferable to use
  per-instance detection instead of global config
- Update ReadInstanceClusterAttributes to check instance ProviderType
- Store and read provider_type in mkInsertOdkuForInstances/readInstanceRow
- Set ProviderType="postgresql" during PG discovery
- Merge MySQL and PG analysis results in GetReplicationAnalysis when
  both provider types are present in the backend DB
- Update tests for new provider_type column in INSERT/UPDATE SQL

Closes #73
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ca79e29c-8f54-4a61-927b-a4d0fc112dd7

📥 Commits

Reviewing files that changed from the base of the PR and between 97eb0db and e4d5b2a.

📒 Files selected for processing (6)
  • go/db/generate_patches.go
  • go/inst/analysis_dao.go
  • go/inst/instance.go
  • go/inst/instance_dao.go
  • go/inst/instance_dao_postgresql.go
  • go/inst/instance_dao_test.go

📝 Walkthrough

Walkthrough

These changes implement per-instance provider type detection and storage, moving away from a global ProviderType configuration. A new provider_type column is added to the database_instance table, provider type is detected and stored per-instance, and replication analysis is updated to conditionally merge results from both MySQL and PostgreSQL providers based on stored provider types.

Changes

Cohort / File(s) Summary
Schema & Database Setup
go/db/generate_patches.go
Added SQL migration to introduce provider_type column (varchar(20), default 'mysql') to database_instance table after replication_group_primary_port.
Instance Provider Detection & Management
go/inst/instance.go, go/inst/instance_dao.go, go/inst/instance_dao_postgresql.go
Added ProviderType field to Instance struct; implemented detectProviderType() function using database lookup, port heuristics (5432→postgresql), and config fallback; updated instance read/write paths to handle provider_type column; set provider type during PostgreSQL instance discovery.
Multi-Provider Replication Analysis
go/inst/analysis_dao.go
Added hasPostgreSQLInstances() and hasMySQLInstances() helper functions; modified GetReplicationAnalysis() to conditionally merge PostgreSQL results alongside MySQL analysis based on cluster provider composition.
Testing & Validation
go/inst/instance_dao_test.go
Updated fixture tests to validate provider_type column inclusion in insert/update statements; added three new test functions validating ProviderType initialization, persistence, and "mysql" defaulting behavior.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant AnalysisRouter as GetReplicationAnalysis
    participant MySQLCheck as hasMySQLInstances
    participant PostgreSQLCheck as hasPostgreSQLInstances
    participant MySQLAnalysis as GetMySQLReplicationAnalysis
    participant PostgreSQLAnalysis as GetPostgreSQLReplicationAnalysis
    participant Database as Database

    Client->>AnalysisRouter: GetReplicationAnalysis(clusterName)
    
    alt PostgreSQL-only Mode
        AnalysisRouter->>PostgreSQLCheck: hasPostgreSQLInstances()
        PostgreSQLCheck->>Database: Query provider_type='postgresql'
        PostgreSQLCheck-->>AnalysisRouter: true
        AnalysisRouter->>PostgreSQLAnalysis: Analyze cluster
        PostgreSQLAnalysis-->>AnalysisRouter: Results
        AnalysisRouter-->>Client: PostgreSQL results
    else Mixed or MySQL Mode
        AnalysisRouter->>MySQLAnalysis: Analyze cluster
        MySQLAnalysis->>Database: Query MySQL instances
        MySQLAnalysis-->>AnalysisRouter: MySQL results
        
        AnalysisRouter->>PostgreSQLCheck: hasPostgreSQLInstances()
        PostgreSQLCheck->>Database: Query provider_type='postgresql'
        PostgreSQLCheck-->>AnalysisRouter: true/false
        
        alt PostgreSQL instances exist
            AnalysisRouter->>PostgreSQLAnalysis: Analyze cluster
            PostgreSQLAnalysis-->>AnalysisRouter: PostgreSQL results
            AnalysisRouter->>AnalysisRouter: Merge results
        end
        
        AnalysisRouter-->>Client: Merged MySQL + PostgreSQL results
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A patch for each provider, with types now stored,
MySQL and PostgreSQL, no longer at odds!
Detection and routing, per-instance proud,
The rabbit's burrow now holds both kinds loud! 🌿✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue73-multi-cluster

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.

@renecannao renecannao merged commit ee065a2 into master Apr 8, 2026
4 of 6 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 support for managing both MySQL and PostgreSQL instances by adding a "provider_type" field to the database schema and instance metadata. It implements a detection mechanism using port heuristics and database lookups, updates replication analysis to aggregate results from both providers, and includes corresponding test updates. Review feedback suggests optimizing performance by caching provider type detection results to reduce database load and removing redundant lookups in the discovery workflow where the provider type is already known.

Comment thread go/inst/instance_dao.go
Comment on lines 60 to 61
var instanceReadChan = make(chan bool, backendDBConcurrency)
var instanceWriteChan = make(chan bool, backendDBConcurrency)
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

To avoid frequent and redundant database queries to the orchestrator backend during the discovery process, consider introducing a cache for the instance provider type. This is especially important as detectProviderType is called multiple times per discovery cycle for each instance.

Suggested change
var instanceReadChan = make(chan bool, backendDBConcurrency)
var instanceWriteChan = make(chan bool, backendDBConcurrency)
var instanceReadChan = make(chan bool, backendDBConcurrency)
var instanceWriteChan = make(chan bool, backendDBConcurrency)
var providerTypeCache = cache.New(time.Hour, time.Hour)

Comment thread go/inst/instance_dao.go
Comment on lines +68 to +87
func detectProviderType(instanceKey *InstanceKey) string {
// Check if we already know this instance's provider type from the backend DB
query := `SELECT provider_type FROM database_instance WHERE hostname = ? AND port = ?`
var providerType string
err := db.QueryOrchestrator(query, sqlutils.Args(instanceKey.Hostname, instanceKey.Port), func(m sqlutils.RowMap) error {
providerType = m.GetString("provider_type")
return nil
})
if err == nil && providerType != "" {
return providerType
}

// Port-based heuristic for new instances
if instanceKey.Port == 5432 {
return "postgresql"
}

// Fall back to global config (backward compatibility)
return config.Config.ProviderType
}
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 detectProviderType function performs a synchronous database query every time it's called. Implementing caching here will significantly reduce the load on the orchestrator backend database during discovery. Additionally, the logic can be simplified to consolidate the return paths.

func detectProviderType(instanceKey *InstanceKey) string {
	cacheKey := instanceKey.StringCode()
	if val, found := providerTypeCache.Get(cacheKey); found {
		return val.(string)
	}

	var providerType string
	// Check if we already know this instance's provider type from the backend DB
	query := `SELECT provider_type FROM database_instance WHERE hostname = ? AND port = ?`
	err := db.QueryOrchestrator(query, sqlutils.Args(instanceKey.Hostname, instanceKey.Port), func(m sqlutils.RowMap) error {
		providerType = m.GetString("provider_type")
		return nil
	})

	if err == nil && providerType != "" {
		// Found in DB
	} else if instanceKey.Port == 5432 {
		providerType = "postgresql"
	} else {
		providerType = config.Config.ProviderType
	}

	providerTypeCache.Set(cacheKey, providerType, cache.DefaultExpiration)
	return providerType
}

Comment thread go/inst/instance_dao.go
// PostgreSQL provider sets ClusterName directly during discovery.
// Skip the master-lookup logic which is MySQL-specific.
if config.Config.ProviderType == "postgresql" {
if instance.ProviderType == "postgresql" || detectProviderType(&instance.Key) == "postgresql" {
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

Calling detectProviderType here is redundant if instance.ProviderType is already populated (which it should be after discovery or loading from the DB). Even with caching, it's better to avoid the extra function call and map lookup. You can also use this opportunity to ensure instance.ProviderType is initialized if it happens to be empty.

	if instance.ProviderType == "" {
		instance.ProviderType = detectProviderType(&instance.Key)
	}
	if instance.ProviderType == "postgresql" {

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.

Multi-cluster support: manage MySQL and PostgreSQL simultaneously

1 participant