andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone

Blame 0241-Ticket-616-High-contention-on-computed-attribute-loc.patch

dc8c34
From 7d4c51ed99704e6b8e8aab1b1d6a80157af48784 Mon Sep 17 00:00:00 2001
dc8c34
From: "Thierry bordaz (tbordaz)" <tbordaz@redhat.com>
dc8c34
Date: Thu, 14 Mar 2013 18:35:21 +0100
dc8c34
Subject: [PATCH 241/243] Ticket 616 - High contention on computed attribute
dc8c34
 lock
dc8c34
dc8c34
Bug Description:
dc8c34
	On search request, some requested attributes are computed:
dc8c34
		- subschemasubentry
dc8c34
		- nscpentrywsi
dc8c34
		- numsubordinates
dc8c34
		- hassubordinates
dc8c34
	This is done with a linked list of registered callback.
dc8c34
	When sending the results, each requested attribute is evaluated against all the callbacks.
dc8c34
	When going through the callbacks list the thread hold the lock on the list.
dc8c34
	Currently all the server callbacks are registered at startup time with only
dc8c34
	one thread registering them (main).
dc8c34
dc8c34
	A custom plugin may register custom callback using slapi_compute_add_evaluator
dc8c34
dc8c34
	The important point is that callback are only added to the list.
dc8c34
	Also on standard deployment without custom plugin adding new computed attribute,
dc8c34
	this list is built at startup.
dc8c34
dc8c34
	The problem on computed attribute is that it can create contention between search threads
dc8c34
	trying to evaluate/compute the requested attributes
dc8c34
dc8c34
	The same description applies to computed filters. For example with 'views' or
dc8c34
	filters containing "subordinates", the filter is recomputed.
dc8c34
	This using a linked list of callbacks registered during startup.
dc8c34
	A custom plugin may register custom callback using slapi_compute_add_search_rewriter.
dc8c34
dc8c34
	In short:
dc8c34
		On standard deployment computed filters and computed attributes lists are unchanged
dc8c34
	after startup.
dc8c34
		Computed filters/attribute are only added to the lists.
dc8c34
dc8c34
Fix Description:
dc8c34
dc8c34
	The fix consist to not protect the access to computed filter/attribute unless a
dc8c34
	computed filter/attribute is added after startup.
dc8c34
dc8c34
	The fix also contains a improvement of rsearch to support filters larger than 256 char
dc8c34
dc8c34
	The fix also contains reworks according to review feedback.
dc8c34
	Basically using a temporary variable to hold the actual value of require_*
dc8c34
dc8c34
	The benefit is limited to 2% in terms of search throughput (8*4core machine)
dc8c34
dc8c34
https://fedorahosted.org/389/ticket/616
dc8c34
dc8c34
Reviewed by: Rich Megginson (thanks Rich !)
dc8c34
dc8c34
Platforms tested: 1*4cores fedora and 8*4cores RHEL6.4
dc8c34
dc8c34
Flag Day: no
dc8c34
dc8c34
Doc impact: no
dc8c34
(cherry picked from commit 4b7791f3ab9370ccf30b09077656e247682bf384)
dc8c34
(cherry picked from commit ff5a8770da1266d96f5e1b36b026d539cb55ec25)
dc8c34
---
dc8c34
 ldap/servers/slapd/computed.c                | 149 +++++++++++++++++++++------
dc8c34
 ldap/servers/slapd/main.c                    |   3 +-
dc8c34
 ldap/servers/slapd/proto-slap.h              |   1 +
dc8c34
 ldap/servers/slapd/tools/rsearch/nametable.c |   4 +-
dc8c34
 4 files changed, 125 insertions(+), 32 deletions(-)
dc8c34
dc8c34
diff --git a/ldap/servers/slapd/computed.c b/ldap/servers/slapd/computed.c
dc8c34
index b82e2e8..7c99b45 100644
dc8c34
--- a/ldap/servers/slapd/computed.c
dc8c34
+++ b/ldap/servers/slapd/computed.c
dc8c34
@@ -44,6 +44,7 @@
dc8c34
 /* Handles computed attributes for entries as they're returned to the client */
dc8c34
 
dc8c34
 #include "slap.h"
dc8c34
+#include "proto-slap.h"
dc8c34
 
dc8c34
 
dc8c34
 /* Structure used to pass the context needed for completing a computed attribute operation */
dc8c34
@@ -61,8 +62,11 @@ struct _compute_evaluator {
dc8c34
 };
dc8c34
 typedef struct _compute_evaluator compute_evaluator;
dc8c34
 
dc8c34
+static PRBool startup_completed = PR_FALSE;
dc8c34
+
dc8c34
 static compute_evaluator *compute_evaluators = NULL;
