Skip to content

Commit 1b3dd37

Browse files
Merge pull request #1747 from satya-bodapati/trunk
Merge PXB-3658 to trunk from 8.4
2 parents ac98ceb + 38922a0 commit 1b3dd37

2 files changed

Lines changed: 329 additions & 9 deletions

File tree

storage/innobase/xtrabackup/src/ds_local.cc

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,12 @@ static int local_write_sparse(ds_file_t *file, const void *buf, size_t len,
182182
[[maybe_unused]] bool punch_hole_supported) {
183183
auto local_file = ((ds_local_file_t *)file->ptr);
184184
File fd = local_file->fd;
185-
[[maybe_unused]] ulonglong seek = 0;
186185

187186
const uchar *ptr = static_cast<const uchar *>(buf);
188187

189188
for (size_t i = 0; i < sparse_map_size; ++i) {
190189
my_off_t rc;
191190

192-
seek = my_tell(fd, MYF(MY_WME));
193191
rc = my_seek(fd, sparse_map[i].skip, MY_SEEK_CUR, MYF(MY_WME));
194192
if (rc == MY_FILEPOS_ERROR) {
195193
return 1;
@@ -206,21 +204,17 @@ static int local_write_sparse(ds_file_t *file, const void *buf, size_t len,
206204
std::memory_order_relaxed);
207205
}
208206

209-
#ifdef HAVE_FALLOC_PUNCH_HOLE_AND_KEEP_SIZE
210-
if (punch_hole_supported) {
211-
fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, seek,
212-
sparse_map[i].skip);
213-
}
214-
#endif
215-
216207
ptr += sparse_map[i].len;
217208
}
209+
218210
/* to track if last page is sparse */
219211
if (sparse_map[sparse_map_size - 1].len == 0) {
220212
local_file->last_seek = sparse_map[sparse_map_size - 1].skip;
221213
} else
222214
local_file->last_seek = 0;
223215

