.B slapd\-meta(5)
for details.
+.TP
+.B conn\-ttl <time> [<interval>]
+This directive causes a persistent connection to be dropped after
+it has been established for the specified
+.I time,
+no more often that one connection per
+.I interval
+per target. The connection will be re-created the next time it is selected for use.
+If there are pending requests in its queue, the connection will be
+marked as closing and no new requests will be proxied through it.
+It will be dropped after the last request one has either received a result or has timed out.
+To avoid too many connections being dropped at the same time, the \fBinterval\fP
+directive is introduced, to specify the minimum time interval between resetting a connection
+to the same target.
+The default value is 1 second. If
+.I time
+is set to 0, the oldest connection will be chosen for reset at every interval period. If
+.I interval
+is set to 0, all connections exceeding their ttl will be dropped as soon as there are no more
+pending operations.
+
+[<d>d][<h>h][<m>m][<s>[s]]
+
+where <d>, <h>, <m> and <s> are respectively treated as days, hours,
+minutes and seconds.
+
+.B EXAMPLES
+
+.B conn\-ttl 60s 5s
+ - Connections older that 60 seconds will be reset, no more often that one connection per
+target every 5 seconds.
+
+.B conn\-ttl 0 5s
+ - Every 5 seconds, the oldest connection of each target will be reset.
+
+.B conn\-ttl 1h
+ - Connection older than 1h will be reset, no more often than once a second per target.
+
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive. It is disabled by default.
+
.TP
.B default\-target [<target>]
The "default\-target" directive can also be used during target specification.
.TP
.B idle\-timeout <time>
-This directive causes a a persistent connection to be dropped after
+This directive causes a persistent connection to be dropped after
it has been idle for the specified time. The connection will be re-created
the next time it is selected for use. A connection is considered idle if no
attempts have been made by the backend to use it to send a request to
#define META_BACK_FCONN_INITED (0x00100000U)
#define META_BACK_FCONN_CREATING (0x00200000U)
+/* connection is invalid, do not read or send, close as soon as possible */
#define META_BACK_FCONN_INVALID (0x00400000U)
+/* connection is closing, do not send, but keep open for reading, reset
+ when no more data is expected */
+#define META_BACK_FCONN_CLOSING (0x00800000U)
#define META_BACK_CONN_INITED(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_INITED)
#define META_BACK_CONN_INITED_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_INITED)
#define META_BACK_CONN_INVALID(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_INVALID)
#define META_BACK_CONN_INVALID_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_INVALID)
#define META_BACK_CONN_INVALID_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_INVALID)
+#define META_BACK_CONN_CLOSING(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_CLOSING)
+#define META_BACK_CONN_CLOSING_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_CLOSING)
+#define META_BACK_CONN_CLOSING_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_CLOSING)
+
struct a_metainfo_t;
struct a_metaconn_t;
time_t msc_time;
time_t msc_binding_time;
time_t msc_result_time;
+ time_t msc_reset_time;
+ time_t msc_established_time;
struct berval msc_bound_ndn;
struct berval msc_cred;
unsigned msc_mscflags;
-
+ int msc_pending_ops;
/* NOTE: lc_lcflags is redefined to msc_mscflags to reuse the macros
* defined for back-ldap */
#define lc_lcflags msc_mscflags
volatile int msc_active;
+ struct a_metaconn_t *mc;
/* Connection for the select */
Connection *conn;
} a_metasingleconn_t;
slap_retry_info_t mc_quarantine;
time_t mc_network_timeout;
struct timeval mc_bind_timeout;
+ time_t mc_conn_ttl;
+ time_t mc_conn_reset_interval;
#define META_BIND_TIMEOUT LDAP_BACK_RESULT_UTIMEOUT
time_t mc_timeout[ SLAP_OP_LAST ];
} a_metacommon_t;
#define mt_bind_timeout mt_mc.mc_bind_timeout
#define mt_timeout mt_mc.mc_timeout
#define mt_quarantine mt_mc.mc_quarantine
+#define mt_conn_ttl mt_mc.mc_conn_ttl
+#define mt_conn_reset_interval mt_mc.mc_conn_reset_interval
#define META_BACK_TGT_ISSET(mt,f) ( ( (mt)->mt_flags & (f) ) == (f) )
#define META_BACK_TGT_ISMASK(mt,m,f) ( ( (mt)->mt_flags & (m) ) == (f) )
#define META_BACK_CFG_MAX_TIMEOUT_LOOP 0x70000
slap_mask_t mt_rep_flags;
int mt_timeout_ops;
+ /* last time a connection for this target was reset */
+ time_t msc_reset_time;
} a_metatarget_t;
typedef struct a_metadncache_t {
#define mi_bind_timeout mi_mc.mc_bind_timeout
#define mi_timeout mi_mc.mc_timeout
#define mi_quarantine mi_mc.mc_quarantine
-
+#define mi_conn_ttl mi_mc.mc_conn_ttl
+#define mi_conn_reset_interval mi_mc.mc_conn_reset_interval
a_metatarget_t **mi_targets;
a_metacandidates_t *mi_candidates;
LDAP_BACK_CFG_CANCEL,
LDAP_BACK_CFG_CHASE,
LDAP_BACK_CFG_CLIENT_PR,
+ LDAP_BACK_CFG_CONN_TTL,
LDAP_BACK_CFG_DEFAULT_T,
LDAP_BACK_CFG_NETWORK_TIMEOUT,
LDAP_BACK_CFG_NOREFS,
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL },
+ { "conn-ttl", "ttl", 2, 3, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CONN_TTL,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.16 "
+ "NAME 'olcDbConnTtl' "
+ "DESC 'connection ttl' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
{ "network-timeout", "timeout", 2, 2, 0,
ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
asyncmeta_back_cf_gen, "( OLcfgDbAt:3.17 "
"$ olcDbRebindAsUser " \
ST_ATTR \
"$ olcDbStartTLS " \
- "$ olcDbTFSupport "
+ "$ olcDbTFSupport " \
+ "$ olcDbConnTtl "
static ConfigOCs a_metaocs[] = {
{ "( OLcfgDbOc:3.4 "
a_metaconn_t *mc = &mi->mi_conns[i];
mc->mc_conns = ch_realloc( mc->mc_conns, sizeof( a_metasingleconn_t ) * mi->mi_ntargets);
memset( &(mc->mc_conns[mi->mi_ntargets-1]), 0, sizeof( a_metasingleconn_t ) );
+ mc->mc_conns[mi->mi_ntargets-1].mc = mc;
}
/* If this is the first target, start the timeout loop */
if ( mi->mi_ntargets == 1 ) {
value_add_one( &c->rvalue_vals, &bv );
break;
#endif /* SLAPD_META_CLIENT_PR */
-
+ case LDAP_BACK_CFG_CONN_TTL:
+ if ( mc->mc_conn_ttl == 0 && mc->mc_conn_reset_interval == 0) {
+ return 1;
+ } else {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ char *p = buf;
+ lutil_unparse_time( p, sizeof( p ), mc->mc_conn_ttl );
+ if (mc->mc_conn_reset_interval != 0) {
+ p += strlen( buf );
+ *p = ' ';
+ lutil_unparse_time( p, sizeof( p ), mc->mc_conn_reset_interval );
+ }
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
case LDAP_BACK_CFG_DEFAULT_T:
if ( mt || mi->mi_defaulttarget == META_DEFAULT_TARGET_NONE )
return 1;
mc->mc_ps = META_CLIENT_PR_DISABLE;
break;
#endif /* SLAPD_META_CLIENT_PR */
-
+ case LDAP_BACK_CFG_CONN_TTL:
+ mc->mc_conn_ttl = 0;
+ mc->mc_conn_reset_interval = 0;
+ break;
case LDAP_BACK_CFG_DEFAULT_T:
mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
break;
}
}
break;
+ case LDAP_BACK_CFG_CONN_TTL: {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse conn ttl \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_conn_ttl = (time_t)t;
+ if ( ( c->argv[ 2 ] != NULL ) && lutil_parse_time( c->argv[ 2 ], &t ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse conn ttl reset interval \"%s\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+
+ }
+ mc->mc_conn_reset_interval = (time_t)t;
+ } break;
case LDAP_BACK_CFG_IDASSERT_BIND:
/* idassert-bind */
rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
{
a_metaconn_t *mc;
int ntargets = mi->mi_ntargets;
-
+ int j;
assert( ntargets > 0 );
/* malloc all in one */
ldap_pvt_thread_mutex_init( &mc->mc_om_mutex);
mc->mc_authz_target = META_BOUND_NONE;
mc->mc_conns = (a_metasingleconn_t *)(mc+1);
+
+ for (j = 0; j < mi->mi_ntargets; j++) {
+ mc->mc_conns[j].mc = mc;
+ }
return mc;
}
return rs->sr_err;
}
}
- msc = &mc->mc_conns[candidate];
- /*
- * Already init'ed
- */
- if ( LDAP_BACK_CONN_ISBOUND( msc )
- || LDAP_BACK_CONN_ISANON( msc ) )
- {
+ msc = &mc->mc_conns[candidate];
+
+ if ( META_BACK_CONN_CLOSING( msc ) ) {
+ rs->sr_err = -1;
+ return rs->sr_err;
+
+ } else if ( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc ) ) {
+ /*
+ * Already init'ed
+ */
assert( msc->msc_ld != NULL );
rs->sr_err = LDAP_SUCCESS;
return rs->sr_err;
goto error_return;
}
+ msc->msc_established_time = slap_get_time();
+
/*
* Set LDAP version. This will always succeed: If the client
* bound with a particular version, then so can we.
i = META_TARGET_NONE,
err = LDAP_SUCCESS,
new_conn = 0,
- ncandidates = 0;
+ ncandidates = 0,
+ num_conns = mi->mi_num_conns;
meta_op_type op_type = META_OP_REQUIRE_SINGLE;
struct berval ndn = op->o_req_ndn,
pndn;
+choose_again:
+ /* in case we are sent here because the connection is set to closing */
+ if ( mc != NULL && alloc_new > 0 ) {
+ asyncmeta_back_conn_free( mc );
+ }
if (alloc_new > 0) {
mc = asyncmeta_conn_alloc(mi);
new_conn = 0;
candidates[ i ].sr_err = asyncmeta_init_one_conn( op,
rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
!new_conn );
+ if ( candidates[ i ].sr_err == -1 ) {
+ if ( num_conns -1 > 0 ) {
+ num_conns--;
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ goto choose_again;
+ } else {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Unable to find a valid connection";
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ return NULL;
+ }
+ }
+
if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
if ( new_conn ) {
LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
*/
err = asyncmeta_init_one_conn( op, rs, mc, i,
LDAP_BACK_CONN_ISPRIV( &mc_curr ), !new_conn );
+ if ( err == -1 ) {
+ if ( num_conns-1 > 0 ) {
+ num_conns--;
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ goto choose_again;
+ } else {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Unable to find a valid connection";
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ return NULL;
+ }
+ }
if ( err != LDAP_SUCCESS ) {
/*
* FIXME: in case one target cannot
int lerr = asyncmeta_init_one_conn( op, rs, mc, i,
LDAP_BACK_CONN_ISPRIV( &mc_curr ),
!new_conn );
+ /* the chosen connection is closing */
+ if ( lerr == -1 ) {
+ if ( num_conns-1 > 0 ) {
+ num_conns--;
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ goto choose_again;
+ } else {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Unable to find a valid connection";
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ return NULL;
+ }
+ }
candidates[ i ].sr_err = lerr;
if ( lerr == LDAP_SUCCESS ) {
META_CANDIDATE_SET( &candidates[ i ] );
msc->msc_time = 0;
msc->msc_binding_time = 0;
msc->msc_result_time = 0;
+ msc->msc_established_time = 0;
+ msc->msc_reset_time = slap_get_time();
+ if ( mt )
+ mt->msc_reset_time = msc->msc_reset_time;
return 0;
}
mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_NOANON;
}
+ mt->msc_reset_time = slap_get_time();
return 0;
}
{
a_metainfo_t *mi = (a_metainfo_t *)be->be_private;
char msg[SLAP_TEXT_BUFLEN];
- int i;
+ int i,j;
mi->mi_disabled = 0;
if ( mi->mi_ntargets == 0 ) {
if ( mi->mi_ntargets > 0 ) {
mc->mc_conns = ch_calloc( mi->mi_ntargets, sizeof( a_metasingleconn_t ));
+ for (j = 0; j < mi->mi_ntargets; j++) {
+ mc->mc_conns[j].mc = mc;
+ }
} else {
mc->mc_conns = NULL;
}
mc->mc_info = mi;
LDAP_STAILQ_INIT( &mc->mc_om_list );
}
-
ber_dupbv ( &mi->mi_suffix, &be->be_suffix[0] );
msc->msc_time = slap_get_time();
}
+static void
+asyncmeta_update_msc_pending_ops( a_metaconn_t *mc,
+ bm_context_t *bc)
+{
+ int i;
+ a_metainfo_t *mi = mc->mc_info;
+ for (i=0; i<mi->mi_ntargets; i++) {
+ if ( !META_IS_CANDIDATE( &bc->candidates[ i ] ) ||
+ bc->candidates[i].sr_msgid == META_MSGID_IGNORE ||
+ bc->candidates[i].sr_type == REP_RESULT) {
+ continue;
+ }
+ mc->mc_conns[i].msc_pending_ops++;
+ }
+}
+
+static void
+asyncmeta_reset_msc_pending_ops( a_metaconn_t *mc )
+{
+ int i;
+ a_metainfo_t *mi = mc->mc_info;
+ for (i=0; i<mi->mi_ntargets; i++) {
+ mc->mc_conns[i].msc_pending_ops = 0;
+ }
+}
+
void* asyncmeta_timeout_loop(void *ctx, void *arg)
{
struct re_s* rtask = arg;
a_metainfo_t *mi = rtask->arg;
bm_context_t *bc, *onext;
time_t current_time = slap_get_time();
+ time_t conn_ttl;
+ time_t conn_reset_interval;
int i, j;
LDAP_STAILQ_HEAD(BCList, bm_context_t) timeout_list;
LDAP_STAILQ_INIT( &timeout_list );
+ a_metasingleconn_t* oldest[mi->mi_ntargets];
Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time );
if ( mi->mi_disabled > 0 && asyncmeta_db_has_pending_ops( mi ) == 0 ) {
return NULL;
}
void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);
+
+ for (i=0; i<mi->mi_ntargets; i++ ) {
+ oldest[i] = NULL;
+ }
for (i=0; i<mi->mi_num_conns; i++) {
a_metaconn_t * mc= &mi->mi_conns[i];
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ asyncmeta_reset_msc_pending_ops(mc);
for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
onext = LDAP_STAILQ_NEXT(bc, bc_next);
if (bc->bc_active > 0) {
+ asyncmeta_update_msc_pending_ops( mc, bc);
continue;
}
for (j=0; j<mi->mi_ntargets; j++) {
if (bc->candidates[j].sr_msgid >= 0) {
a_metasingleconn_t *msc = &mc->mc_conns[j];
- if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ if ( op->o_tag == LDAP_REQ_SEARCH &&
+ !META_BACK_CONN_CLOSING(msc) ) {
msc->msc_active++;
asyncmeta_back_cancel( mc, op,
bc->candidates[ j ].sr_msgid, j );
if (bc->candidates[j].sr_msgid >= 0) {
a_metasingleconn_t *msc = &mc->mc_conns[j];
asyncmeta_set_msc_time(msc);
- if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ if ( op->o_tag == LDAP_REQ_SEARCH &&
+ !META_BACK_CONN_CLOSING(msc) ) {
msc->msc_active++;
asyncmeta_back_cancel( mc, op,
bc->candidates[ j ].sr_msgid, j );
}
}
}
+ asyncmeta_update_msc_pending_ops( mc, bc);
}
+ for (j=0; j<mi->mi_ntargets; j++) {
+ a_metasingleconn_t *msc = &mc->mc_conns[j];
+ if ( META_BACK_CONN_CLOSING(msc) &&
+ msc->msc_pending_ops == 0 &&
+ msc->msc_active <= 0 ) {
+ asyncmeta_clear_one_msc (mi->mi_targets[j], msc, __FUNCTION__ );
+ }
+ }
+
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
for (bc = LDAP_STAILQ_FIRST(&timeout_list); bc; bc = onext) {
}
}
}
+ /* find the oldest connection to reset */
+ for (j=0; j<mi->mi_ntargets; j++) {
+ a_metasingleconn_t *msc = &mc->mc_conns[j];
+ if ( msc->msc_established_time > 0 &&
+ ( oldest[j] == NULL || oldest[j]->msc_established_time > msc->msc_established_time ) ) {
+ oldest[j] = msc;
+ }
+ }
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
}
+ current_time = slap_get_time();
+ for (j=0; j<mi->mi_ntargets; j++) {
+ a_metasingleconn_t *msc = oldest[j];
+ a_metatarget_t *mt = mi->mi_targets[j];
+ conn_ttl = (mt->mt_conn_ttl > 0)?mt->mt_conn_ttl:mi->mi_conn_ttl;
+ conn_reset_interval = (mt->mt_conn_reset_interval > 0) ?
+ mt->mt_conn_reset_interval:mi->mi_conn_reset_interval;
+
+ if ( conn_ttl || conn_reset_interval ) {
+ if ( (current_time - mt->msc_reset_time) <= conn_reset_interval )
+ continue;
+ if ( msc != NULL && msc->msc_established_time
+ && (msc->msc_established_time + conn_ttl <= current_time) ) {
+ ldap_pvt_thread_mutex_lock( &msc->mc->mc_om_mutex );
+ META_BACK_CONN_CLOSING_SET( msc );
+ ldap_pvt_thread_mutex_unlock( &msc->mc->mc_om_mutex );
+ }
+ }
+ }
slap_sl_mem_setctx(ctx, oldctx);
current_time = slap_get_time();
Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] stop at [%ld] \n", rtask, current_time );
/* Target Attributes */
static AttributeDescription *ad_olmTgtURIList; /* mt_uri */
static AttributeDescription *ad_olmTgtQuarantined; /*mt_isquarantined*/
+static AttributeDescription *ad_olmTgtConnLastReset; /*msc_reset_time*/
static AttributeDescription *ad_olmTgtTimeoutOps; /*mt_timeout_ops*/
/* Connection Group (a_metaconn_t) attributes */
static AttributeDescription *ad_olmCGID;
static AttributeDescription *ad_olmTargetConnLastUseTime; /* msc_time */
static AttributeDescription *ad_olmTargetConnBoundTime; /* msc_binding_time */
static AttributeDescription *ad_olmTargetConnResultTime; /* msc_result_time */
+static AttributeDescription *ad_olmTargetConnEstablishedTime; /* msc_established_time */
+static AttributeDescription *ad_olmTargetConnResetTime; /* msc_reset_time */
static AttributeDescription *ad_olmTargetConnFlags; /* msc_mscflags */
static AttributeDescription *ad_olmTargetConnURI;
static AttributeDescription *ad_olmTargetConnPeerAddress;
{ META_BACK_FCONN_INITED, BER_BVC( "initialized" ) },
{ META_BACK_FCONN_CREATING, BER_BVC( "creating" ) },
{ META_BACK_FCONN_INVALID, BER_BVC( "invalid" ) },
+ { META_BACK_FCONN_CLOSING, BER_BVC( "closing" ) },
{ 0 }
};
"NO-USER-MODIFICATION "
"USAGE dSAOperation )",
&ad_olmTargetConnPeerAddress },
+ { "( olmAsyncmetaAttributes:13 "
+ "NAME ( 'olmTargetConnEstablishedTime' ) "
+ "DESC 'Time the connection was established' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmTargetConnEstablishedTime },
+ { "( olmAsyncmetaAttributes:14 "
+ "NAME ( 'olmTargetConnResetTime' ) "
+ "DESC 'Last time the connection was reset' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmTargetConnResetTime },
+ { "( olmAsyncmetaAttributes:15 "
+ "NAME ( 'olmTgtConnLastReset' ) "
+ "DESC 'Last time a connection to this target was reset' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmTgtConnLastReset },
{ NULL }
};
"MAY ( "
"olmTgtURIList "
"$ olmTgtQuarantined "
+ "$ olmTgtConnLastReset "
"$ olmTgtTimeoutOps "
") )",
&oc_olmAsyncmetaTarget },
"olmTargetConnLastUseTime "
"$ olmTargetConnBoundTime "
"$ olmTargetConnResultTime "
- "$ olmTargetConnFlags "
- "$ olmTargetConnURI "
- "$ olmTargetConnPeerAddress"
+ "$ olmTargetConnResetTime "
+ "$ olmTargetConnEstablishedTime "
+ "$ olmTargetConnFlags "
+ "$ olmTargetConnURI "
+ "$ olmTargetConnPeerAddress"
") )",
&oc_olmAsyncmetaTargetConnection },
bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", msc->msc_result_time );
ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ a = attr_find( e->e_attrs, ad_olmTargetConnResetTime );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", msc->msc_reset_time );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
+ a = attr_find( e->e_attrs, ad_olmTargetConnEstablishedTime );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", msc->msc_established_time );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
a = attr_find( e->e_attrs, ad_olmTargetConnFlags );
assert( a != NULL );
bv.bv_val = buf;
cb->mc_free = asyncmeta_back_monitor_target_conn_free;
cb->mc_private = (void *)&mc->mc_conns[i];
- a = attrs_alloc( 1 + 6 );
+ a = attrs_alloc( 1 + 8 );
a->a_desc = slap_schema.si_ad_objectClass;
attr_valadd( a, &oc_olmAsyncmetaTargetConnection->soc_cname, NULL, 1 );
next->a_desc = ad_olmTargetConnPeerAddress;
attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
+ next->a_desc = ad_olmTargetConnResetTime;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
+ next->a_desc = ad_olmTargetConnEstablishedTime;
+ attr_valadd( next, &bv, NULL, 1 );
rc = mbe->register_entry( e, NULL, ms, MONITOR_F_PERSISTENT_CH );
if ( rc != LDAP_SUCCESS )
bv.bv_len = snprintf( buf, sizeof( buf ), "%i", mt->mt_timeout_ops );
ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ a = attr_find( e->e_attrs, ad_olmTgtConnLastReset );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", mt->msc_reset_time );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
return SLAP_CB_CONTINUE;
}
cb->mc_free = asyncmeta_back_monitor_targets_free;
cb->mc_private = (void *)&mi->mi_targets[i];
- a = attrs_alloc( 1 + 3 );
+ a = attrs_alloc( 1 + 4 );
a->a_desc = slap_schema.si_ad_objectClass;
attr_valadd( a, &oc_olmAsyncmetaTarget->soc_cname, NULL, 1 );
attr_valadd( next, &bv, NULL, 1 );
next = next->a_next;
+ next->a_desc = ad_olmTgtConnLastReset;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
next->a_desc = ad_olmTgtTimeoutOps;
attr_valadd( next, &bv, NULL, 1 );
--- /dev/null
+dn: cn=Target Connection 1,cn=Connection Group 1,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
+
+dn: cn=Target Connection 2,cn=Connection Group 1,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
--- /dev/null
+dn: cn=Target Connection 1,cn=Connection Group 2,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
+
+dn: cn=Target Connection 2,cn=Connection Group 2,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
--- /dev/null
+dn: cn=Connections,cn=database 1,cn=databases,cn=monitor
+
+dn: cn=Connection Group 1,cn=Connections,cn=database 1,cn=databases,cn=monitor
+
+dn: cn=Target Connection 1,cn=Connection Group 1,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: anonymous,initialized
+
+dn: cn=Target Connection 2,cn=Connection Group 1,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: anonymous,initialized
+
+dn: cn=Connection Group 2,cn=Connections,cn=database 1,cn=databases,cn=monitor
+
+dn: cn=Target Connection 1,cn=Connection Group 2,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: anonymous,initialized
+
+dn: cn=Target Connection 2,cn=Connection Group 2,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: anonymous,initialized
+
+dn: cn=Connection Group 3,cn=Connections,cn=database 1,cn=databases,cn=monitor
+
+dn: cn=Target Connection 1,cn=Connection Group 3,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: anonymous,initialized
+
+dn: cn=Target Connection 2,cn=Connection Group 3,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: anonymous,initialized
--- /dev/null
+dn: cn=Target Connection 1,cn=Connection Group 1,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
+
+dn: cn=Target Connection 2,cn=Connection Group 1,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
+
+dn: cn=Target Connection 1,cn=Connection Group 2,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
+
+dn: cn=Target Connection 2,cn=Connection Group 2,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
+
+dn: cn=Target Connection 1,cn=Connection Group 3,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
+
+dn: cn=Target Connection 2,cn=Connection Group 3,cn=Connections,cn=database 1,
+ cn=databases,cn=monitor
+olmTargetConnFlags: closed
--- /dev/null
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2025 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#asyncmetamod#modulepath ../servers/slapd/back-asyncmeta/
+#asyncmetamod#moduleload back_asyncmeta.la
+
+# seems to improve behavior under very heavy load
+# (i.e. it alleviates load on target systems)
+threads 8
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database asyncmeta
+suffix "o=Example,c=US"
+rootdn "cn=Manager,o=Example,c=US"
+rootpw secret
+chase-referrals no
+#nretries forever
+nretries 100
+#norefs true
+network-timeout 500
+#max-timeout-ops 50
+#max-pending-ops 128
+max-target-conns 3
+conn-ttl 10s 5s
+
+monitoring on
+
+# local
+uri "@URI2@ou=Meta,o=Example,c=US"
+subtree-exclude "ou=Excluded,ou=Meta,o=Example,c=US"
+suffixmassage "ou=Meta,o=Example,c=US" "ou=Meta,dc=example,dc=com"
+###pseudorootdn "cn=manager,ou=meta,dc=example,dc=com"
+###pseudorootpw secret
+idassert-bind bindmethod=simple
+ binddn="cn=manager,ou=meta,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+idassert-authzFrom "dn.exact:cn=Manager,o=Local"
+
+# remote
+uri "@URI1@o=Example,c=US"
+subtree-include "dn.subtree:o=Example,c=US"
+suffixmassage "o=Example,c=US" "dc=example,dc=com"
+###pseudorootdn "cn=manager,dc=example,dc=com"
+###pseudorootpw secret
+idassert-bind bindmethod=simple
+ binddn="cn=manager,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+idassert-authzFrom "dn.exact:cn=Manager,o=Local"
+
+database monitor
METACONF1=$DATADIR/slapd-meta-target1.conf
METACONF2=$DATADIR/slapd-meta-target2.conf
ASYNCMETACONF=$DATADIR/slapd-asyncmeta.conf
+ASYNCMETACONF2=$DATADIR/slapd-asyncmeta-conttl.conf
GLUELDAPCONF=$DATADIR/slapd-glue-ldap.conf
ACICONF=$DATADIR/slapd-aci.conf
VALSORTCONF=$DATADIR/slapd-valsort.conf
--- /dev/null
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2025 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKASYNCMETA = asyncmetano ; then
+ echo "asyncmeta backend not available, test skipped"
+ exit 0
+fi
+
+if test $BACKLDAP = ldapno ; then
+ echo "ldap backend not available, test skipped"
+ exit 0
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -H $URI2 -w $PASSWD < \
+ $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $ASYNCMETACONF2 > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+#search cn=monitor, all connections are closed
+SEARCHDN="cn=Connections,cn=database 1,cn=databases,cn=monitor"
+echo "Verifying that all target connections are closed..."
+cat /dev/null > $SEARCHOUT
+
+echo " base=\"$SEARCHDN\"..."
+echo "# base=\"$SEARCHDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI3 \
+ -b "$SEARCHDN" \
+ "olmTargetConnFlags=closed" 'olmTargetConnFlags' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Unable to read monitor ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < data/asyncmeta.closed.out > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+#run 3 searches
+for i in 0 1 2; do
+ BASEDN="o=Example,c=US"
+ echo "Searching base=\"$BASEDN\"..."
+ echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+ RC=$?
+ #if test $RC != 0 ; then
+ # echo "Search failed ($RC)!"
+ # test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ # exit $RC
+ #fi
+ case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+ sleep 2
+done
+# search cn=monitor - no connections should be closed yet
+SEARCHDN="cn=Connections,cn=database 1,cn=databases,cn=monitor"
+echo "Verifying that all target connections are open..."
+cat /dev/null > $SEARCHOUT
+
+echo " base=\"$SEARCHDN\"..."
+echo "# base=\"$SEARCHDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI3 \
+ -b "$SEARCHDN" \
+ 'olmTargetConnFlags' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Unable to read monitor ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < data/asyncmeta.allopen.out > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+# wait 6 seconds and search cn=monitor - connection group 1 is closed
+SEARCHDN="cn=Connections,cn=database 1,cn=databases,cn=monitor"
+echo "Verifying that connection group 1 is closed..."
+sleep 5
+cat /dev/null > $SEARCHOUT
+
+echo " base=\"$SEARCHDN\"..."
+echo "# base=\"$SEARCHDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI3 \
+ -b "$SEARCHDN" \
+ "olmTargetConnFlags=closed" 'olmTargetConnFlags' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Unable to read monitor ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < data/asyncmeta.1.out > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+SEARCHDN="cn=Connections,cn=database 1,cn=databases,cn=monitor"
+echo "Verifying that target connection group 2 is closed..."
+sleep 6
+cat /dev/null > $SEARCHOUT
+
+echo " base=\"$SEARCHDN\"..."
+echo "# base=\"$SEARCHDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI3 \
+ -b "cn=Connection Group 2,$SEARCHDN" \
+ "olmTargetConnFlags=closed" 'olmTargetConnFlags' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Unable to read monitor ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < data/asyncmeta.2.out > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+SEARCHDN="cn=Connections,cn=database 1,cn=databases,cn=monitor"
+echo "Verifying that all target connections are closed..."
+# wait 6 seconds, search, all connections should be closed
+sleep 6
+cat /dev/null > $SEARCHOUT
+
+echo " base=\"$SEARCHDN\"..."
+echo "# base=\"$SEARCHDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI3 \
+ -b "$SEARCHDN" \
+ "olmTargetConnFlags=closed" 'olmTargetConnFlags' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Unable to read monitor ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < data/asyncmeta.closed.out > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+cat /dev/null > $SEARCHOUT
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0