dc8c34
 static Slapi_RWLock *compute_evaluators_lock = NULL;
dc8c34
+static PRBool require_compute_evaluator_lock = PR_FALSE;
dc8c34
 
dc8c34
 static int
dc8c34
 compute_stock_evaluator(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn);
dc8c34
@@ -75,6 +79,7 @@ typedef struct _compute_rewriter compute_rewriter;
dc8c34
 
dc8c34
 static compute_rewriter *compute_rewriters = NULL;
dc8c34
 static Slapi_RWLock *compute_rewriters_lock = NULL;
dc8c34
+static PRBool require_compute_rewriters_lock = PR_FALSE;
dc8c34
 
dc8c34
 /* Function called by evaluators to have the value output */
dc8c34
 static int
dc8c34
@@ -83,17 +88,36 @@ compute_output_callback(computed_attr_context *c,Slapi_Attr *a , Slapi_Entry *e)
dc8c34
 	return encode_attr (c->pb, c->ber, e, a, c->attrsonly, c->requested_type);
dc8c34
 }
dc8c34
 
dc8c34
+static
dc8c34
+int compute_call_evaluators_nolock(computed_attr_context *c,slapi_compute_output_t outfn,char *type,Slapi_Entry *e)
dc8c34
+{
dc8c34
+        int rc = -1;
dc8c34
+        compute_evaluator *current = NULL;
dc8c34
+        
dc8c34
+        for (current = compute_evaluators; (current != NULL) && (-1 == rc); current = current->next) {
dc8c34
+                rc = (*(current->function))(c,type,e,outfn);
dc8c34
+        }
dc8c34
+        return rc;
dc8c34
+}
dc8c34
+
dc8c34
 static int
dc8c34
 compute_call_evaluators(computed_attr_context *c,slapi_compute_output_t outfn,char *type,Slapi_Entry *e)
dc8c34
 {
dc8c34
 	int rc = -1;
dc8c34
-	compute_evaluator *current = NULL;
dc8c34
+        int need_lock = require_compute_evaluator_lock;
dc8c34
+        
dc8c34
 	/* Walk along the list (locked) calling the evaluator functions util one says yes, an error happens, or we finish */
dc8c34
-	slapi_rwlock_rdlock(compute_evaluators_lock);
dc8c34
-	for (current = compute_evaluators; (current != NULL) && (-1 == rc); current = current->next) {
dc8c34
-		rc = (*(current->function))(c,type,e,outfn);
dc8c34
-	}
dc8c34
-	slapi_rwlock_unlock(compute_evaluators_lock);
dc8c34
+
dc8c34
+        if (need_lock) {
dc8c34
+                slapi_rwlock_rdlock(compute_evaluators_lock);
dc8c34
+        }
dc8c34
+       
dc8c34
+        rc = compute_call_evaluators_nolock(c, outfn, type, e);
dc8c34
+        
dc8c34
+        if (need_lock) {
dc8c34
+                slapi_rwlock_unlock(compute_evaluators_lock);
dc8c34
+        }   
dc8c34
+
dc8c34
 	return rc;
dc8c34
 }
dc8c34
 
dc8c34
@@ -132,25 +156,54 @@ compute_stock_evaluator(computed_attr_context *c,char* type,Slapi_Entry *e,slapi
dc8c34
 	return rc; /* I see no ships */
dc8c34
 }
dc8c34
 
dc8c34
+static void
dc8c34
+compute_add_evaluator_nolock(slapi_compute_callback_t function, compute_evaluator *new_eval)
dc8c34
+{
dc8c34
+    new_eval->next = compute_evaluators;
dc8c34
+    new_eval->function = function;
dc8c34
+    compute_evaluators = new_eval;
dc8c34
+}
dc8c34
 int slapi_compute_add_evaluator(slapi_compute_callback_t function)
