Blame SOURCES/0015-Issue-51157-Reindex-task-may-create-abandoned-index-.patch

a26cad
From 3b3faee01e645577ad77ff4f38429a9e0806231b Mon Sep 17 00:00:00 2001
a26cad
From: Simon Pichugin <simon.pichugin@gmail.com>
a26cad
Date: Tue, 16 Jun 2020 20:35:05 +0200
a26cad
Subject: [PATCH] Issue 51157 - Reindex task may create abandoned index file
a26cad
a26cad
Bug Description: Recreating an index for the same attribute but changing
a26cad
the case of for example 1 letter, results in abandoned indexfile.
a26cad
a26cad
Fix Decsription: Add a test case to a newly created 'indexes' test suite.
a26cad
When we remove the index config from the backend, - remove the attribute
a26cad
info from LDBM instance attributes.
a26cad
a26cad
https://pagure.io/389-ds-base/issue/51157
a26cad
a26cad
Reviewed by: firstyear, mreynolds (Thanks!)
a26cad
---
a26cad
 dirsrvtests/tests/suites/indexes/__init__.py  |   3 +
a26cad
 .../tests/suites/indexes/regression_test.py   | 125 ++++++++++++++++++
a26cad
 ldap/servers/slapd/back-ldbm/ldbm_attr.c      |   7 +
a26cad
 .../slapd/back-ldbm/ldbm_index_config.c       |   3 +
a26cad
 .../servers/slapd/back-ldbm/proto-back-ldbm.h |   1 +
a26cad
 5 files changed, 139 insertions(+)
a26cad
 create mode 100644 dirsrvtests/tests/suites/indexes/__init__.py
a26cad
 create mode 100644 dirsrvtests/tests/suites/indexes/regression_test.py
a26cad
a26cad
diff --git a/dirsrvtests/tests/suites/indexes/__init__.py b/dirsrvtests/tests/suites/indexes/__init__.py
a26cad
new file mode 100644
a26cad
index 000000000..04441667e
a26cad
--- /dev/null
a26cad
+++ b/dirsrvtests/tests/suites/indexes/__init__.py
a26cad
@@ -0,0 +1,3 @@
a26cad
+"""
a26cad
+   :Requirement: 389-ds-base: Indexes
a26cad
+"""
a26cad
diff --git a/dirsrvtests/tests/suites/indexes/regression_test.py b/dirsrvtests/tests/suites/indexes/regression_test.py
a26cad
new file mode 100644
a26cad
index 000000000..1a71f16e9
a26cad
--- /dev/null
a26cad
+++ b/dirsrvtests/tests/suites/indexes/regression_test.py
a26cad
@@ -0,0 +1,125 @@
a26cad
+# --- BEGIN COPYRIGHT BLOCK ---
a26cad
+# Copyright (C) 2020 Red Hat, Inc.
a26cad
+# All rights reserved.
a26cad
+#
a26cad
+# License: GPL (version 3 or any later version).
a26cad
+# See LICENSE for details.
a26cad
+# --- END COPYRIGHT BLOCK ---
a26cad
+#
a26cad
+import time
a26cad
+import os
a26cad
+import pytest
a26cad
+import ldap
a26cad
+from lib389._constants import DEFAULT_BENAME, DEFAULT_SUFFIX
a26cad
+from lib389.index import Indexes
a26cad
+from lib389.backend import Backends
a26cad
+from lib389.idm.user import UserAccounts
a26cad
+from lib389.topologies import topology_st as topo
a26cad
+
a26cad
+pytestmark = pytest.mark.tier1
a26cad
+
a26cad
+
a26cad
+def test_reindex_task_creates_abandoned_index_file(topo):
a26cad
+    """
a26cad
+    Recreating an index for the same attribute but changing
a26cad
+    the case of for example 1 letter, results in abandoned indexfile
a26cad
+
a26cad
+    :id: 07ae5274-481a-4fa8-8074-e0de50d89ac6
a26cad
+    :setup: Standalone instance
a26cad
+    :steps:
a26cad
+        1. Create a user object with additional attributes:
a26cad
+           objectClass: mozillaabpersonalpha
a26cad
+           mozillaCustom1: xyz
a26cad
+        2. Add an index entry mozillacustom1
a26cad
+        3. Reindex the backend
a26cad
+        4. Check the content of the index (after it has been flushed to disk) mozillacustom1.db
a26cad
+        5. Remove the index
a26cad
+        6. Notice the mozillacustom1.db is removed
a26cad
+        7. Recreate the index but now use the exact case as mentioned in the schema
a26cad
+        8. Reindex the backend
a26cad
+        9. Check the content of the index (after it has been flushed to disk) mozillaCustom1.db
a26cad
+        10. Check that an ldapsearch does not return a result (mozillacustom1=xyz)
a26cad
+        11. Check that an ldapsearch returns the results (mozillaCustom1=xyz)
a26cad
+        12. Restart the instance
a26cad
+        13. Notice that an ldapsearch does not return a result(mozillacustom1=xyz)
a26cad
+        15. Check that an ldapsearch does not return a result (mozillacustom1=xyz)
a26cad
+        16. Check that an ldapsearch returns the results (mozillaCustom1=xyz)
a26cad
+        17. Reindex the backend
a26cad
+        18. Notice the second indexfile for this attribute
a26cad
+        19. Check the content of the index (after it has been flushed to disk) no mozillacustom1.db
a26cad
+        20. Check the content of the index (after it has been flushed to disk) mozillaCustom1.db
a26cad
+    :expectedresults:
a26cad
+        1. Should Success.
a26cad
+        2. Should Success.
a26cad
+        3. Should Success.
a26cad
+        4. Should Success.
a26cad
+        5. Should Success.
a26cad
+        6. Should Success.
a26cad
+        7. Should Success.
a26cad
+        8. Should Success.
a26cad
+        9. Should Success.
a26cad
+        10. Should Success.
a26cad
+        11. Should Success.
a26cad
+        12. Should Success.
a26cad
+        13. Should Success.
a26cad
+        14. Should Success.
a26cad
+        15. Should Success.
a26cad
+        16. Should Success.
a26cad
+        17. Should Success.
a26cad
+        18. Should Success.
a26cad
+        19. Should Success.
a26cad
+        20. Should Success.
a26cad
+    """
a26cad
+
a26cad
+    inst = topo.standalone
a26cad
+    attr_name = "mozillaCustom1"
a26cad
+    attr_value = "xyz"
a26cad
+
a26cad
+    users = UserAccounts(inst, DEFAULT_SUFFIX)
a26cad
+    user = users.create_test_user()
a26cad
+    user.add("objectClass", "mozillaabpersonalpha")
a26cad
+    user.add(attr_name, attr_value)
a26cad
+
a26cad
+    backends = Backends(inst)
a26cad
+    backend = backends.get(DEFAULT_BENAME)
a26cad
+    indexes = backend.get_indexes()
a26cad
+    index = indexes.create(properties={
a26cad
+        'cn': attr_name.lower(),
a26cad
+        'nsSystemIndex': 'false',
a26cad
+        'nsIndexType': ['eq', 'pres']
a26cad
+        })
a26cad
+
a26cad
+    backend.reindex()
a26cad
+    time.sleep(3)
a26cad
+    assert os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name.lower()}.db")
a26cad
+    index.delete()
a26cad
+    assert not os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name.lower()}.db")
a26cad
+
a26cad
+    index = indexes.create(properties={
a26cad
+        'cn': attr_name,
a26cad
+        'nsSystemIndex': 'false',
a26cad
+        'nsIndexType': ['eq', 'pres']
a26cad
+        })
a26cad
+
a26cad
+    backend.reindex()
a26cad
+    time.sleep(3)
a26cad
+    assert not os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name.lower()}.db")
a26cad
+    assert os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name}.db")
a26cad
+
a26cad
+    entries = inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f"{attr_name}={attr_value}")
a26cad
+    assert len(entries) > 0
a26cad
+    inst.restart()
a26cad
+    entries = inst.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f"{attr_name}={attr_value}")
a26cad
+    assert len(entries) > 0
a26cad
+
a26cad
+    backend.reindex()
a26cad
+    time.sleep(3)
a26cad
+    assert not os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name.lower()}.db")
a26cad
+    assert os.path.exists(f"{inst.ds_paths.db_home_dir}/{DEFAULT_BENAME}/{attr_name}.db")
a26cad
+
a26cad
+
a26cad
+if __name__ == "__main__":
a26cad
+    # Run isolated
a26cad
+    # -s for DEBUG mode
a26cad
+    CURRENT_FILE = os.path.realpath(__file__)
a26cad
+    pytest.main("-s %s" % CURRENT_FILE)
a26cad
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_attr.c b/ldap/servers/slapd/back-ldbm/ldbm_attr.c
a26cad
index f0d418572..688c4f137 100644
a26cad
--- a/ldap/servers/slapd/back-ldbm/ldbm_attr.c
a26cad
+++ b/ldap/servers/slapd/back-ldbm/ldbm_attr.c
a26cad
@@ -98,6 +98,13 @@ ainfo_cmp(
a26cad
     return (strcasecmp(a->ai_type, b->ai_type));
a26cad
 }
