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

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