|
|
1d0431 |
From 44e8e5f79616fb80edf8af010332c18a628af861 Mon Sep 17 00:00:00 2001
|
|
|
1d0431 |
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
|
1d0431 |
Date: Thu, 29 Oct 2015 17:34:48 +0100
|
|
|
1d0431 |
Subject: [PATCH 1/2] slapi-nis: delay sending responses from compat tree after
|
|
|
1d0431 |
map search
|
|
|
1d0431 |
|
|
|
1d0431 |
When slapi-nis plugin responds on a search query, it holds read lock for
|
|
|
1d0431 |
the internal structure called 'map cache'. The map cache lock can also be taken
|
|
|
1d0431 |
for write when modification would be required like responding to DELETE, ADD, or
|
|
|
1d0431 |
MODIFY operations.
|
|
|
1d0431 |
|
|
|
1d0431 |
As result of the lock semantics, write lock owner is blocked until all read lock
|
|
|
1d0431 |
owners release their locks. This is generally not a problem but when readers sent
|
|
|
1d0431 |
out LDAP query results, they call into SLAPI function that might take long time
|
|
|
1d0431 |
to send out the data due to external reasons (network latencies, clients being
|
|
|
1d0431 |
blocked, etc) and all this time map cache is locked for write operations.
|
|
|
1d0431 |
|
|
|
1d0431 |
When Kerberos KDC issues a TGT, it needs to modify few Kerberos-related attributes
|
|
|
1d0431 |
in the principal's LDAP entry. These updates are generating MOD operations visible
|
|
|
1d0431 |
by slapi-nis plugin which triggers re-scan of map cache to potentially replace
|
|
|
1d0431 |
the affected entries. To perform potential replacement, slapi-nis has to take a write
|
|
|
1d0431 |
lock and be blocked by outstanding readers.
|
|
|
1d0431 |
|
|
|
1d0431 |
Therefore, it is possible to encounter a situation where an LDAP client uses
|
|
|
1d0431 |
SASL GSSAPI authentication and existing Kerberos ticket did expire in a course
|
|
|
1d0431 |
of outstanding search request. According to LDAPv3 protocol specification, an
|
|
|
1d0431 |
LDAP client must perform re-negotiation before reading any outstanding PDUs. It
|
|
|
1d0431 |
would ask Kerberos KDC for a new (or renewed) TGT, that would cause MOD updates
|
|
|
1d0431 |
for the primary tree which is tracked for changes by slapi-nis. These changes
|
|
|
1d0431 |
would be blocked by a slapi-nis reader as the client cannot finish reading
|
|
|
1d0431 |
outstanding PDUs yet.
|
|
|
1d0431 |
|
|
|
1d0431 |
To solve this problem, we avoid sending LDAP entries while keeping map cache
|
|
|
1d0431 |
lock. Instead, we generate a linked list of copies of entries which will be
|
|
|
1d0431 |
sent out. To allow sharing of entries between multiple parallel queries, we
|
|
|
1d0431 |
hash the entry and reference the cached entry in the linked list with increased
|
|
|
1d0431 |
reference count. Once entry is actually sent, its reference count decreased and
|
|
|
1d0431 |
on reaching zero it is removed from the hash.
|
|
|
1d0431 |
|
|
|
1d0431 |
o solve this problem, we avoid sending LDAP entries while keeping map cache
|
|
|
1d0431 |
lock. Instead, we generate a linked list of copies of entries which will be
|
|
|
1d0431 |
sent out. To allow sharing of entries between multiple parallel queries, we
|
|
|
1d0431 |
hash the entry and reference the cached entry in the linked list with increased
|
|
|
1d0431 |
reference count. Once entry is actually sent, its reference count decreased and
|
|
|
1d0431 |
on reaching zero it is removed from the hash.
|
|
|
1d0431 |
|
|
|
1d0431 |
The entry in the hash table might become outdated. This is detected by comparing
|
|
|
1d0431 |
both modifyTimestamp and entryUSN values of the entry to be sent and entry in the
|
|
|
1d0431 |
hash table. If new version of the entry is different, hash table's entry reference
|
|
|
1d0431 |
is replaced with a new copy. The old entry is not removed because it is still
|
|
|
1d0431 |
referenced by some outstanding query processing. Thus, the hash table always
|
|
|
1d0431 |
references the most recent version of an entry but there might be multiple copies
|
|
|
1d0431 |
in possesion of the linked lists from the separate parallel queries.
|
|
|
1d0431 |
|
|
|
1d0431 |
An entry sharing via hash table can be disabled by setting
|
|
|
1d0431 |
slapi-entry-cache: 0
|
|
|
1d0431 |
in the definition, cn=Schema Compatibility,cn=plugins,cn=config
|
|
|
1d0431 |
|
|
|
1d0431 |
Resolves: rhbz#1273587
|
|
|
1d0431 |
|
|
|
1d0431 |
https://bugzilla.redhat.com/show_bug.cgi?id=1273587
|
|
|
1d0431 |
---
|
|
|
1d0431 |
doc/sch-configuration.txt | 7 ++
|
|
|
1d0431 |
src/back-sch.c | 159 ++++++++++++++++++++++++++++++++++++++++++----
|
|
|
1d0431 |
src/back-sch.h | 20 ++++++
|
|
|
1d0431 |
src/plug-sch.c | 34 ++++++++++
|
|
|
1d0431 |
src/plugin.h | 3 +
|
|
|
1d0431 |
5 files changed, 212 insertions(+), 11 deletions(-)
|
|
|
1d0431 |
|
|
|
1d0431 |
diff --git a/doc/sch-configuration.txt b/doc/sch-configuration.txt
|
|
|
1d0431 |
index e07a4af..dd8b3c4 100644
|
|
|
1d0431 |
--- a/doc/sch-configuration.txt
|
|
|
1d0431 |
+++ b/doc/sch-configuration.txt
|
|
|
1d0431 |
@@ -16,6 +16,13 @@ look like this:
|
|
|
1d0431 |
nsslapd-version: 0.0
|
|
|
1d0431 |
nsslapd-pluginvendor: redhat.com
|
|
|
1d0431 |
nsslapd-plugindescription: Schema Compatibility Plugin
|
|
|
1d0431 |
+ slapi-entry-cache: 1
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+The only optional attribute is 'slapi-entry-cache' (default to 1)
|
|
|
1d0431 |
+controls whether the plugin should use an entry cache for outstanding
|
|
|
1d0431 |
+query requests. The entry cache is an optimization technique to
|
|
|
1d0431 |
+help reduce memory pressure during parallel requests. Specify 0 to disable
|
|
|
1d0431 |
+an entry cache.
|
|
|
1d0431 |
|
|
|
1d0431 |
Configuration for individual sets should be stored in entries directly
|
|
|
1d0431 |
beneath the plugin's entry. These attributes are recognized:
|
|
|
1d0431 |
diff --git a/src/back-sch.c b/src/back-sch.c
|
|
|
1d0431 |
index dd6f92d..b2362d0 100644
|
|
|
1d0431 |
--- a/src/back-sch.c
|
|
|
1d0431 |
+++ b/src/back-sch.c
|
|
|
1d0431 |
@@ -32,6 +32,7 @@
|
|
|
1d0431 |
|
|
|
1d0431 |
#ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H
|
|
|
1d0431 |
#include <nspr.h>
|
|
|
1d0431 |
+#include <plhash.h>
|
|
|
1d0431 |
#include <nss.h>
|
|
|
1d0431 |
#include <dirsrv/slapi-plugin.h>
|
|
|
1d0431 |
#else
|
|
|
1d0431 |
@@ -53,6 +54,9 @@
|
|
|
1d0431 |
#include "map.h"
|
|
|
1d0431 |
#include "back-sch.h"
|
|
|
1d0431 |
|
|
|
1d0431 |
+static void
|
|
|
1d0431 |
+backend_entries_to_return_push(struct backend_search_cbdata *cbdata, Slapi_Entry *e);
|
|
|
1d0431 |
+
|
|
|
1d0431 |
#define SCH_CONTAINER_CONFIGURATION_FILTER "(&(" SCH_CONTAINER_CONFIGURATION_GROUP_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_BASE_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_FILTER_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_RDN_ATTR "=*))"
|
|
|
1d0431 |
|
|
|
1d0431 |
/* Read the name of the NIS master. A dummy function for the schema
|
|
|
1d0431 |
@@ -996,7 +1000,7 @@ backend_search_entry_cb(const char *domain, const char *map, bool_t secure,
|
|
|
1d0431 |
void *backend_data, void *cb_data)
|
|
|
1d0431 |
{
|
|
|
1d0431 |
Slapi_DN *sdn;
|
|
|
1d0431 |
- Slapi_Entry *entry;
|
|
|
1d0431 |
+ Slapi_Entry *entry = NULL; /* prevent to free an uninitialized entry */
|
|
|
1d0431 |
Slapi_Attr *attr = NULL;
|
|
|
1d0431 |
struct backend_search_cbdata *cbdata;
|
|
|
1d0431 |
struct backend_entry_data *entry_data;
|
|
|
1d0431 |
@@ -1052,8 +1056,7 @@ backend_search_entry_cb(const char *domain, const char *map, bool_t secure,
|
|
|
1d0431 |
slapi_entry_delete_string(entry, "objectClass", "ipaOverrideTarget");
|
|
|
1d0431 |
}
|
|
|
1d0431 |
#endif
|
|
|
1d0431 |
- slapi_send_ldap_search_entry(cbdata->pb, entry, NULL,
|
|
|
1d0431 |
- cbdata->attrs, cbdata->attrsonly);
|
|
|
1d0431 |
+ backend_entries_to_return_push(cbdata, entry);
|
|
|
1d0431 |
cbdata->n_entries++;
|
|
|
1d0431 |
|
|
|
1d0431 |
if (entry != entry_data->e) {
|
|
|
1d0431 |
@@ -1070,7 +1073,7 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag,
|
|
|
1d0431 |
{
|
|
|
1d0431 |
struct backend_search_cbdata *cbdata;
|
|
|
1d0431 |
struct backend_set_data *set_data;
|
|
|
1d0431 |
- Slapi_Entry *set_entry;
|
|
|
1d0431 |
+ Slapi_Entry *set_entry = NULL ; /* prevent to free an uninitialized entry */
|
|
|
1d0431 |
int result, n_entries;
|
|
|
1d0431 |
int n_entries_without_nsswitch;
|
|
|
1d0431 |
const char *ndn;
|
|
|
1d0431 |
@@ -1124,12 +1127,11 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag,
|
|
|
1d0431 |
set_data->common.group, set_entry);
|
|
|
1d0431 |
}
|
|
|
1d0431 |
#endif
|
|
|
1d0431 |
- slapi_send_ldap_search_entry(cbdata->pb, set_entry,
|
|
|
1d0431 |
- NULL, cbdata->attrs,
|
|
|
1d0431 |
- cbdata->attrsonly);
|
|
|
1d0431 |
+ backend_entries_to_return_push(cbdata, set_entry);
|
|
|
1d0431 |
cbdata->n_entries++;
|
|
|
1d0431 |
break;
|
|
|
1d0431 |
}
|
|
|
1d0431 |
+
|
|
|
1d0431 |
slapi_entry_free(set_entry);
|
|
|
1d0431 |
}
|
|
|
1d0431 |
|
|
|
1d0431 |
@@ -1244,7 +1246,7 @@ backend_search_group_cb(const char *group, void *cb_data)
|
|
|
1d0431 |
{
|
|
|
1d0431 |
struct backend_search_cbdata *cbdata;
|
|
|
1d0431 |
Slapi_DN *group_dn;
|
|
|
1d0431 |
- Slapi_Entry *group_entry;
|
|
|
1d0431 |
+ Slapi_Entry *group_entry = NULL; /* prevent to free an uninitialized entry */
|
|
|
1d0431 |
int result, n_maps;
|
|
|
1d0431 |
|
|
|
1d0431 |
cbdata = cb_data;
|
|
|
1d0431 |
@@ -1279,12 +1281,11 @@ backend_search_group_cb(const char *group, void *cb_data)
|
|
|
1d0431 |
idview_process_overrides(cbdata, NULL, NULL, group, group_entry);
|
|
|
1d0431 |
}
|
|
|
1d0431 |
#endif
|
|
|
1d0431 |
- slapi_send_ldap_search_entry(cbdata->pb, group_entry,
|
|
|
1d0431 |
- NULL, cbdata->attrs,
|
|
|
1d0431 |
- cbdata->attrsonly);
|
|
|
1d0431 |
+ backend_entries_to_return_push(cbdata, group_entry);
|
|
|
1d0431 |
cbdata->n_entries++;
|
|
|
1d0431 |
break;
|
|
|
1d0431 |
}
|
|
|
1d0431 |
+
|
|
|
1d0431 |
slapi_entry_free(group_entry);
|
|
|
1d0431 |
}
|
|
|
1d0431 |
|
|
|
1d0431 |
@@ -1343,6 +1344,138 @@ backend_sch_scope_as_string(int scope)
|
|
|
1d0431 |
return "";
|
|
|
1d0431 |
}
|
|
|
1d0431 |
|
|
|
1d0431 |
+/* The entries are pushed (added) at the end of the list
|
|
|
1d0431 |
+ * so that they will be send in the head->tail order
|
|
|
1d0431 |
+ */
|
|
|
1d0431 |
+static void
|
|
|
1d0431 |
+backend_entries_to_return_push(struct backend_search_cbdata *cbdata, Slapi_Entry *e)
|
|
|
1d0431 |
+{
|
|
|
1d0431 |
+ struct entries_to_send *e_to_send = NULL;
|
|
|
1d0431 |
+ struct cached_entry *entry = NULL;
|
|
|
1d0431 |
+ bool_t dont_cache = FALSE;
|
|
|
1d0431 |
+ PLHashTable* ht = (PLHashTable*) cbdata->state->cached_entries;
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ if ((cbdata == NULL) || (e == NULL)) return;
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ e_to_send = (struct entries_to_send *) slapi_ch_calloc(1, sizeof(struct entries_to_send));
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ dont_cache = cbdata->state->use_entry_cache ? FALSE : TRUE;
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ if (!wrap_rwlock_wrlock(cbdata->state->cached_entries_lock)) {
|
|
|
1d0431 |
+ entry = PL_HashTableLookup(ht, slapi_entry_get_ndn(e));
|
|
|
1d0431 |
+ if (entry != NULL) {
|
|
|
1d0431 |
+ /* There is an entry in the hash table but is it the same? */
|
|
|
1d0431 |
+ char *e_modifyTimestamp = slapi_entry_attr_get_charptr(e, "modifyTimestamp");
|
|
|
1d0431 |
+ char *entry_modifyTimestamp = slapi_entry_attr_get_charptr(entry->entry, "modifyTimestamp");
|
|
|
1d0431 |
+ unsigned long e_usn = slapi_entry_attr_get_ulong(e, "entryUSN");
|
|
|
1d0431 |
+ unsigned long entry_usn = slapi_entry_attr_get_ulong(entry->entry, "entryUSN");
|
|
|
1d0431 |
+ int res = -1;
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ /* Our comparison strategy is following:
|
|
|
1d0431 |
+ * - compare modifyTimestamp values first,
|
|
|
1d0431 |
+ * - if they are the same (modifyTimestamp in slapi-nis is down to a second precision),
|
|
|
1d0431 |
+ * compare entryUSN values if they exist
|
|
|
1d0431 |
+ * - default to not using the cached entry to be on safe side if both comparisons don't
|
|
|
1d0431 |
+ * give us a definite answer */
|
|
|
1d0431 |
+ if ((e_modifyTimestamp != NULL) && (entry_modifyTimestamp != NULL)) {
|
|
|
1d0431 |
+ res = strncmp(e_modifyTimestamp, entry_modifyTimestamp, strlen(e_modifyTimestamp));
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ if ((res == 0) && ((e_usn != 0) && (entry_usn != 0))) {
|
|
|
1d0431 |
+ res = e_usn != entry_usn ? 1 : 0;
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ if (res != 0) {
|
|
|
1d0431 |
+ /* Cached entry is different, evict it from the hash table */
|
|
|
1d0431 |
+ (void) PL_HashTableRemove(ht, slapi_entry_get_ndn(entry->entry));
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ /* We don't want to clear the entry because it is still in use by other thread.
|
|
|
1d0431 |
+ * Instead, we'll insert new entry into hash table, let the linked list in other
|
|
|
1d0431 |
+ * search to remove the entry itself, but mark it as non-cached. */
|
|
|
1d0431 |
+ entry->not_cached = TRUE;
|
|
|
1d0431 |
+ entry = NULL;
|
|
|
1d0431 |
+ } else {
|
|
|
1d0431 |
+ slapi_log_error(SLAPI_LOG_PLUGIN,
|
|
|
1d0431 |
+ cbdata->state->plugin_desc->spd_id,
|
|
|
1d0431 |
+ "referenced entry [%s], USNs: %ld vs %ld, [%s] vs [%s]\n",
|
|
|
1d0431 |
+ slapi_entry_get_ndn(e), e_usn, entry_usn, e_modifyTimestamp, entry_modifyTimestamp);
|
|
|
1d0431 |
+ /* It is the same entry, reference it for us */
|
|
|
1d0431 |
+ (void) PR_AtomicIncrement(&entry->refcount);
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ if (e_modifyTimestamp != NULL)
|
|
|
1d0431 |
+ slapi_ch_free_string(&e_modifyTimestamp);
|
|
|
1d0431 |
+ if (entry_modifyTimestamp != NULL)
|
|
|
1d0431 |
+ slapi_ch_free_string(&entry_modifyTimestamp);
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ if (entry == NULL) {
|
|
|
1d0431 |
+ /* no cached entry for this DN */
|
|
|
1d0431 |
+ entry = (struct cached_entry *) slapi_ch_calloc(1, sizeof(struct cached_entry));
|
|
|
1d0431 |
+ entry->entry = slapi_entry_dup(e);
|
|
|
1d0431 |
+ entry->not_cached = FALSE;
|
|
|
1d0431 |
+ (void) PR_AtomicSet(&entry->refcount, 1);
|
|
|
1d0431 |
+ if ((ht != NULL) && (entry->entry != NULL) && (!dont_cache)) {
|
|
|
1d0431 |
+ (void) PL_HashTableAdd(ht, slapi_entry_get_ndn(entry->entry), entry);
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ wrap_rwlock_unlock(cbdata->state->cached_entries_lock);
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ e_to_send->entry = entry;
|
|
|
1d0431 |
+ if (cbdata->entries_tail == NULL) {
|
|
|
1d0431 |
+ /* First entry in that list */
|
|
|
1d0431 |
+ cbdata->entries_tail = e_to_send;
|
|
|
1d0431 |
+ cbdata->entries_head = e_to_send;
|
|
|
1d0431 |
+ } else {
|
|
|
1d0431 |
+ cbdata->entries_tail->next = e_to_send;
|
|
|
1d0431 |
+ cbdata->entries_tail = e_to_send;
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+}
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+static void
|
|
|
1d0431 |
+backend_send_mapped_entries(struct backend_search_cbdata *cbdata)
|
|
|
1d0431 |
+{
|
|
|
1d0431 |
+ struct entries_to_send *e_to_send, *next;
|
|
|
1d0431 |
+ PLHashTable* ht = NULL;
|
|
|
1d0431 |
+ int i = 0;
|
|
|
1d0431 |
+ PRInt32 count;
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ if (cbdata == NULL) return;
|
|
|
1d0431 |
+ ht = (PLHashTable*) cbdata->state->cached_entries;
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ /* iterate from head->tail sending the stored entries */
|
|
|
1d0431 |
+ for (e_to_send = cbdata->entries_head, i = 0; e_to_send != NULL; i++) {
|
|
|
1d0431 |
+ next = e_to_send->next;
|
|
|
1d0431 |
+ if (e_to_send->entry->refcount > 0) {
|
|
|
1d0431 |
+ slapi_send_ldap_search_entry(cbdata->pb, e_to_send->entry->entry, NULL,
|
|
|
1d0431 |
+ cbdata->attrs, cbdata->attrsonly);
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ /* Clean up entry only if there is no reference to it any more in any outstanding request */
|
|
|
1d0431 |
+ wrap_rwlock_wrlock(cbdata->state->cached_entries_lock);
|
|
|
1d0431 |
+ count = PR_AtomicDecrement(&e_to_send->entry->refcount);
|
|
|
1d0431 |
+ if (count == 0) {
|
|
|
1d0431 |
+ if (!e_to_send->entry->not_cached) {
|
|
|
1d0431 |
+ (void) PL_HashTableRemove(ht, slapi_entry_get_ndn(e_to_send->entry->entry));
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+ /* free this returned entry */
|
|
|
1d0431 |
+ slapi_entry_free(e_to_send->entry->entry);
|
|
|
1d0431 |
+ e_to_send->entry->entry = NULL;
|
|
|
1d0431 |
+ slapi_ch_free((void **) &e_to_send->entry);
|
|
|
1d0431 |
+ e_to_send->entry = NULL;
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+ wrap_rwlock_unlock(cbdata->state->cached_entries_lock);
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+ /* Otherwise only free list item */
|
|
|
1d0431 |
+ slapi_ch_free((void **) &e_to_send);
|
|
|
1d0431 |
+ e_to_send = next;
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+ cbdata->entries_head = NULL;
|
|
|
1d0431 |
+ cbdata->entries_tail = NULL;
|
|
|
1d0431 |
+}
|
|
|
1d0431 |
+
|
|
|
1d0431 |
static int
|
|
|
1d0431 |
backend_search_cb(Slapi_PBlock *pb)
|
|
|
1d0431 |
{
|
|
|
1d0431 |
@@ -1443,6 +1576,8 @@ backend_search_cb(Slapi_PBlock *pb)
|
|
|
1d0431 |
cbdata.state->plugin_desc->spd_id,
|
|
|
1d0431 |
"unable to acquire read lock\n");
|
|
|
1d0431 |
}
|
|
|
1d0431 |
+ /* Return existing collected entries */
|
|
|
1d0431 |
+ backend_send_mapped_entries(&cbdata);
|
|
|
1d0431 |
wrap_dec_call_level();
|
|
|
1d0431 |
#ifdef USE_NSSWITCH
|
|
|
1d0431 |
/* If during search of some sets we staged additional lookups, perform them. */
|
|
|
1d0431 |
@@ -1525,6 +1660,8 @@ backend_search_cb(Slapi_PBlock *pb)
|
|
|
1d0431 |
if (map_rdlock() == 0) {
|
|
|
1d0431 |
map_data_foreach_domain(cbdata.state, backend_search_group_cb, &cbdata);
|
|
|
1d0431 |
map_unlock();
|
|
|
1d0431 |
+ /* Return newly acquired entries */
|
|
|
1d0431 |
+ backend_send_mapped_entries(&cbdata);
|
|
|
1d0431 |
} else {
|
|
|
1d0431 |
slapi_log_error(SLAPI_LOG_PLUGIN,
|
|
|
1d0431 |
cbdata.state->plugin_desc->spd_id,
|
|
|
1d0431 |
diff --git a/src/back-sch.h b/src/back-sch.h
|
|
|
1d0431 |
index 1aedf36..e8ec400 100644
|
|
|
1d0431 |
--- a/src/back-sch.h
|
|
|
1d0431 |
+++ b/src/back-sch.h
|
|
|
1d0431 |
@@ -63,6 +63,24 @@ struct backend_staged_search {
|
|
|
1d0431 |
Slapi_Entry **entries;
|
|
|
1d0431 |
};
|
|
|
1d0431 |
|
|
|
1d0431 |
+/* Entry to be send to clients is cached to allow multiple threads to re-use results.
|
|
|
1d0431 |
+ */
|
|
|
1d0431 |
+struct cached_entry {
|
|
|
1d0431 |
+ Slapi_Entry *entry;
|
|
|
1d0431 |
+ PRInt32 refcount;
|
|
|
1d0431 |
+ bool_t not_cached;
|
|
|
1d0431 |
+};
|
|
|
1d0431 |
+
|
|
|
1d0431 |
+/* list of entries to actually send, sorted as a linked list
|
|
|
1d0431 |
+ * Entries are references to the ones stored in a cache
|
|
|
1d0431 |
+ * Before sending them out one needs to refcount the entry
|
|
|
1d0431 |
+ */
|
|
|
1d0431 |
+struct entries_to_send {
|
|
|
1d0431 |
+ struct entries_to_send *next;
|
|
|
1d0431 |
+ struct entries_to_send *prev;
|
|
|
1d0431 |
+ struct cached_entry *entry;
|
|
|
1d0431 |
+};
|
|
|
1d0431 |
+
|
|
|
1d0431 |
/* Intercept a search request, and if it belongs to one of our compatibility
|
|
|
1d0431 |
* trees, answer from our cache before letting the default database have a
|
|
|
1d0431 |
* crack at it. */
|
|
|
1d0431 |
@@ -88,6 +106,8 @@ struct backend_search_cbdata {
|
|
|
1d0431 |
int n_entries;
|
|
|
1d0431 |
struct backend_staged_search *staged;
|
|
|
1d0431 |
struct backend_staged_search *cur_staged;
|
|
|
1d0431 |
+ struct entries_to_send *entries_head;
|
|
|
1d0431 |
+ struct entries_to_send *entries_tail;
|
|
|
1d0431 |
};
|
|
|
1d0431 |
|
|
|
1d0431 |
struct backend_search_filter_config {
|
|
|
1d0431 |
diff --git a/src/plug-sch.c b/src/plug-sch.c
|
|
|
1d0431 |
index 5a6e736..f132e6d 100644
|
|
|
1d0431 |
--- a/src/plug-sch.c
|
|
|
1d0431 |
+++ b/src/plug-sch.c
|
|
|
1d0431 |
@@ -44,6 +44,7 @@
|
|
|
1d0431 |
|
|
|
1d0431 |
#ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H
|
|
|
1d0431 |
#include <nspr.h>
|
|
|
1d0431 |
+#include <plhash.h>
|
|
|
1d0431 |
#include <nss.h>
|
|
|
1d0431 |
#include <dirsrv/slapi-plugin.h>
|
|
|
1d0431 |
#else
|
|
|
1d0431 |
@@ -100,6 +101,7 @@ plugin_startup(Slapi_PBlock *pb)
|
|
|
1d0431 |
{
|
|
|
1d0431 |
/* Populate the maps and data. */
|
|
|
1d0431 |
struct plugin_state *state;
|
|
|
1d0431 |
+ Slapi_Entry *plugin_entry = NULL;
|
|
|
1d0431 |
slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state);
|
|
|
1d0431 |
slapi_pblock_get(pb, SLAPI_TARGET_DN, &state->plugin_base);
|
|
|
1d0431 |
slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id,
|
|
|
1d0431 |
@@ -111,12 +113,35 @@ plugin_startup(Slapi_PBlock *pb)
|
|
|
1d0431 |
backend_startup(pb, state);
|
|
|
1d0431 |
state->pam_lock = wrap_new_rwlock();
|
|
|
1d0431 |
backend_nss_init_context((struct nss_ops_ctx**) &state->nss_context);
|
|
|
1d0431 |
+ if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) &&
|
|
|
1d0431 |
+ (plugin_entry != NULL)) {
|
|
|
1d0431 |
+ state->use_entry_cache = backend_shr_get_vattr_boolean(state, plugin_entry,
|
|
|
1d0431 |
+ "slapi-entry-cache",
|
|
|
1d0431 |
+ 1);
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+ state->cached_entries_lock = wrap_new_rwlock();
|
|
|
1d0431 |
+ wrap_rwlock_wrlock(state->cached_entries_lock);
|
|
|
1d0431 |
+ state->cached_entries = PL_NewHashTable(0, PL_HashString, PL_CompareStrings, PL_CompareValues, 0, 0);
|
|
|
1d0431 |
+ wrap_rwlock_unlock(state->cached_entries_lock);
|
|
|
1d0431 |
/* Note that the plugin is ready to go. */
|
|
|
1d0431 |
slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id,
|
|
|
1d0431 |
"plugin startup completed\n");
|
|
|
1d0431 |
return 0;
|
|
|
1d0431 |
}
|
|
|
1d0431 |
|
|
|
1d0431 |
+static PRIntn
|
|
|
1d0431 |
+remove_cached_entries_cb(PLHashEntry *he, PRIntn i, void *arg)
|
|
|
1d0431 |
+{
|
|
|
1d0431 |
+ struct cached_entry *e = (struct cached_entry*) he->value;
|
|
|
1d0431 |
+ if (e != NULL) {
|
|
|
1d0431 |
+ if (e->entry != NULL) {
|
|
|
1d0431 |
+ slapi_entry_free(e->entry);
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+ slapi_ch_free((void **) &e);
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
+ return HT_ENUMERATE_REMOVE;
|
|
|
1d0431 |
+}
|
|
|
1d0431 |
+
|
|
|
1d0431 |
static int
|
|
|
1d0431 |
plugin_shutdown(Slapi_PBlock *pb)
|
|
|
1d0431 |
{
|
|
|
1d0431 |
@@ -126,6 +151,15 @@ plugin_shutdown(Slapi_PBlock *pb)
|
|
|
1d0431 |
wrap_free_rwlock(state->pam_lock);
|
|
|
1d0431 |
state->pam_lock = NULL;
|
|
|
1d0431 |
backend_nss_free_context((struct nss_ops_ctx**) &state->nss_context);
|
|
|
1d0431 |
+ if (state->cached_entries != NULL) {
|
|
|
1d0431 |
+ wrap_rwlock_wrlock(state->cached_entries_lock);
|
|
|
1d0431 |
+ PL_HashTableEnumerateEntries(state->cached_entries, remove_cached_entries_cb, NULL);
|
|
|
1d0431 |
+ PL_HashTableDestroy(state->cached_entries);
|
|
|
1d0431 |
+ state->cached_entries = NULL;
|
|
|
1d0431 |
+ wrap_rwlock_unlock(state->cached_entries_lock);
|
|
|
1d0431 |
+ wrap_free_rwlock(state->cached_entries_lock);
|
|
|
1d0431 |
+ state->cached_entries_lock = NULL;
|
|
|
1d0431 |
+ }
|
|
|
1d0431 |
state->plugin_base = NULL;
|
|
|
1d0431 |
slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id,
|
|
|
1d0431 |
"plugin shutdown completed\n");
|
|
|
1d0431 |
diff --git a/src/plugin.h b/src/plugin.h
|
|
|
1d0431 |
index 94ad747..429e291 100644
|
|
|
1d0431 |
--- a/src/plugin.h
|
|
|
1d0431 |
+++ b/src/plugin.h
|
|
|
1d0431 |
@@ -47,6 +47,9 @@ struct plugin_state {
|
|
|
1d0431 |
/* Schema compat-specific data. */
|
|
|
1d0431 |
struct wrapped_rwlock *pam_lock;
|
|
|
1d0431 |
void *nss_context;
|
|
|
1d0431 |
+ int use_entry_cache;
|
|
|
1d0431 |
+ void *cached_entries;
|
|
|
1d0431 |
+ struct wrapped_rwlock *cached_entries_lock;
|
|
|
1d0431 |
};
|
|
|
1d0431 |
|
|
|
1d0431 |
#endif
|
|
|
1d0431 |
--
|
|
|
1d0431 |
2.5.0
|
|
|
1d0431 |
|