a26cad
 
a26cad
+void
a26cad
+attrinfo_delete_from_tree(backend *be, struct attrinfo *ai)
a26cad
+{
a26cad
+    ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
a26cad
+    avl_delete(&inst->inst_attrs, ai, ainfo_cmp);
a26cad
+}
a26cad
+
a26cad
 /*
a26cad
  * Called when a duplicate "index" line is encountered.
a26cad
  *
a26cad
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
a26cad
index 720f93036..9722d0ce7 100644
a26cad
--- a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
a26cad
+++ b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
a26cad
@@ -201,7 +201,10 @@ ldbm_instance_index_config_delete_callback(Slapi_PBlock *pb,
a26cad
             *returncode = LDAP_UNWILLING_TO_PERFORM;
a26cad
             rc = SLAPI_DSE_CALLBACK_ERROR;
a26cad
         }
a26cad
+        attrinfo_delete_from_tree(inst->inst_be, ainfo);
a26cad
     }
a26cad
+    /* Free attrinfo structure */
a26cad
+    attrinfo_delete(&ainfo);
a26cad
 bail:
a26cad
     return rc;
a26cad
 }
a26cad
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
a26cad
index a07acee5e..4d2524fd9 100644
a26cad
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
a26cad
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
a26cad
@@ -21,6 +21,7 @@
a26cad
  */
a26cad
 struct attrinfo *attrinfo_new(void);
a26cad
 void attrinfo_delete(struct attrinfo **pp);
a26cad
+void attrinfo_delete_from_tree(backend *be, struct attrinfo *ai);
a26cad
 void ainfo_get(backend *be, char *type, struct attrinfo **at);
a26cad
 void attr_masks(backend *be, char *type, int *indexmask, int *syntaxmask);
a26cad
 void attr_masks_ex(backend *be, char *type, int *indexmask, int *syntaxmask, struct attrinfo **at);
a26cad
-- 
a26cad
2.26.2
a26cad