Blame SOURCES/0026-Issue-4443-Internal-unindexed-searches-in-syncrepl-r.patch

22b3c5
From c8413c93adb59db89fde84f4e8a7942246732aa5 Mon Sep 17 00:00:00 2001
22b3c5
From: Mark Reynolds <mreynolds@redhat.com>
22b3c5
Date: Tue, 13 Jul 2021 14:18:03 -0400
22b3c5
Subject: [PATCH 1/4] Issue 4443 - Internal unindexed searches in
22b3c5
 syncrepl/retro changelog
22b3c5
22b3c5
Bug Description:
22b3c5
22b3c5
When a non-system index is added to a backend it is
22b3c5
disabled until the database is initialized or reindexed.
22b3c5
So in the case of the retro changelog the changenumber index
22b3c5
is alway disabled by default since it is never initialized.
22b3c5
This leads to unexpected unindexed searches of the retro
22b3c5
changelog.
22b3c5
22b3c5
Fix Description:
22b3c5
22b3c5
If an index has "nsSystemIndex" set to "true" then enable it
22b3c5
immediately.
22b3c5
22b3c5
relates:  https://github.com/389ds/389-ds-base/issues/4443
22b3c5
22b3c5
Reviewed by: spichugi & tbordaz(Thanks!!)
22b3c5
---
22b3c5
 .../suites/retrocl/retrocl_indexing_test.py   | 68 +++++++++++++++++++
22b3c5
 ldap/servers/plugins/retrocl/retrocl_create.c |  2 +-
22b3c5
 .../slapd/back-ldbm/ldbm_index_config.c       | 27 ++++++--
22b3c5
 src/lib389/lib389/_mapped_object.py           | 13 ++++
22b3c5
 4 files changed, 103 insertions(+), 7 deletions(-)
22b3c5
 create mode 100644 dirsrvtests/tests/suites/retrocl/retrocl_indexing_test.py
22b3c5
22b3c5
diff --git a/dirsrvtests/tests/suites/retrocl/retrocl_indexing_test.py b/dirsrvtests/tests/suites/retrocl/retrocl_indexing_test.py
22b3c5
new file mode 100644
22b3c5
index 000000000..9782368b4
22b3c5
--- /dev/null
22b3c5
+++ b/dirsrvtests/tests/suites/retrocl/retrocl_indexing_test.py
22b3c5
@@ -0,0 +1,68 @@
22b3c5
+import logging
22b3c5
+import pytest
22b3c5
+import os
22b3c5
+from lib389._constants import RETROCL_SUFFIX, DEFAULT_SUFFIX
22b3c5
+from lib389.topologies import topology_st as topo
22b3c5
+from lib389.plugins import RetroChangelogPlugin
22b3c5
+from lib389.idm.user import UserAccounts
22b3c5
+from lib389._mapped_object import DSLdapObjects
22b3c5
+log = logging.getLogger(__name__)
22b3c5
+
22b3c5
+
22b3c5
+def test_indexing_is_online(topo):
22b3c5
+    """Test that the changenmumber index is online right after enabling the plugin
22b3c5
+
22b3c5
+    :id: 16f4c001-9e0c-4448-a2b3-08ac1e85d40f
22b3c5
+    :setup: Standalone Instance
22b3c5
+    :steps:
22b3c5
+        1. Enable retro cl
22b3c5
+        2. Perform some updates
22b3c5
+        3. Search for "(changenumber>=-1)", and it is not partially unindexed
22b3c5
+        4. Search for "(&(changenumber>=-1)(targetuniqueid=*))", and it is not partially unindexed
22b3c5
+    :expectedresults:
22b3c5
+        1. Success
22b3c5
+        2. Success
22b3c5
+        3. Success
22b3c5
+        4. Success
22b3c5
+    """
22b3c5
+
22b3c5
+    # Enable plugin
22b3c5
+    topo.standalone.config.set('nsslapd-accesslog-logbuffering',  'off')
22b3c5
+    plugin = RetroChangelogPlugin(topo.standalone)
22b3c5
+    plugin.enable()
22b3c5
+    topo.standalone.restart()
22b3c5
+
22b3c5
+    # Do a bunch of updates
22b3c5
+    users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
22b3c5
+    user_entry = users.create(properties={
22b3c5
+        'sn': '1',
22b3c5
+        'cn': 'user 1',
22b3c5
+        'uid': 'user1',
22b3c5
+        'uidNumber': '11',
22b3c5
+        'gidNumber': '111',
22b3c5
+        'givenname': 'user1',
22b3c5
+        'homePhone': '0861234567',
22b3c5
+        'carLicense': '131D16674',
22b3c5
+        'mail': 'user1@whereever.com',
22b3c5
+        'homeDirectory': '/home'
22b3c5
+    })
22b3c5
+    for count in range(0, 10):
22b3c5
+        user_entry.replace('mail', 'test%d@test.com' % count)
22b3c5
+
22b3c5
+    # Search the retro cl, and check for error messages
22b3c5
+    filter_simple = '(changenumber>=-1)'
22b3c5
+    filter_compound = '(&(changenumber>=-1)(targetuniqueid=*))'
22b3c5
+    retro_changelog_suffix = DSLdapObjects(topo.standalone, basedn=RETROCL_SUFFIX)
22b3c5
+    retro_changelog_suffix.filter(filter_simple)
22b3c5
+    assert not topo.standalone.searchAccessLog('Partially Unindexed Filter')
22b3c5
+
22b3c5
+    # Search the retro cl again with compound filter
22b3c5
+    retro_changelog_suffix.filter(filter_compound)
22b3c5
+    assert not topo.standalone.searchAccessLog('Partially Unindexed Filter')
22b3c5
+
22b3c5
+
22b3c5
+if __name__ == '__main__':
22b3c5
+    # Run isolated
22b3c5
+    # -s for DEBUG mode
22b3c5
+    CURRENT_FILE = os.path.realpath(__file__)
22b3c5
+    pytest.main(["-s", CURRENT_FILE])
22b3c5
diff --git a/ldap/servers/plugins/retrocl/retrocl_create.c b/ldap/servers/plugins/retrocl/retrocl_create.c
22b3c5
index fb1503520..09f0a4f90 100644
22b3c5
--- a/ldap/servers/plugins/retrocl/retrocl_create.c
22b3c5
+++ b/ldap/servers/plugins/retrocl/retrocl_create.c
22b3c5
@@ -133,7 +133,7 @@ retrocl_create_be(const char *bedir)
22b3c5
     val.bv_len = strlen(val.bv_val);
