Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

Add basic support for group replication #1180

Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cf2b55e
Initial support for group replication.
ejortegau Jun 1, 2020
d98af1b
Initial support for group replication.
ejortegau May 31, 2020
bd4f4db
Initial support for group replication.
ejortegau May 31, 2020
03264a5
Initial support for group replication.
ejortegau May 31, 2020
30f5b1a
Initial support for group replication.
ejortegau May 31, 2020
b1c57f4
Initial support for group replication.
ejortegau May 31, 2020
dd2716a
Initial support for group replication.
ejortegau May 31, 2020
62e554f
Initial support for group replication.
ejortegau Jun 1, 2020
a44205d
Initial support for group replication.
ejortegau Jun 1, 2020
87fba96
Initial support for group replication.
ejortegau Jun 1, 2020
cf67e24
Initial support for group replication.
ejortegau Jun 2, 2020
04b759f
Initial support for Group Replication
ejortegau Jun 2, 2020
51e6bc0
Initial support for Group Replication
ejortegau Jun 2, 2020
10e3ab2
Initial support for Group Replication
ejortegau Jun 2, 2020
cd5c79f
Initial support for Group Replication
ejortegau Jun 3, 2020
f63f7a6
Initial support for Group Replication - Addressing PR comments
ejortegau Jun 12, 2020
105cdc6
Initial support for Group Replication - Addressing PR comments
ejortegau Jun 12, 2020
bd6be2f
Initial support for Group Replication - Addressing PR comments
ejortegau Jun 12, 2020
2af7934
Initial support for Group Replication
ejortegau Jun 12, 2020
03b088a
Merge branch 'master' into ejortegau/add_basic_support_for_group_repl…
shlomi-noach Jun 24, 2020
6d643ec
Merge branch 'master' into ejortegau/add_basic_support_for_group_repl…
shlomi-noach Jul 22, 2020
c31d71a
Merge branch 'master' into ejortegau/add_basic_support_for_group_repl…
eurrego-at-booking Jul 23, 2020
2d4817f
Add info on grants needed for GR support
eurrego-at-booking Jul 23, 2020
9d582de
formatting
shlomi-noach Jul 26, 2020
80274ac
formatting
shlomi-noach Jul 26, 2020
ea180cf
simplify nesting
shlomi-noach Jul 26, 2020
950e500
formatting
shlomi-noach Jul 26, 2020
7fcf1c9
formatting
shlomi-noach Jul 26, 2020
3188102
formatting
shlomi-noach Jul 26, 2020
7053a44
sql syntax
shlomi-noach Jul 26, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/configuration-discovery-basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ CREATE USER 'orchestrator'@'orc_host' IDENTIFIED BY 'orc_topology_password';
GRANT SUPER, PROCESS, REPLICATION SLAVE, REPLICATION CLIENT, RELOAD ON *.* TO 'orchestrator'@'orc_host';
GRANT SELECT ON meta.* TO 'orchestrator'@'orc_host';
GRANT SELECT ON ndbinfo.processes TO 'orchestrator'@'orc_host'; -- Only for NDB Cluster
GRANT SELECT ON performance_schema.replication_group_members TO 'orchestrator'@'orc_host'; -- Only for Group Replication / InnoDB cluster
```
16 changes: 15 additions & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,21 @@ No.

### Does orchestrator support MySQL Group Replication?

No. Group replication (as of MySQL 5.7.17) is not supported.
Partially. Replication groups in single primary mode are somewhat supported under MySQL 8.0. The extent of the support so far is:

* Orchestrator understands that all group members are part of the same cluster, retrieves replication group information
as part of instance discovery, stores it in its database, and exposes it via the API.
* The orchestrator web UI displays single primary group members. They are shown like this:
* All group secondary members as replicating from the primary.
* All group members have an icon that shows they are group members (as opposed to traditional async/semi-sync replicas).
* Hovering over the icon mentioned above provides information about the state and role of the DB instance in the
group.
* Some relocation operations are forbidden for group members. In particular, orchestrator will refuse to relocate a secondary group member, as it, by definition, replicates always from the group primary. It will also reject an attempt to relocate a group primary under a secondary of the same group.

No support has been added (yet) to handling group member failure. If all you have is a single replication group, this is fine, because you don't need it; the group will handle all failures as long as it can secure a majority.

If, however, you have the primary of a group as a replica to another instance; or you have replicas under your group
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

members, know that this has not been tested and results are, therefore, unpredictable at the moment. It *might* work, but it might also create a singularity and suck your database under the event horizon.

### Does orchestrator support Yet Another Type of Replication?

Expand Down
36 changes: 36 additions & 0 deletions go/db/generate_patches.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,5 +580,41 @@ var generateSQLPatches = []string{
ALTER TABLE /* sqlite3-skip */
database_instance
MODIFY semi_sync_master_timeout BIGINT UNSIGNED NOT NULL DEFAULT 0
`,
// Fields related to Replication Group the instance belongs to
`
ALTER TABLE
database_instance
ADD COLUMN replication_group_name VARCHAR(64) CHARACTER SET ascii NOT NULL DEFAULT '' AFTER gtid_mode
ejortegau marked this conversation as resolved.
Show resolved Hide resolved
`,
`
ALTER TABLE
database_instance
ADD COLUMN replication_group_is_single_primary_mode TINYINT UNSIGNED NOT NULL DEFAULT 1 AFTER replication_group_name
`,
`
ALTER TABLE
database_instance
ADD COLUMN replication_group_member_state VARCHAR(16) CHARACTER SET ascii NOT NULL DEFAULT '' AFTER replication_group_is_single_primary_mode
`,
`
ALTER TABLE
database_instance
ADD COLUMN replication_group_member_role VARCHAR(16) CHARACTER SET ascii NOT NULL DEFAULT '' AFTER replication_group_member_state
`,
`
ALTER TABLE
database_instance
ADD COLUMN replication_group_members text CHARACTER SET ascii NOT NULL AFTER replication_group_member_role
`,
`
ALTER TABLE
database_instance
ADD COLUMN replication_group_primary_host varchar(128) CHARACTER SET ascii NOT NULL DEFAULT '' AFTER replication_group_members
`,
`
ALTER TABLE
database_instance
ADD COLUMN replication_group_primary_port smallint(5) unsigned NOT NULL DEFAULT 0 AFTER replication_group_primary_host
`,
}
14 changes: 11 additions & 3 deletions go/inst/analysis_dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,19 @@ func GetReplicationAnalysis(clusterName string, hints *ReplicationAnalysisHints)
master_instance.last_checked <= master_instance.last_seen
and master_instance.last_attempted_check <= master_instance.last_seen + interval ? second
) = 1 AS is_last_check_valid,
/* To be considered a master, traditional async replication must not be present/valid AND the host should either */
/* not be a replication group member OR be the primary of the replication group */
MIN(master_instance.last_check_partial_success) as last_check_partial_success,
MIN(
master_instance.master_host IN ('', '_')
OR master_instance.master_port = 0
OR substr(master_instance.master_host, 1, 2) = '//'
(
master_instance.master_host IN ('', '_')
OR master_instance.master_port = 0
OR substr(master_instance.master_host, 1, 2) = '//'
)
AND (
master_instance.replication_group_name = ''
OR master_instance.replication_group_member_role = 'PRIMARY'
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

) AS is_master,
MIN(master_instance.is_co_master) AS is_co_master,
MIN(
Expand Down
65 changes: 61 additions & 4 deletions go/inst/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,32 @@ type Instance struct {
LastDiscoveryLatency time.Duration

seed bool // Means we force this instance to be written to backend, even if it's invalid, empty or forgotten

/* All things Group Replication below */

// Group replication global variables
ReplicationGroupName string
ReplicationGroupIsSinglePrimary bool

// Replication group members information. See
// https://dev.mysql.com/doc/refman/8.0/en/replication-group-members-table.html for details.
ReplicationGroupMemberState string
ReplicationGroupMemberRole string

// List of all known members of the same group
ReplicationGroupMembers InstanceKeyMap

// Primary of the replication group
ReplicationGroupPrimaryInstanceKey InstanceKey
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

}

// NewInstance creates a new, empty instance

func NewInstance() *Instance {
return &Instance{
Replicas: make(map[InstanceKey]bool),
Problems: []string{},
Replicas: make(map[InstanceKey]bool),
ReplicationGroupMembers: make(map[InstanceKey]bool),
Problems: []string{},
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

}
}

Expand Down Expand Up @@ -228,6 +247,21 @@ func (this *Instance) IsNDB() bool {
return strings.Contains(this.Version, "-ndb-")
}

// IsReplicationGroup checks whether the host thinks it is part of a known replication group. Notice that this might
// return True even if the group has decided to expel the member represented by this instance, as the instance might not
// know that under certain circumstances
func (this *Instance) IsReplicationGroupMember() bool {
return this.ReplicationGroupName != ""
}

func (this *Instance) IsReplicationGroupPrimary() bool {
return this.IsReplicationGroupMember() && this.ReplicationGroupPrimaryInstanceKey.Equals(&this.Key)
}

func (this *Instance) IsReplicationGroupSecondary() bool {
return this.IsReplicationGroupMember() && !this.ReplicationGroupPrimaryInstanceKey.Equals(&this.Key)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍


// IsBinlogServer checks whether this is any type of a binlog server (currently only maxscale)
func (this *Instance) IsBinlogServer() bool {
if this.isMaxScale() {
Expand Down Expand Up @@ -293,9 +327,27 @@ func (this *Instance) IsReplica() bool {
return this.MasterKey.Hostname != "" && this.MasterKey.Hostname != "_" && this.MasterKey.Port != 0 && (this.ReadBinlogCoordinates.LogFile != "" || this.UsingGTID())
}

// IsMaster makes simple heuristics to decide whether this instance is a master (not replicating from any other server)
// IsMaster makes simple heuristics to decide whether this instance is a master (not replicating from any other server),
// either via traditional async/semisync replication or group replication
func (this *Instance) IsMaster() bool {
return !this.IsReplica()
// If traditional replication is configured, it is for sure not a master
if this.IsReplica() {
return false
} else {
// If traditional replication is not configured, and it is also not part of a replication group, this host is
// a master
if !this.IsReplicationGroupMember() {
return true
} else {
// If traditional replication is not configured, and this host is part of a group, it is only considered a
// master if it has the role of group Primary. Otherwise it is not a master.
if this.ReplicationGroupMemberRole == GroupReplicationMemberRolePrimary {
return true
} else {
return false
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm replacing this code:

func (this *Instance) IsMaster() bool {
	// If traditional replication is configured, it is for sure not a master
	if this.IsReplica() {
		return false
	} else {
		// If traditional replication is not configured, and it is also not part of a replication group, this host is
		// a master
		if !this.IsReplicationGroupMember() {
			return true
		} else {
			// If traditional replication is not configured, and this host is part of a group, it is only considered a
			// master if it has the role of group Primary. Otherwise it is not a master.
			if this.ReplicationGroupMemberRole == GroupReplicationMemberRolePrimary {
				return true
			} else {
				return false
			}
		}
	}
}

with simplified nesting:

func (this *Instance) IsMaster() bool {
	// If traditional replication is configured, it is for sure not a master
	if this.IsReplica() {
		return false
	}
	// If traditional replication is not configured, and it is also not part of a replication group, this host is
	// a master
	if !this.IsReplicationGroupMember() {
		return true
	}
	// If traditional replication is not configured, and this host is part of a group, it is only considered a
	// master if it has the role of group Primary. Otherwise it is not a master.
	if this.ReplicationGroupMemberRole == GroupReplicationMemberRolePrimary {
		return true
	}
	return false
}

}

// ReplicaRunning returns true when this instance's status is of a replicating replica.
Expand Down Expand Up @@ -358,6 +410,11 @@ func (this *Instance) AddReplicaKey(replicaKey *InstanceKey) {
this.Replicas.AddKey(*replicaKey)
}

// AddGroupMemberKey adds a group member to the list of this instance's group members.
func (this *Instance) AddGroupMemberKey(groupMemberKey *InstanceKey) {
this.ReplicationGroupMembers.AddKey(*groupMemberKey)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍


// GetNextBinaryLog returns the successive, if any, binary log file to the one given
func (this *Instance) GetNextBinaryLog(binlogCoordinates BinlogCoordinates) (BinlogCoordinates, error) {
if binlogCoordinates.LogFile == this.SelfBinlogCoordinates.LogFile {
Expand Down
Loading