dc8c34
 {
dc8c34
 	int rc = 0;
dc8c34
 	compute_evaluator *new_eval = NULL;
dc8c34
 	PR_ASSERT(NULL != function);
dc8c34
 	PR_ASSERT(NULL != compute_evaluators_lock);
dc8c34
-	slapi_rwlock_wrlock(compute_evaluators_lock);
dc8c34
+        if (startup_completed) {
dc8c34
+            /* We are now in multi-threaded and we still add
dc8c34
+             * a attribute evaluator.
dc8c34
+             * switch to use locking mechanimsm
dc8c34
+             */
dc8c34
+            require_compute_evaluator_lock = PR_TRUE;
dc8c34
+        }
dc8c34
+
dc8c34
 	new_eval = (compute_evaluator *)slapi_ch_calloc(1,sizeof (compute_evaluator));
dc8c34
 	if (NULL == new_eval) {
dc8c34
 		rc = ENOMEM;
dc8c34
 	} else {
dc8c34
-		new_eval->next = compute_evaluators;
dc8c34
-		new_eval->function = function;
dc8c34
-		compute_evaluators = new_eval;
dc8c34
+                int need_lock = require_compute_evaluator_lock;
dc8c34
+                
dc8c34
+                if (need_lock) {
dc8c34
+                    slapi_rwlock_wrlock(compute_evaluators_lock);
dc8c34
+                }
dc8c34
+                
dc8c34
+                compute_add_evaluator_nolock(function, new_eval);
dc8c34
+                
dc8c34
+                if (need_lock) {
dc8c34
+                    slapi_rwlock_unlock(compute_evaluators_lock);
dc8c34
+                }
dc8c34
 	}
dc8c34
-	slapi_rwlock_unlock(compute_evaluators_lock);
dc8c34
+
dc8c34
 	return rc;
dc8c34
 }
dc8c34
 
dc8c34
+/* Called when */
dc8c34
+void
dc8c34
+compute_plugins_started()
dc8c34
+{
dc8c34
+    startup_completed = PR_TRUE;
dc8c34
+}
dc8c34
+
dc8c34
 /* Call this on server startup, before the first LDAP operation is serviced */
dc8c34
 int compute_init()
dc8c34
 {
dc8c34
@@ -200,6 +253,14 @@ int compute_terminate()
dc8c34
 	return 0;
dc8c34
 }
dc8c34
 
dc8c34
+static void
dc8c34
+compute_add_search_rewrite_nolock(slapi_search_rewrite_callback_t function, compute_rewriter *new_rewriter)
dc8c34
+{
dc8c34
+    new_rewriter->next = compute_rewriters;
dc8c34
+    new_rewriter->function = function;
dc8c34
+    compute_rewriters = new_rewriter;
dc8c34
+}
dc8c34
+
dc8c34
 /* Functions dealing with re-writing of search filters */
dc8c34
 
dc8c34
 int slapi_compute_add_search_rewriter(slapi_search_rewrite_callback_t function)
dc8c34
@@ -208,36 +269,66 @@ int slapi_compute_add_search_rewriter(slapi_search_rewrite_callback_t function)
dc8c34
 	compute_rewriter *new_rewriter = NULL;
dc8c34
 	PR_ASSERT(NULL != function);
dc8c34
 	PR_ASSERT(NULL != compute_rewriters_lock);
dc8c34
+        if (startup_completed) {
dc8c34
+            /* We are now in multi-threaded and we still add
dc8c34
+             * a filter rewriter.
dc8c34
+             * switch to use locking mechanimsm
dc8c34
+             */
dc8c34
+            require_compute_rewriters_lock = PR_TRUE;
dc8c34
+        }
dc8c34
 	new_rewriter = (compute_rewriter *)slapi_ch_calloc(1,sizeof (compute_rewriter));
dc8c34
 	if (NULL == new_rewriter) {
dc8c34
 		rc = ENOMEM;
dc8c34
 	} else {
dc8c34
-		slapi_rwlock_wrlock(compute_rewriters_lock);
dc8c34
-		new_rewriter->next = compute_rewriters;
dc8c34
-		new_rewriter->function = function;
dc8c34
-		compute_rewriters = new_rewriter;
dc8c34
-		slapi_rwlock_unlock(compute_rewriters_lock);
dc8c34
+                int need_lock = require_compute_rewriters_lock;
dc8c34
+                
dc8c34
+		if (need_lock) {
dc8c34
+                    slapi_rwlock_wrlock(compute_rewriters_lock);
dc8c34
+                }
dc8c34
+                
dc8c34
+                compute_add_search_rewrite_nolock(function, new_rewriter);
dc8c34
+                
dc8c34
+                if (need_lock) {
dc8c34
+                    slapi_rwlock_unlock(compute_rewriters_lock);
dc8c34
+                }
dc8c34
 	}
dc8c34
 	return rc;
dc8c34
 }
dc8c34
 
