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