Blame SOURCES/0009-Issue-4623-RFE-Monitor-the-current-DB-locks-4762.patch

5d81fc
From 8df95679519364d0993572ecbea72ab89e5250a5 Mon Sep 17 00:00:00 2001
5d81fc
From: Simon Pichugin <spichugi@redhat.com>
5d81fc
Date: Thu, 20 May 2021 14:24:25 +0200
5d81fc
Subject: [PATCH 09/12] Issue 4623 - RFE - Monitor the current DB locks (#4762)
5d81fc
5d81fc
Description: DB lock gets exhausted because of unindexed internal searches
5d81fc
(under a transaction). Indexing those searches is the way to prevent exhaustion.
5d81fc
If db lock get exhausted during a txn, it leads to db panic and the later recovery
5d81fc
can possibly fail. That leads to a full reinit of the instance where the db locks
5d81fc
got exhausted.
5d81fc
5d81fc
Add three attributes to global BDB config: "nsslapd-db-locks-monitoring-enabled",
5d81fc
 "nsslapd-db-locks-monitoring-threshold" and "nsslapd-db-locks-monitoring-pause".
5d81fc
By default, nsslapd-db-locks-monitoring-enabled is turned on, nsslapd-db-locks-monitoring-threshold is set to 90% and nsslapd-db-locks-monitoring-threshold is 500ms.
5d81fc
5d81fc
When current locks are close to the maximum locks value of 90% - returning
5d81fc
the next candidate will fail until the maximum of locks won't be
5d81fc
increased or current locks are released.
5d81fc
The monitoring thread runs with the configurable interval of 500ms.
5d81fc
5d81fc
Add the setting to UI and CLI tools.
5d81fc
5d81fc
Fixes: https://github.com/389ds/389-ds-base/issues/4623
5d81fc
5d81fc
Reviewed by: @Firstyear, @tbordaz, @jchapma, @mreynolds389 (Thank you!!)
5d81fc
---
5d81fc
 .../suites/monitor/db_locks_monitor_test.py   | 251 ++++++++++++++++++
5d81fc
 ldap/servers/slapd/back-ldbm/back-ldbm.h      |  13 +-
5d81fc
 .../slapd/back-ldbm/db-bdb/bdb_config.c       |  99 +++++++
5d81fc
 .../slapd/back-ldbm/db-bdb/bdb_layer.c        |  85 ++++++
5d81fc
 ldap/servers/slapd/back-ldbm/init.c           |   3 +
5d81fc
 ldap/servers/slapd/back-ldbm/ldbm_config.c    |   3 +
5d81fc
 ldap/servers/slapd/back-ldbm/ldbm_config.h    |   3 +
5d81fc
 ldap/servers/slapd/back-ldbm/ldbm_search.c    |  13 +
5d81fc
 ldap/servers/slapd/libglobs.c                 |   4 +-
5d81fc
 src/cockpit/389-console/src/css/ds.css        |   4 +
5d81fc
 src/cockpit/389-console/src/database.jsx      |   7 +
5d81fc
 src/cockpit/389-console/src/index.html        |   2 +-
5d81fc
 .../src/lib/database/databaseConfig.jsx       |  88 +++++-
5d81fc
 src/lib389/lib389/backend.py                  |   3 +
5d81fc
 src/lib389/lib389/cli_conf/backend.py         |  10 +
5d81fc
 15 files changed, 576 insertions(+), 12 deletions(-)
5d81fc
 create mode 100644 dirsrvtests/tests/suites/monitor/db_locks_monitor_test.py
5d81fc
5d81fc
diff --git a/dirsrvtests/tests/suites/monitor/db_locks_monitor_test.py b/dirsrvtests/tests/suites/monitor/db_locks_monitor_test.py
5d81fc
new file mode 100644
5d81fc
index 000000000..7f9938f30
5d81fc
--- /dev/null
5d81fc
+++ b/dirsrvtests/tests/suites/monitor/db_locks_monitor_test.py
5d81fc
@@ -0,0 +1,251 @@
5d81fc
+# --- BEGIN COPYRIGHT BLOCK ---
5d81fc
+# Copyright (C) 2021 Red Hat, Inc.
5d81fc
+# All rights reserved.
5d81fc
+#
5d81fc
+# License: GPL (version 3 or any later version).
5d81fc
+# See LICENSE for details.
5d81fc
+# --- END COPYRIGHT BLOCK ---
5d81fc
+#
5d81fc
+import logging
5d81fc
+import pytest
5d81fc
+import datetime
5d81fc
+import subprocess
5d81fc
+from multiprocessing import Process, Queue
5d81fc
+from lib389 import pid_from_file
5d81fc
+from lib389.utils import ldap, os
5d81fc
+from lib389._constants import DEFAULT_SUFFIX, ReplicaRole
5d81fc
+from lib389.cli_base import LogCapture
5d81fc
+from lib389.idm.user import UserAccounts
5d81fc
+from lib389.idm.organizationalunit import OrganizationalUnits
5d81fc
+from lib389.tasks import AccessLog
5d81fc
+from lib389.backend import Backends
5d81fc
+from lib389.ldclt import Ldclt
5d81fc
+from lib389.dbgen import dbgen_users
5d81fc
+from lib389.tasks import ImportTask
5d81fc
+from lib389.index import Indexes
5d81fc
+from lib389.plugins import AttributeUniquenessPlugin
5d81fc
+from lib389.config import BDB_LDBMConfig
5d81fc
+from lib389.monitor import MonitorLDBM
5d81fc
+from lib389.topologies import create_topology, _remove_ssca_db
5d81fc
+
5d81fc
+pytestmark = pytest.mark.tier2
5d81fc
+db_locks_monitoring_ack = pytest.mark.skipif(not os.environ.get('DB_LOCKS_MONITORING_ACK', False),
5d81fc
+                                                                reason="DB locks monitoring tests may take hours if the feature is not present or another failure exists. "
5d81fc
+                                                                    "Also, the feature requires a big amount of space as we set nsslapd-db-locks to 1300000.")
5d81fc
+
5d81fc
+DEBUGGING = os.getenv('DEBUGGING', default=False)
5d81fc
+if DEBUGGING:
5d81fc
+    logging.getLogger(__name__).setLevel(logging.DEBUG)
5d81fc
+else:
5d81fc
+    logging.getLogger(__name__).setLevel(logging.INFO)
5d81fc
+log = logging.getLogger(__name__)
5d81fc
+
5d81fc
+
5d81fc
+def _kill_ns_slapd(inst):
5d81fc
+    pid = str(pid_from_file(inst.ds_paths.pid_file))
5d81fc
+    cmd = ['kill', '-9', pid]
5d81fc
+    subprocess.Popen(cmd, stdout=subprocess.PIPE)
5d81fc
+
5d81fc
+
5d81fc
+@pytest.fixture(scope="function")
5d81fc
+def topology_st_fn(request):
5d81fc
+    """Create DS standalone instance for each test case"""
5d81fc
+
5d81fc
+    topology = create_topology({ReplicaRole.STANDALONE: 1})
5d81fc
+
5d81fc
+    def fin():
5d81fc
+        # Kill the hanging process at the end of test to prevent failures in the following tests
5d81fc
+        if DEBUGGING:
5d81fc
+            [_kill_ns_slapd(inst) for inst in topology]
5d81fc
+        else:
5d81fc
+            [_kill_ns_slapd(inst) for inst in topology]
5d81fc
+            assert _remove_ssca_db(topology)
5d81fc
+            [inst.stop() for inst in topology if inst.exists()]
5d81fc
+            [inst.delete() for inst in topology if inst.exists()]
5d81fc
+    request.addfinalizer(fin)
5d81fc
+
5d81fc
+    topology.logcap = LogCapture()
5d81fc
+    return topology
5d81fc
+
5d81fc
+
5d81fc
+@pytest.fixture(scope="function")
5d81fc
+def setup_attruniq_index_be_import(topology_st_fn):
5d81fc
+    """Enable Attribute Uniqueness, disable indexes and
5d81fc
+    import 120000 entries to the default backend
5d81fc
+    """
5d81fc
+    inst = topology_st_fn.standalone
5d81fc
+
5d81fc
+    inst.config.loglevel([AccessLog.DEFAULT, AccessLog.INTERNAL], service='access')
5d81fc
+    inst.config.set('nsslapd-plugin-logging', 'on')
5d81fc
+    inst.restart()
5d81fc
+
5d81fc
+    attruniq = AttributeUniquenessPlugin(inst, dn="cn=attruniq,cn=plugins,cn=config")
5d81fc
+    attruniq.create(properties={'cn': 'attruniq'})
5d81fc
+    for cn in ['uid', 'cn', 'sn', 'uidNumber', 'gidNumber', 'homeDirectory', 'givenName', 'description']:
5d81fc
+        attruniq.add_unique_attribute(cn)
5d81fc
+    attruniq.add_unique_subtree(DEFAULT_SUFFIX)
5d81fc
+    attruniq.enable_all_subtrees()
5d81fc
+    attruniq.enable()
5d81fc
+
5d81fc
+    indexes = Indexes(inst)
5d81fc
+    for cn in ['uid', 'cn', 'sn', 'uidNumber', 'gidNumber', 'homeDirectory', 'givenName', 'description']:
5d81fc
+        indexes.ensure_state(properties={
5d81fc
+            'cn': cn,
5d81fc
+            'nsSystemIndex': 'false',
5d81fc
+            'nsIndexType': 'none'})
5d81fc
+
5d81fc
+    bdb_config = BDB_LDBMConfig(inst)
5d81fc
+    bdb_config.replace("nsslapd-db-locks", "130000")
5d81fc
+    inst.restart()
5d81fc
+
5d81fc
+    ldif_dir = inst.get_ldif_dir()
5d81fc
+    import_ldif = ldif_dir + '/perf_import.ldif'
5d81fc
+
5d81fc
+    # Valid online import
5d81fc
+    import_task = ImportTask(inst)
5d81fc
+    dbgen_users(inst, 120000, import_ldif, DEFAULT_SUFFIX, entry_name="userNew")
5d81fc
+    import_task.import_suffix_from_ldif(ldiffile=import_ldif, suffix=DEFAULT_SUFFIX)
5d81fc
+    import_task.wait()
5d81fc
+    assert import_task.is_complete()
5d81fc
+
5d81fc
+
5d81fc
+def create_user_wrapper(q, users):
5d81fc
+    try:
5d81fc
+        users.create_test_user()
5d81fc
+    except Exception as ex:
5d81fc
+        q.put(ex)
5d81fc
+
5d81fc
+
5d81fc
+def spawn_worker_thread(function, users, log, timeout, info):
5d81fc
+    log.info(f"Starting the thread - {info}")
5d81fc
+    q = Queue()
5d81fc
+    p = Process(target=function, args=(q,users,))
5d81fc
+    p.start()
5d81fc
+
5d81fc
+    log.info(f"Waiting for {timeout} seconds for the thread to finish")
5d81fc
+    p.join(timeout)
5d81fc
+
5d81fc
+    if p.is_alive():
5d81fc
+        log.info("Killing the thread as it's still running")
5d81fc
+        p.terminate()
5d81fc
+        p.join()
5d81fc
+        raise RuntimeError(f"Function call was aborted: {info}")
5d81fc
+    result = q.get()
5d81fc
+    if isinstance(result, Exception):
5d81fc
+        raise result
5d81fc
+    else:
5d81fc
+        return result
5d81fc
+
5d81fc
+
5d81fc
+@db_locks_monitoring_ack 
5d81fc
+@pytest.mark.parametrize("lock_threshold", [("70"), ("80"), ("95")])
5d81fc
+def test_exhaust_db_locks_basic(topology_st_fn, setup_attruniq_index_be_import, lock_threshold):
5d81fc
+    """Test that when all of the locks are exhausted the instance still working
5d81fc
+    and database is not corrupted
5d81fc
+
5d81fc
+    :id: 299108cc-04d8-4ddc-b58e-99157fccd643
5d81fc
+    :setup: Standalone instance with Attr Uniq plugin and user indexes disabled
5d81fc
+    :steps: 1. Set nsslapd-db-locks to 11000
5d81fc
+            2. Check that we stop acquiring new locks when the threshold is reached
5d81fc
+            3. Check that we can regulate a pause interval for DB locks monitoring thread
5d81fc
+            4. Make sure the feature works for different backends on the same suffix
5d81fc
+    :expectedresults:
5d81fc
+            1. Success
5d81fc
+            2. Success
5d81fc
+            3. Success
5d81fc
+            4. Success
5d81fc
+    """
5d81fc
+
5d81fc
+    inst = topology_st_fn.standalone
5d81fc
+    ADDITIONAL_SUFFIX = 'ou=newpeople,dc=example,dc=com'
5d81fc
+
5d81fc
+    backends = Backends(inst)
5d81fc
+    backends.create(properties={'nsslapd-suffix': ADDITIONAL_SUFFIX,
5d81fc
+                                'name': ADDITIONAL_SUFFIX[-3:]})
5d81fc
+    ous = OrganizationalUnits(inst, DEFAULT_SUFFIX)
5d81fc
+    ous.create(properties={'ou': 'newpeople'})
5d81fc
+
5d81fc
+    bdb_config = BDB_LDBMConfig(inst)
5d81fc
+    bdb_config.replace("nsslapd-db-locks", "11000")
5d81fc
+
5d81fc
+    # Restart server
5d81fc
+    inst.restart()
5d81fc
+
5d81fc
+    for lock_enabled in ["on", "off"]:
5d81fc
+        for lock_pause in ["100", "500", "1000"]:
5d81fc
+            bdb_config.replace("nsslapd-db-locks-monitoring-enabled", lock_enabled)
5d81fc
+            bdb_config.replace("nsslapd-db-locks-monitoring-threshold", lock_threshold)
5d81fc
+            bdb_config.replace("nsslapd-db-locks-monitoring-pause", lock_pause)
5d81fc
+            inst.restart()
5d81fc
+
5d81fc
+            if lock_enabled == "off":
5d81fc
+                raised_exception = (RuntimeError, ldap.SERVER_DOWN)
5d81fc
+            else:
5d81fc
+                raised_exception = ldap.OPERATIONS_ERROR
5d81fc
+
5d81fc
+            users = UserAccounts(inst, DEFAULT_SUFFIX)
5d81fc
+            with pytest.raises(raised_exception):
5d81fc
+                spawn_worker_thread(create_user_wrapper, users, log, 30,
5d81fc
+                                    f"Adding user with monitoring enabled='{lock_enabled}'; "
5d81fc
+                                    f"threshold='{lock_threshold}'; pause='{lock_pause}'.")
5d81fc
+            # Restart because we already run out of locks and the next unindexed searches will fail eventually
5d81fc
+            if lock_enabled == "off":
5d81fc
+                _kill_ns_slapd(inst)
5d81fc
+                inst.restart()
5d81fc
+
5d81fc
+            users = UserAccounts(inst, ADDITIONAL_SUFFIX, rdn=None)
5d81fc
+            with pytest.raises(raised_exception):
5d81fc
+                spawn_worker_thread(create_user_wrapper, users, log, 30,
5d81fc
+                                    f"Adding user with monitoring enabled='{lock_enabled}'; "
5d81fc
+                                    f"threshold='{lock_threshold}'; pause='{lock_pause}'.")
5d81fc
+            # In case feature is disabled - restart for the clean up
5d81fc
+            if lock_enabled == "off":
5d81fc
+                _kill_ns_slapd(inst)
5d81fc
+            inst.restart()
5d81fc
+
5d81fc
+
5d81fc
+@db_locks_monitoring_ack
5d81fc
+def test_exhaust_db_locks_big_pause(topology_st_fn, setup_attruniq_index_be_import):
5d81fc
+    """Test that DB lock pause setting increases the wait interval value for the monitoring thread
5d81fc
+
5d81fc
+    :id: 7d5bf838-5d4e-4ad5-8c03-5716afb84ea6
5d81fc
+    :setup: Standalone instance with Attr Uniq plugin and user indexes disabled
5d81fc
+    :steps: 1. Set nsslapd-db-locks to 20000 while using the default threshold value (95%)
5d81fc
+            2. Set nsslapd-db-locks-monitoring-pause to 10000 (10 seconds)
5d81fc
+            3. Make sure that the pause is successfully increased a few times in a row
5d81fc
+    :expectedresults:
5d81fc
+            1. Success
5d81fc
+            2. Success
5d81fc
+            3. Success
5d81fc
+    """
5d81fc
+
5d81fc
+    inst = topology_st_fn.standalone
5d81fc
+
5d81fc
+    bdb_config = BDB_LDBMConfig(inst)
5d81fc
+    bdb_config.replace("nsslapd-db-locks", "20000")
5d81fc
+    lock_pause = bdb_config.get_attr_val_int("nsslapd-db-locks-monitoring-pause")
5d81fc
+    assert lock_pause == 500
5d81fc
+    lock_pause = "10000"
5d81fc
+    bdb_config.replace("nsslapd-db-locks-monitoring-pause", lock_pause)
5d81fc
+
5d81fc
+    # Restart server
5d81fc
+    inst.restart()
5d81fc
+
5d81fc
+    lock_enabled = bdb_config.get_attr_val_utf8_l("nsslapd-db-locks-monitoring-enabled")
5d81fc
+    lock_threshold = bdb_config.get_attr_val_int("nsslapd-db-locks-monitoring-threshold")
5d81fc
+    assert lock_enabled == "on"
5d81fc
+    assert lock_threshold == 90
5d81fc
+
5d81fc
+    users = UserAccounts(inst, DEFAULT_SUFFIX)
5d81fc
+    start = datetime.datetime.now()
5d81fc
+    with pytest.raises(ldap.OPERATIONS_ERROR):
5d81fc
+        spawn_worker_thread(create_user_wrapper, users, log, 30,
5d81fc
+                            f"Adding user with monitoring enabled='{lock_enabled}'; "
5d81fc
+                            f"threshold='{lock_threshold}'; pause='{lock_pause}'. Expect it to 'Work'")
5d81fc
+    end = datetime.datetime.now()
5d81fc
+    time_delta = end - start
5d81fc
+    if time_delta.seconds < 9:
5d81fc
+        raise RuntimeError("nsslapd-db-locks-monitoring-pause attribute doesn't function correctly. "
5d81fc
+                            f"Finished the execution in {time_delta.seconds} seconds")
5d81fc
+    # In case something has failed - restart for the clean up
5d81fc
+    inst.restart()
5d81fc
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
5d81fc
index 571b0a58b..afb831c32 100644
5d81fc
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
5d81fc
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
5d81fc
@@ -155,6 +155,8 @@ typedef unsigned short u_int16_t;
5d81fc
 #define DEFAULT_DNCACHE_MAXCOUNT -1 /* no limit */
5d81fc
 #define DEFAULT_DBCACHE_SIZE     33554432
5d81fc
 #define DEFAULT_DBCACHE_SIZE_STR "33554432"
5d81fc
+#define DEFAULT_DBLOCK_PAUSE     500
5d81fc
+#define DEFAULT_DBLOCK_PAUSE_STR "500"
5d81fc
 #define DEFAULT_MODE             0600
5d81fc
 #define DEFAULT_ALLIDSTHRESHOLD  4000
5d81fc
 #define DEFAULT_IDL_TUNE         1
5d81fc
@@ -575,12 +577,21 @@ struct ldbminfo
5d81fc
     char *li_backend_implement;          /* low layer backend implementation */
5d81fc
     int li_noparentcheck;                /* check if parent exists on add */
5d81fc
 
5d81fc
-    /* the next 3 fields are for the params that don't get changed until
5d81fc
+    /* db lock monitoring */
5d81fc
+    /* if we decide to move the values to bdb_config, we can use slapi_back_get_info function to retrieve the values */
5d81fc
+    int32_t li_dblock_monitoring;          /* enables db locks monitoring thread - requires restart  */
5d81fc
+    uint32_t li_dblock_monitoring_pause;   /* an interval for db locks monitoring thread */
5d81fc
+    uint32_t li_dblock_threshold;          /* when the percentage is reached, abort the search in ldbm_back_next_search_entry - requires restart*/
5d81fc
+    uint32_t li_dblock_threshold_reached;
5d81fc
+
5d81fc
+    /* the next 4 fields are for the params that don't get changed until
5d81fc
      * the server is restarted (used by the admin console)
5d81fc
      */
5d81fc
     char *li_new_directory;
5d81fc
     uint64_t li_new_dbcachesize;
5d81fc
     int li_new_dblock;
5d81fc
+    int32_t li_new_dblock_monitoring;
5d81fc
+    uint64_t li_new_dblock_threshold;
5d81fc
 
5d81fc
     int li_new_dbncache;
5d81fc
 
5d81fc
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_config.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_config.c
5d81fc
index 738b841aa..167644943 100644
5d81fc
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_config.c
5d81fc
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_config.c
5d81fc
@@ -190,6 +190,102 @@ bdb_config_db_lock_set(void *arg, void *value, char *errorbuf, int phase, int ap
5d81fc
     return retval;
5d81fc
 }
5d81fc
 
5d81fc
+static void *
5d81fc
+bdb_config_db_lock_monitoring_get(void *arg)
5d81fc
+{
5d81fc
+    struct ldbminfo *li = (struct ldbminfo *)arg;
5d81fc
+
5d81fc
+    return (void *)((intptr_t)(li->li_new_dblock_monitoring));
5d81fc
+}
5d81fc
+
5d81fc
+static int
5d81fc
+bdb_config_db_lock_monitoring_set(void *arg, void *value, char *errorbuf __attribute__((unused)), int phase __attribute__((unused)), int apply)
5d81fc
+{
5d81fc
+    struct ldbminfo *li = (struct ldbminfo *)arg;
5d81fc
+    int retval = LDAP_SUCCESS;
5d81fc
+    int val = (int32_t)((intptr_t)value);
5d81fc
+
5d81fc
+    if (apply) {
5d81fc
+        if (CONFIG_PHASE_RUNNING == phase) {
5d81fc
+            li->li_new_dblock_monitoring = val;
5d81fc
+            slapi_log_err(SLAPI_LOG_NOTICE, "bdb_config_db_lock_monitoring_set",
5d81fc
+                          "New nsslapd-db-lock-monitoring value will not take affect until the server is restarted\n");
5d81fc
+        } else {
5d81fc
+            li->li_new_dblock_monitoring = val;
5d81fc
+            li->li_dblock_monitoring = val;
5d81fc
+        }
5d81fc
+    }
5d81fc
+
5d81fc
+    return retval;
5d81fc
+}
5d81fc
+
5d81fc
+static void *
5d81fc
+bdb_config_db_lock_pause_get(void *arg)
5d81fc
+{
5d81fc
+    struct ldbminfo *li = (struct ldbminfo *)arg;
5d81fc
+
5d81fc
+    return (void *)((uintptr_t)(slapi_atomic_load_32((int32_t *)&(li->li_dblock_monitoring_pause), __ATOMIC_RELAXED)));
5d81fc
+}
5d81fc
+
5d81fc
+static int
5d81fc
+bdb_config_db_lock_pause_set(void *arg, void *value, char *errorbuf, int phase __attribute__((unused)), int apply)
5d81fc
+{
5d81fc
+    struct ldbminfo *li = (struct ldbminfo *)arg;
5d81fc
+    int retval = LDAP_SUCCESS;
5d81fc
+    u_int32_t val = (u_int32_t)((uintptr_t)value);
5d81fc
+
5d81fc
+    if (val == 0) {
5d81fc
+        slapi_log_err(SLAPI_LOG_NOTICE, "bdb_config_db_lock_pause_set",
5d81fc
+                      "%s was set to '0'. The default value will be used (%s)",
5d81fc
+                      CONFIG_DB_LOCKS_PAUSE, DEFAULT_DBLOCK_PAUSE_STR);
5d81fc
+        val = DEFAULT_DBLOCK_PAUSE;
5d81fc
+    }
5d81fc
+
5d81fc
+    if (apply) {
5d81fc
+        slapi_atomic_store_32((int32_t *)&(li->li_dblock_monitoring_pause), val, __ATOMIC_RELAXED);
5d81fc
+    }
5d81fc
+    return retval;
5d81fc
+}
5d81fc
+
5d81fc
+static void *
5d81fc
+bdb_config_db_lock_threshold_get(void *arg)
5d81fc
+{
5d81fc
+    struct ldbminfo *li = (struct ldbminfo *)arg;
5d81fc
+
5d81fc
+    return (void *)((uintptr_t)(li->li_new_dblock_threshold));
5d81fc
+}
5d81fc
+
5d81fc
+static int
5d81fc
+bdb_config_db_lock_threshold_set(void *arg, void *value, char *errorbuf, int phase __attribute__((unused)), int apply)
5d81fc
+{
5d81fc
+    struct ldbminfo *li = (struct ldbminfo *)arg;
5d81fc
+    int retval = LDAP_SUCCESS;
5d81fc
+    u_int32_t val = (u_int32_t)((uintptr_t)value);
5d81fc
+
5d81fc
+    if (val < 70 || val > 95) {
5d81fc
+        slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
5d81fc
+                              "%s: \"%d\" is invalid, threshold is indicated as a percentage and it must lie in range of 70 and 95",
5d81fc
+                              CONFIG_DB_LOCKS_THRESHOLD, val);
5d81fc
+        slapi_log_err(SLAPI_LOG_ERR, "bdb_config_db_lock_threshold_set",
5d81fc
+                      "%s: \"%d\" is invalid, threshold is indicated as a percentage and it must lie in range of 70 and 95",
5d81fc
+                      CONFIG_DB_LOCKS_THRESHOLD, val);
5d81fc
+        retval = LDAP_OPERATIONS_ERROR;
5d81fc
+        return retval;
5d81fc
+    }
5d81fc
+
5d81fc
+    if (apply) {
5d81fc
+        if (CONFIG_PHASE_RUNNING == phase) {
5d81fc
+            li->li_new_dblock_threshold = val;
5d81fc
+            slapi_log_err(SLAPI_LOG_NOTICE, "bdb_config_db_lock_threshold_set",
5d81fc
+                          "New nsslapd-db-lock-monitoring-threshold value will not take affect until the server is restarted\n");
5d81fc
+        } else {
5d81fc
+            li->li_new_dblock_threshold = val;
5d81fc
+            li->li_dblock_threshold = val;
5d81fc
+        }
5d81fc
+    }
5d81fc
+    return retval;
5d81fc
+}
5d81fc
+
5d81fc
 static void *
