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

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