Restore the default scrub behaviour of the previous version

If the user like to scrub new blocks, he can now use the -p new
option, and then call again scrub with a percentage to scrub the
old blocks.

Scrubbing old and new blocks are two different operations, and make
sense to keep them separated. Mixing them could instead raise
problematic issues, like not being able to predict the time required
for a full scrub cycle, as it depends also on the amount of synced
data.
This commit is contained in:
Andrea Mazzoleni 2015-11-09 23:21:39 +01:00
parent 9f263b6fa8
commit 1917814d2d
9 changed files with 158 additions and 177 deletions

21
HISTORY
View file

@ -8,25 +8,22 @@ SnapRAID HISTORY
This was triggered by a very specific pattern of fragmented files
and bad blocks combination, not so easy to reproduce.
This had no bad effect, if not preventing the command to run.
* The synced blocks are now scrubbed with priority over all
the already scrubbed ones.
This allows to verify as early as possible that the written
parity during sync is really correct.
This means that the selected percentage to scrub should take care
also of the just synced blocks. For this reason the default scrub
percentage is now increased to the 15%.
The "status" command reports the amount of scrub necessary to
ensure to scrub all the synced blocks.
* The -p, --percentage option supports the new the arguments:
"bad", "sync" and "full" to define the kind of scrub to do.
* Drastically reduces the memory usage. For each block, it now
uses 17 bytes of memory, instead of the previous 28 bytes
(for 32 bit) or 36 bytes (for 64 bit).
This could result is a memory saving of up the 50%.
* The -p, --percentage option can be used to define a scrub plan:
"new", "bad" and "full".
The "new" plan scrubs all the new synced blocks not yet scrubbed.
This allows to verify as early as possible that the written
parity during sync is really correct. You can use the "status" command
to show the amount blocks not yet scrubbed.
The "bad" scrubs only bad blocks.
The "full" scrubs all blocks.
* Content files are now written asyncronously from different
threads to avoid the unfortunate condition that a memory
error affects all of them in the same way.
After writing them, they are also read again to verify their CRC.
After writing, they are read again to verify their CRC.
This is done to ensure thay they are really OK, even in the
case of the worst possible silent errors.
* Extends the -D, --force-device option to ignore more

View file

@ -451,7 +451,7 @@ endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -o 0 scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -p full scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -p sync scrub
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -p new scrub
echo --- Silently corrupt some files, and sync with error presents
$(TESTENV) ./mktest$(EXEEXT) write 2 1 1 bench/disk1/a/*
$(TESTENV) ./mktest$(EXEEXT) damage 3 1 1 bench/disk2/a/*

View file

@ -1038,19 +1038,6 @@ static inline int info_get_justsynced(snapraid_info info)
return (info & 0x4) != 0;
}
/**
* Get the latest scrub time.
*
* If the block was not scrubbed, 0 is returned.
*/
static inline time_t info_get_scrubtime(snapraid_info info)
{
if (info_get_justsynced(info))
return 0;
else
return info_get_time(info);
}
/**
* Mark the block address as with error.
*/

View file