5d81fc
 bdb_config_dbcachesize_get(void *arg)
5d81fc
 {
5d81fc
@@ -1409,6 +1505,9 @@ static config_info bdb_config_param[] = {
5d81fc
     {CONFIG_SERIAL_LOCK, CONFIG_TYPE_ONOFF, "on", &bdb_config_serial_lock_get, &bdb_config_serial_lock_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
5d81fc
     {CONFIG_USE_LEGACY_ERRORCODE, CONFIG_TYPE_ONOFF, "off", &bdb_config_legacy_errcode_get, &bdb_config_legacy_errcode_set, 0},
5d81fc
     {CONFIG_DB_DEADLOCK_POLICY, CONFIG_TYPE_INT, STRINGIFYDEFINE(DB_LOCK_YOUNGEST), &bdb_config_db_deadlock_policy_get, &bdb_config_db_deadlock_policy_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
5d81fc
+    {CONFIG_DB_LOCKS_MONITORING, CONFIG_TYPE_ONOFF, "on", &bdb_config_db_lock_monitoring_get, &bdb_config_db_lock_monitoring_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
5d81fc
+    {CONFIG_DB_LOCKS_THRESHOLD, CONFIG_TYPE_INT, "90", &bdb_config_db_lock_threshold_get, &bdb_config_db_lock_threshold_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
5d81fc
+    {CONFIG_DB_LOCKS_PAUSE, CONFIG_TYPE_INT, DEFAULT_DBLOCK_PAUSE_STR, &bdb_config_db_lock_pause_get, &bdb_config_db_lock_pause_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
5d81fc
     {NULL, 0, NULL, NULL, NULL, 0}};
5d81fc
 
5d81fc
 void
5d81fc
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
5d81fc
index 6cccad8e6..2f25f67a2 100644
5d81fc
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
5d81fc
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
5d81fc
@@ -35,6 +35,8 @@
5d81fc
     (env)->txn_checkpoint((env), (kbyte), (min), (flags))
5d81fc
 #define MEMP_STAT(env, gsp, fsp, flags, malloc) \
5d81fc
     (env)->memp_stat((env), (gsp), (fsp), (flags))
5d81fc
+#define LOCK_STAT(env, statp, flags, malloc) \
5d81fc
+    (env)->lock_stat((env), (statp), (flags))
5d81fc
 #define MEMP_TRICKLE(env, pct, nwrotep) \
5d81fc
     (env)->memp_trickle((env), (pct), (nwrotep))
5d81fc
 #define LOG_ARCHIVE(env, listp, flags, malloc) \
5d81fc
@@ -66,6 +68,7 @@
5d81fc
 #define NEWDIR_MODE 0755
5d81fc
 #define DB_REGION_PREFIX "__db."
5d81fc
 
5d81fc
+static int locks_monitoring_threadmain(void *param);
5d81fc
 static int perf_threadmain(void *param);
5d81fc
 static int checkpoint_threadmain(void *param);
5d81fc
 static int trickle_threadmain(void *param);
5d81fc
@@ -84,6 +87,7 @@ static int bdb_start_checkpoint_thread(struct ldbminfo *li);
5d81fc
 static int bdb_start_trickle_thread(struct ldbminfo *li);
5d81fc
 static int bdb_start_perf_thread(struct ldbminfo *li);
5d81fc
 static int bdb_start_txn_test_thread(struct ldbminfo *li);
5d81fc
+static int bdb_start_locks_monitoring_thread(struct ldbminfo *li);
5d81fc
 static int trans_batch_count = 0;
5d81fc
 static int trans_batch_limit = 0;
5d81fc
 static int trans_batch_txn_min_sleep = 50; /* ms */
5d81fc
@@ -1299,6 +1303,10 @@ bdb_start(struct ldbminfo *li, int dbmode)
5d81fc
                 return return_value;
5d81fc
             }
5d81fc
 
5d81fc
+            if (0 != (return_value = bdb_start_locks_monitoring_thread(li))) {
5d81fc
+                return return_value;
5d81fc
+            }
5d81fc
+
5d81fc
             /* We need to free the memory to avoid a leak
5d81fc
              * Also, we have to evaluate if the performance counter
5d81fc
              * should be preserved or not for database restore.
5d81fc
@@ -2885,6 +2893,7 @@ bdb_start_perf_thread(struct ldbminfo *li)
5d81fc
     return return_value;
5d81fc
 }
5d81fc
 
5d81fc
+
5d81fc
 /* Performance thread */
5d81fc
 static int
5d81fc
 perf_threadmain(void *param)
5d81fc
@@ -2910,6 +2919,82 @@ perf_threadmain(void *param)
5d81fc
     return 0;
5d81fc
 }
5d81fc
 
5d81fc
+
5d81fc
+/*
5d81fc
+ * create a thread for locks_monitoring_threadmain
5d81fc
+ */
5d81fc
+static int
5d81fc
+bdb_start_locks_monitoring_thread(struct ldbminfo *li)
5d81fc
+{
5d81fc
+    int return_value = 0;
5d81fc
+    if (li->li_dblock_monitoring) {
5d81fc
+        if (NULL == PR_CreateThread(PR_USER_THREAD,
5d81fc
+                                    (VFP)(void *)locks_monitoring_threadmain, li,
5d81fc
+                                    PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
5d81fc
+                                    PR_UNJOINABLE_THREAD,
5d81fc
+                                    SLAPD_DEFAULT_THREAD_STACKSIZE)) {
5d81fc
+            PRErrorCode prerr = PR_GetError();
5d81fc
+            slapi_log_err(SLAPI_LOG_ERR, "bdb_start_locks_monitoring_thread",
5d81fc
+                        "Failed to create database locks monitoring thread, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
5d81fc
+                        prerr, slapd_pr_strerror(prerr));
5d81fc
+            return_value = -1;
5d81fc
+        }
5d81fc
+    }
5d81fc
+    return return_value;
5d81fc
+}
5d81fc
+
5d81fc
+
5d81fc
+/* DB Locks Monitoring thread */
5d81fc
+static int
5d81fc
+locks_monitoring_threadmain(void *param)
5d81fc
+{
5d81fc
+    int ret = 0;
5d81fc
+    uint64_t current_locks = 0;
5d81fc
+    uint64_t max_locks = 0;
5d81fc
+    uint32_t lock_exhaustion = 0;
5d81fc
+    PRIntervalTime interval;
5d81fc
+    struct ldbminfo *li = NULL;
5d81fc
+
5d81fc
+    PR_ASSERT(NULL != param);
5d81fc
+    li = (struct ldbminfo *)param;
5d81fc
+
5d81fc
+    dblayer_private *priv = li->li_dblayer_private;
5d81fc
+    bdb_db_env *pEnv = (bdb_db_env *)priv->dblayer_env;
5d81fc
+    PR_ASSERT(NULL != priv);
5d81fc
+
5d81fc
+    INCR_THREAD_COUNT(pEnv);
5d81fc
+
5d81fc
+    while (!BDB_CONFIG(li)->bdb_stop_threads) {
5d81fc
+        if (dblayer_db_uses_locking(pEnv->bdb_DB_ENV)) {
5d81fc
+            DB_LOCK_STAT *lockstat = NULL;
5d81fc
+            ret = LOCK_STAT(pEnv->bdb_DB_ENV, &lockstat, 0, (void *)slapi_ch_malloc);
5d81fc
+            if (0 == ret) {
5d81fc
+                current_locks = lockstat->st_nlocks;
5d81fc
+                max_locks = lockstat->st_maxlocks;
5d81fc
+                if (max_locks){
5d81fc
+                    lock_exhaustion = (uint32_t)((double)current_locks / (double)max_locks * 100.0);
5d81fc
+                } else {
5d81fc
+                    lock_exhaustion = 0;
5d81fc
+                }
5d81fc
+                if ((li->li_dblock_threshold) &&
5d81fc
+                    (lock_exhaustion >= li->li_dblock_threshold)) {
5d81fc
+                    slapi_atomic_store_32((int32_t *)&(li->li_dblock_threshold_reached), 1, __ATOMIC_RELAXED);
5d81fc
+                } else {
5d81fc
+                    slapi_atomic_store_32((int32_t *)&(li->li_dblock_threshold_reached), 0, __ATOMIC_RELAXED);
5d81fc
+                }
5d81fc
+            }
5d81fc
+            slapi_ch_free((void **)&lockstat);
5d81fc
+        }
5d81fc
+        interval = PR_MillisecondsToInterval(slapi_atomic_load_32((int32_t *)&(li->li_dblock_monitoring_pause), __ATOMIC_RELAXED));
5d81fc
+        DS_Sleep(interval);
5d81fc
+    }
5d81fc
+
5d81fc
+    DECR_THREAD_COUNT(pEnv);
5d81fc
+    slapi_log_err(SLAPI_LOG_TRACE, "locks_monitoring_threadmain", "Leaving locks_monitoring_threadmain\n");
5d81fc
+    return 0;
5d81fc
+}
5d81fc
+
5d81fc
+
5d81fc
 /*
5d81fc
  * create a thread for deadlock_threadmain
5d81fc
  */
5d81fc
diff --git a/ldap/servers/slapd/back-ldbm/init.c b/ldap/servers/slapd/back-ldbm/init.c
5d81fc
index 893776699..4165c8fad 100644
5d81fc
--- a/ldap/servers/slapd/back-ldbm/init.c
5d81fc
+++ b/ldap/servers/slapd/back-ldbm/init.c
5d81fc
@@ -70,6 +70,9 @@ ldbm_back_init(Slapi_PBlock *pb)
5d81fc
     /* Initialize the set of instances. */
5d81fc
     li->li_instance_set = objset_new(&ldbm_back_instance_set_destructor);
5d81fc
 
5d81fc
+    /* Init lock threshold value */
5d81fc
+    li->li_dblock_threshold_reached = 0;
5d81fc
+
5d81fc
     /* ask the factory to give us space in the Connection object
5d81fc
          * (only bulk import uses this)
5d81fc
          */
5d81fc
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.c b/ldap/servers/slapd/back-ldbm/ldbm_config.c
5d81fc
index 10cef250f..60884cf33 100644
5d81fc
--- a/ldap/servers/slapd/back-ldbm/ldbm_config.c
5d81fc
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.c
5d81fc
@@ -87,6 +87,9 @@ static char *ldbm_config_moved_attributes[] =
5d81fc
         CONFIG_SERIAL_LOCK,
5d81fc
         CONFIG_USE_LEGACY_ERRORCODE,
5d81fc
         CONFIG_DB_DEADLOCK_POLICY,
5d81fc
+        CONFIG_DB_LOCKS_MONITORING,
5d81fc
+        CONFIG_DB_LOCKS_THRESHOLD,
5d81fc
+        CONFIG_DB_LOCKS_PAUSE,
5d81fc
         ""};
5d81fc
 
5d81fc
 /* Used to add an array of entries, like the one above and
5d81fc
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.h b/ldap/servers/slapd/back-ldbm/ldbm_config.h
5d81fc
index 58e64799c..6fa8292eb 100644
5d81fc
--- a/ldap/servers/slapd/back-ldbm/ldbm_config.h
5d81fc
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.h
5d81fc
@@ -104,6 +104,9 @@ struct config_info
5d81fc
 #define CONFIG_DB_VERBOSE "nsslapd-db-verbose"
5d81fc
 #define CONFIG_DB_DEBUG "nsslapd-db-debug"
5d81fc
 #define CONFIG_DB_LOCK "nsslapd-db-locks"
5d81fc
+#define CONFIG_DB_LOCKS_MONITORING "nsslapd-db-locks-monitoring-enabled"
5d81fc
+#define CONFIG_DB_LOCKS_THRESHOLD "nsslapd-db-locks-monitoring-threshold"
5d81fc
+#define CONFIG_DB_LOCKS_PAUSE "nsslapd-db-locks-monitoring-pause"
5d81fc
 #define CONFIG_DB_NAMED_REGIONS "nsslapd-db-named-regions"
5d81fc
 #define CONFIG_DB_PRIVATE_MEM "nsslapd-db-private-mem"
5d81fc
 #define CONFIG_DB_PRIVATE_IMPORT_MEM "nsslapd-db-private-import-mem"
5d81fc
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_search.c b/ldap/servers/slapd/back-ldbm/ldbm_search.c
5d81fc
index 1a7b510d4..6e22debde 100644
5d81fc
--- a/ldap/servers/slapd/back-ldbm/ldbm_search.c
5d81fc
+++ b/ldap/servers/slapd/back-ldbm/ldbm_search.c
5d81fc
@@ -1472,6 +1472,7 @@ ldbm_back_next_search_entry_ext(Slapi_PBlock *pb, int use_extension)
5d81fc
     slapi_pblock_get(pb, SLAPI_CONNECTION, &conn;;
5d81fc
     slapi_pblock_get(pb, SLAPI_OPERATION, &op);
5d81fc
 
5d81fc
+
5d81fc
     if ((reverse_list = operation_is_flag_set(op, OP_FLAG_REVERSE_CANDIDATE_ORDER))) {
5d81fc
         /*
5d81fc
          * Start at the end of the list and work our way forward.  Since a single
5d81fc
@@ -1538,6 +1539,18 @@ ldbm_back_next_search_entry_ext(Slapi_PBlock *pb, int use_extension)
5d81fc
 
5d81fc
     /* Find the next candidate entry and return it. */
5d81fc
     while (1) {
5d81fc
+        if (li->li_dblock_monitoring &&
5d81fc
+            slapi_atomic_load_32((int32_t *)&(li->li_dblock_threshold_reached), __ATOMIC_RELAXED)) {
5d81fc
+            slapi_log_err(SLAPI_LOG_CRIT, "ldbm_back_next_search_entry",
5d81fc
+                          "DB locks threshold is reached (nsslapd-db-locks-monitoring-threshold "
5d81fc
+                          "under cn=bdb,cn=config,cn=ldbm database,cn=plugins,cn=config). "
5d81fc
+                          "Please, increase nsslapd-db-locks according to your needs.\n");
5d81fc
+            slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_ENTRY, NULL);
5d81fc
+            delete_search_result_set(pb, &sr);
5d81fc
+            rc = SLAPI_FAIL_GENERAL;
5d81fc
+            slapi_send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "DB locks threshold is reached (nsslapd-db-locks-monitoring-threshold)", 0, NULL);
5d81fc
+            goto bail;
5d81fc
+        }
5d81fc
 
5d81fc
         /* check for abandon */
5d81fc
         if (slapi_op_abandoned(pb) || (NULL == sr)) {
5d81fc
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
5d81fc
index 388616b36..db7d01bbc 100644
5d81fc
--- a/ldap/servers/slapd/libglobs.c
5d81fc
+++ b/ldap/servers/slapd/libglobs.c
5d81fc
@@ -8171,8 +8171,8 @@ config_set(const char *attr, struct berval **values, char *errorbuf, int apply)
5d81fc
 #if 0
5d81fc
         debugHashTable(attr);
5d81fc
 #endif
5d81fc
-        slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "Unknown attribute %s will be ignored", attr);
5d81fc
-        slapi_log_err(SLAPI_LOG_ERR, "config_set", "Unknown attribute %s will be ignored", attr);
5d81fc
+        slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "Unknown attribute %s will be ignored\n", attr);
5d81fc
+        slapi_log_err(SLAPI_LOG_ERR, "config_set", "Unknown attribute %s will be ignored\n", attr);
5d81fc
         return LDAP_NO_SUCH_ATTRIBUTE;
5d81fc
     }
