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

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