22b3c5
     slapi_entry_add_values(e, "cn", vals);
22b3c5
 
22b3c5
-    val.bv_val = "false";
22b3c5
+    val.bv_val = "true"; /* enables the index */
22b3c5
     val.bv_len = strlen(val.bv_val);
22b3c5
     slapi_entry_add_values(e, "nssystemindex", vals);
22b3c5
 
22b3c5
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
22b3c5
index 95e76c8dc..59bbbcd45 100644
22b3c5
--- a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
22b3c5
+++ b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
22b3c5
@@ -25,7 +25,7 @@ int ldbm_instance_index_config_delete_callback(Slapi_PBlock *pb, Slapi_Entry *en
22b3c5
 #define INDEXTYPE_NONE 1
22b3c5
 
22b3c5
 static int
22b3c5
-ldbm_index_parse_entry(ldbm_instance *inst, Slapi_Entry *e, const char *trace_string, char **index_name)
22b3c5
+ldbm_index_parse_entry(ldbm_instance *inst, Slapi_Entry *e, const char *trace_string, char **index_name, PRBool *is_system_index)
22b3c5
 {
22b3c5
     Slapi_Attr *attr;
22b3c5
     const struct berval *attrValue;
22b3c5
@@ -66,6 +66,15 @@ ldbm_index_parse_entry(ldbm_instance *inst, Slapi_Entry *e, const char *trace_st
22b3c5
         }
22b3c5
     }
22b3c5
 
22b3c5
+    *is_system_index = PR_FALSE;
22b3c5
+    if (0 == slapi_entry_attr_find(e, "nsSystemIndex", &attr)) {
22b3c5
+        slapi_attr_first_value(attr, &sval);
22b3c5
+        attrValue = slapi_value_get_berval(sval);
22b3c5
+        if (strcasecmp(attrValue->bv_val, "true") == 0) {
22b3c5
+            *is_system_index = PR_TRUE;
22b3c5
+        }
22b3c5
+    }
22b3c5
+
22b3c5
     /* ok the entry is good to process, pass it to attr_index_config */
22b3c5
     if (attr_index_config(inst->inst_be, (char *)trace_string, 0, e, 0, 0)) {
22b3c5
         slapi_ch_free_string(index_name);
22b3c5
@@ -89,9 +98,10 @@ ldbm_index_init_entry_callback(Slapi_PBlock *pb __attribute__((unused)),
22b3c5
                                void *arg)
22b3c5
 {
22b3c5
     ldbm_instance *inst = (ldbm_instance *)arg;
22b3c5
+    PRBool is_system_index = PR_FALSE;
22b3c5
 
22b3c5
     returntext[0] = '\0';
22b3c5
-    *returncode = ldbm_index_parse_entry(inst, e, "from ldbm instance init", NULL);
22b3c5
+    *returncode = ldbm_index_parse_entry(inst, e, "from ldbm instance init", NULL, &is_system_index /* not used */);
22b3c5
     if (*returncode == LDAP_SUCCESS) {
22b3c5
         return SLAPI_DSE_CALLBACK_OK;
22b3c5
     } else {
22b3c5
@@ -113,18 +123,22 @@ ldbm_instance_index_config_add_callback(Slapi_PBlock *pb __attribute__((unused))
22b3c5
                                         void *arg)
22b3c5
 {
22b3c5
     ldbm_instance *inst = (ldbm_instance *)arg;
22b3c5
-    char *index_name;
22b3c5
+    char *index_name = NULL;
22b3c5
+    PRBool is_system_index = PR_FALSE;
22b3c5
 
22b3c5
     returntext[0] = '\0';
22b3c5
-    *returncode = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name);
22b3c5
+    *returncode = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name, &is_system_index);
22b3c5
     if (*returncode == LDAP_SUCCESS) {
22b3c5
         struct attrinfo *ai = NULL;
22b3c5
         /* if the index is a "system" index, we assume it's being added by
22b3c5
          * by the server, and it's okay for the index to go online immediately.
22b3c5
          * if not, we set the index "offline" so it won't actually be used
22b3c5
          * until someone runs db2index on it.
22b3c5
+         * If caller wants to add an index that they want to be online
22b3c5
+         * immediately they can also set "nsSystemIndex" to "true" in the
22b3c5
+         * index config entry (e.g. is_system_index).
22b3c5
          */
22b3c5
-        if (!ldbm_attribute_always_indexed(index_name)) {
22b3c5
+        if (!is_system_index && !ldbm_attribute_always_indexed(index_name)) {
22b3c5
             ainfo_get(inst->inst_be, index_name, &ai;;
22b3c5
             PR_ASSERT(ai != NULL);
22b3c5
             ai->ai_indexmask |= INDEX_OFFLINE;
22b3c5
@@ -357,13 +371,14 @@ ldbm_instance_index_config_enable_index(ldbm_instance *inst, Slapi_Entry *e)
22b3c5
     char *index_name = NULL;
22b3c5
     int rc = LDAP_SUCCESS;
22b3c5
     struct attrinfo *ai = NULL;
22b3c5
+    PRBool is_system_index = PR_FALSE;
22b3c5
 
22b3c5
     index_name = slapi_entry_attr_get_charptr(e, "cn");
22b3c5
     if (index_name) {
22b3c5
         ainfo_get(inst->inst_be, index_name, &ai;;
22b3c5
     }
22b3c5
     if (!ai) {
22b3c5
-        rc = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name);
22b3c5
+        rc = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name, &is_system_index /* not used */);
22b3c5
     }
22b3c5
     if (rc == LDAP_SUCCESS) {
22b3c5
         /* Assume the caller knows if it is OK to go online immediately */
22b3c5
diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py
22b3c5
index ada3f4976..903045c5c 100644
22b3c5
--- a/src/lib389/lib389/_mapped_object.py
22b3c5
+++ b/src/lib389/lib389/_mapped_object.py
22b3c5
@@ -142,6 +142,19 @@ class DSLdapObject(DSLogging):
22b3c5
 
22b3c5
         return True
22b3c5
 
22b3c5
+    def search(self, scope="subtree", filter='objectclass=*'):
22b3c5
+        search_scope = ldap.SCOPE_SUBTREE
22b3c5
+        if scope == 'base':
22b3c5
+            search_scope = ldap.SCOPE_BASE
22b3c5
+        elif scope == 'one':
22b3c5
+            search_scope = ldap.SCOPE_ONE
22b3c5
+        elif scope == 'subtree':
22b3c5
+            search_scope = ldap.SCOPE_SUBTREE
22b3c5
+        return self._instance.search_ext_s(self._dn, search_scope, filter,
22b3c5
+                                           serverctrls=self._server_controls,
22b3c5
+                                           clientctrls=self._client_controls,
22b3c5
+                                           escapehatch='i am sure')
22b3c5
+
22b3c5
     def display(self):
22b3c5
         """Get an entry but represent it as a string LDIF
22b3c5
 
22b3c5
-- 
22b3c5
2.31.1
22b3c5