216+
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
217+
224218
return 0;
225219
}
226220

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
#
2+
# PXB-3658: Verify that page-compressed tables remain sparse after
3+
# backup/restore, and that allocated disk size (du) exactly matches
4+
# the original server files.
5+
#
6+
# Strategy: restart the server after inserting data to force a clean
7+
# shutdown (all dirty pages flushed, checkpoint complete). This gives
8+
# us a deterministic .ibd state. After backup + prepare (which is a
9+
# no-op for data pages since redo log is empty) + copy-back, the
10+
# restored .ibd must have the exact same allocated size.
11+
#
12+
# Tests five restore paths:
13+
# 1. Local backup (no streaming)
14+
# 2. xbstream extract (no xtrabackup compression) -- SPARSE chunks
15+
# 3. xbstream extract --decompress (with lz4 compression) -- dense + restore_sparseness via xbstream
16+
# 4. Local backup with --compress=zstd + xtrabackup --decompress -- restore_sparseness via xtrabackup
17+
# 5. Streamed backup with --compress=zstd + xbstream extract (no decompress) + xtrabackup --decompress
18+
#
19+
20+
. inc/common.sh
21+
. inc/keyring_file.sh
22+
23+
require_lz4
24+
require_zstd
25+
26+
get_allocated_size() {
27+
du --block-size=1 "$1" | awk '{print $1}'
28+
}
29+
30+
get_apparent_size() {
31+
stat --printf "%s" "$1"
32+
}
33+
34+
check_sparse_exact() {
35+
local filepath=$1
36+
local label=$2
37+
local expected_alloc=$3
38+
local tolerance=${4:-0}
39+
40+
if ! is_sparse_file "$filepath" ; then
41+
die "$label: $filepath is NOT sparse"
42+
fi
43+
44+
local apparent=$(get_apparent_size "$filepath")
45+
local allocated=$(get_allocated_size "$filepath")
46+
47+
vlog "$label: apparent=$apparent allocated=$allocated expected=$expected_alloc tolerance=$tolerance"
48+
49+
local diff=$(( allocated - expected_alloc ))
50+
# absolute value
51+
if [ "$diff" -lt 0 ] ; then
52+
diff=$(( -diff ))
53+
fi
54+
55+
if [ "$diff" -gt "$tolerance" ] ; then
56+
die "$label: allocated size mismatch: got $allocated, expected $expected_alloc (diff=$diff, tolerance=$tolerance)"
57+
fi
58+
59+
vlog "$label: PASS (diff=$diff within tolerance=$tolerance)"
60+
}
61+
62+
setup_and_get_baseline() {
63+
run_cmd $MYSQL $MYSQL_ARGS test <<EOF
64+
CREATE TABLE t_zlib (c1 INT AUTO_INCREMENT PRIMARY KEY, c2 BLOB) COMPRESSION='zlib';
65+
CREATE TABLE t_plain (c1 INT AUTO_INCREMENT PRIMARY KEY, c2 BLOB);
66+
EOF
67+
68+
for tbl in t_zlib t_plain ; do
69+
run_cmd $MYSQL $MYSQL_ARGS test <<EOF
70+
INSERT INTO $tbl (c2) VALUES (REPEAT('x', 5000));
71+
INSERT INTO $tbl (c2) SELECT c2 FROM $tbl;
72+
INSERT INTO $tbl (c2) SELECT c2 FROM $tbl;
73+
INSERT INTO $tbl (c2) SELECT c2 FROM $tbl;
74+
INSERT INTO $tbl (c2) SELECT c2 FROM $tbl;
75+
EOF
76+
done
77+
78+
# Graceful shutdown flushes all dirty pages with punch holes,
79+
# then restart gives us a clean, deterministic .ibd state.
80+
shutdown_server
81+
start_server
82+
83+
if ! is_sparse_file "$mysql_datadir/test/t_zlib.ibd" ; then
84+
die "original t_zlib.ibd is NOT sparse after restart"
85+
fi
86+
87+
orig_zlib_alloc=$(get_allocated_size "$mysql_datadir/test/t_zlib.ibd")
88+
orig_zlib_apparent=$(get_apparent_size "$mysql_datadir/test/t_zlib.ibd")
89+
90+
vlog "Baseline t_zlib.ibd: apparent=$orig_zlib_apparent allocated=$orig_zlib_alloc"
91+
}
92+
93+
#
94+
# Start server and check punch hole support
95+
#
96+
start_server
97+
98+
if grep -q 'PUNCH HOLE support not available' $MYSQLD_ERRFILE ; then
99+
skip_test 'punch hole support is not available'
100+
fi
101+
102+
########################################################################
103+
# Path 1: Local backup (no streaming)
104+
########################################################################
105+
106+
vlog "===== Path 1: Local backup ====="
107+
108+
setup_and_get_baseline
109+
110+
record_db_state test
111+
112+
xtrabackup --backup --target-dir=$topdir/backup
113+
114+
xtrabackup --prepare --target-dir=$topdir/backup
115+
116+
check_sparse_exact "$topdir/backup/test/t_zlib.ibd" \
117+
"local-backup t_zlib (after prepare)" "$orig_zlib_alloc"
118+
119+
stop_server
120+
rm -rf $mysql_datadir
121+
122+
xtrabackup --copy-back --target-dir=$topdir/backup
123+
124+
start_server
125+
126+
check_sparse_exact "$mysql_datadir/test/t_zlib.ibd" \
127+
"local-backup t_zlib (after copy-back)" "$orig_zlib_alloc"
128+
129+
if is_sparse_file "$mysql_datadir/test/t_plain.ibd" ; then
130+
die "t_plain.ibd should NOT be sparse"
131+
fi
132+
133+
verify_db_state test
134+
135+
run_cmd $MYSQL $MYSQL_ARGS test <<EOF
136+
DROP TABLE IF EXISTS t_zlib;
137+
DROP TABLE IF EXISTS t_plain;
138+
EOF
139+
140+
stop_server
141+
rm -rf $mysql_datadir
142+
rm -rf $topdir/backup
143+
144+
########################################################################
145+
# Path 2: xbstream extract (no xtrabackup compression)
146+
########################################################################
147+
148+
vlog "===== Path 2: xbstream extract (no xtrabackup compression) ====="
149+
150+
start_server
151+
setup_and_get_baseline
152+
153+
record_db_state test
154+
155+
xtrabackup --backup --stream=xbstream --target-dir=$topdir/tmp \
156+
> $topdir/backup.xbs
157+
158+
rm -rf $topdir/backup && mkdir $topdir/backup
159+
xbstream -x -v -C $topdir/backup < $topdir/backup.xbs
160+
161+
xtrabackup --prepare --target-dir=$topdir/backup
162+
163+
check_sparse_exact "$topdir/backup/test/t_zlib.ibd" \
164+
"xbstream t_zlib (after prepare)" "$orig_zlib_alloc"
165+
166+
stop_server
167+
rm -rf $mysql_datadir
168+
169+
xtrabackup --copy-back --target-dir=$topdir/backup
170+
171+
start_server
172+
173+
check_sparse_exact "$mysql_datadir/test/t_zlib.ibd" \
174+
"xbstream t_zlib (after copy-back)" "$orig_zlib_alloc"
175+
176+
if is_sparse_file "$mysql_datadir/test/t_plain.ibd" ; then
177+
die "t_plain.ibd should NOT be sparse (xbstream path)"
178+
fi
179+
180+
verify_db_state test
181+
182+
run_cmd $MYSQL $MYSQL_ARGS test <<EOF
183+
DROP TABLE IF EXISTS t_zlib;
184+
DROP TABLE IF EXISTS t_plain;
185+
EOF
186+
187+
stop_server
188+
rm -rf $mysql_datadir
189+
rm -rf $topdir/backup $topdir/backup.xbs
190+
191+
########################################################################
192+
# Path 3: xbstream extract --decompress (with lz4 compression)
193+
########################################################################
194+
195+
vlog "===== Path 3: xbstream --decompress (lz4) ====="
196+
197+
start_server
198+
setup_and_get_baseline
199+
200+
record_db_state test
201+
202+
xtrabackup --backup --compress=lz4 --stream=xbstream --target-dir=$topdir/tmp \
203+
> $topdir/backup.xbs
204+
205+
rm -rf $topdir/backup && mkdir $topdir/backup
206+
xbstream -x -v -C $topdir/backup --decompress < $topdir/backup.xbs
207+
208+
xtrabackup --prepare --target-dir=$topdir/backup
209+
210+
check_sparse_exact "$topdir/backup/test/t_zlib.ibd" \
211+
"xbstream-decompress t_zlib (after prepare)" "$orig_zlib_alloc"
212+
213+
stop_server
214+
rm -rf $mysql_datadir
215+
216+
xtrabackup --copy-back --target-dir=$topdir/backup
217+
218+
start_server
219+
220+
check_sparse_exact "$mysql_datadir/test/t_zlib.ibd" \
221+
"xbstream-decompress t_zlib (after copy-back)" "$orig_zlib_alloc"
222+
223+
if is_sparse_file "$mysql_datadir/test/t_plain.ibd" ; then
224+
die "t_plain.ibd should NOT be sparse (decompress path)"
225+
fi
226+
227+
verify_db_state test
228+
229+
run_cmd $MYSQL $MYSQL_ARGS test <<EOF
230+
DROP TABLE IF EXISTS t_zlib;
231+
DROP TABLE IF EXISTS t_plain;
232+
EOF
233+
234+
stop_server
235+
rm -rf $mysql_datadir
236+
rm -rf $topdir/backup $topdir/backup.xbs
237+
238+
########################################################################
239+
# Path 4: Local backup with --compress=zstd + xtrabackup --decompress
240+
########################################################################
241+
242+
vlog "===== Path 4: Local backup + compress=zstd + xtrabackup --decompress ====="
243+
244+
start_server
245+
setup_and_get_baseline
246+
247+
record_db_state test
248+
249+
xtrabackup --backup --compress=zstd --target-dir=$topdir/backup
250+
251+
xtrabackup --decompress --target-dir=$topdir/backup
252+
253+
xtrabackup --prepare --target-dir=$topdir/backup
254+
255+
check_sparse_exact "$topdir/backup/test/t_zlib.ibd" \
256+
"local-zstd-decompress t_zlib (after prepare)" "$orig_zlib_alloc"
257+
258+
stop_server
259+
rm -rf $mysql_datadir
260+
261+
xtrabackup --copy-back --target-dir=$topdir/backup
262+
263+
start_server
264+
265+
check_sparse_exact "$mysql_datadir/test/t_zlib.ibd" \
266+
"local-zstd-decompress t_zlib (after copy-back)" "$orig_zlib_alloc"
267+
268+
if is_sparse_file "$mysql_datadir/test/t_plain.ibd" ; then
269+
die "t_plain.ibd should NOT be sparse (local zstd decompress path)"
270+
fi
271+
272+
verify_db_state test
273+
274+
run_cmd $MYSQL $MYSQL_ARGS test <<EOF
275+
DROP TABLE IF EXISTS t_zlib;
276+
DROP TABLE IF EXISTS t_plain;
277+
EOF
278+
279+
stop_server
280+
rm -rf $mysql_datadir
281+
rm -rf $topdir/backup
282+
283+
########################################################################
284+
# Path 5: Stream + compress=zstd + xbstream extract + xtrabackup --decompress
285+
########################################################################
286+
287+
vlog "===== Path 5: xbstream extract (no decompress) + xtrabackup --decompress (zstd) ====="
288+
289+
start_server
290+
setup_and_get_baseline
291+
292+
record_db_state test
293+
294+
xtrabackup --backup --compress=zstd --stream=xbstream --target-dir=$topdir/tmp \
295+
> $topdir/backup.xbs
296+
297+
rm -rf $topdir/backup && mkdir $topdir/backup
298+
xbstream -x -v -C $topdir/backup < $topdir/backup.xbs
299+
300+
xtrabackup --decompress --target-dir=$topdir/backup
301+
302+
xtrabackup --prepare --target-dir=$topdir/backup
303+
304+
check_sparse_exact "$topdir/backup/test/t_zlib.ibd" \
305+
"stream-zstd-xb-decompress t_zlib (after prepare)" "$orig_zlib_alloc"
306+
307+
stop_server
308+
rm -rf $mysql_datadir
309+
310+
xtrabackup --copy-back --target-dir=$topdir/backup
311+
312+
start_server
313+
314+
check_sparse_exact "$mysql_datadir/test/t_zlib.ibd" \
315+
"stream-zstd-xb-decompress t_zlib (after copy-back)" "$orig_zlib_alloc"
316+
317+
if is_sparse_file "$mysql_datadir/test/t_plain.ibd" ; then
318+
die "t_plain.ibd should NOT be sparse (stream zstd xb-decompress path)"
319+
fi
320+
321+
verify_db_state test
322+
323+
run_cmd $MYSQL $MYSQL_ARGS test <<EOF
324+
DROP TABLE IF EXISTS t_zlib;
325+
DROP TABLE IF EXISTS t_plain;
326+
EOF

0 commit comments

Comments
 (0)