5d81fc
 
5d81fc
diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css
5d81fc
index 9248116e7..3cf50b593 100644
5d81fc
--- a/src/cockpit/389-console/src/css/ds.css
5d81fc
+++ b/src/cockpit/389-console/src/css/ds.css
5d81fc
@@ -639,6 +639,10 @@ option {
5d81fc
     padding-right: 0 !important;
5d81fc
 }
5d81fc
 
5d81fc
+.ds-vertical-scroll-auto {
5d81fc
+  overflow-y: auto !important;
5d81fc
+}
5d81fc
+
5d81fc
 .alert {
5d81fc
     max-width: 750px;
5d81fc
 }
5d81fc
diff --git a/src/cockpit/389-console/src/database.jsx b/src/cockpit/389-console/src/database.jsx
5d81fc
index efa3ce6d5..11cae972c 100644
5d81fc
--- a/src/cockpit/389-console/src/database.jsx
5d81fc
+++ b/src/cockpit/389-console/src/database.jsx
5d81fc
@@ -157,6 +157,7 @@ export class Database extends React.Component {
5d81fc
                     const attrs = config.attrs;
5d81fc
                     let db_cache_auto = false;
5d81fc
                     let import_cache_auto = false;
5d81fc
+                    let dblocksMonitoring = false;
5d81fc
                     let dbhome = "";
5d81fc
 
5d81fc
                     if ('nsslapd-db-home-directory' in attrs) {
5d81fc
@@ -168,6 +169,9 @@ export class Database extends React.Component {
5d81fc
                     if (attrs['nsslapd-import-cache-autosize'] != "0") {
5d81fc
                         import_cache_auto = true;
5d81fc
                     }
5d81fc
+                    if (attrs['nsslapd-db-locks-monitoring-enabled'][0] == "on") {
5d81fc
+                        dblocksMonitoring = true;
5d81fc
+                    }
5d81fc
 
5d81fc
                     this.setState(() => (
5d81fc
                         {
5d81fc
@@ -187,6 +191,9 @@ export class Database extends React.Component {
5d81fc
                                     txnlogdir: attrs['nsslapd-db-logdirectory'],
5d81fc
                                     dbhomedir: dbhome,
5d81fc
                                     dblocks: attrs['nsslapd-db-locks'],
5d81fc
+                                    dblocksMonitoring: dblocksMonitoring,
5d81fc
+                                    dblocksMonitoringThreshold: attrs['nsslapd-db-locks-monitoring-threshold'],
5d81fc
+                                    dblocksMonitoringPause: attrs['nsslapd-db-locks-monitoring-pause'],
5d81fc
                                     chxpoint: attrs['nsslapd-db-checkpoint-interval'],
5d81fc
                                     compactinterval: attrs['nsslapd-db-compactdb-interval'],
5d81fc
                                     importcacheauto: attrs['nsslapd-import-cache-autosize'],
5d81fc
diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html
5d81fc
index 1278844fc..fd0eeb669 100644
5d81fc
--- a/src/cockpit/389-console/src/index.html
5d81fc
+++ b/src/cockpit/389-console/src/index.html
5d81fc
@@ -12,7 +12,7 @@
5d81fc
 </head>
5d81fc
 
5d81fc
 
5d81fc
-<body>
5d81fc
+<body class="ds-vertical-scroll-auto">
5d81fc
     
5d81fc
     <script src="index.js"></script>
5d81fc
 </body>
5d81fc
diff --git a/src/cockpit/389-console/src/lib/database/databaseConfig.jsx b/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
5d81fc
index f6e662bca..6a71c138d 100644
5d81fc
--- a/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
5d81fc
+++ b/src/cockpit/389-console/src/lib/database/databaseConfig.jsx
5d81fc
@@ -31,6 +31,9 @@ export class GlobalDatabaseConfig extends React.Component {
5d81fc
             txnlogdir: this.props.data.txnlogdir,
5d81fc
             dbhomedir: this.props.data.dbhomedir,
5d81fc
             dblocks: this.props.data.dblocks,
5d81fc
+            dblocksMonitoring: this.props.data.dblocksMonitoring,
5d81fc
+            dblocksMonitoringThreshold: this.props.data.dblocksMonitoringThreshold,
5d81fc
+            dblocksMonitoringPause: this.props.data.dblocksMonitoringPause,
5d81fc
             chxpoint: this.props.data.chxpoint,
5d81fc
             compactinterval: this.props.data.compactinterval,
5d81fc
             importcachesize: this.props.data.importcachesize,
5d81fc
@@ -47,6 +50,9 @@ export class GlobalDatabaseConfig extends React.Component {
5d81fc
             _txnlogdir: this.props.data.txnlogdir,
5d81fc
             _dbhomedir: this.props.data.dbhomedir,
5d81fc
             _dblocks: this.props.data.dblocks,
5d81fc
+            _dblocksMonitoring: this.props.data.dblocksMonitoring,
5d81fc
+            _dblocksMonitoringThreshold: this.props.data.dblocksMonitoringThreshold,
5d81fc
+            _dblocksMonitoringPause: this.props.data.dblocksMonitoringPause,
5d81fc
             _chxpoint: this.props.data.chxpoint,
5d81fc
             _compactinterval: this.props.data.compactinterval,
5d81fc
             _importcachesize: this.props.data.importcachesize,
5d81fc
@@ -55,6 +61,7 @@ export class GlobalDatabaseConfig extends React.Component {
5d81fc
             _import_cache_auto: this.props.data.import_cache_auto,
5d81fc
         };
5d81fc
         this.handleChange = this.handleChange.bind(this);
5d81fc
+        this.select_db_locks_monitoring = this.select_db_locks_monitoring.bind(this);
5d81fc
         this.select_auto_cache = this.select_auto_cache.bind(this);
5d81fc
         this.select_auto_import_cache = this.select_auto_import_cache.bind(this);
5d81fc
         this.save_db_config = this.save_db_config.bind(this);
5d81fc
@@ -76,6 +83,12 @@ export class GlobalDatabaseConfig extends React.Component {
5d81fc
         }, this.handleChange(e));
5d81fc
     }
5d81fc
 
5d81fc
+    select_db_locks_monitoring (val, e) {
5d81fc
+        this.setState({
5d81fc
+            dblocksMonitoring: !this.state.dblocksMonitoring
5d81fc
+        }, this.handleChange(val, e));
5d81fc
+    }
5d81fc
+
5d81fc
     handleChange(e) {
5d81fc
         // Generic
5d81fc
         const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
5d81fc
@@ -150,6 +163,21 @@ export class GlobalDatabaseConfig extends React.Component {
5d81fc
             cmd.push("--locks=" + this.state.dblocks);
5d81fc
             requireRestart = true;
5d81fc
         }
5d81fc
+        if (this.state._dblocksMonitoring != this.state.dblocksMonitoring) {
5d81fc
+            if (this.state.dblocksMonitoring) {
5d81fc
+                cmd.push("--locks-monitoring-enabled=on");
5d81fc
+            } else {
5d81fc
+                cmd.push("--locks-monitoring-enabled=off");
5d81fc
+            }
5d81fc
+            requireRestart = true;
5d81fc
+        }
5d81fc
+        if (this.state._dblocksMonitoringThreshold != this.state.dblocksMonitoringThreshold) {
5d81fc
+            cmd.push("--locks-monitoring-threshold=" + this.state.dblocksMonitoringThreshold);
5d81fc
+            requireRestart = true;
5d81fc
+        }
5d81fc
+        if (this.state._dblocksMonitoringPause != this.state.dblocksMonitoringPause) {
5d81fc
+            cmd.push("--locks-monitoring-pause=" + this.state.dblocksMonitoringPause);
5d81fc
+        }
5d81fc
         if (this.state._chxpoint != this.state.chxpoint) {
5d81fc
             cmd.push("--checkpoint-interval=" + this.state.chxpoint);
5d81fc
             requireRestart = true;
5d81fc
@@ -216,6 +244,28 @@ export class GlobalDatabaseConfig extends React.Component {
5d81fc
         let import_cache_form;
5d81fc
         let db_auto_checked = false;
5d81fc
         let import_auto_checked = false;
5d81fc
+        let dblocksMonitor = "";
5d81fc
+
5d81fc
+        if (this.state.dblocksMonitoring) {
5d81fc
+            dblocksMonitor = 
5d81fc
+                <Row className="ds-margin-top" title="Sets the DB lock exhaustion value in percentage (valid range is 70-95). If too many locks are acquired, the server will abort the searches while the number of locks are not decreased. It helps to avoid DB corruption and long recovery. (nsslapd-db-locks-monitoring-threshold)">
5d81fc
+                    
5d81fc
+                        DB Locks Threshold Percentage
5d81fc
+                    
5d81fc
+                    
5d81fc
+                        <input className="ds-input" type="number" id="dblocksMonitoringThreshold" size="10" onChange={this.handleChange} value={this.state.dblocksMonitoringThreshold} />
5d81fc
+                    
5d81fc
+                </Row>
5d81fc
+                <Row className="ds-margin-top" title="Sets the amount of time (milliseconds) that the monitoring thread spends waiting between checks. (nsslapd-db-locks-monitoring-pause)">
5d81fc
+                    
5d81fc
+                        DB Locks Pause Milliseconds
5d81fc
+                    
5d81fc
+                    
5d81fc
+                        <input className="ds-input" type="number" id="dblocksMonitoringPause" size="10" onChange={this.handleChange} value={this.state.dblocksMonitoringPause} />
5d81fc
+                    
5d81fc
+                </Row>
5d81fc
+            ;
5d81fc
+        }
5d81fc
 
5d81fc
         if (this.state.db_cache_auto) {
5d81fc
             db_cache_form = 
5d81fc
@@ -422,14 +472,6 @@ export class GlobalDatabaseConfig extends React.Component {
5d81fc
                                             <input id="dbhomedir" value={this.state.dbhomedir} onChange={this.handleChange} className="ds-input-auto" type="text" />
5d81fc
                                         
5d81fc
                                     </Row>
5d81fc
-                                    <Row className="ds-margin-top" title="The number of database locks (nsslapd-db-locks).">
5d81fc
-                                        
5d81fc
-                                            Database Locks
5d81fc
-                                        
5d81fc
-                                        
5d81fc
-                                            <input id="dblocks" value={this.state.dblocks} onChange={this.handleChange} className="ds-input-auto" type="text" />
5d81fc
-                                        
5d81fc
-                                    </Row>
5d81fc
                                     <Row className="ds-margin-top" title="Amount of time in seconds after which the Directory Server sends a checkpoint entry to the database transaction log (nsslapd-db-checkpoint-interval).">
5d81fc
                                         
5d81fc
                                             Database Checkpoint Interval
5d81fc
@@ -446,6 +488,36 @@ export class GlobalDatabaseConfig extends React.Component {
5d81fc
                                             <input id="compactinterval" value={this.state.compactinterval} onChange={this.handleChange} className="ds-input-auto" type="text" />
5d81fc
                                         
5d81fc
                                     </Row>
5d81fc
+                                    <Row className="ds-margin-top" title="The number of database locks (nsslapd-db-locks).">
5d81fc
+                                        
5d81fc
+                                            Database Locks
5d81fc
+                                        
5d81fc
+                                        
5d81fc
+                                            <input id="dblocks" value={this.state.dblocks} onChange={this.handleChange} className="ds-input-auto" type="text" />
5d81fc
+                                        
5d81fc
+                                    </Row>
5d81fc
+                                    <Row>
5d81fc
+                                        
5d81fc
+                                            
DB Locks Monitoring
5d81fc
+                                            
5d81fc
+                                        
5d81fc
+                                    </Row>
5d81fc
+                                    <Row>
5d81fc
+                                        
5d81fc
+                                            
5d81fc
+                                                id="dblocksMonitoring"
5d81fc
+                                                checked={this.state.dblocksMonitoring}
5d81fc
+                                                onChange={this.select_db_locks_monitoring}
5d81fc
+                                            >
5d81fc
+                                                Enable Monitoring
5d81fc
+                                            </Checkbox>
5d81fc
+                                        
5d81fc
+                                    </Row>
5d81fc
+                                    <Row>
5d81fc
+                                        
5d81fc
+                                            {dblocksMonitor}
5d81fc
+                                        
5d81fc
+                                    </Row>
5d81fc
                                 </Form>
5d81fc
                             
5d81fc
                         
5d81fc
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
5d81fc
index bcd7b383f..13bb27842 100644
5d81fc
--- a/src/lib389/lib389/backend.py
5d81fc
+++ b/src/lib389/lib389/backend.py
5d81fc
@@ -1011,6 +1011,9 @@ class DatabaseConfig(DSLdapObject):
5d81fc
                     'nsslapd-db-transaction-batch-max-wait',
5d81fc
                     'nsslapd-db-logbuf-size',
5d81fc
                     'nsslapd-db-locks',
5d81fc
+                    'nsslapd-db-locks-monitoring-enabled',
5d81fc
+                    'nsslapd-db-locks-monitoring-threshold',
5d81fc
+                    'nsslapd-db-locks-monitoring-pause',
5d81fc
                     'nsslapd-db-private-import-mem',
5d81fc
                     'nsslapd-import-cache-autosize',
5d81fc
                     'nsslapd-cache-autosize',
5d81fc
diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py
5d81fc
index 6bfbcb036..722764d10 100644
5d81fc
--- a/src/lib389/lib389/cli_conf/backend.py
5d81fc
+++ b/src/lib389/lib389/cli_conf/backend.py
5d81fc
@@ -46,6 +46,9 @@ arg_to_attr = {
5d81fc
         'txn_batch_max': 'nsslapd-db-transaction-batch-max-wait',
5d81fc
         'logbufsize': 'nsslapd-db-logbuf-size',
5d81fc
         'locks': 'nsslapd-db-locks',
5d81fc
+        'locks_monitoring_enabled': 'nsslapd-db-locks-monitoring-enabled',
5d81fc
+        'locks_monitoring_threshold': 'nsslapd-db-locks-monitoring-threshold',
5d81fc
+        'locks_monitoring_pause': 'nsslapd-db-locks-monitoring-pause',
5d81fc
         'import_cache_autosize': 'nsslapd-import-cache-autosize',
5d81fc
         'cache_autosize': 'nsslapd-cache-autosize',
5d81fc
         'cache_autosize_split': 'nsslapd-cache-autosize-split',
5d81fc
@@ -998,6 +1001,13 @@ def create_parser(subparsers):
5d81fc
                                                               'the batch count (only works when txn-batch-val is set)')
5d81fc
     set_db_config_parser.add_argument('--logbufsize', help='Specifies the transaction log information buffer size')
5d81fc
     set_db_config_parser.add_argument('--locks', help='Sets the maximum number of database locks')
5d81fc
+    set_db_config_parser.add_argument('--locks-monitoring-enabled', help='Set to "on" or "off" to monitor DB locks. When it crosses the percentage value '
5d81fc
+                                                                         'set with "--locks-monitoring-threshold" ("on" by default)')
5d81fc
+    set_db_config_parser.add_argument('--locks-monitoring-threshold', help='Sets the DB lock exhaustion value in percentage (valid range is 70-95). If too many locks are '
5d81fc
+                                                                           'acquired, the server will abort the searches while the number of locks '
5d81fc
+                                                                           'are not decreased. It helps to avoid DB corruption and long recovery.')
5d81fc
+    set_db_config_parser.add_argument('--locks-monitoring-pause', help='Sets the DB lock monitoring value in milliseconds for the amount of time '
5d81fc
+                                                                       'that the monitoring thread spends waiting between checks.')
5d81fc
     set_db_config_parser.add_argument('--import-cache-autosize', help='Set to "on" or "off" to automatically set the size of the import '
5d81fc
                                                                        'cache to be used during the the import process of LDIF files')
5d81fc
     set_db_config_parser.add_argument('--cache-autosize', help='Sets the percentage of free memory that is used in total for the database '
5d81fc
-- 
5d81fc
2.26.3
5d81fc