Skip to content

Commit ac2c9c5

Browse files
committed
Customize update-pk integration test
Add support for test-specific execution so that we can guarantee that we're specifically testing the DML apply phase
1 parent b123226 commit ac2c9c5

4 files changed

Lines changed: 130 additions & 78 deletions

File tree

localtests/panic-on-warnings-update-pk-with-duplicate-on-new-unique-index/create.sql

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,3 @@ create table gh_ost_test (
88
insert into gh_ost_test (email) values ('alice@example.com');
99
insert into gh_ost_test (email) values ('bob@example.com');
1010
insert into gh_ost_test (email) values ('charlie@example.com');
11-
12-
drop event if exists gh_ost_test;
13-
delimiter ;;
14-
create event gh_ost_test
15-
on schedule every 1 second
16-
starts current_timestamp
17-
ends current_timestamp + interval 60 second
18-
on completion not preserve
19-
enable
20-
do
21-
begin
22-
-- Keep inserting rows, then updating their PK to create DELETE+INSERT binlog events
23-
-- Use alice's email so it conflicts with id=1 when applied to ghost table
24-
insert ignore into gh_ost_test (email) values ('temp@example.com');
25-
update gh_ost_test set id = 1000 + id, email = 'alice@example.com'
26-
where email = 'temp@example.com' order by id desc limit 1;
27-
end ;;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
--panic-on-warnings --alter "ADD UNIQUE KEY email_unique (email)"
1+
--panic-on-warnings --alter "ADD UNIQUE KEY email_unique (email)" --postpone-cut-over-flag-file=/tmp/gh-ost-test.postpone-cutover
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/bin/bash
2+
# Custom test: inject conflicting data AFTER row copy completes
3+
# This tests the DML event application code path, not row copy
4+
5+
# Create postpone flag file (referenced in extra_args)
6+
postpone_flag_file=/tmp/gh-ost-test.postpone-cutover
7+
touch $postpone_flag_file
8+
9+
# Build gh-ost command using framework function
10+
build_ghost_command
11+
12+
# Run in background
13+
echo_dot
14+
# Clear log file before starting gh-ost
15+
echo > $test_logfile
16+
bash -c "$cmd" >>$test_logfile 2>&1 &
17+
ghost_pid=$!
18+
19+
# Wait for row copy to complete
20+
echo_dot
21+
for i in {1..30}; do
22+
grep -q "Row copy complete" $test_logfile && break
23+
ps -p $ghost_pid > /dev/null || { echo; echo "ERROR gh-ost exited early"; rm -f $postpone_flag_file; return 1; }
24+
sleep 1; echo_dot
25+
done
26+
27+
# Inject conflicting SQL after row copy (UPDATE with PK change creates DELETE+INSERT in binlog)
28+
echo_dot
29+
gh-ost-test-mysql-master test -e "update gh_ost_test set id = 200, email = 'alice@example.com' where id = 2"
30+
31+
# Wait for binlog event to replicate and be applied
32+
sleep 10; echo_dot
33+
34+
# Complete cutover by removing postpone flag
35+
rm -f $postpone_flag_file
36+
37+
# Wait for gh-ost to complete
38+
wait $ghost_pid
39+
execution_result=$?
40+
rm -f $postpone_flag_file
41+
42+
# Validate using framework function
43+
validate_expected_failure
44+
return $?

localtests/test.sh

Lines changed: 85 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,77 @@ start_replication() {
135135
done
136136
}
137137

138+
build_ghost_command() {
139+
# Build gh-ost command with all standard options
140+
# Expects: ghost_binary, replica_host, replica_port, master_host, master_port,
141+
# table_name, storage_engine, throttle_flag_file, extra_args
142+
cmd="GOTRACEBACK=crash $ghost_binary \
143+
--user=gh-ost \
144+
--password=gh-ost \
145+
--host=$replica_host \
146+
--port=$replica_port \
147+
--assume-master-host=${master_host}:${master_port} \
148+
--database=test \
149+
--table=${table_name} \
150+
--storage-engine=${storage_engine} \
151+
--alter='engine=${storage_engine}' \
152+
--exact-rowcount \
153+
--assume-rbr \
154+
--skip-metadata-lock-check \
155+
--initially-drop-old-table \
156+
--initially-drop-ghost-table \
157+
--throttle-query='select timestampdiff(second, min(last_update), now()) < 5 from _${table_name}_ghc' \
158+
--throttle-flag-file=$throttle_flag_file \
159+
--serve-socket-file=/tmp/gh-ost.test.sock \
160+
--initially-drop-socket-file \
161+
--test-on-replica \
162+
--default-retries=3 \
163+
--chunk-size=10 \
164+
--verbose \
165+
--debug \
166+
--stack \
167+
--checkpoint \
168+
--execute ${extra_args[@]}"
169+
}
170+
171+
validate_expected_failure() {
172+
# Check if test expected to fail and validate error message
173+
# Expects: tests_path, test_name, execution_result, test_logfile
174+
if [ -f $tests_path/$test_name/expect_failure ]; then
175+
if [ $execution_result -eq 0 ]; then
176+
echo
177+
echo "ERROR $test_name execution was expected to exit on error but did not."
178+
echo "=== Last 50 lines of $test_logfile ==="
179+
tail -n 50 $test_logfile
180+
echo "=== End log excerpt ==="
181+
return 1
182+
fi
183+
if [ -s $tests_path/$test_name/expect_failure ]; then
184+
# 'expect_failure' file has content. We expect to find this content in the log.
185+
expected_error_message="$(cat $tests_path/$test_name/expect_failure)"
186+
if grep -q "$expected_error_message" $test_logfile; then
187+
return 0
188+
fi
189+
echo
190+
echo "ERROR $test_name execution was expected to exit with error message '${expected_error_message}' but did not."
191+
echo "=== Last 50 lines of $test_logfile ==="
192+
tail -n 50 $test_logfile
193+
echo "=== End log excerpt ==="
194+
return 1
195+
fi
196+
# 'expect_failure' file has no content. We generally agree that the failure is correct
197+
return 0
198+
fi
199+
200+
if [ $execution_result -ne 0 ]; then
201+
echo
202+
echo "ERROR $test_name execution failure. cat $test_logfile:"
203+
cat $test_logfile
204+
return 1
205+
fi
206+
return 0
207+
}
208+
138209
sysbench_prepare() {
139210
local mysql_host="$1"
140211
local mysql_port="$2"
@@ -253,6 +324,15 @@ test_single() {
253324

254325
table_name="gh_ost_test"
255326
ghost_table_name="_gh_ost_test_gho"
327+
328+
# Check for custom test script
329+
if [ -f $tests_path/$test_name/test.sh ]; then
330+
# Source the custom test script which can override default behavior
331+
# It has access to all variables and functions from this script
332+
source $tests_path/$test_name/test.sh
333+
return $?
334+
fi
335+
256336
# test with sysbench oltp write load
257337
if [[ "$test_name" == "sysbench" ]]; then
258338
if ! command -v sysbench &>/dev/null; then
@@ -273,34 +353,8 @@ test_single() {
273353
fi
274354
trap cleanup SIGINT
275355

276-
#
277-
cmd="GOTRACEBACK=crash $ghost_binary \
278-
--user=gh-ost \
279-
--password=gh-ost \
280-
--host=$replica_host \
281-
--port=$replica_port \
282-
--assume-master-host=${master_host}:${master_port}
283-
--database=test \
284-
--table=${table_name} \
285-
--storage-engine=${storage_engine} \
286-
--alter='engine=${storage_engine}' \
287-
--exact-rowcount \
288-
--assume-rbr \
289-
--skip-metadata-lock-check \
290-
--initially-drop-old-table \
291-
--initially-drop-ghost-table \
292-
--throttle-query='select timestampdiff(second, min(last_update), now()) < 5 from _${table_name}_ghc' \
293-
--throttle-flag-file=$throttle_flag_file \
294-
--serve-socket-file=/tmp/gh-ost.test.sock \
295-
--initially-drop-socket-file \
296-
--test-on-replica \
297-
--default-retries=3 \
298-
--chunk-size=10 \
299-
--verbose \
300-
--debug \
301-
--stack \
302-
--checkpoint \
303-
--execute ${extra_args[@]}"
356+
# Build and execute gh-ost command
357+
build_ghost_command
304358
echo_dot
305359
echo $cmd >$exec_command_file
306360
echo_dot
@@ -323,38 +377,9 @@ test_single() {
323377
gh-ost-test-mysql-master --default-character-set=utf8mb4 test <$tests_path/$test_name/destroy.sql
324378
fi
325379

326-
if [ -f $tests_path/$test_name/expect_failure ]; then
327-
if [ $execution_result -eq 0 ]; then
328-
echo
329-
echo "ERROR $test_name execution was expected to exit on error but did not."
330-
echo "=== Last 50 lines of $test_logfile ==="
331-
tail -n 50 $test_logfile
332-
echo "=== End log excerpt ==="
333-
return 1
334-
fi
335-
if [ -s $tests_path/$test_name/expect_failure ]; then
336-
# 'expect_failure' file has content. We expect to find this content in the log.
337-
expected_error_message="$(cat $tests_path/$test_name/expect_failure)"
338-
if grep -q "$expected_error_message" $test_logfile; then
339-
return 0
340-
fi
341-
echo
342-
echo "ERROR $test_name execution was expected to exit with error message '${expected_error_message}' but did not."
343-
echo "=== Last 50 lines of $test_logfile ==="
344-
tail -n 50 $test_logfile
345-
echo "=== End log excerpt ==="
346-
return 1
347-
fi
348-
# 'expect_failure' file has no content. We generally agree that the failure is correct
349-
return 0
350-
fi
351-
352-
if [ $execution_result -ne 0 ]; then
353-
echo
354-
echo "ERROR $test_name execution failure. cat $test_logfile:"
355-
cat $test_logfile
356-
return 1
357-
fi
380+
# Validate expected failure or success
381+
validate_expected_failure
382+
return $?
358383

359384
gh-ost-test-mysql-replica --default-character-set=utf8mb4 test -e "show create table ${ghost_table_name}\G" -ss >$ghost_structure_output_file
360385

0 commit comments

Comments
 (0)