Skip to content

Commit f1ccb2f

Browse files
LevKatssergepetrenko
authored andcommitted
wal: use existing logs sizes after restart
During the `checkpoint_wal_threshold` exceedance checks tarantool used to ignore the sizes of files created prior to the server restart which resulted in cases when the server didn't create any checkpoints and generate a huge amount of logs which were replayed each server restart. Now the `wal_dir` is scanned for proper `writer->checkpoint_wal_size` calculation after the recovery process. Fixes tarantool#9811 NO_DOC=bugfix
1 parent d590c72 commit f1ccb2f

File tree

5 files changed

+122
-3
lines changed

5 files changed

+122
-3
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## bugfix/box
2+
3+
* Fixed a bug when the sizes of *.xlog files created before the server restart
4+
were not taken into account during the `checkpoint_wal_threshold` exceedance
5+
checks (gh-9811).

src/box/box.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6348,9 +6348,14 @@ box_storage_init(void)
63486348
cfg_geti64("wal_max_size"));
63496349
enum wal_mode wal_mode = box_check_wal_mode(cfg_gets("wal_mode"));
63506350
double wal_retention_period = box_check_wal_retention_period_xc();
6351+
struct vclock *checkpoint_vclock = NULL;
6352+
struct gc_checkpoint *last_checkpoint = gc_last_checkpoint();
6353+
if (last_checkpoint != NULL)
6354+
checkpoint_vclock = &last_checkpoint->vclock;
63516355
if (wal_init(wal_mode, cfg_gets("wal_dir"), wal_max_size,
63526356
wal_retention_period, &INSTANCE_UUID,
6353-
&instance_vclock_storage, on_wal_garbage_collection,
6357+
&instance_vclock_storage, checkpoint_vclock,
6358+
on_wal_garbage_collection,
63546359
on_wal_checkpoint_threshold) != 0) {
63556360
diag_raise();
63566361
}

