Skip to content

Commit

Permalink
Issue 5532 - Make db compaction TOD day more robust.
Browse files Browse the repository at this point in the history
Bug Description:

The time of day compaction setting does promise that the compaction
will happen as configured.  This is becuase the compaction interval
starts when the server is started.  Once it wakes up and we are "past"
the TOD setting then we compact, but it can happen at any time
once the TOD has passed.

Fix Description:

Once the compaction interval is hit we create an "event" with the
exact time the compaction should start.

relates: #5532

Reviewed by: tbordaz & spichugi(Thanks!!)
  • Loading branch information
mreynolds389 committed Nov 17, 2022
1 parent 9d3f233 commit 4945895
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 76 deletions.
35 changes: 32 additions & 3 deletions dirsrvtests/tests/suites/config/compact_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
# --- BEGIN COPYRIGHT BLOCK ---
# Copyright (C) 2022 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---

import logging
import pytest
import os
import time
import datetime
from lib389.utils import get_default_db_lib
from lib389.tasks import DBCompactTask
from lib389.backend import DatabaseConfig
Expand Down Expand Up @@ -72,11 +81,31 @@ def test_compaction_interval_and_time(topo):
"""

inst = topo.ms["supplier1"]

# Calculate the compaction time (1 minute from now)
now = datetime.datetime.now()
current_hour = now.hour
current_minute = now.minute + 2
if current_hour < 10:
hour = "0" + str(current_hour)
else:
hour = str(current_hour)
if current_minute < 10:
minute = "0" + str(current_minute)
else:
minute = str(current_minute)
compact_time = hour + ":" + minute

# Set compaction TOD
config = DatabaseConfig(inst)
config.set([('nsslapd-db-compactdb-interval', '2'), ('nsslapd-db-compactdb-time', '00:01')])
inst.deleteErrorLogs()
config.set([('nsslapd-db-compactdb-interval', '2'), ('nsslapd-db-compactdb-time', compact_time)])
inst.deleteErrorLogs(restart=True)

# Check compaction occurred as expected
time.sleep(60)
assert not inst.searchErrorsLog("Compacting databases")

time.sleep(6)
time.sleep(61)
assert inst.searchErrorsLog("Compacting databases")
inst.deleteErrorLogs(restart=False)

Expand Down
161 changes: 89 additions & 72 deletions ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ static int trans_batch_txn_max_sleep = 50;
static PRBool log_flush_thread = PR_FALSE;
static int txn_in_progress_count = 0;
static int *txn_log_flush_pending = NULL;
static PRBool compaction_scheduled = PR_FALSE;

static pthread_mutex_t sync_txn_log_flush;
static pthread_cond_t sync_txn_log_flush_done;
Expand Down Expand Up @@ -3732,13 +3733,12 @@ bdb_log_flush_threadmain(void *param)
}

/*
* This refreshes the TOD expiration. So live changes to the configuration
* will take effect immediately.
* Get the time in seconds when the compaction should occur
*/
static time_t
bdb_get_tod_expiration(char *expire_time)
{
time_t start_time, todays_elapsed_time, now = time(NULL);
time_t todays_elapsed_time, now = time(NULL);
struct tm *tm_struct = localtime(&now);
char hour_str[3] = {0};
char min_str[3] = {0};
Expand All @@ -3748,9 +3748,8 @@ bdb_get_tod_expiration(char *expire_time)

/* Get today's start time */
todays_elapsed_time = (tm_struct->tm_hour * 3600) + (tm_struct->tm_min * 60) + (tm_struct->tm_sec);
start_time = slapi_current_utc_time() - todays_elapsed_time;

/* Get the hour and minute and calculate the expiring time. The time was
/* Get the hour and minute and calculate the expiring TOD. The time was
* already validated in bdb_config.c: HH:MM */
hour_str[0] = *s++;
hour_str[1] = *s++;
Expand All @@ -3761,7 +3760,73 @@ bdb_get_tod_expiration(char *expire_time)
min = strtoll(min_str, &endp, 10);
expiring_time = (hour * 60 * 60) + (min * 60);

return start_time + expiring_time;
/* Calculate the time in seconds when the compaction should start, midnight
* requires special treatment (for both current time and configured TOD) */
if (expiring_time == 0) {
/* Compaction TOD configured for midnight */
if (todays_elapsed_time == 0) {
/* It's currently midnight, compact now! */
return 0;
} else {
/* Return the time until it's midnight */
return _SEC_PER_DAY - todays_elapsed_time;
}
} else if (todays_elapsed_time == 0) {
/* It's currently midnight, just use the configured TOD */
return expiring_time;
} else if (todays_elapsed_time > expiring_time) {
/* We missed TOD today, do it tomorrow */
return _SEC_PER_DAY - (todays_elapsed_time - expiring_time);
} else {
/* Compaction is coming up later today */
return expiring_time - todays_elapsed_time;
}
}

static void
bdb_compact(time_t when, void *arg)
{
struct ldbminfo *li = (struct ldbminfo *)arg;
Object *inst_obj;
ldbm_instance *inst;
DB *db = NULL;
int rc = 0;

for (inst_obj = objset_first_obj(li->li_instance_set);
inst_obj;
inst_obj = objset_next_obj(li->li_instance_set, inst_obj))
{
inst = (ldbm_instance *)object_get_data(inst_obj);
rc = dblayer_get_id2entry(inst->inst_be, (dbi_db_t **)&db);
if (!db || rc) {
continue;
}
slapi_log_err(SLAPI_LOG_NOTICE, "bdb_compact", "Compacting DB start: %s\n",
inst->inst_name);

rc = bdb_db_compact_one_db(db, inst);
if (rc) {
slapi_log_err(SLAPI_LOG_ERR, "bdb_compact",
"Failed to compact id2entry for %s; db error - %d %s\n",
inst->inst_name, rc, db_strerror(rc));
break;
}

/* Time to compact the DB's */
bdb_force_checkpoint(li);
bdb_do_compact(li, PR_FALSE);
bdb_force_checkpoint(li);

/* Now reset the timer and compacting flag */
rc = bdb_db_compact_one_db(db, inst);
if (rc) {
slapi_log_err(SLAPI_LOG_ERR, "bdb_compact",
"Failed to compact for %s; db error - %d %s\n",
inst->inst_name, rc, db_strerror(rc));
break;
}
}
compaction_scheduled = PR_FALSE;
}

/*
Expand Down Expand Up @@ -3806,7 +3871,6 @@ bdb_checkpoint_threadmain(void *param)
time_t compactdb_interval = 0;
time_t checkpoint_interval = 0;
int32_t compactdb_time = 0;
PRBool compacting = PR_FALSE;

PR_ASSERT(NULL != param);
li = (struct ldbminfo *)param;
Expand Down Expand Up @@ -3848,15 +3912,6 @@ bdb_checkpoint_threadmain(void *param)
PR_Lock(li->li_config_mutex);
checkpoint_interval_update = (time_t)BDB_CONFIG(li)->bdb_checkpoint_interval;
compactdb_interval_update = (time_t)BDB_CONFIG(li)->bdb_compactdb_interval;
if (!compacting) {
/* Once we know we want to compact we need to stop refreshing the
* TOD expiration. Otherwise if the compact time is close to
* midnight we could roll over past midnight during the checkpoint
* sleep interval, and we'd never actually compact the databases.
* We also need to get this value before the sleep.
*/
compactdb_time = bdb_get_tod_expiration((char *)BDB_CONFIG(li)->bdb_compactdb_time);
}
PR_Unlock(li->li_config_mutex);

if (compactdb_interval_update != compactdb_interval) {
Expand Down Expand Up @@ -3946,59 +4001,21 @@ bdb_checkpoint_threadmain(void *param)
* this could have been a bug in fact, where compactdb_interval
* was 0, if you change while running it would never take effect ....
*/
if (slapi_timespec_expire_check(&compactdb_expire) == TIMER_EXPIRED) {
compacting = PR_TRUE;
if (slapi_current_utc_time() < compactdb_time) {
/* We have passed the interval, but we need to wait for a
* particular TOD to pass before compacting */
continue;
}
}

if (compactdb_interval_update != compactdb_interval ||
slapi_timespec_expire_check(&compactdb_expire) == TIMER_EXPIRED) {
int rc = 0;
Object *inst_obj;
ldbm_instance *inst;
DB *db = NULL;

for (inst_obj = objset_first_obj(li->li_instance_set);
inst_obj;
inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
inst = (ldbm_instance *)object_get_data(inst_obj);
rc = dblayer_get_id2entry(inst->inst_be, (dbi_db_t **)&db);
if (!db || rc) {
continue;
}
slapi_log_err(SLAPI_LOG_NOTICE, "bdb_checkpoint_threadmain", "Compacting DB start: %s\n",
inst->inst_name);

rc = bdb_db_compact_one_db(db, inst);
if (rc) {
slapi_log_err(SLAPI_LOG_ERR, "bdb_checkpoint_threadmain",
"compactdb: failed to compact id2entry for %s; db error - %d %s\n",
inst->inst_name, rc, db_strerror(rc));
break;
}
(slapi_timespec_expire_check(&compactdb_expire) == TIMER_EXPIRED && !compaction_scheduled))
{
/* Get the time in second when the compaction should occur */
PR_Lock(li->li_config_mutex);
compactdb_time = bdb_get_tod_expiration((char *)BDB_CONFIG(li)->bdb_compactdb_time);
PR_Unlock(li->li_config_mutex);

/* Time to compact the DB's */
bdb_force_checkpoint(li);
bdb_compact(li, PR_FALSE);
bdb_force_checkpoint(li);

/* Now reset the timer and compacting flag */
rc = bdb_db_compact_one_db(db, inst);
if (rc) {
slapi_log_err(SLAPI_LOG_ERR, "bdb_checkpoint_threadmain",
"compactdb: failed to compact changelog for %s; db error - %d %s\n",
inst->inst_name, rc, db_strerror(rc));
break;
}
}
/* Start compaction event */
compaction_scheduled = PR_TRUE;
slapi_eq_once_rel(bdb_compact, (void *)li, slapi_current_rel_time_t() + compactdb_time);

/* reset interval timer */
compactdb_interval = compactdb_interval_update;
slapi_timespec_expire_at(compactdb_interval, &compactdb_expire);
compacting = PR_FALSE;
}
}
slapi_log_err(SLAPI_LOG_TRACE, "bdb_checkpoint_threadmain", "Check point before leaving\n");
Expand Down Expand Up @@ -7078,20 +7095,20 @@ bdb_public_dblayer_compact(Slapi_Backend *be, PRBool just_changelog)

li = (struct ldbminfo *)be->be_database->plg_private;
bdb_force_checkpoint(li);
rc = bdb_compact(li, just_changelog);
rc = bdb_do_compact(li, just_changelog);
bdb_force_checkpoint(li);
return rc;
}

int
bdb_compact(struct ldbminfo *li, PRBool just_changelog)
bdb_do_compact(struct ldbminfo *li, PRBool just_changelog)
{
Object *inst_obj;
ldbm_instance *inst;
DB *db = NULL;
int rc = 0;

slapi_log_err(SLAPI_LOG_NOTICE, "bdb_compact",
slapi_log_err(SLAPI_LOG_NOTICE, "bdb_do_compact",
"Compacting databases ...\n");
for (inst_obj = objset_first_obj(li->li_instance_set);
inst_obj;
Expand All @@ -7103,33 +7120,33 @@ bdb_compact(struct ldbminfo *li, PRBool just_changelog)
if (!db || rc) {
continue;
}
slapi_log_err(SLAPI_LOG_NOTICE, "bdb_compact",
slapi_log_err(SLAPI_LOG_NOTICE, "bdb_do_compact",
"Compacting DB: %s\n", inst->inst_name);
rc = bdb_db_compact_one_db(db, inst);
if (rc) {
slapi_log_err(SLAPI_LOG_ERR, "bdb_compact",
slapi_log_err(SLAPI_LOG_ERR, "bdb_do_compact",
"failed to compact id2entry for %s; db error - %d %s\n",
inst->inst_name, rc, db_strerror(rc));
break;
}
}

/* Compact changelog db */
slapi_log_err(SLAPI_LOG_NOTICE, "bdb_compact",
slapi_log_err(SLAPI_LOG_NOTICE, "bdb_do_compact",
"Compacting Replication Changelog: %s\n", inst->inst_name);
dblayer_get_changelog(inst->inst_be, (dbi_db_t **)&db, 0);
if (db) {
rc = bdb_db_compact_one_db(db, inst);
if (rc) {
slapi_log_err(SLAPI_LOG_ERR, "bdb_compact",
slapi_log_err(SLAPI_LOG_ERR, "bdb_do_compact",
"failed to compact changelog for %s; db error - %d %s\n",
inst->inst_name, rc, db_strerror(rc));
break;
}
}
}

slapi_log_err(SLAPI_LOG_NOTICE, "bdb_compact", "Compacting databases finished.\n");
slapi_log_err(SLAPI_LOG_NOTICE, "bdb_do_compact", "Compacting databases finished.\n");

return rc;
}
Expand Down
2 changes: 1 addition & 1 deletion ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ int bdb_db_size(Slapi_PBlock *pb);
int bdb_upgradedb(Slapi_PBlock *pb);
int bdb_upgradednformat(Slapi_PBlock *pb);
int bdb_upgradeddformat(Slapi_PBlock *pb);
int bdb_compact(struct ldbminfo *li, PRBool just_changelog);
int bdb_do_compact(struct ldbminfo *li, PRBool just_changelog);
int bdb_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task);
int bdb_cleanup(struct ldbminfo *li);
int bdb_txn_begin(struct ldbminfo *li, back_txnid parent_txn, back_txn *txn, PRBool use_lock);
Expand Down

0 comments on commit 4945895

Please sign in to comment.