Skip to content

Commit 023af8a

Browse files
Merge pull request #30 from kamil-holubicki/DISTMYSQL-396
DISTMYSQL-396: Fix for DISTMYSQL-243 can lead to Orchestrator hitting…
2 parents f13d2ba + d7183d1 commit 023af8a

File tree

2 files changed

+87
-50
lines changed

2 files changed

+87
-50
lines changed

go/inst/cluster_alias_dao.go

Lines changed: 86 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -114,65 +114,102 @@ func writeClusterAliasManualOverride(clusterName string, alias string) error {
114114
return ExecDBWriteFunc(writeFunc)
115115
}
116116

117+
// Original, safe approach, which uses REPLACE INTO
118+
func updateClusterAliasesUsingReplace() error {
119+
_, err := db.ExecOrchestrator(`
120+
replace into
121+
cluster_alias (alias, cluster_name, last_registered)
122+
select
123+
suggested_cluster_alias,
124+
cluster_name,
125+
now()
126+
from
127+
database_instance
128+
left join database_instance_downtime using (hostname, port)
129+
where
130+
suggested_cluster_alias!=''
131+
/* exclude newly demoted, downtimed masters */
132+
and ifnull(
133+
database_instance_downtime.downtime_active = 1
134+
and database_instance_downtime.end_timestamp > now()
135+
and database_instance_downtime.reason = ?
136+
, 0) = 0
137+
order by
138+
ifnull(last_checked <= last_seen, 0) asc,
139+
read_only desc,
140+
num_slave_hosts asc
141+
`, DowntimeLostInRecoveryMessage)
142+
143+
return err
144+
}
145+
146+
// Optimized approach using INSERT INTO ... ON DUPLICATE KEY UPDATE
147+
// While this approach is much faster and works in most cases, it is not
148+
// guaranteed to be working in every case.
149+
// cluster_alias table has two unique indexes:
150+
// 1. primary on cluster_name column
151+
// 2. alias_uidx on alias colum
152+
//
153+
// The data which is going to be inserted originates from database_instance
154+
// table, in particular the following columns:
155+
// 1. `cluster_name` varchar(128) NOT NULL
156+
// 2. `suggested_cluster_alias` varchar(128) CHARACTER SET ascii COLLATE
157+
// ascii_general_ci NOT NULL
158+
//
159+
// So it is possible to end up in the following situation when we use this
160+
// approach:
161+
// create table t1 (a int primary key, b int, unique key (b));
162+
// insert into t1 values (0, 1);
163+
// insert into t1 values (1, 2);
164+
// insert into t1 values (0, 2) on duplicate key update a=0, b=2;
165+
// ERROR 1062 (23000): Duplicate entry '2' for key 't1.b'
166+
func updateClusterAliasesUsingInsert() error {
167+
_, err := db.ExecOrchestrator(`
168+
INSERT INTO cluster_alias
169+
(
170+
alias,
171+
cluster_name,
172+
last_registered
173+
)
174+
SELECT di.suggested_cluster_alias,
175+
di.cluster_name,
176+
now()
177+
FROM database_instance di
178+
LEFT JOIN database_instance_downtime did
179+
USING (hostname, port)
180+
WHERE di.suggested_cluster_alias != ''
181+
/* exclude newly demoted, downtimed masters */
182+
AND Ifnull(did.downtime_active = 1
183+
AND did.end_timestamp > Now()
184+
AND did.reason = ?, 0) = 0
185+
ORDER BY Ifnull(di.last_checked <= di.last_seen, 0) ASC,
186+
di.read_only DESC,
187+
di.num_slave_hosts ASC
188+
ON DUPLICATE KEY
189+
UPDATE alias = di.suggested_cluster_alias,
190+
cluster_name = di.cluster_name,
191+
last_registered = now()
192+
`, DowntimeLostInRecoveryMessage)
193+
194+
return err
195+
}
196+
117197
// UpdateClusterAliases writes down the cluster_alias table based on information
118198
// gained from database_instance
119199
func UpdateClusterAliases() error {
120200
writeFunc := func() error {
121201
var err error
122202
if IsSQLite() {
123203
// Sql lite backend
124-
_, err = db.ExecOrchestrator(`
125-
replace into
126-
cluster_alias (alias, cluster_name, last_registered)
127-
select
128-
suggested_cluster_alias,
129-
cluster_name,
130-
now()
131-
from
132-
database_instance
133-
left join database_instance_downtime using (hostname, port)
134-
where
135-
suggested_cluster_alias!=''
136-
/* exclude newly demoted, downtimed masters */
137-
and ifnull(
138-
database_instance_downtime.downtime_active = 1
139-
and database_instance_downtime.end_timestamp > now()
140-
and database_instance_downtime.reason = ?
141-
, 0) = 0
142-
order by
143-
ifnull(last_checked <= last_seen, 0) asc,
144-
read_only desc,
145-
num_slave_hosts asc
146-
`, DowntimeLostInRecoveryMessage)
204+
err = updateClusterAliasesUsingReplace()
147205
} else {
148206
// MySQL backend (Orchestrator supports only SQLite and MySQL backends)
149207
// INSERT ON DUPLICATE KEY UPDATE is more performant than REPLACE in MySQL
150-
_, err = db.ExecOrchestrator(`
151-
INSERT INTO cluster_alias
152-
(
153-
alias,
154-
cluster_name,
155-
last_registered
156-
)
157-
SELECT di.suggested_cluster_alias,
158-
di.cluster_name,
159-
now()
160-
FROM database_instance di
161-
LEFT JOIN database_instance_downtime did
162-
USING (hostname, port)
163-
WHERE di.suggested_cluster_alias != ''
164-
/* exclude newly demoted, downtimed masters */
165-
AND Ifnull(did.downtime_active = 1
166-
AND did.end_timestamp > Now()
167-
AND did.reason = ?, 0) = 0
168-
ORDER BY Ifnull(di.last_checked <= di.last_seen, 0) ASC,
169-
di.read_only DESC,
170-
di.num_slave_hosts ASC
171-
ON DUPLICATE KEY
172-
UPDATE alias = di.suggested_cluster_alias,
173-
cluster_name = di.cluster_name,
174-
last_registered = now()
175-
`, DowntimeLostInRecoveryMessage)
208+
err = updateClusterAliasesUsingInsert()
209+
if (err != nil) {
210+
// Fallback to the original, safe implementation
211+
err = updateClusterAliasesUsingReplace()
212+
}
176213
}
177214
return log.Errore(err)
178215
}

script/ensure-go-installed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22

33
PREFERRED_GO_VERSION=go1.20.3
4-
SUPPORTED_GO_VERSIONS='go1.1[6789]|go1.2[0]'
4+
SUPPORTED_GO_VERSIONS='go1.1[6789]|go1.2[01]'
55

66
export ROOTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"
77
cd $ROOTDIR

0 commit comments

Comments
 (0)