Add TXG timestamp database

This feature enables tracking of when TXGs are committed to disk,
providing an estimated timestamp for each TXG.

With this information, it becomes possible to perform scrubs based
on specific date ranges, improving the granularity of data
management and recovery operations.

Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com>
Reviewed-by: Paul Dagnelie <paul.dagnelie@klarasystems.com>
Signed-off-by: Mariusz Zaborski <mariusz.zaborski@klarasystems.com>
Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Closes #16853
This commit is contained in:
Mariusz Zaborski
2025-08-06 19:31:21 +02:00
committed by GitHub
parent c3496b5cc6
commit 894edd084e
21 changed files with 736 additions and 10 deletions
+57 -6
View File
@@ -513,8 +513,8 @@ get_usage(zpool_help_t idx)
return (gettext("\tinitialize [-c | -s | -u] [-w] <-a | <pool> "
"[<device> ...]>\n"));
case HELP_SCRUB:
return (gettext("\tscrub [-e | -s | -p | -C] [-w] <-a | "
"<pool> [<pool> ...]>\n"));
return (gettext("\tscrub [-e | -s | -p | -C | -E | -S] [-w] "
"<-a | <pool> [<pool> ...]>\n"));
case HELP_RESILVER:
return (gettext("\tresilver <pool> ...\n"));
case HELP_TRIM:
@@ -8359,6 +8359,8 @@ zpool_do_reopen(int argc, char **argv)
typedef struct scrub_cbdata {
int cb_type;
pool_scrub_cmd_t cb_scrub_cmd;
time_t cb_date_start;
time_t cb_date_end;
} scrub_cbdata_t;
static boolean_t
@@ -8402,8 +8404,8 @@ scrub_callback(zpool_handle_t *zhp, void *data)
return (1);
}
err = zpool_scan(zhp, cb->cb_type, cb->cb_scrub_cmd);
err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
cb->cb_date_start, cb->cb_date_end);
if (err == 0 && zpool_has_checkpoint(zhp) &&
cb->cb_type == POOL_SCAN_SCRUB) {
(void) printf(gettext("warning: will not scrub state that "
@@ -8421,10 +8423,34 @@ wait_callback(zpool_handle_t *zhp, void *data)
return (zpool_wait(zhp, *act));
}
static time_t
date_string_to_sec(const char *timestr, boolean_t rounding)
{
struct tm tm = {0};
int adjustment = rounding ? 1 : 0;
/* Allow mktime to determine timezone. */
tm.tm_isdst = -1;
if (strptime(timestr, "%Y-%m-%d %H:%M", &tm) == NULL) {
if (strptime(timestr, "%Y-%m-%d", &tm) == NULL) {
fprintf(stderr, gettext("Failed to parse the date.\n"));
usage(B_FALSE);
}
adjustment *= 24 * 60 * 60;
} else {
adjustment *= 60;
}
return (mktime(&tm) + adjustment);
}
/*
* zpool scrub [-e | -s | -p | -C] [-w] <pool> ...
* zpool scrub [-e | -s | -p | -C | -E | -S] [-w] <pool> ...
*
* -e Only scrub blocks in the error log.
* -E End date of scrub.
* -S Start date of scrub.
* -s Stop. Stops any in-progress scrub.
* -p Pause. Pause in-progress scrub.
* -w Wait. Blocks until scrub has completed.
@@ -8440,6 +8466,7 @@ zpool_do_scrub(int argc, char **argv)
cb.cb_type = POOL_SCAN_SCRUB;
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
cb.cb_date_start = cb.cb_date_end = 0;
boolean_t is_error_scrub = B_FALSE;
boolean_t is_pause = B_FALSE;
@@ -8448,7 +8475,7 @@ zpool_do_scrub(int argc, char **argv)
boolean_t scrub_all = B_FALSE;
/* check options */
while ((c = getopt(argc, argv, "aspweC")) != -1) {
while ((c = getopt(argc, argv, "aspweCE:S:")) != -1) {
switch (c) {
case 'a':
scrub_all = B_TRUE;
@@ -8456,9 +8483,19 @@ zpool_do_scrub(int argc, char **argv)
case 'e':
is_error_scrub = B_TRUE;
break;
case 'E':
/*
* Round the date. It's better to scrub more data than
* less. This also makes the date inclusive.
*/
cb.cb_date_end = date_string_to_sec(optarg, B_TRUE);
break;
case 's':
is_stop = B_TRUE;
break;
case 'S':
cb.cb_date_start = date_string_to_sec(optarg, B_FALSE);
break;
case 'p':
is_pause = B_TRUE;
break;
@@ -8506,6 +8543,19 @@ zpool_do_scrub(int argc, char **argv)
}
}
if ((cb.cb_date_start != 0 || cb.cb_date_end != 0) &&
cb.cb_scrub_cmd != POOL_SCRUB_NORMAL) {
(void) fprintf(stderr, gettext("invalid option combination: "
"start/end date is available only with normal scrub\n"));
usage(B_FALSE);
}
if (cb.cb_date_start != 0 && cb.cb_date_end != 0 &&
cb.cb_date_start > cb.cb_date_end) {
(void) fprintf(stderr, gettext("invalid arguments: "
"end date has to be later than start date\n"));
usage(B_FALSE);
}
if (wait && (cb.cb_type == POOL_SCAN_NONE ||
cb.cb_scrub_cmd == POOL_SCRUB_PAUSE)) {
(void) fprintf(stderr, gettext("invalid option combination: "
@@ -8546,6 +8596,7 @@ zpool_do_resilver(int argc, char **argv)
cb.cb_type = POOL_SCAN_RESILVER;
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
cb.cb_date_start = cb.cb_date_end = 0;
/* check options */
while ((c = getopt(argc, argv, "")) != -1) {