@matheusbsbs / matheus-bagliano / commits / b08ff1fee0
Fork 0

maintenance: add --schedule option and config

Maintenance currently triggers when certain data-size thresholds are met, such as number of pack-files or loose objects. Users may want to run certain maintenance tasks based on frequency instead. For example, a user may want to perform a 'prefetch' task every hour, or 'gc' task every day. To help these users, update the 'git maintenance run' command to include a '--schedule=<frequency>' option. The allowed frequencies are 'hourly', 'daily', and 'weekly'. These values are also allowed in a new config value 'maintenance.<task>.schedule'. The 'git maintenance run --schedule=<frequency>' checks the '*.schedule' config value for each enabled task to see if the configured frequency is at least as frequent as the frequency from the '--schedule' argument. We use the following order, for full clarity: 'hourly' > 'daily' > 'weekly' Use new 'enum schedule_priority' to track these values numerically. The following cron table would run the scheduled tasks with the correct frequencies: 0 1-23 * * * git -C <repo> maintenance run --schedule=hourly 0 0 * * 1-6 git -C <repo> maintenance run --schedule=daily 0 0 * * 0 git -C <repo> maintenance run --schedule=weekly This cron schedule will run --schedule=hourly every hour except at midnight. This avoids a concurrent run with the --schedule=daily that runs at midnight every day except the first day of the week. This avoids a concurrent run with the --schedule=weekly that runs at midnight on the first day of the week. Since --schedule=daily also runs the 'hourly' tasks and --schedule=weekly runs the 'hourly' and 'daily' tasks, we will still see all tasks run with the proper frequencies. Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Sep 11, 2020 at 17:49 UTC b08ff1fee000230ad22fee823aabff2613bd4e62
4 files changed +118 -4
Documentation/config/maintenance.txt
+5
@@ -10,6 +10,11 @@ maintenance.<task>.enabled::
10 `--task` option exists. By default, only `maintenance.gc.enabled`
11 is true.
12
13 +maintenance.<task>.schedule::
14 + This config option controls whether or not the given `<task>` runs
15 + during a `git maintenance run --schedule=<frequency>` command. The
16 + value must be one of "hourly", "daily", or "weekly".
17 +
18 maintenance.commit-graph.auto::
19 This integer config option controls how often the `commit-graph` task
20 should be run as part of `git maintenance run --auto`. If zero, then
Documentation/git-maintenance.txt
+12 -1
@@ -110,7 +110,18 @@ OPTIONS
110 only if certain thresholds are met. For example, the `gc` task
111 runs when the number of loose objects exceeds the number stored
112 in the `gc.auto` config setting, or when the number of pack-files
113 - exceeds the `gc.autoPackLimit` config setting.
113 + exceeds the `gc.autoPackLimit` config setting. Not compatible with
114 + the `--schedule` option.
115 +
116 +--schedule::
117 + When combined with the `run` subcommand, run maintenance tasks
118 + only if certain time conditions are met, as specified by the
119 + `maintenance.<task>.schedule` config value for each `<task>`.
120 + This config value specifies a number of seconds since the last
121 + time that task ran, according to the `maintenance.<task>.lastRun`
122 + config value. The tasks that are tested are those provided by
123 + the `--task=<task>` option(s) or those with
124 + `maintenance.<task>.enabled` set to true.
125
126 --quiet::
127 Do not report progress or other information over `stderr`.
builtin/gc.c
+61 -3
@@ -703,14 +703,51 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
703 return 0;
704 }
705
706 -static const char * const builtin_maintenance_run_usage[] = {
707 - N_("git maintenance run [--auto] [--[no-]quiet] [--task=<task>]"),
706 +static const char *const builtin_maintenance_run_usage[] = {
707 + N_("git maintenance run [--auto] [--[no-]quiet] [--task=<task>] [--schedule]"),
708 NULL
709 };
710
711 +enum schedule_priority {
712 + SCHEDULE_NONE = 0,
713 + SCHEDULE_WEEKLY = 1,
714 + SCHEDULE_DAILY = 2,
715 + SCHEDULE_HOURLY = 3,
716 +};
717 +
718 +static enum schedule_priority parse_schedule(const char *value)
719 +{
720 + if (!value)
721 + return SCHEDULE_NONE;
722 + if (!strcasecmp(value, "hourly"))
723 + return SCHEDULE_HOURLY;
724 + if (!strcasecmp(value, "daily"))
725 + return SCHEDULE_DAILY;
726 + if (!strcasecmp(value, "weekly"))
727 + return SCHEDULE_WEEKLY;
728 + return SCHEDULE_NONE;
729 +}
730 +
731 +static int maintenance_opt_schedule(const struct option *opt, const char *arg,
732 + int unset)
733 +{
734 + enum schedule_priority *priority = opt->value;
735 +
736 + if (unset)
737 + die(_("--no-schedule is not allowed"));
738 +
739 + *priority = parse_schedule(arg);
740 +
741 + if (!*priority)
742 + die(_("unrecognized --schedule argument '%s'"), arg);
743 +
744 + return 0;
745 +}
746 +
747 struct maintenance_run_opts {
748 int auto_flag;
749 int quiet;
750 + enum schedule_priority schedule;
751 };
752
753 /* Remember to update object flag allocation in object.h */
@@ -1158,6 +1195,8 @@ struct maintenance_task {
1195 maintenance_auto_fn *auto_condition;
1196 unsigned enabled:1;
1197
1198 + enum schedule_priority schedule;
1199 +
1200 /* -1 if not selected. */
1201 int selected_order;
1202 };
@@ -1253,6 +1292,9 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts)
1292 !tasks[i].auto_condition()))
1293 continue;
1294
1295 + if (opts->schedule && tasks[i].schedule < opts->schedule)
1296 + continue;
1297 +
1298 trace2_region_enter("maintenance", tasks[i].name, r);
1299 if (tasks[i].fn(opts)) {
1300 error(_("task '%s' failed"), tasks[i].name);
@@ -1273,13 +1315,23 @@ static void initialize_task_config(void)
1315
1316 for (i = 0; i < TASK__COUNT; i++) {
1317 int config_value;
1318 + char *config_str;
1319
1277 - strbuf_setlen(&config_name, 0);
1320 + strbuf_reset(&config_name);
1321 strbuf_addf(&config_name, "maintenance.%s.enabled",
1322 tasks[i].name);
1323
1324 if (!git_config_get_bool(config_name.buf, &config_value))
1325 tasks[i].enabled = config_value;
1326 +
1327 + strbuf_reset(&config_name);
1328 + strbuf_addf(&config_name, "maintenance.%s.schedule",
1329 + tasks[i].name);
1330 +
1331 + if (!git_config_get_string(config_name.buf, &config_str)) {
1332 + tasks[i].schedule = parse_schedule(config_str);
1333 + free(config_str);
1334 + }
1335 }
1336
1337 strbuf_release(&config_name);
@@ -1323,6 +1375,9 @@ static int maintenance_run(int argc, const char **argv, const char *prefix)
1375 struct option builtin_maintenance_run_options[] = {
1376 OPT_BOOL(0, "auto", &opts.auto_flag,
1377 N_("run tasks based on the state of the repository")),
1378 + OPT_CALLBACK(0, "schedule", &opts.schedule, N_("frequency"),
1379 + N_("run tasks based on frequency"),
1380 + maintenance_opt_schedule),
1381 OPT_BOOL(0, "quiet", &opts.quiet,
1382 N_("do not report progress or other information over stderr")),
1383 OPT_CALLBACK_F(0, "task", NULL, N_("task"),
@@ -1343,6 +1398,9 @@ static int maintenance_run(int argc, const char **argv, const char *prefix)
1398 builtin_maintenance_run_usage,
1399 PARSE_OPT_STOP_AT_NON_OPTION);
1400
1401 + if (opts.auto_flag && opts.schedule)
1402 + die(_("use at most one of --auto and --schedule=<frequency>"));
1403 +
1404 if (argc != 0)
1405 usage_with_options(builtin_maintenance_run_usage,
1406 builtin_maintenance_run_options);
t/t7900-maintenance.sh
+40
@@ -260,4 +260,44 @@ test_expect_success 'maintenance.incremental-repack.auto' '
260 test_subcommand git multi-pack-index write --no-progress <trace-B
261 '
262
263 +test_expect_success '--auto and --schedule incompatible' '
264 + test_must_fail git maintenance run --auto --schedule=daily 2>err &&
265 + test_i18ngrep "at most one" err
266 +'
267 +
268 +test_expect_success 'invalid --schedule value' '
269 + test_must_fail git maintenance run --schedule=annually 2>err &&
270 + test_i18ngrep "unrecognized --schedule" err
271 +'
272 +
273 +test_expect_success '--schedule inheritance weekly -> daily -> hourly' '
274 + git config maintenance.loose-objects.enabled true &&
275 + git config maintenance.loose-objects.schedule hourly &&
276 + git config maintenance.commit-graph.enabled true &&
277 + git config maintenance.commit-graph.schedule daily &&
278 + git config maintenance.incremental-repack.enabled true &&
279 + git config maintenance.incremental-repack.schedule weekly &&
280 +
281 + GIT_TRACE2_EVENT="$(pwd)/hourly.txt" \
282 + git maintenance run --schedule=hourly 2>/dev/null &&
283 + test_subcommand git prune-packed --quiet <hourly.txt &&
284 + test_subcommand ! git commit-graph write --split --reachable \
285 + --no-progress <hourly.txt &&
286 + test_subcommand ! git multi-pack-index write --no-progress <hourly.txt &&
287 +
288 + GIT_TRACE2_EVENT="$(pwd)/daily.txt" \
289 + git maintenance run --schedule=daily 2>/dev/null &&
290 + test_subcommand git prune-packed --quiet <daily.txt &&
291 + test_subcommand git commit-graph write --split --reachable \
292 + --no-progress <daily.txt &&
293 + test_subcommand ! git multi-pack-index write --no-progress <daily.txt &&
294 +
295 + GIT_TRACE2_EVENT="$(pwd)/weekly.txt" \
296 + git maintenance run --schedule=weekly 2>/dev/null &&
297 + test_subcommand git prune-packed --quiet <weekly.txt &&
298 + test_subcommand git commit-graph write --split --reachable \
299 + --no-progress <weekly.txt &&
300 + test_subcommand git multi-pack-index write --no-progress <weekly.txt
301 +'
302 +
303 test_done