Skip to content

Commit 5790280

Browse files
committed
Bound reload autosize high-water growth
The previous reload autosize logic used LMDB's me_last_pgno high-water mark as a direct sizing input. That value can remain high or creep upward across repeated drop/rebuild cycles even when the active trust database entry set is stable, so auto mode could keep increasing the map while reporting misleading active-page capacity warnings around 40-50 percent. Compute reload headroom from a bounded rebuild working set instead: the active DB plus one extra active-sized copy for copy-on-write reloads where old pages or freelist bookkeeping cannot be reused immediately. Auto-mode size messages are now informational and distinguish active capacity from reload headroom, while manual sizing still warns because administrators must act on it.
1 parent 395e1e2 commit 5790280

1 file changed

Lines changed: 75 additions & 18 deletions

File tree

src/library/database.c

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ typedef enum { DB_NO_OP, ONE_FILE, RELOAD_DB, FLUSH_CACHE, RELOAD_RULES } db_ops
7070
#define TRUST_DB_RELOAD_HIGHWATER_PERCENT 85
7171
#define TRUST_DB_SHRINK_TRIGGER_PERCENT 65
7272
#define TRUST_DB_SHRINK_HYSTERESIS_PERCENT 75
73+
#define TRUST_DB_RELOAD_WORK_FACTOR 2
7374
/*
7475
* The decision worker configuration is added later in the worker-pool
7576
* roadmap. Size LMDB readers now for that planned cap plus maintenance users
@@ -136,6 +137,7 @@ struct trust_db_sizing_state {
136137
size_t map_pages;
137138
size_t active_pages;
138139
size_t allocated_pages;
140+
size_t reload_work_pages;
139141
size_t active_target_pages;
140142
size_t highwater_target_pages;
141143
size_t recommended_pages;
@@ -339,6 +341,20 @@ static size_t pages_for_percent(size_t pages, unsigned int percent)
339341
return ((pages * 100) + percent - 1) / percent;
340342
}
341343

344+
/*
345+
* pages_times - multiply page counts without wrapping.
346+
* @pages: base page count.
347+
* @factor: integer multiplier.
348+
*
349+
* Returns the product, or the largest size_t value if it would overflow.
350+
*/
351+
static size_t pages_times(size_t pages, unsigned int factor)
352+
{
353+
if (factor != 0 && pages > ((size_t)-1) / factor)
354+
return (size_t)-1;
355+
return pages * factor;
356+
}
357+
342358
/*
343359
* percent_of - calculate an integer percentage.
344360
* @used: numerator value.
@@ -397,10 +413,33 @@ static unsigned int pages_to_mb(size_t pages, size_t page_size)
397413
*/
398414
static void complete_lmdb_sizing_state(struct trust_db_sizing_state *state)
399415
{
416+
size_t reload_cap = pages_times(state->active_pages,
417+
TRUST_DB_RELOAD_WORK_FACTOR);
418+
419+
/*
420+
* me_last_pgno is LMDB's file high-water mark. It is useful for
421+
* seeing that reload churn has touched most of the map, but it is not
422+
* the same as live trust data. If auto sizing chases that monotonic
423+
* high-water mark directly, every drop/rebuild can make the next
424+
* target slightly larger even when the backend entry set is unchanged.
425+
*
426+
* Bound the reload target to a working set derived from the active DB:
427+
* one copy for the currently published trust set and one copy for a
428+
* rebuild when old pages cannot be reused immediately because LMDB
429+
* readers or freelist bookkeeping still reference them. This keeps
430+
* enough headroom for copy-on-write reloads without turning file
431+
* high-water growth into permanent map growth.
432+
*/
433+
state->reload_work_pages = state->allocated_pages;
434+
if (state->reload_work_pages < state->active_pages)
435+
state->reload_work_pages = state->active_pages;
436+
if (reload_cap && state->reload_work_pages > reload_cap)
437+
state->reload_work_pages = reload_cap;
438+
400439
state->active_target_pages = pages_for_percent(state->active_pages,
401440
TRUST_DB_ACTIVE_TARGET_PERCENT);
402441
state->highwater_target_pages =
403-
pages_for_percent(state->allocated_pages,
442+
pages_for_percent(state->reload_work_pages,
404443
TRUST_DB_RELOAD_HIGHWATER_PERCENT);
405444
state->recommended_pages = state->active_target_pages;
406445
if (state->highwater_target_pages > state->recommended_pages)
@@ -506,10 +545,10 @@ static int autosize_shrink_allowed(const conf_t *config,
506545
*
507546
* Auto sizing has two targets. The active-page target keeps the final trust
508547
* database near 75 percent utilization so normal package changes have room.
509-
* The high-water target keeps LMDB allocated pages below 85 percent so a full
510-
* drop/rebuild reload has room to commit metadata before old pages are
511-
* reusable. Manual configurations are not changed here; callers should log
512-
* the recommended size for administrators.
548+
* The reload target keeps a bounded copy-on-write working set below 85
549+
* percent so a full drop/rebuild reload has room to commit metadata before
550+
* old pages are reusable. Manual configurations are not changed here; callers
551+
* should log the recommended size for administrators.
513552
*
514553
* Returns 1 when config->db_max_size changed, 0 otherwise.
515554
*/
@@ -530,11 +569,12 @@ static int apply_autosize_plan(conf_t *config,
530569
msg(LOG_INFO,
531570
"autosize: %s growing map %u->%u MiB "
532571
"(entries=%zu active=%zu/%zu pages %lu%%, "
533-
"allocated=%zu/%zu pages %lu%%)",
572+
"allocated=%zu/%zu pages %lu%%, reload_work=%zu)",
534573
context, config->db_max_size, target_mb, state->entries,
535574
state->active_pages, state->map_pages,
536575
state->active_percent, state->allocated_pages,
537-
state->map_pages, state->allocated_percent);
576+
state->map_pages, state->allocated_percent,
577+
state->reload_work_pages);
538578
config->db_max_size = target_mb;
539579
if (autosize_reload_floor_mb < target_mb)
540580
autosize_reload_floor_mb = target_mb;
@@ -554,11 +594,11 @@ static int apply_autosize_plan(conf_t *config,
554594
msg(LOG_INFO,
555595
"autosize: %s shrinking map %u->%u MiB "
556596
"(entries=%zu active=%zu/%zu pages %lu%%, "
557-
"allocated=%zu/%zu pages %lu%%)",
597+
"allocated=%zu/%zu pages %lu%%, reload_work=%zu)",
558598
context, config->db_max_size, target_mb, state->entries,
559599
state->active_pages, state->map_pages, state->active_percent,
560600
state->allocated_pages, state->map_pages,
561-
state->allocated_percent);
601+
state->allocated_percent, state->reload_work_pages);
562602
config->db_max_size = target_mb;
563603
return 1;
564604
}
@@ -1018,30 +1058,47 @@ static void check_db_size(const conf_t *config)
10181058
max_pages = state.map_pages;
10191059

10201060
// Active DB pages can stay steady while LMDB's high-water mark grows
1021-
// if readers pin old pages across repeated rebuilds.
1061+
// across repeated rebuilds. In auto mode this is informational unless
1062+
// a write actually fails; manual mode keeps it as a warning because the
1063+
// administrator may need to adjust db_max_size.
10221064
msg(LOG_DEBUG,
10231065
"Trust database active pages: %lu (%lu%%), allocated pages: %zu (%lu%%)",
10241066
pages, state.active_percent, state.allocated_pages,
10251067
state.allocated_percent);
10261068
if (state.allocated_percent > TRUST_DB_RELOAD_HIGHWATER_PERCENT &&
10271069
state.allocated_percent > state.active_percent) {
1028-
msg(LOG_WARNING,
1070+
int priority = config->do_audit_db_sizing ? LOG_INFO :
1071+
LOG_WARNING;
1072+
1073+
msg(priority,
10291074
"Trust database LMDB map high-water at %lu%% capacity "
10301075
"while active DB pages are at %lu%%",
10311076
state.allocated_percent, state.active_percent);
1032-
log_lmdb_state(LOG_WARNING, "trust database size check", 0);
1077+
if (!config->do_audit_db_sizing)
1078+
log_lmdb_state(LOG_WARNING,
1079+
"trust database size check", 0);
10331080
}
10341081

10351082
target_mb = autosize_effective_target_mb(&state);
10361083
if (config->do_audit_db_sizing) {
1037-
if (target_mb > config->db_max_size)
1038-
msg(LOG_WARNING, "Trust database at %lu%% capacity - "
1039-
"map will grow automatically before next rebuild",
1040-
state.active_percent);
1041-
else if (autosize_shrink_allowed(config, &state, target_mb))
1042-
msg(LOG_WARNING, "Trust database at %lu%% capacity - "
1084+
if (target_mb > config->db_max_size) {
1085+
if (state.active_target_pages > state.map_pages)
1086+
msg(LOG_INFO,
1087+
"Trust database at %lu%% capacity - "
1088+
"map will grow automatically before next rebuild",
1089+
state.active_percent);
1090+
else
1091+
msg(LOG_INFO,
1092+
"Trust database reload headroom target is %u MiB "
1093+
"(active=%lu%% high-water=%lu%%) - "
1094+
"map will grow automatically before next rebuild",
1095+
target_mb, state.active_percent,
1096+
state.allocated_percent);
1097+
} else if (autosize_shrink_allowed(config, &state, target_mb)) {
1098+
msg(LOG_INFO, "Trust database at %lu%% capacity - "
10431099
"map will shrink automatically on next rebuild",
10441100
state.active_percent);
1101+
}
10451102
return;
10461103
}
10471104

0 commit comments

Comments
 (0)