/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 2009 The OpenLDAP Foundation.
+ * Copyright 2009-2012 The OpenLDAP Foundation.
* Portions copyright 2009 Symas Corporation.
* All rights reserved.
*
*/
/* ACKNOWLEDGEMENTS:
* This work was initially developed by Howard Chu for inclusion in
- * OpenLDAP Software.
+ * OpenLDAP Software. Support for multiple sorts per connection added
+ * by Raphael Ouazana.
*/
#include "portable.h"
#include "lutil.h"
#include "config.h"
+#include "../../../libraries/liblber/lber-int.h" /* ber_rewind */
+
/* RFC2891: Server Side Sorting
* RFC2696: Paged Results
*/
#define SAFESTR(macro_str, macro_def) ((macro_str) ? (macro_str) : (macro_def))
#define SSSVLV_DEFAULT_MAX_KEYS 5
+#define SSSVLV_DEFAULT_MAX_REQUEST_PER_CONN 5
+
+#define NO_PS_COOKIE (PagedResultsCookie) -1
+#define NO_VC_CONTEXT (unsigned long) -1
typedef struct vlv_ctrl {
int vc_before;
typedef struct sort_ctrl {
int sc_nkeys;
- sort_key sc_keys[0];
+ sort_key sc_keys[1];
} sort_ctrl;
typedef struct sort_node
{
int sn_conn;
+ int sn_session;
struct berval sn_dn;
struct berval *sn_vals;
} sort_node;
int svi_max; /* max concurrent sorts */
int svi_num; /* current # sorts */
int svi_max_keys; /* max sort keys per request */
+ int svi_max_percon; /* max concurrent sorts per con */
} sssvlv_info;
typedef struct sort_op
int so_vlv;
int so_vlv_rc;
int so_vlv_target;
+ int so_session;
unsigned long so_vcontext;
} sort_op;
/* There is only one conn table for all overlay instances */
-static sort_op **sort_conns;
+/* Each conn can handle one session by context */
+static sort_op ***sort_conns;
static ldap_pvt_thread_mutex_t sort_conns_mutex;
+static int ov_count;
static const char *debug_header = "sssvlv";
static int sss_cid;
{
struct berval* ber1, *ber2;
MatchingRule *mr = key->sk_ordering;
- int i, cmp;
+ unsigned i;
+ int cmp;
ber1 = &(attr->a_nvals[0]);
ber2 = ber1+1;
{
sort_node *sn1 = (sort_node *)val1;
sort_node *sn2 = (sort_node *)val2;
- sort_ctrl *sc = sort_conns[sn1->sn_conn]->so_ctrl;
+ sort_ctrl *sc;
MatchingRule *mr;
int i, cmp = 0;
+ assert( sort_conns[sn1->sn_conn]
+ && sort_conns[sn1->sn_conn][sn1->sn_session]
+ && sort_conns[sn1->sn_conn][sn1->sn_session]->so_ctrl );
+ sc = sort_conns[sn1->sn_conn][sn1->sn_session]->so_ctrl;
for ( i=0; cmp == 0 && i<sc->sc_nkeys; i++ ) {
if ( BER_BVISNULL( &sn1->sn_vals[i] )) {
ber_init2( ber, NULL, LBER_USE_DER );
ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
- rc = ber_printf( ber, "{iii", so->so_vlv_target, so->so_nentries,
+ rc = ber_printf( ber, "{iie", so->so_vlv_target, so->so_nentries,
so->so_vlv_rc );
if ( rc != -1 && so->so_vcontext ) {
return rs->sr_err;
}
+/* Return the session id or -1 if unknown */
+static int find_session_by_so(
+ int svi_max_percon,
+ int conn_id,
+ sort_op *so )
+{
+ int sess_id;
+ if (so == NULL) {
+ return -1;
+ }
+ for (sess_id = 0; sess_id < svi_max_percon; sess_id++) {
+ if ( sort_conns[conn_id] && sort_conns[conn_id][sess_id] == so )
+ return sess_id;
+ }
+ return -1;
+}
+
+/* Return the session id or -1 if unknown */
+static int find_session_by_context(
+ int svi_max_percon,
+ int conn_id,
+ unsigned long vc_context,
+ PagedResultsCookie ps_cookie )
+{
+ int sess_id;
+ for(sess_id = 0; sess_id < svi_max_percon; sess_id++) {
+ if( sort_conns[conn_id] && sort_conns[conn_id][sess_id] &&
+ ( sort_conns[conn_id][sess_id]->so_vcontext == vc_context ||
+ (PagedResultsCookie) sort_conns[conn_id][sess_id]->so_tree == ps_cookie ) )
+ return sess_id;
+ }
+ return -1;
+}
+
+static int find_next_session(
+ int svi_max_percon,
+ int conn_id )
+{
+ int sess_id;
+ assert(sort_conns[conn_id] != NULL);
+ for(sess_id = 0; sess_id < svi_max_percon; sess_id++) {
+ if(!sort_conns[conn_id][sess_id]) {
+ return sess_id;
+ }
+ }
+ if (sess_id >= svi_max_percon) {
+ return -1;
+ } else {
+ return sess_id;
+ }
+}
+
static void free_sort_op( Connection *conn, sort_op *so )
{
+ int sess_id;
if ( so->so_tree ) {
tavl_free( so->so_tree, ch_free );
so->so_tree = NULL;
}
ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
- sort_conns[conn->c_conn_idx] = NULL;
+ sess_id = find_session_by_so( so->so_info->svi_max_percon, conn->c_conn_idx, so );
+ sort_conns[conn->c_conn_idx][sess_id] = NULL;
so->so_info->svi_num--;
ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
ch_free( so );
}
-static int send_list(
+static void free_sort_ops( Connection *conn, sort_op **sos, int svi_max_percon )
+{
+ int sess_id;
+ sort_op *so;
+
+ for( sess_id = 0; sess_id < svi_max_percon ; sess_id++ ) {
+ so = sort_conns[conn->c_conn_idx][sess_id];
+ if ( so ) {
+ free_sort_op( conn, so );
+ sort_conns[conn->c_conn_idx][sess_id] = NULL;
+ }
+ }
+}
+
+static void send_list(
Operation *op,
SlapReply *rs,
sort_op *so)
Entry *e;
LDAPControl *ctrls[2];
+ rs->sr_attrs = op->ors_attrs;
+
/* FIXME: it may be better to just flatten the tree into
* an array before doing all of this...
*/
sc->sc_nkeys * sizeof(struct berval), op->o_tmpmemctx );
sn->sn_vals = (struct berval *)(sn+1);
sn->sn_conn = op->o_conn->c_conn_idx;
+ sn->sn_session = find_session_by_so( so->so_info->svi_max_percon, op->o_conn->c_conn_idx, so );
sn->sn_vals[0] = bv;
for (i=1; i<sc->sc_nkeys; i++) {
BER_BVZERO( &sn->sn_vals[i] );
}
cur_node = tavl_find3( so->so_tree, sn, node_cmp, &j );
/* didn't find >= match */
- if ( j > 0 )
- cur_node = NULL;
+ if ( j > 0 ) {
+ if ( cur_node )
+ cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
+ }
op->o_tmpfree( sn, op->o_tmpmemctx );
if ( !cur_node ) {
}
for (i=0; tmp_node != cur_node;
tmp_node = tavl_next( tmp_node, dir ), i++);
- so->so_vlv_target = i;
+ so->so_vlv_target = (dir == TAVL_DIR_RIGHT) ? i+1 : so->so_nentries - i;
}
if ( bv.bv_val != vc->vc_value.bv_val )
op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
be = op->o_bd;
for ( i=0; i<j; i++ ) {
sort_node *sn = cur_node->avl_data;
-
+
+ if ( slapd_shutdown ) break;
+
op->o_bd = select_backend( &sn->sn_dn, 0 );
e = NULL;
rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
Avlnode *cur_node = so->so_tree;
Avlnode *next_node = NULL;
BackendDB *be = op->o_bd;
- sort_node *sn;
Entry *e;
int rc;
+ rs->sr_attrs = op->ors_attrs;
+
while ( cur_node && rs->sr_nentries < so->so_page_size ) {
sort_node *sn = cur_node->avl_data;
+ if ( slapd_shutdown ) break;
+
next_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
op->o_bd = select_backend( &sn->sn_dn, 0 );
"%s: response control: status=%d, text=%s\n",
debug_header, rs->sr_err, SAFESTR(rs->sr_text, "<None>"));
+ if ( !so->so_tree )
+ return;
+
/* RFC 2891: If critical then send the entries iff they were
* succesfully sorted. If non-critical send all entries
* whether they were sorted or not.
op->o_tmpfree( sn, op->o_tmpmemctx );
sn = sn2;
sn->sn_conn = op->o_conn->c_conn_idx;
+ sn->sn_session = find_session_by_so( so->so_info->svi_max_percon, op->o_conn->c_conn_idx, so );
/* Insert into the AVL tree */
tavl_insert(&(so->so_tree), sn, node_insert, avl_dup_error);
sssvlv_info *si = on->on_bi.bi_private;
int rc = SLAP_CB_CONTINUE;
int ok;
- sort_op *so, so2;
- sort_ctrl *sc = op->o_controls[sss_cid];
+ sort_op *so = NULL, so2;
+ sort_ctrl *sc;
PagedResultsState *ps;
vlv_ctrl *vc;
+ int sess_id;
if ( op->o_ctrlflag[sss_cid] <= SLAP_CONTROL_IGNORED ) {
if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
so2.so_vlv_target = 0;
so2.so_nentries = 0;
so2.so_vlv_rc = LDAP_VLV_SSS_MISSING;
+ so2.so_vlv = op->o_ctrlflag[vlv_cid];
rc = pack_vlv_response_control( op, rs, &so2, ctrls );
if ( rc == LDAP_SUCCESS ) {
ctrls[1] = NULL;
op->o_req_dn.bv_val, op->ors_filterstr.bv_val,
op->o_ctrlflag[sss_cid]);
+ sc = op->o_controls[sss_cid];
if ( sc->sc_nkeys > si->svi_max_keys ) {
rs->sr_text = "Too many sort keys";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
ok = 1;
ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
- so = sort_conns[op->o_conn->c_conn_idx];
/* Is there already a sort running on this conn? */
- if ( so ) {
+ sess_id = find_session_by_context( si->svi_max_percon, op->o_conn->c_conn_idx, vc ? vc->vc_context : NO_VC_CONTEXT, ps ? ps->ps_cookie : NO_PS_COOKIE );
+ if ( sess_id >= 0 ) {
+ so = sort_conns[op->o_conn->c_conn_idx][sess_id];
/* Is it a continuation of a VLV search? */
if ( !vc || so->so_vlv <= SLAP_CONTROL_IGNORED ||
vc->vc_context != so->so_vcontext ) {
/* Are there too many running overall? */
} else if ( si->svi_num >= si->svi_max ) {
ok = 0;
+ } else if ( ( sess_id = find_next_session(si->svi_max_percon, op->o_conn->c_conn_idx ) ) < 0 ) {
+ ok = 0;
} else {
/* OK, this connection now has a sort running */
si->svi_num++;
- sort_conns[op->o_conn->c_conn_idx] = &so2;
+ sort_conns[op->o_conn->c_conn_idx][sess_id] = &so2;
+ sort_conns[op->o_conn->c_conn_idx][sess_id]->so_session = sess_id;
}
ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
if ( ok ) {
+ /* If we're a global overlay, this check got bypassed */
+ if ( !op->ors_limit && limits_check( op, rs ))
+ return rs->sr_err;
/* are we continuing a VLV search? */
- if ( vc && vc->vc_context ) {
+ if ( so && vc && vc->vc_context ) {
so->so_ctrl = sc;
send_list( op, rs, so );
send_result( op, rs, so );
rc = LDAP_SUCCESS;
/* are we continuing a paged search? */
- } else if ( ps && ps->ps_cookie ) {
+ } else if ( so && ps && ps->ps_cookie ) {
so->so_ctrl = sc;
send_page( op, rs, so );
send_result( op, rs, so );
op->o_tmpmemctx );
/* Install serversort response callback to handle a new search */
if ( ps || vc ) {
- so = ch_malloc( sizeof(sort_op));
+ so = ch_calloc( 1, sizeof(sort_op));
} else {
- so = op->o_tmpalloc( sizeof(sort_op), op->o_tmpmemctx );
+ so = op->o_tmpcalloc( 1, sizeof(sort_op), op->o_tmpmemctx );
}
- sort_conns[op->o_conn->c_conn_idx] = so;
+ sort_conns[op->o_conn->c_conn_idx][sess_id] = so;
cb->sc_cleanup = NULL;
cb->sc_response = sssvlv_op_response;
} else {
so->so_paged = 0;
so->so_page_size = 0;
- if ( vc )
+ if ( vc ) {
so->so_vlv = op->o_ctrlflag[vlv_cid];
+ so->so_vlv_target = 0;
+ so->so_vlv_rc = 0;
+ } else {
+ so->so_vlv = SLAP_CONTROL_NONE;
+ }
}
+ so->so_session = sess_id;
+ so->so_vlv = op->o_ctrlflag[vlv_cid];
so->so_vcontext = (unsigned long)so;
so->so_nentries = 0;
i = count_key( ber );
sc = op->o_tmpalloc( sizeof(sort_ctrl) +
- i * sizeof(sort_key), op->o_tmpmemctx );
+ (i-1) * sizeof(sort_key), op->o_tmpmemctx );
sc->sc_nkeys = i;
op->o_controls[sss_cid] = sc;
BerElement *ber;
ber_tag_t tag;
ber_len_t len;
- int i;
vlv_ctrl *vc, vc2;
rs->sr_err = LDAP_PROTOCOL_ERROR;
static int sssvlv_connection_destroy( BackendDB *be, Connection *conn )
{
slap_overinst *on = (slap_overinst *)be->bd_info;
+ sssvlv_info *si = on->on_bi.bi_private;
- if ( sort_conns[conn->c_conn_idx] )
- free_sort_op( conn, sort_conns[conn->c_conn_idx] );
+ if ( sort_conns[conn->c_conn_idx] ) {
+ free_sort_ops( conn, sort_conns[conn->c_conn_idx], si->svi_max_percon );
+ }
return LDAP_SUCCESS;
}
{
slap_overinst *on = (slap_overinst *)be->bd_info;
sssvlv_info *si = on->on_bi.bi_private;
+ int rc;
+ int conn_index;
/* If not set, default to 1/2 of available threads */
if ( !si->svi_max )
si->svi_max = connection_pool_max / 2;
- int rc = overlay_register_control( be, LDAP_CONTROL_SORTREQUEST );
+ if ( dtblsize && !sort_conns ) {
+ ldap_pvt_thread_mutex_init( &sort_conns_mutex );
+ /* accommodate for c_conn_idx == -1 */
+ sort_conns = ch_calloc( dtblsize + 1, sizeof(sort_op **) );
+ for ( conn_index = 0 ; conn_index < dtblsize + 1 ; conn_index++ ) {
+ sort_conns[conn_index] = ch_calloc( si->svi_max_percon, sizeof(sort_op *) );
+ }
+ sort_conns++;
+ }
+
+ rc = overlay_register_control( be, LDAP_CONTROL_SORTREQUEST );
if ( rc == LDAP_SUCCESS )
rc = overlay_register_control( be, LDAP_CONTROL_VLVREQUEST );
return rc;
"( OLcfgOvAt:21.2 NAME 'olcSssVlvMaxKeys' "
"DESC 'Maximum number of Keys in a Sort request' "
"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "sssvlv-maxpercon", "num",
+ 2, 2, 0, ARG_INT|ARG_OFFSET,
+ (void *)offsetof(sssvlv_info, svi_max_percon),
+ "( OLcfgOvAt:21.3 NAME 'olcSssVlvMaxPerConn' "
+ "DESC 'Maximum number of concurrent paged search requests per connection' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};
{
slap_overinst *on = (slap_overinst *)be->bd_info;
sssvlv_info *si;
+
+ if ( ov_count == 0 ) {
+ int rc;
+
+ rc = register_supported_control2( LDAP_CONTROL_SORTREQUEST,
+ SLAP_CTRL_SEARCH,
+ NULL,
+ sss_parseCtrl,
+ 1 /* replace */,
+ &sss_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register Sort Request control '%s' (%d)\n",
+ LDAP_CONTROL_SORTREQUEST, rc, 0 );
+ return rc;
+ }
+
+ rc = register_supported_control2( LDAP_CONTROL_VLVREQUEST,
+ SLAP_CTRL_SEARCH,
+ NULL,
+ vlv_parseCtrl,
+ 1 /* replace */,
+ &vlv_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register VLV Request control '%s' (%d)\n",
+ LDAP_CONTROL_VLVREQUEST, rc, 0 );
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_SORTREQUEST );
+ unregister_supported_control( LDAP_CONTROL_SORTREQUEST );
+#endif /* SLAP_CONFIG_DELETE */
+ return rc;
+ }
+ }
si = (sssvlv_info *)ch_malloc(sizeof(sssvlv_info));
on->on_bi.bi_private = si;
si->svi_max = 0;
si->svi_num = 0;
si->svi_max_keys = SSSVLV_DEFAULT_MAX_KEYS;
+ si->svi_max_percon = SSSVLV_DEFAULT_MAX_REQUEST_PER_CONN;
- if ( dtblsize && !sort_conns ) {
- ldap_pvt_thread_mutex_init( &sort_conns_mutex );
- /* accommodate for c_conn_idx == -1 */
- sort_conns = ch_calloc( sizeof(sort_op *), dtblsize + 1 );
- sort_conns++;
- }
+ ov_count++;
return LDAP_SUCCESS;
}
{
slap_overinst *on = (slap_overinst *)be->bd_info;
sssvlv_info *si = (sssvlv_info *)on->on_bi.bi_private;
-
+ int conn_index;
+
+ ov_count--;
+ if ( !ov_count && sort_conns) {
+ sort_conns--;
+ for ( conn_index = 0 ; conn_index < dtblsize + 1 ; conn_index++ ) {
+ ch_free(sort_conns[conn_index]);
+ }
+ ch_free(sort_conns);
+ ldap_pvt_thread_mutex_destroy( &sort_conns_mutex );
+ }
+
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_SORTREQUEST );
+ overlay_unregister_control( be, LDAP_CONTROL_VLVREQUEST );
+ if ( ov_count == 0 ) {
+ unregister_supported_control( LDAP_CONTROL_SORTREQUEST );
+ unregister_supported_control( LDAP_CONTROL_VLVREQUEST );
+ }
+#endif /* SLAP_CONFIG_DELETE */
+
if ( si ) {
ch_free( si );
on->on_bi.bi_private = NULL;
if ( rc )
return rc;
- rc = register_supported_control2( LDAP_CONTROL_SORTREQUEST,
- SLAP_CTRL_SEARCH,
- NULL,
- sss_parseCtrl,
- 1 /* replace */,
- &sss_cid );
-
- if ( rc == LDAP_SUCCESS ) {
- rc = register_supported_control2( LDAP_CONTROL_VLVREQUEST,
- SLAP_CTRL_SEARCH,
- NULL,
- vlv_parseCtrl,
- 1 /* replace */,
- &vlv_cid );
- }
-
- if ( rc == LDAP_SUCCESS ) {
- rc = overlay_register( &sssvlv );
- if ( rc != LDAP_SUCCESS ) {
- fprintf( stderr, "Failed to register server side sort overlay\n" );
- }
- }
- else {
- fprintf( stderr, "Failed to register control %d\n", rc );
+ rc = overlay_register( &sssvlv );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register server side sort overlay\n", 0, 0, 0 );
}
return rc;