dc8c34
-int	compute_rewrite_search_filter(Slapi_PBlock *pb)
dc8c34
+static
dc8c34
+int compute_rewrite_search_filter_nolock(Slapi_PBlock *pb)
dc8c34
+{
dc8c34
+        int rc = -1;
dc8c34
+        compute_rewriter *current = NULL;
dc8c34
+        
dc8c34
+        for (current = compute_rewriters; (current != NULL) && (-1 == rc); current = current->next) {
dc8c34
+                rc = (*(current->function))(pb);
dc8c34
+                /* Meaning of the return code :
dc8c34
+                   -1 : keep looking
dc8c34
+                    0 : rewrote OK
dc8c34
+                    1 : refuse to do this search
dc8c34
+                    2 : operations error
dc8c34
+                */
dc8c34
+        }
dc8c34
+        return(rc);
dc8c34
+}
dc8c34
+
dc8c34
+int compute_rewrite_search_filter(Slapi_PBlock *pb)
dc8c34
 {
dc8c34
 	/* Iterate through the listed rewriters until one says it matched */
dc8c34
 	int rc = -1;
dc8c34
-	compute_rewriter *current = NULL;
dc8c34
+        int need_lock = require_compute_rewriters_lock;
dc8c34
+
dc8c34
 	/* Walk along the list (locked) calling the evaluator functions util one says yes, an error happens, or we finish */
dc8c34
-	slapi_rwlock_rdlock(compute_rewriters_lock);
dc8c34
-	for (current = compute_rewriters; (current != NULL) && (-1 == rc); current = current->next) {
dc8c34
-		rc = (*(current->function))(pb);
dc8c34
-		/* Meaning of the return code :
dc8c34
-		 -1 : keep looking
dc8c34
-		  0 : rewrote OK
dc8c34
-		  1 : refuse to do this search
dc8c34
-		  2 : operations error
dc8c34
-		 */
dc8c34
-	}
dc8c34
-	slapi_rwlock_unlock(compute_rewriters_lock);
dc8c34
+        if (need_lock) {
dc8c34
+                slapi_rwlock_rdlock(compute_rewriters_lock);
dc8c34
+        }
dc8c34
+        
dc8c34
+        rc = compute_rewrite_search_filter_nolock(pb);
dc8c34
+        
dc8c34
+        if (need_lock) {
dc8c34
+                slapi_rwlock_unlock(compute_rewriters_lock);
dc8c34
+        }
dc8c34
 	return rc;
dc8c34
 
dc8c34
 }
dc8c34
diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c
dc8c34
index 737da5d..bc07cbb 100644
dc8c34
--- a/ldap/servers/slapd/main.c
dc8c34
+++ b/ldap/servers/slapd/main.c
dc8c34
@@ -1171,7 +1171,8 @@ main( int argc, char **argv)
dc8c34
 		slapi_td_dn_init();
dc8c34
 
dc8c34
 		plugin_print_lists();
dc8c34
-		plugin_startall(argc, argv, 1 /* Start Backends */, 1 /* Start Globals */); 
dc8c34
+		plugin_startall(argc, argv, 1 /* Start Backends */, 1 /* Start Globals */);
dc8c34
+                compute_plugins_started();
dc8c34
 		if (housekeeping_start((time_t)0, NULL) == NULL) {
dc8c34
 			return_value = 1;
dc8c34
 			goto cleanup;
dc8c34
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
dc8c34
index 1fb782b..76489ab 100644
dc8c34
--- a/ldap/servers/slapd/proto-slap.h
dc8c34
+++ b/ldap/servers/slapd/proto-slap.h
dc8c34
@@ -248,6 +248,7 @@ void do_compare( Slapi_PBlock *pb );
dc8c34
 int compute_attribute(char *type, Slapi_PBlock *pb,BerElement *ber,Slapi_Entry *e,int attrsonly,char *requested_type);
dc8c34
 int compute_init();
dc8c34
 int compute_terminate();
dc8c34
+void compute_plugins_started();
dc8c34
 
dc8c34
 
dc8c34
 /*
dc8c34
diff --git a/ldap/servers/slapd/tools/rsearch/nametable.c b/ldap/servers/slapd/tools/rsearch/nametable.c
dc8c34
index 0a4b6fc..e5d04cd 100644
dc8c34
--- a/ldap/servers/slapd/tools/rsearch/nametable.c
dc8c34
+++ b/ldap/servers/slapd/tools/rsearch/nametable.c
dc8c34
@@ -152,8 +152,8 @@ int nt_load(NameTable *nt, const char *filename)
dc8c34
     if (!fd) return 0;
dc8c34
 
dc8c34
     while (PR_Available(fd) > 0) {
dc8c34
-	char temp[256], *s;
dc8c34
-	if (PR_GetLine(fd, temp, 256)) break;
dc8c34
+	char temp[4096], *s;
dc8c34
+	if (PR_GetLine(fd, temp, sizeof(temp))) break;
dc8c34
 	s = strdup(temp);
dc8c34
 	if (!s) break;
dc8c34
 	if (!nt_push(nt, s)) break;
dc8c34
-- 
dc8c34
1.8.1.4
dc8c34