@ -35,54 +35,71 @@ struct snapraid_rehash {
struct snapraid_block* block;
};
/**
* Scrub plan to use.
*/
struct snapraid_plan {
int plan; /**< One of the SCRUB_*. */
time_t timelimit; /**< Time limit. Valid only with SCRUB_AUTO. */
block_off_t lastlimit; /**< Number of blocks allowed with time exactly at ::timelimit. */
block_off_t countlast; /**< Counter of blocks with time exactly at ::timelimit. */
};
/**
* Check if we have to process the specified block index ::i.
*/
static int block_is_enabled(struct snapraid_state* state, block_off_t i, time_t timelimit, block_off_t lastlimit, block_off_t* countlast)
static int block_is_enabled(struct snapraid_state* state, block_off_t i, struct snapraid_plan* plan)
{
time_t blocktime;
snapraid_info info;
/* if it's unused */
/* don't scrub unused blocks in all plans */
info = info_get(&state->infoarr, i);
if (info == 0) {
if (info == 0)
return 0;
/* bad blocks are always scrubbed in all plans */
if (info_get_bad(info))
return 1;
switch (plan->plan) {
case SCRUB_FULL :
/* in 'full' plan everything is scrubbed */
return 1;
case SCRUB_EVEN :
/* in 'even' plan, scrub only even blocks */
return i % 2 == 0;
case SCRUB_NEW :
/* in 'sync' plan, only blocks never scrubbed */
return info_get_justsynced(info);
case SCRUB_BAD :
/* in 'bad' plan, only bad blocks (already reported) */
return 0;
}
/* if it's too new */
blocktime = info_get_time(info);
if (blocktime > plan->timelimit) {
/* skip it */
return 0;
}
/* blocks marked as bad are always checked */
if (!info_get_bad(info)) {
/* if it's too new */
blocktime = info_get_scrubtime(info);
if (blocktime > timelimit) {
/* if the time is less than the limit, always include */
/* otherwise, check if we reached the last limit count */
if (blocktime == plan->timelimit) {
/* if we reached the count limit */
if (plan->countlast >= plan->lastlimit) {
/* skip it */
return 0;
}
/* skip odd blocks, used only for testing */
if (state->opt.force_scrub_even && (i % 2) != 0) {
/* skip it */
return 0;
}
/* if the time is less than the limit, always include */
/* otherwise, check if we reached the last limit count */
if (blocktime == timelimit) {
/* if we reached the count limit */
if (*countlast >= lastlimit) {
/* skip it */
return 0;
}
++*countlast;
}
++plan->countlast;
}
return 1;
}
static int state_scrub_process(struct snapraid_state* state, struct snapraid_parity_handle** parity, block_off_t blockstart, block_off_t blockmax, time_t timelimit, block_off_t lastlimit, time_t now)
static int state_scrub_process(struct snapraid_state* state, struct snapraid_parity_handle** parity, block_off_t blockstart, block_off_t blockmax, struct snapraid_plan* plan, time_t now)
{
struct snapraid_handle* handle;
void* rehandle_alloc;
@ -96,7 +113,6 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
data_off_t countsize;
block_off_t countpos;
block_off_t countmax;
block_off_t countlast;
block_off_t autosavedone;
block_off_t autosavelimit;
block_off_t autosavemissing;
@ -125,9 +141,9 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
/* first count the number of blocks to process */
countmax = 0;
countlast = 0;
plan->countlast = 0;
for (i = blockstart; i < blockmax; ++i) {
if (!block_is_enabled(state, i, timelimit, lastlimit, &countlast))
if (!block_is_enabled(state, i, plan))
continue;
++countmax;
@ -145,7 +161,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
countsize = 0;
countpos = 0;
countlast = 0;
plan->countlast = 0;
state_progress_begin(state, blockstart, blockmax, countmax);
for (i = blockstart; i < blockmax; ++i) {
snapraid_info info;
@ -155,7 +171,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
int block_is_unsynced;
int rehash;
if (!block_is_enabled(state, i, timelimit, lastlimit, &countlast))
if (!block_is_enabled(state, i, plan))
continue;
/* one more block processed for autosave */
@ -558,15 +574,13 @@ int state_scrub(struct snapraid_state* state, int percentage, int olderthan)
{
block_off_t blockmax;
block_off_t countlimit;
block_off_t lastlimit;
block_off_t i;
block_off_t count_unscrubbed;
block_off_t count;
time_t timelimit;
time_t recentlimit;
int ret;
struct snapraid_parity_handle parity[LEV_MAX];
struct snapraid_parity_handle* parity_ptr[LEV_MAX];
struct snapraid_plan plan;
time_t* timemap;
unsigned error;
time_t now;
@ -577,7 +591,7 @@ int state_scrub(struct snapraid_state* state, int percentage, int olderthan)
msg_progress("Initializing...\n");
if ((percentage == SCRUB_BAD || percentage == SCRUB_SYNC || percentage == SCRUB_FULL)
if ((percentage == SCRUB_BAD || percentage == SCRUB_NEW || percentage == SCRUB_FULL)
&& olderthan >= 0) {
/* LCOV_EXCL_START */
log_fatal("You cannot specify -o, --older-than only with a numeric percentage.\n");
@ -587,26 +601,30 @@ int state_scrub(struct snapraid_state* state, int percentage, int olderthan)
blockmax = parity_allocated_size(state);
/* preinitialize to avoid warnings */
countlimit = 0;
recentlimit = 0;
if (state->opt.force_scrub_even) {
/* no limit */
countlimit = blockmax;
recentlimit = now;
plan.plan = SCRUB_EVEN;
} else if (percentage == SCRUB_FULL) {
plan.plan = SCRUB_FULL;
} else if (percentage == SCRUB_NEW) {
plan.plan = SCRUB_NEW;
} else if (percentage == SCRUB_BAD) {
plan.plan = SCRUB_BAD;
} else if (state->opt.force_scrub_at) {
/* scrub the specified amount of blocks */
plan.plan = SCRUB_AUTO;
countlimit = state->opt.force_scrub_at;
recentlimit = now;
} else if (percentage == SCRUB_FULL || percentage == SCRUB_SYNC) {
countlimit = blockmax;
recentlimit = now;
} else if (percentage == SCRUB_BAD) {
countlimit = 0;
recentlimit = now;
} else {
plan.plan = SCRUB_AUTO;
if (percentage >= 0) {
countlimit = md(blockmax, percentage, 100);
} else {
/* by default scrub 15% of the array (100*3/20=15) */
countlimit = md(blockmax, 3, 20);
/* by default scrub 12.5% of the array (100/8=12.5) */
countlimit = md(blockmax, 1, 8);
}
if (olderthan >= 0) {
@ -624,22 +642,15 @@ int state_scrub(struct snapraid_state* state, int percentage, int olderthan)
/* copy the info in the temp vector */
count = 0;
count_unscrubbed = 0;
log_tag("block_count:%u\n", blockmax);
for (i = 0; i < blockmax; ++i) {
snapraid_info info = info_get(&state->infoarr, i);
time_t scrub_time;
/* skip unused blocks */
if (info == 0)
continue;
scrub_time = info_get_scrubtime(info);
if (scrub_time == 0)
++count_unscrubbed;
timemap[count++] = scrub_time;
timemap[count++] = info_get_time(info);
}
if (!count) {
@ -663,39 +674,37 @@ int state_scrub(struct snapraid_state* state, int percentage, int olderthan)
i = j;
}
/* scrub the number of sync block if required */
if (percentage == SCRUB_SYNC) {
countlimit = count_unscrubbed;
recentlimit = now;
/* compute the limits from count/recentlimit */
if (plan.plan == SCRUB_AUTO) {
/* no more than the full count */
if (countlimit > count)
countlimit = count;
/* decrease until we reach the specific recentlimit */
while (countlimit > 0 && timemap[countlimit - 1] > recentlimit)
--countlimit;
/* if there is something to scrub */
if (countlimit > 0) {
/* get the most recent time we want to scrub */
plan.timelimit = timemap[countlimit - 1];
/* count how many entries for this exact time we have to scrub */
/* if the blocks have all the same time, we end with countlimit == lastlimit */
plan.lastlimit = 1;
while (countlimit > plan.lastlimit && timemap[countlimit - plan.lastlimit - 1] == plan.timelimit)
++plan.lastlimit;
} else {
/* if nothing to scrub, disable also other limits */
plan.timelimit = 0;
plan.lastlimit = 0;
}
log_tag("count_limit:%u\n", countlimit);
}
/* no more than the full count */
if (countlimit > count)
countlimit = count;
/* decrease until we reach the specific recentlimit */
while (countlimit > 0 && timemap[countlimit - 1] > recentlimit)
--countlimit;
/* if there is something to scrub */
if (countlimit > 0) {
/* get the most recent time we want to scrub */
timelimit = timemap[countlimit - 1];
/* count how many entries for this exact time we have to scrub */
/* if the blocks have all the same time, we end with countlimit == lastlimit */
lastlimit = 1;
while (countlimit > lastlimit && timemap[countlimit - lastlimit - 1] == timelimit)
++lastlimit;
} else {
/* if nothing to scrub, disable also other limits */
timelimit = 0;
lastlimit = 0;
}
log_tag("count_limit:%u\n", countlimit);
log_tag("time_limit:%" PRIu64 "\n", (uint64_t)timelimit);
log_tag("last_limit:%u\n", lastlimit);
log_tag("time_limit:%" PRIu64 "\n", (uint64_t)plan.timelimit);
log_tag("last_limit:%u\n", plan.lastlimit);
/* free the temp vector */
free(timemap);
@ -716,7 +725,7 @@ int state_scrub(struct snapraid_state* state, int percentage, int olderthan)
error = 0;
ret = state_scrub_process(state, parity_ptr, 0, blockmax, timelimit, lastlimit, now);
ret = state_scrub_process(state, parity_ptr, 0, blockmax, &plan, now);
if (ret == -1) {
++error;
/* continue, as we are already exiting */

View file

@ -514,8 +514,8 @@ int main(int argc, char* argv[])
case 'p' :
if (strcmp(optarg, "bad") == 0) {
percentage = SCRUB_BAD;
} else if (strcmp(optarg, "sync") == 0) {
percentage = SCRUB_SYNC;
} else if (strcmp(optarg, "new") == 0) {
percentage = SCRUB_NEW;
} else if (strcmp(optarg, "full") == 0) {
percentage = SCRUB_FULL;
} else {

View file

@ -246,8 +246,9 @@ void state_rehash(struct snapraid_state* state);
*/
#define SCRUB_AUTO -1 /**< Automatic selection. */
#define SCRUB_BAD -2 /**< Scrub only the bad blocks. */
#define SCRUB_SYNC -3 /**< Scub the latest synched data. */
#define SCRUB_NEW -3 /**< Scub the new blocks. */
#define SCRUB_FULL -4 /**< Scrub everything. */
#define SCRUB_EVEN -5 /**< Even blocks. */
/**
* Scrub the files.

View file

@ -18,7 +18,7 @@ snapraid [\-c, \-\-conf CONFIG]
.PD 0
.PP
.PD
[\-p, \-\-percentage PERC|bad|sync|full]
[\-p, \-\-percentage PERC|bad|new|full]
.PD 0
.PP
.PD
@ -307,7 +307,7 @@ snapraid scrub
This command verifies the data in your array comparing it with
the hash computed in the \[dq]sync\[dq] command.
.PP
Every run of the command checks the 15% of the array, but not data
Every run of the command checks the 12.5% of the array, but not data
already scrubbed in the previous 10 days.
You can use the \-p, \-\-percentage option to specify a different amount,
and the \-o, \-\-older\-than option to specify a different age in days.
@ -774,30 +774,23 @@ The files in the array are NOT modified.
Scrubs the array, checking for silent or input/output errors in data
and parity disks.
.PP
For each command invocation, the 15% of the array is checked, but
For each command invocation, the 12.5% of the array is checked, but
nothing that was already scrubbed in the last 10 days.
This means that scrubbing once a week, every bit of data is checked
at least one time every two months.
.PP
You can define a different scrub amount using the \-p, \-\-percentage
You can define a different scrub plan or amount using the \-p, \-\-percentage
option that takes as argument:
bad \- Scrub blocks marked bad.
sync \- Scrub new synced blocks never scrubbed.
new \- Scrub just synced blocks not yet scrubbed.
full \- Scrub everything.
0\-100 \- Scrub the exact percentage of blocks.
.PP
If you specify a percentage amount, you can also use the \-o, \-\-older\-than
option to define how old the block should be.
If the specified amount doesn\'t allow a full scrub, the blocks are
scrubbed with this priority:
.PD 0
.IP \(bu
First blocks marked as bad, to verify their status.
.IP \(bu
Then new synced blocks never scrubbed, to verify if their
parity data was written correctly.
.IP \(bu
And finally all the reimainig blocks, starting from ones with the oldest
scrub time.
.PD
The oldest blocks are scrubbed first ensuring an optimal check.
If instead you want to scrub the just synced blocks, not yet scrubbed,
you should use the \[dq]\-p new\[dq] option.
.PP
To get the details of the scrub status use the \[dq]status\[dq] command.
.PP
@ -985,12 +978,12 @@ It processes only the blocks marked with silent or input/output
errors during \[dq]sync\[dq] and \[dq]scrub\[dq], and listed in \[dq]status\[dq].
This option can be used only with \[dq]check\[dq] and \[dq]fix\[dq].
.TP
.B \-p, \-\-percentage PERC|bad|sync|full
Selects the part of the array to process in \[dq]scrub\[dq].
PERC is a numeric value from 0 to 100, default is 20.
Instead of a percentage, you can also specify the kind of scrub
you want: \[dq]bad\[dq] scrubs bad blocks, \[dq]sync\[dq] the blocks not yet
scrubbed, and \[dq]full\[dq] for everything.
.B \-p, \-\-percentage PERC|bad|new|full
Selects the scrub plan. If PERC is a numeric value from 0 to 100,
it\'s interpreted as the percentage of blocks to scrub.
Instead of a percentage, you can also specify a plan:
\[dq]bad\[dq] scrubs bad blocks, \[dq]new\[dq] the blocks not yet scrubbed,
and \[dq]full\[dq] for everything.
This option can be used only with \[dq]scrub\[dq].
.TP
.B \-o, \-\-older\-than DAYS

View file

@ -6,7 +6,7 @@ Synopsis
: [-f, --filter PATTERN] [-d, --filter-disk NAME]
: [-m, --filter-missing] [-e, --filter-error]
: [-a, --audit-only] [-h, --pre-hash] [-i, --import DIR]
: [-p, --percentage PERC|bad|sync|full]
: [-p, --percentage PERC|bad|new|full]
: [-o, --older-than DAYS] [-l, --log FILE]
: [-Z, --force-zero] [-E, --force-empty]
: [-U, --force-uuid] [-D, --force-device]
@ -171,7 +171,7 @@ Getting Started
This command verifies the data in your array comparing it with
the hash computed in the "sync" command.
Every run of the command checks the 15% of the array, but not data
Every run of the command checks the 12.5% of the array, but not data
already scrubbed in the previous 10 days.
You can use the -p, --percentage option to specify a different amount,
and the -o, --older-than option to specify a different age in days.
@ -482,26 +482,23 @@ Commands
Scrubs the array, checking for silent or input/output errors in data
and parity disks.
For each command invocation, the 15% of the array is checked, but
For each command invocation, the 12.5% of the array is checked, but
nothing that was already scrubbed in the last 10 days.
This means that scrubbing once a week, every bit of data is checked
at least one time every two months.
You can define a different scrub amount using the -p, --percentage
You can define a different scrub plan or amount using the -p, --percentage
option that takes as argument:
bad - Scrub blocks marked bad.
sync - Scrub new synced blocks never scrubbed.
new - Scrub just synced blocks not yet scrubbed.
full - Scrub everything.
0-100 - Scrub the exact percentage of blocks.
If you specify a percentage amount, you can also use the -o, --older-than
option to define how old the block should be.
If the specified amount doesn't allow a full scrub, the blocks are
scrubbed with this priority:
* First blocks marked as bad, to verify their status.
* Then new synced blocks never scrubbed, to verify if their
parity data was written correctly.
* And finally all the reimainig blocks, starting from ones with the oldest
scrub time.
The oldest blocks are scrubbed first ensuring an optimal check.
If instead you want to scrub the just synced blocks, not yet scrubbed,
you should use the "-p new" option.
To get the details of the scrub status use the "status" command.
@ -699,12 +696,12 @@ Options
errors during "sync" and "scrub", and listed in "status".
This option can be used only with "check" and "fix".
-p, --percentage PERC|bad|sync|full
Selects the part of the array to process in "scrub".
PERC is a numeric value from 0 to 100, default is 20.
Instead of a percentage, you can also specify the kind of scrub
you want: "bad" scrubs bad blocks, "sync" the blocks not yet
scrubbed, and "full" for everything.
-p, --percentage PERC|bad|new|full
Selects the scrub plan. If PERC is a numeric value from 0 to 100,
it's interpreted as the percentage of blocks to scrub.
Instead of a percentage, you can also specify a plan:
"bad" scrubs bad blocks, "new" the blocks not yet scrubbed,
and "full" for everything.
This option can be used only with "scrub".
-o, --older-than DAYS

View file

@ -10,7 +10,7 @@ snapraid [-c, --conf CONFIG]
[-f, --filter PATTERN] [-d, --filter-disk NAME]
[-m, --filter-missing] [-e, --filter-error]
[-a, --audit-only] [-h, --pre-hash] [-i, --import DIR]
[-p, --percentage PERC|bad|sync|full]
[-p, --percentage PERC|bad|new|full]
[-o, --older-than DAYS] [-l, --log FILE]
[-Z, --force-zero] [-E, --force-empty]
[-U, --force-uuid] [-D, --force-device]
@ -186,7 +186,7 @@ run the "scrub" command.
This command verifies the data in your array comparing it with
the hash computed in the "sync" command.
Every run of the command checks the 15% of the array, but not data
Every run of the command checks the 12.5% of the array, but not data
already scrubbed in the previous 10 days.
You can use the -p, --percentage option to specify a different amount,
and the -o, --older-than option to specify a different age in days.
@ -520,26 +520,23 @@ The files in the array are NOT modified.
Scrubs the array, checking for silent or input/output errors in data
and parity disks.
For each command invocation, the 15% of the array is checked, but
For each command invocation, the 12.5% of the array is checked, but
nothing that was already scrubbed in the last 10 days.
This means that scrubbing once a week, every bit of data is checked
at least one time every two months.
You can define a different scrub amount using the -p, --percentage
You can define a different scrub plan or amount using the -p, --percentage
option that takes as argument:
bad - Scrub blocks marked bad.
sync - Scrub new synced blocks never scrubbed.
new - Scrub just synced blocks not yet scrubbed.
full - Scrub everything.
0-100 - Scrub the exact percentage of blocks.
If you specify a percentage amount, you can also use the -o, --older-than
option to define how old the block should be.
If the specified amount doesn't allow a full scrub, the blocks are
scrubbed with this priority:
* First blocks marked as bad, to verify their status.
* Then new synced blocks never scrubbed, to verify if their
parity data was written correctly.
* And finally all the reimainig blocks, starting from ones with the oldest
scrub time.
The oldest blocks are scrubbed first ensuring an optimal check.
If instead you want to scrub the just synced blocks, not yet scrubbed,
you should use the "-p new" option.
To get the details of the scrub status use the "status" command.
@ -758,12 +755,12 @@ SnapRAID provides the following options:
errors during "sync" and "scrub", and listed in "status".
This option can be used only with "check" and "fix".
-p, --percentage PERC|bad|sync|full
Selects the part of the array to process in "scrub".
PERC is a numeric value from 0 to 100, default is 20.
Instead of a percentage, you can also specify the kind of scrub
you want: "bad" scrubs bad blocks, "sync" the blocks not yet
scrubbed, and "full" for everything.
-p, --percentage PERC|bad|new|full
Selects the scrub plan. If PERC is a numeric value from 0 to 100,
it's interpreted as the percentage of blocks to scrub.
Instead of a percentage, you can also specify a plan:
"bad" scrubs bad blocks, "new" the blocks not yet scrubbed,
and "full" for everything.
This option can be used only with "scrub".
-o, --older-than DAYS