Skip to content

Named channels phases 4-6: API, failover, docs & tests#79

Merged
renecannao merged 3 commits into
masterfrom
issue77-named-channels-phase2
Apr 8, 2026
Merged

Named channels phases 4-6: API, failover, docs & tests#79
renecannao merged 3 commits into
masterfrom
issue77-named-channels-phase2

Conversation

@renecannao

Copy link
Copy Markdown

Summary

Implements phases 4-6 of MySQL named replication channels support (issue #77), building on phases 1-3 merged in PR #78.

  • Phase 4 -- API & Visualization: Add GET /api/instance-channels/{host}/{port} and GET /api/v2/instances/{host}/{port}/channels endpoints to expose per-instance replication channel data. The existing /api/instance/{host}/{port} already includes ReplicationChannels in its JSON response.

  • Phase 5 -- Channel-Aware Failover: GracefulMasterTakeover now uses ChangeMasterToForChannel and StartReplicationForChannel with the managed channel name, preserving non-managed channels on multi-source replicas. The recoverDeadMaster path was already channel-aware via the Phase 1-3 plumbing (StopReplication/ChangeMasterTo delegate to their ForChannel variants).

  • Phase 6 -- Documentation & Tests: Add docs/named-channels.md covering discovery, failover, API endpoints, and limitations. Add go/inst/channel_test.go with 15 tests covering canonical channel selection, GR channel handling, SQL generation with FOR CHANNEL clauses, and backward compatibility for single-source instances.

Test plan

  • go build -o /dev/null ./go/cmd/orchestrator passes
  • go test ./go/... -vet=off passes (all packages)
  • New channel tests pass: go test ./go/inst/... -run TestChannel -v
  • Manual: verify /api/instance-channels/{host}/{port} returns channel data for a multi-source replica
  • Manual: verify graceful master takeover preserves non-managed channels

Phase 4 of named channels support (#77):
- Add GET /api/instance-channels/{host}/{port} endpoint
- Add GET /api/v2/instances/{host}/{port}/channels endpoint
- ReplicationChannels field already serializes in instance JSON
Phase 5 of named channels support (#77):
- GracefulMasterTakeover uses ChangeMasterToForChannel and
  StartReplicationForChannel with the managed channel name
- Preserves non-managed channels on multi-source replicas
- recoverDeadMaster already channel-aware via Phase 1-3 changes
  (StopReplication/ChangeMasterTo delegate to ForChannel variants)
- Candidate selection already valid for multi-source replicas
Phase 6 of named channels support (#77):
- Add docs/named-channels.md covering discovery, failover, API,
  and limitations of multi-source replication support
- Add go/inst/channel_test.go with tests for:
  - selectCanonicalChannelIndex with various channel combinations
  - Single-source behavior unchanged (backward compatibility)
  - Channel-aware SQL generation (FOR CHANNEL clause)
  - GR internal channel detection
  - MySQL 5.7 and 8.4+ syntax variants
@coderabbitai

coderabbitai Bot commented Apr 8, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

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

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 15 minutes and 51 seconds.

⌛ 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: 14029fc3-3553-4d35-bc7f-e644e816196a

📥 Commits

Reviewing files that changed from the base of the PR and between 56293ce and e08fb45.

📒 Files selected for processing (5)
  • docs/named-channels.md
  • go/http/api.go
  • go/http/apiv2.go
  • go/inst/channel_test.go
  • go/logic/topology_recovery.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue77-named-channels-phase2

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 6cd86fa into master Apr 8, 2026
3 of 6 checks passed

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

Copy link
Copy Markdown

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 MySQL multi-source replication via named channels, including discovery, management, and channel-aware failover operations. New API endpoints (V1 and V2) are added to retrieve channel status, and comprehensive documentation and tests are provided. Feedback includes addressing a potential nil pointer dereference during graceful master takeover and ensuring that API responses return an empty array instead of null when no replication channels are present to maintain a consistent API contract.

Comment on lines +2232 to +2233
managedChannel := clusterMaster.ManagedChannelName
clusterMaster, err = inst.ChangeMasterToForChannel(&clusterMaster.Key, &designatedInstance.Key, promotedMasterCoordinates, false, gtidHint, managedChannel)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The assignment to clusterMaster at line 2233 can potentially result in a nil pointer if inst.ChangeMasterToForChannel fails to read the instance after the operation. This would cause a panic on the subsequent line when accessing clusterMaster.SelfBinlogCoordinates. It is safer to use a temporary variable and only update clusterMaster if the result is non-nil, ensuring the pointer remains valid for the sanity check and subsequent replication operations.

	managedChannel := clusterMaster.ManagedChannelName
	if updatedMaster, changeErr := inst.ChangeMasterToForChannel(&clusterMaster.Key, &designatedInstance.Key, promotedMasterCoordinates, false, gtidHint, managedChannel); updatedMaster != nil {
		clusterMaster = updatedMaster
		err = changeErr
	} else {
		err = changeErr
	}

Comment thread go/http/api.go
Respond(w, &APIResponse{Code: ERROR, Message: fmt.Sprintf("Cannot read instance: %+v", instanceKey)})
return
}
renderJSON(w, http.StatusOK, instance.ReplicationChannels)

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

If the instance has no replication channels, instance.ReplicationChannels may be nil, causing the API to return null instead of an empty array []. For consistency with other collection-returning endpoints in the V1 API, it is better to ensure an empty slice is returned.

	channels := instance.ReplicationChannels
	if channels == nil {
		channels = []inst.ChannelStatus{}
	}
	renderJSON(w, http.StatusOK, channels)

Comment thread go/http/apiv2.go
respondNotFound(w, fmt.Sprintf("Instance not found: %s:%s", host, port))
return
}
respondOK(w, instance.ReplicationChannels)

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

Similar to the V1 API, instance.ReplicationChannels might be nil if no channels exist, resulting in a null value in the JSON response. Initializing it to an empty slice ensures a more predictable API contract for clients expecting an array.

Suggested change
respondOK(w, instance.ReplicationChannels)
channels := instance.ReplicationChannels
if channels == nil {
channels = []inst.ChannelStatus{}
}
respondOK(w, channels)

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.

1 participant