src/box/wal.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ wal_writer_create(struct wal_writer *writer, enum wal_mode wal_mode,
418418
double wal_retention_period,
419419
const struct tt_uuid *instance_uuid,
420420
struct vclock *instance_vclock,
421+
struct vclock *checkpoint_vclock,
421422
wal_on_garbage_collection_f on_garbage_collection,
422423
wal_on_checkpoint_threshold_f on_checkpoint_threshold)
423424
{
@@ -450,6 +451,8 @@ wal_writer_create(struct wal_writer *writer, enum wal_mode wal_mode,
450451

451452
vclock_create(&writer->vclock);
452453
vclock_create(&writer->checkpoint_vclock);
454+
if (checkpoint_vclock != NULL)
455+
vclock_copy(&writer->checkpoint_vclock, checkpoint_vclock);
453456
rlist_create(&writer->watchers);
454457

455458
writer->on_garbage_collection = on_garbage_collection;
@@ -478,11 +481,30 @@ wal_open_f(struct cbus_call_msg *msg)
478481
const char *path = xdir_format_filename(&writer->wal_dir,
479482
vclock_sum(&writer->vclock), NONE);
480483
assert(!xlog_is_open(&writer->current_wal));
484+
485+
uint64_t sum_already_on_disk = 0;
486+
for (struct vclock *vclock = vclockset_first(&writer->wal_dir.index);
487+
vclock != NULL;
488+
vclock = vclockset_next(&writer->wal_dir.index, vclock)) {
489+
if (vclock_compare(&writer->checkpoint_vclock, vclock) > 0)
490+
continue;
491+
const char *name = xdir_format_filename(&writer->wal_dir,
492+
vclock_sum(vclock),
493+
NONE);
494+
struct stat attr;
495+
if (stat(name, &attr) != 0)
496+
say_warn("failed to get file size %s, consider it"
497+
" equal to zero", name);
498+
else
499+
sum_already_on_disk += (uint64_t)attr.st_size;
500+
}
501+
writer->checkpoint_wal_size = sum_already_on_disk;
481502
return xlog_open(&writer->current_wal, path, &writer->wal_dir.opts);
482503
}
483504

484505
/**
485-
* Try to open the current WAL file for appending if it exists.
506+
* Try to open the current WAL file for appending if it exists
507+
* and scan writer->wal_dir to set writer->checkpoint_wal_size.
486508
*/
487509
static int
488510
wal_open(struct wal_writer *writer)
@@ -544,14 +566,16 @@ wal_init(enum wal_mode wal_mode, const char *wal_dirname,
544566
int64_t wal_max_size, double wal_retention_period,
545567
const struct tt_uuid *instance_uuid,
546568
struct vclock *instance_vclock,
569+
struct vclock *checkpoint_vclock,
547570
wal_on_garbage_collection_f on_garbage_collection,
548571
wal_on_checkpoint_threshold_f on_checkpoint_threshold)
549572
{
550573
/* Initialize the state. */
551574
struct wal_writer *writer = &wal_writer_singleton;
552575
wal_writer_create(writer, wal_mode, wal_dirname, wal_max_size,
553576
wal_retention_period, instance_uuid, instance_vclock,
554-
on_garbage_collection, on_checkpoint_threshold);
577+
checkpoint_vclock, on_garbage_collection,
578+
on_checkpoint_threshold);
555579

556580
/* Start WAL thread. */
557581
if (cord_costart(&writer->cord, "wal", wal_writer_f, NULL) != 0)

src/box/wal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ wal_init(enum wal_mode wal_mode, const char *wal_dirname,
110110
int64_t wal_max_size, double wal_retention_period,
111111
const struct tt_uuid *instance_uuid,
112112
struct vclock *instance_vclock,
113+
struct vclock *checkpoint_vclock,
113114
wal_on_garbage_collection_f on_garbage_collection,
114115
wal_on_checkpoint_threshold_f on_checkpoint_threshold);
115116

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
local t = require('luatest')
2+
local server = require('luatest.server')
3+
4+
-- 1. Many small xlogs. Let's check that threshold check indeed use
5+
-- the sum of xlog sizes.
6+
-- 2. One big xlog. Let's check that xlog right after the snapshot is
7+
-- taken into account.
8+
local group_config = {{wal_max_size = 50000}, {wal_max_size = 50000 * 10}}
9+
local g = t.group("wal_max_size", group_config)
10+
11+
g.before_each(function(cg)
12+
cg.server = server:new({box_cfg = {
13+
wal_max_size = cg.params.wal_max_size,
14+
checkpoint_wal_threshold = 10 * 50000,
15+
checkpoint_count = 1e8,
16+
checkpoint_interval = 0,
17+
}})
18+
cg.server:start()
19+
cg.server:exec(function()
20+
local s = box.schema.space.create('test')
21+
s:create_index('pk', {sequence = true})
22+
-- Let's delete all existing xlogs so ones that were produced during
23+
-- server start and index creation don't interfere with our test and
24+
-- we don't have to take the sizes of that xlogs into account.
25+
-- 1. Reconfigure so each new checkpoint results in GC trigger and
26+
-- deletion of all logs before the checkpoint.
27+
box.cfg{checkpoint_count = 1}
28+
-- 2. Delete all unintended xlogs.
29+
box.snapshot()
30+
-- 3. Effectively disable GC again.
31+
box.cfg{checkpoint_count = 1e8}
32+
end)
33+
end)
34+
35+
g.test_box_checkpoint_wal_threshold_after_restart = function(cg)
36+
-- 1. Let's create some xlogs followed by a snapshot.
37+
cg.server:exec(function()
38+
local fiber = require("fiber")
39+
local s = box.space.test
40+
local checkpoints_before = #box.info.gc().checkpoints
41+
for _ = 1, 7000 do
42+
s:insert({box.NULL})
43+
end
44+
-- Wait for possible (but unwanted) checkpoint finish.
45+
fiber.sleep(1)
46+
t.assert_equals(#box.info.gc().checkpoints, checkpoints_before,
47+
"Wrong batch size. Change the number of tuples")
48+
box.snapshot()
49+
end)
50+
cg.server:restart()
51+
-- 2. There are xlogs created before the snapshot. Check that they are
52+
-- ignored when calculating the threshold.
53+
cg.server:exec(function()
54+
local fiber = require("fiber")
55+
local s = box.space.test
56+
local checkpoints_before = #box.info.gc().checkpoints
57+
for _ = 1, 7000 do
58+
s:insert({box.NULL})
59+
end
60+
-- Result must be the same -- no new checkpoint.
61+
fiber.sleep(1)
62+
t.assert_equals(#box.info.gc().checkpoints, checkpoints_before,
63+
"old xlogs may be used")
64+
end)
65+
cg.server:restart()
66+
-- 3. We expect a checkpoint after the batch of tuples below since again
67+
-- we have some xlogs before the restart but this time there were no
68+
-- new snapshots.
69+
cg.server:exec(function()
70+
local fiber = require("fiber")
71+
local checkpoints_before = #box.info.gc().checkpoints
72+
local s = box.space.test
73+
-- A checkpoint must be created after that batch.
74+
for _ = 1, 7000 do
75+
s:insert({box.NULL})
76+
end
77+
fiber.sleep(1)
78+
t.assert_gt(#box.info.gc().checkpoints, checkpoints_before)
79+
end)
80+
end
81+
82+
g.after_each(function(cg)
83+
cg.server:drop()
84+
end)

0 commit comments

Comments
 (0)