Blame SOURCES/0031-Issue-4925-Performance-ACI-targetfilter-evaluation-r.patch

33fd71
From 18a8ed29ae0b300083a6b83665b0137948a2ef7c Mon Sep 17 00:00:00 2001
33fd71
From: tbordaz <tbordaz@redhat.com>
33fd71
Date: Thu, 23 Sep 2021 10:48:50 +0200
33fd71
Subject: [PATCH 1/3] Issue 4925 - Performance ACI: targetfilter evaluation
33fd71
 result can be reused (#4926)
33fd71
33fd71
Bug description:
33fd71
	An ACI may contain targetfilter. For a given returned entry, of a
33fd71
        SRCH request, the same targetfilter is evaluated for each of the
33fd71
        returned attributes.
33fd71
        Once the filter has been evaluated, it is useless to reevaluate
33fd71
        it for a next attribute.
33fd71
33fd71
Fix description:
33fd71
	The fix implements a very simple cache (linked list) that keeps
33fd71
        the results of the previously evaluated 'targetfilter'.
33fd71
        This cache is per-entry. For an operation, a aclpb is allocated
33fd71
        that is used to evaluate ACIs against each successive entry.
33fd71
        Each time a candidate entry is added in the aclpb
33fd71
        (acl_access_allowed), the cache (aclpb_curr_entry_targetfilters)
33fd71
        is freed. Then for each 'targetfilter', the original targetfilter
33fd71
        is lookup from the cache. If this is the first evaluation of it
33fd71
        then the result of the evaluation is stored into the cache using
33fd71
        the original targetfilter as the key in the cache
33fd71
33fd71
	The key to lookup/store the cache is the string representation
33fd71
        of the targetfilter. The string contains a redzone to detect
33fd71
        that the filter exceeds the maximum size (2K). If it exceeds
33fd71
        then the key is invalid and the lookup/store is noop.
33fd71
33fd71
relates: #4925
33fd71
33fd71
Reviewed by: Mark Reynolds, William Brown (Thanks)
33fd71
33fd71
Platforms tested: F34
33fd71
---
33fd71
 ldap/servers/plugins/acl/acl.c     | 138 +++++++++++++++++++++++++++--
33fd71
 ldap/servers/plugins/acl/acl.h     |  14 +++
33fd71
 ldap/servers/plugins/acl/acl_ext.c |  12 +++
33fd71
 ldap/servers/slapd/libglobs.c      |  29 ++++++
33fd71
 ldap/servers/slapd/proto-slap.h    |   2 +
33fd71
 ldap/servers/slapd/slap.h          |   2 +
33fd71
 6 files changed, 191 insertions(+), 6 deletions(-)
33fd71
33fd71
diff --git a/ldap/servers/plugins/acl/acl.c b/ldap/servers/plugins/acl/acl.c
33fd71
index 4e811f73a..377c18e68 100644
33fd71
--- a/ldap/servers/plugins/acl/acl.c
33fd71
+++ b/ldap/servers/plugins/acl/acl.c
33fd71
@@ -67,6 +67,9 @@ static void print_access_control_summary(char *source,
33fd71
                                          const char *edn,
33fd71
                                          aclResultReason_t *acl_reason);
33fd71
 static int check_rdn_access(Slapi_PBlock *pb, Slapi_Entry *e, const char *newrdn, int access);
33fd71
+static struct targetfilter_cached_result *targetfilter_cache_lookup(struct acl_pblock *aclpb, char *filter, PRBool filter_valid);
33fd71
+static void targetfilter_cache_add(struct acl_pblock *aclpb, char *filter, int result, PRBool filter_valid);
33fd71
+
33fd71
 
33fd71
 
33fd71
 /*
33fd71
@@ -176,6 +179,70 @@ check_rdn_access(Slapi_PBlock *pb, Slapi_Entry *e, const char *dn, int access)
33fd71
     return (retCode);
33fd71
 }
33fd71
 
33fd71
+/* Retrieves, in the targetfilter cache (list), this
33fd71
+ * filter in case it was already evaluated
33fd71
+ *
33fd71
+ * filter: key to retrieve the evaluation in the cache
33fd71
+ * filter_valid: PR_FALSE means that the filter key is truncated, PR_TRUE else
33fd71
+ */
33fd71
+static struct targetfilter_cached_result *
33fd71
+targetfilter_cache_lookup(struct acl_pblock *aclpb, char *filter, PRBool filter_valid)
33fd71
+{
33fd71
+    struct targetfilter_cached_result *results;
33fd71
+    if (! aclpb->targetfilter_cache_enabled) {
33fd71
+        /* targetfilter cache is disabled */
33fd71
+        return NULL;
33fd71
+    }
33fd71
+    if (filter == NULL) {
33fd71
+        return NULL;
33fd71
+    }
33fd71
+    for(results = aclpb->aclpb_curr_entry_targetfilters; results; results = results->next) {
33fd71
+        if (strcmp(results->filter, filter) == 0) {
33fd71
+            return results;
33fd71
+        }
33fd71
+    }
33fd71
+
33fd71
+    return NULL;
33fd71
+}
33fd71
+
33fd71
+/* Free all evaluations cached for this current entry */
33fd71
+void
33fd71
+targetfilter_cache_free(struct acl_pblock *aclpb)
33fd71
+{
33fd71
+    struct targetfilter_cached_result *results, *next;
33fd71
+    if (aclpb == NULL) {
33fd71
+        return;
33fd71
+    }
33fd71
+    for(results = aclpb->aclpb_curr_entry_targetfilters; results;) {
33fd71
+        next = results->next;
33fd71
+        slapi_ch_free_string(&results->filter);
33fd71
+        slapi_ch_free((void **) &results);
33fd71
+        results = next;
33fd71
+    }
33fd71
+    aclpb->aclpb_curr_entry_targetfilters = NULL;
33fd71
+}
33fd71
+
33fd71
+/* add a new targetfilter evaluation into the cache (per entry)
33fd71
+ * ATM just use a linked list of evaluation
33fd71
+ *
33fd71
+ * filter: key to retrieve the evaluation in the cache
33fd71
+ * result: result of the evaluation
33fd71
+ * filter_valid: PR_FALSE means that the filter key is truncated, PR_TRUE else
33fd71
+ */
33fd71
+static void
33fd71
+targetfilter_cache_add(struct acl_pblock *aclpb, char *filter, int result, PRBool filter_valid)
33fd71
+{
33fd71
+    struct targetfilter_cached_result *results;
33fd71
+    if (! filter_valid || ! aclpb->targetfilter_cache_enabled) {
33fd71
+        /* targetfilter cache is disabled or filter is truncated */
33fd71
+        return;
33fd71
+    }
33fd71
+    results = (struct targetfilter_cached_result *) slapi_ch_calloc(1, (sizeof(struct targetfilter_cached_result)));
33fd71
+    results->filter = slapi_ch_strdup(filter);
33fd71
+    results->next = aclpb->aclpb_curr_entry_targetfilters;
33fd71
+    results->matching_result = result;
33fd71
+    aclpb->aclpb_curr_entry_targetfilters = results;
33fd71
+}
33fd71
 /***************************************************************************
33fd71
 *
33fd71
 * acl_access_allowed
33fd71
@@ -496,6 +563,7 @@ acl_access_allowed(
33fd71
 
33fd71
         /* Keep the ptr to the current entry */
33fd71
         aclpb->aclpb_curr_entry = (Slapi_Entry *)e;
33fd71
+        targetfilter_cache_free(aclpb);
33fd71
 
33fd71
         /* Get the attr info */
33fd71
         deallocate_attrEval = acl__get_attrEval(aclpb, attr);
33fd71
@@ -1924,7 +1992,7 @@ acl_modified(Slapi_PBlock *pb, int optype, Slapi_DN *e_sdn, void *change)
33fd71
 *    None.
33fd71
 *
33fd71
 **************************************************************************/
33fd71
-static int
33fd71
+int
33fd71
 acl__scan_for_acis(Acl_PBlock *aclpb, int *err)
33fd71
 {
33fd71
     aci_t *aci;
33fd71
@@ -2405,10 +2473,68 @@ acl__resource_match_aci(Acl_PBlock *aclpb, aci_t *aci, int skip_attrEval, int *a
33fd71
                                                     ACL_EVAL_TARGET_FILTER);
33fd71
             slapi_ch_free((void **)&lasinfo);
33fd71
         } else {
33fd71
-            if (slapi_vattr_filter_test(NULL, aclpb->aclpb_curr_entry,
33fd71
-                                        aci->targetFilter,
33fd71
-                                        0 /*don't do access check*/) != 0) {
33fd71
-                filter_matched = ACL_FALSE;
33fd71
+            Slapi_DN *sdn;
33fd71
+            char* attr_evaluated = "None";
33fd71
+            char logbuf[2048] = {0};
33fd71
+            char *redzone = "the redzone";
33fd71
+            int32_t redzone_idx;
33fd71
+            char *filterstr; /* key to retrieve/add targetfilter value in the cache */
33fd71
+            PRBool valid_filter;
33fd71
+            struct targetfilter_cached_result *previous_filter_test;
33fd71
+
33fd71
+            /* only usefull for debug purpose */
33fd71
+            if (aclpb->aclpb_curr_attrEval && aclpb->aclpb_curr_attrEval->attrEval_name) {
33fd71
+                attr_evaluated = aclpb->aclpb_curr_attrEval->attrEval_name;
33fd71
+            }
33fd71
+            sdn = slapi_entry_get_sdn(aclpb->aclpb_curr_entry);
33fd71
+
33fd71
+            /* The key for the cache is the string representation of the original filter
33fd71
+             * If the string can not fit into the provided buffer (overwrite redzone)
33fd71
+             * then the filter is said invalid (for the cache) and it will be evaluated
33fd71
+             */
33fd71
+            redzone_idx = sizeof(logbuf) - 1 - strlen(redzone);
33fd71
+            strcpy(&logbuf[redzone_idx], redzone);
33fd71
+            filterstr = slapi_filter_to_string(aci->targetFilter, logbuf, sizeof(logbuf));
33fd71
+
33fd71
+            /* if the redzone was overwritten that means filterstr is truncated and not valid */
33fd71
+            valid_filter = (strcmp(&logbuf[redzone_idx], redzone) == 0);
33fd71
+            if (!valid_filter) {
33fd71
+                strcpy(&logbuf[50], "...");
33fd71
+                slapi_log_err(SLAPI_LOG_ACL, "acl__ressource_match_aci", "targetfilter too large (can not be cache) %s\n", logbuf);
33fd71
+            }
33fd71
+
33fd71
+            previous_filter_test = targetfilter_cache_lookup(aclpb, filterstr, valid_filter);
33fd71
+            if (previous_filter_test) {
33fd71
+                /* The filter was already evaluated against that same entry */
33fd71
+                if (previous_filter_test->matching_result == 0) {
33fd71
+                    slapi_log_err(SLAPI_LOG_ACL, "acl__ressource_match_aci", "cached result for entry %s did NOT match %s (%s)\n",
33fd71
+                            slapi_sdn_get_ndn(sdn),
33fd71
+                            filterstr,
33fd71
+                            attr_evaluated);
33fd71
+                    filter_matched = ACL_FALSE;
33fd71
+                } else {
33fd71
+                    slapi_log_err(SLAPI_LOG_ACL, "acl__ressource_match_aci", "cached result for entry %s did match %s (%s)\n",
33fd71
+                            slapi_sdn_get_ndn(sdn),
33fd71
+                            filterstr,
33fd71
+                            attr_evaluated);
33fd71
+                }
33fd71
+            } else {
33fd71
+                /* The filter has not already been evaluated against that entry
33fd71
+                 * evaluate it and cache the result
33fd71
+                 */
33fd71
+                if (slapi_vattr_filter_test(NULL, aclpb->aclpb_curr_entry,
33fd71
+                        aci->targetFilter,
33fd71
+                        0 /*don't do access check*/) != 0) {
33fd71
+                    filter_matched = ACL_FALSE;
33fd71
+                    targetfilter_cache_add(aclpb, filterstr, 0, valid_filter); /* does not match */
33fd71
+                } else {
33fd71
+                    targetfilter_cache_add(aclpb, filterstr, 1, valid_filter); /* does match */
33fd71
+                }
33fd71
+                slapi_log_err(SLAPI_LOG_ACL, "acl__ressource_match_aci", "entry %s %s match %s (%s)\n",
33fd71
+                        slapi_sdn_get_ndn(sdn),
33fd71
+                        filter_matched == ACL_FALSE ? "does not" : "does",
33fd71
+                        filterstr,
33fd71
+                        attr_evaluated);
33fd71
             }
33fd71
         }
33fd71
 
33fd71
@@ -2858,7 +2984,7 @@ acl__resource_match_aci_EXIT:
33fd71
 *    None.
33fd71
 *
33fd71
 **************************************************************************/
33fd71
-static int
33fd71
+int
33fd71
 acl__TestRights(Acl_PBlock *aclpb, int access, const char **right, const char **map_generic, aclResultReason_t *result_reason)
33fd71
 {
33fd71
     ACLEvalHandle_t *acleval;
33fd71
diff --git a/ldap/servers/plugins/acl/acl.h b/ldap/servers/plugins/acl/acl.h
33fd71
index becc7f920..c9b9efa56 100644
33fd71
--- a/ldap/servers/plugins/acl/acl.h
33fd71
+++ b/ldap/servers/plugins/acl/acl.h
33fd71
@@ -407,6 +407,17 @@ struct aci_container
33fd71
 };
33fd71
 typedef struct aci_container AciContainer;
33fd71
 
33fd71
+/* This structure is stored in the aclpb.
33fd71
+ * It is a linked list containing the result of
33fd71
+ * the filter matching against a specific entry.
33fd71
+ *
33fd71
+ * This list is free for each new entry in the aclpb*/
33fd71
+struct targetfilter_cached_result {
33fd71
+    char *filter;                            /* strdup of string representation of aci->targetFilter */
33fd71
+    int matching_result;                     /* 0 does not match / 1 does match */
33fd71
+    struct targetfilter_cached_result *next; /* next targetfilter already evaluated */
33fd71
+};
33fd71
+
33fd71
 struct acl_pblock
33fd71
 {
33fd71
     int aclpb_state;
33fd71
@@ -476,6 +487,8 @@ struct acl_pblock
33fd71
 
33fd71
     /* Current entry/dn/attr evaluation info */
33fd71
     Slapi_Entry *aclpb_curr_entry; /* current Entry being processed */
33fd71
+    int32_t targetfilter_cache_enabled;
33fd71
+    struct targetfilter_cached_result *aclpb_curr_entry_targetfilters;
33fd71
     int aclpb_num_entries;
33fd71
     Slapi_DN *aclpb_curr_entry_sdn;    /* Entry's SDN */
33fd71
     Slapi_DN *aclpb_authorization_sdn; /* dn used for authorization */
33fd71
@@ -723,6 +736,7 @@ void acl_modified(Slapi_PBlock *pb, int optype, Slapi_DN *e_sdn, void *change);
33fd71
 
33fd71
 int acl_access_allowed_disjoint_resource(Slapi_PBlock *pb, Slapi_Entry *e, char *attr, struct berval *val, int access);
33fd71
 int acl_access_allowed_main(Slapi_PBlock *pb, Slapi_Entry *e, char **attrs, struct berval *val, int access, int flags, char **errbuf);
33fd71
+void targetfilter_cache_free(struct acl_pblock *aclpb);
33fd71
 int acl_access_allowed(Slapi_PBlock *pb, Slapi_Entry *e, char *attr, struct berval *val, int access);
33fd71
 aclUserGroup *acl_get_usersGroup(struct acl_pblock *aclpb, char *n_dn);
33fd71
 void acl_print_acllib_err(NSErr_t *errp, char *str);
33fd71
diff --git a/ldap/servers/plugins/acl/acl_ext.c b/ldap/servers/plugins/acl/acl_ext.c
33fd71
index 797c5d2fd..c88f7389f 100644
33fd71
--- a/ldap/servers/plugins/acl/acl_ext.c
33fd71
+++ b/ldap/servers/plugins/acl/acl_ext.c
33fd71
@@ -189,6 +189,11 @@ acl_operation_ext_constructor(void *object __attribute__((unused)), void *parent
33fd71
         slapi_log_err(SLAPI_LOG_ERR, plugin_name,
33fd71
                       "acl_operation_ext_constructor - Operation extension allocation Failed\n");
33fd71
     }
33fd71
+    /* targetfilter_cache toggle set during aclpb allocation
33fd71
+     * to avoid accessing configuration during the evaluation
33fd71
+     * of each aci
33fd71
+     */
33fd71
+    aclpb->targetfilter_cache_enabled = config_get_targetfilter_cache();
33fd71
 
33fd71
     TNF_PROBE_0_DEBUG(acl_operation_ext_constructor_end, "ACL", "");
33fd71
 
33fd71
@@ -713,6 +718,7 @@ acl__free_aclpb(Acl_PBlock **aclpb_ptr)
33fd71
     slapi_ch_free((void **)&(aclpb->aclpb_curr_entryEval_context.acle_handles_matched_target));
33fd71
     slapi_ch_free((void **)&(aclpb->aclpb_prev_entryEval_context.acle_handles_matched_target));
33fd71
     slapi_ch_free((void **)&(aclpb->aclpb_prev_opEval_context.acle_handles_matched_target));
33fd71
+    targetfilter_cache_free(aclpb);
33fd71
     slapi_sdn_free(&aclpb->aclpb_authorization_sdn);
33fd71
     slapi_sdn_free(&aclpb->aclpb_curr_entry_sdn);
33fd71
     if (aclpb->aclpb_macro_ht) {
33fd71
@@ -921,6 +927,12 @@ acl__done_aclpb(struct acl_pblock *aclpb)
33fd71
                       aclpb->aclpb_acleval ? (char *)aclpb->aclpb_acleval : "NULL");
33fd71
     }
33fd71
 
33fd71
+    /* This aclpb return to the aclpb pool, make sure
33fd71
+     * the cached evaluations are freed and that
33fd71
+     * aclpb_curr_entry_targetfilters is NULL
33fd71
+     */
33fd71
+    targetfilter_cache_free(aclpb);
33fd71
+
33fd71
     /* Now Free the contents or clean it */
33fd71
     slapi_sdn_done(aclpb->aclpb_curr_entry_sdn);
33fd71
     if (aclpb->aclpb_Evalattr)
33fd71
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
33fd71
index db7d01bbc..2ea4cd760 100644
33fd71
--- a/ldap/servers/slapd/libglobs.c
33fd71
+++ b/ldap/servers/slapd/libglobs.c
33fd71
@@ -221,6 +221,7 @@ slapi_onoff_t init_return_exact_case;
33fd71
 slapi_onoff_t init_result_tweak;
33fd71
 slapi_onoff_t init_plugin_track;
33fd71
 slapi_onoff_t init_moddn_aci;
33fd71
+slapi_onoff_t init_targetfilter_cache;
33fd71
 slapi_onoff_t init_lastmod;
33fd71
 slapi_onoff_t init_readonly;
33fd71
 slapi_onoff_t init_accesscontrol;
33fd71
@@ -903,6 +904,11 @@ static struct config_get_and_set
33fd71
      (void **)&global_slapdFrontendConfig.moddn_aci,
33fd71
      CONFIG_ON_OFF, (ConfigGetFunc)config_get_moddn_aci,
33fd71
      &init_moddn_aci, NULL},
33fd71
+    {CONFIG_TARGETFILTER_CACHE_ATTRIBUTE, config_set_targetfilter_cache,
33fd71
+     NULL, 0,
33fd71
+     (void **)&global_slapdFrontendConfig.targetfilter_cache,
33fd71
+     CONFIG_ON_OFF, (ConfigGetFunc)config_get_targetfilter_cache,
33fd71
+     &init_targetfilter_cache, NULL},
33fd71
     {CONFIG_ATTRIBUTE_NAME_EXCEPTION_ATTRIBUTE, config_set_attrname_exceptions,
33fd71
      NULL, 0,
33fd71
      (void **)&global_slapdFrontendConfig.attrname_exceptions,
33fd71
@@ -1688,6 +1694,7 @@ FrontendConfig_init(void)
33fd71
     init_syntaxcheck = cfg->syntaxcheck = LDAP_ON;
33fd71
     init_plugin_track = cfg->plugin_track = LDAP_OFF;
33fd71
     init_moddn_aci = cfg->moddn_aci = LDAP_ON;
33fd71
+    init_targetfilter_cache = cfg->targetfilter_cache = LDAP_ON;
33fd71
     init_syntaxlogging = cfg->syntaxlogging = LDAP_OFF;
33fd71
     init_dn_validate_strict = cfg->dn_validate_strict = LDAP_OFF;
33fd71
     init_ds4_compatible_schema = cfg->ds4_compatible_schema = LDAP_OFF;
33fd71
@@ -4053,6 +4060,21 @@ config_set_moddn_aci(const char *attrname, char *value, char *errorbuf, int appl
33fd71
     return retVal;
33fd71
 }
33fd71
 
33fd71
+int32_t
33fd71
+config_set_targetfilter_cache(const char *attrname, char *value, char *errorbuf, int apply)
33fd71
+{
33fd71
+    int32_t retVal = LDAP_SUCCESS;
33fd71
+    slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
33fd71
+
33fd71
+    retVal = config_set_onoff(attrname,
33fd71
+                              value,
33fd71
+                              &(slapdFrontendConfig->targetfilter_cache),
33fd71
+                              errorbuf,
33fd71
+                              apply);
33fd71
+
33fd71
+    return retVal;
33fd71
+}
33fd71
+
33fd71
 int32_t
33fd71
 config_set_dynamic_plugins(const char *attrname, char *value, char *errorbuf, int apply)
33fd71
 {
33fd71
@@ -5903,6 +5925,13 @@ config_get_moddn_aci(void)
33fd71
     return slapi_atomic_load_32(&(slapdFrontendConfig->moddn_aci), __ATOMIC_ACQUIRE);
33fd71
 }
33fd71
 
33fd71
+int32_t
33fd71
+config_get_targetfilter_cache(void)
33fd71
+{
33fd71
+    slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
33fd71
+    return slapi_atomic_load_32(&(slapdFrontendConfig->targetfilter_cache), __ATOMIC_ACQUIRE);
33fd71
+}
33fd71
+
33fd71
 int32_t
33fd71
 config_get_security(void)
33fd71
 {
33fd71
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
33fd71
index 2768d5a1d..c143f3772 100644
33fd71
--- a/ldap/servers/slapd/proto-slap.h
33fd71
+++ b/ldap/servers/slapd/proto-slap.h
33fd71
@@ -263,6 +263,7 @@ int config_set_lastmod(const char *attrname, char *value, char *errorbuf, int ap
33fd71
 int config_set_nagle(const char *attrname, char *value, char *errorbuf, int apply);
33fd71
 int config_set_accesscontrol(const char *attrname, char *value, char *errorbuf, int apply);
33fd71
 int config_set_moddn_aci(const char *attrname, char *value, char *errorbuf, int apply);
33fd71
+int32_t config_set_targetfilter_cache(const char *attrname, char *value, char *errorbuf, int apply);
33fd71
 int config_set_security(const char *attrname, char *value, char *errorbuf, int apply);
33fd71
 int config_set_readonly(const char *attrname, char *value, char *errorbuf, int apply);
33fd71
 int config_set_schemacheck(const char *attrname, char *value, char *errorbuf, int apply);
33fd71
@@ -469,6 +470,7 @@ int config_get_accesscontrol(void);
33fd71
 int config_get_return_exact_case(void);
33fd71
 int config_get_result_tweak(void);
33fd71
 int config_get_moddn_aci(void);
33fd71
+int32_t config_get_targetfilter_cache(void);
33fd71
 int config_get_security(void);
33fd71
 int config_get_schemacheck(void);
33fd71
 int config_get_syntaxcheck(void);
33fd71
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
33fd71
index c48516157..a3c0eff93 100644
33fd71
--- a/ldap/servers/slapd/slap.h
33fd71
+++ b/ldap/servers/slapd/slap.h
33fd71
@@ -2229,6 +2229,7 @@ typedef struct _slapdEntryPoints
33fd71
 #define CONFIG_REWRITE_RFC1274_ATTRIBUTE "nsslapd-rewrite-rfc1274"
33fd71
 #define CONFIG_PLUGIN_BINDDN_TRACKING_ATTRIBUTE "nsslapd-plugin-binddn-tracking"
33fd71
 #define CONFIG_MODDN_ACI_ATTRIBUTE "nsslapd-moddn-aci"
33fd71
+#define CONFIG_TARGETFILTER_CACHE_ATTRIBUTE "nsslapd-targetfilter-cache"
33fd71
 #define CONFIG_GLOBAL_BACKEND_LOCK "nsslapd-global-backend-lock"
33fd71
 #define CONFIG_ENABLE_NUNC_STANS "nsslapd-enable-nunc-stans"
33fd71
 #define CONFIG_ENABLE_UPGRADE_HASH "nsslapd-enable-upgrade-hash"
33fd71
@@ -2401,6 +2402,7 @@ typedef struct _slapdFrontendConfig
33fd71
     char **plugin;
33fd71
     slapi_onoff_t plugin_track;
33fd71
     slapi_onoff_t moddn_aci;
33fd71
+    slapi_onoff_t targetfilter_cache;
33fd71
     struct pw_scheme *pw_storagescheme;
33fd71
     slapi_onoff_t pwpolicy_local;
33fd71
     slapi_onoff_t pw_is_global_policy;
33fd71
-- 
33fd71
2.31.1
33fd71