]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#10363 - Implement a target connection time-to-live in asyncmeta master 779/head
authorNadezhda Ivanova <nivanova@symas.com>
Tue, 24 Jun 2025 15:10:30 +0000 (18:10 +0300)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 31 Jul 2025 19:01:56 +0000 (19:01 +0000)
14 files changed:
doc/man/man5/slapd-asyncmeta.5
servers/slapd/back-asyncmeta/back-asyncmeta.h
servers/slapd/back-asyncmeta/config.c
servers/slapd/back-asyncmeta/conn.c
servers/slapd/back-asyncmeta/init.c
servers/slapd/back-asyncmeta/meta_result.c
servers/slapd/back-asyncmeta/monitor.c
tests/data/asyncmeta.1.out [new file with mode: 0644]
tests/data/asyncmeta.2.out [new file with mode: 0644]
tests/data/asyncmeta.allopen.out [new file with mode: 0644]
tests/data/asyncmeta.closed.out [new file with mode: 0644]
tests/data/slapd-asyncmeta-conttl.conf [new file with mode: 0644]
tests/scripts/defines.sh
tests/scripts/test090-asyncmeta-conttl [new file with mode: 0755]

index 44fe95ade95a996d16ceb2c256a627ca4e4d89cf..6b3c5d928677edd32b76e200d6d0260aa9c90ee0 100644 (file)
@@ -268,6 +268,47 @@ irrespective of the client's request. See
 .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.
@@ -331,7 +372,7 @@ for details.
 
 .TP
 .B idle\-timeout <time>
-This directive causes 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
index 03f8eb1d8c4ce1f0712c1b839c0261b81d19c44d..da8a495b277a0d5975d980ec49ba5f345c7c5af4 100644 (file)
@@ -63,7 +63,11 @@ LDAP_BEGIN_DECL
 
 #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)
@@ -76,6 +80,10 @@ LDAP_BEGIN_DECL
 #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;
@@ -129,14 +137,17 @@ typedef struct a_metasingleconn_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;
@@ -240,6 +251,8 @@ typedef struct a_metacommon_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;
@@ -299,6 +312,8 @@ typedef struct a_metatarget_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) )
@@ -337,6 +352,8 @@ typedef struct a_metatarget_t {
 #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 {
@@ -382,7 +399,8 @@ typedef struct a_metainfo_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;
 
index 9271ba86a342a2e68cf8c60a5425d2a3ae5ab64c..9b39df08beec929ab77c0bf00db92d56097c3d24 100644 (file)
@@ -68,6 +68,7 @@ enum {
        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,
@@ -171,6 +172,15 @@ static ConfigTable a_metacfg[] = {
                        "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 "
@@ -412,7 +422,8 @@ static ConfigTable a_metacfg[] = {
                        "$ olcDbRebindAsUser " \
                        ST_ATTR \
                        "$ olcDbStartTLS " \
-                       "$ olcDbTFSupport "
+                       "$ olcDbTFSupport " \
+                       "$ olcDbConnTtl "
 
 static ConfigOCs a_metaocs[] = {
        { "( OLcfgDbOc:3.4 "
@@ -518,6 +529,7 @@ asyncmeta_back_new_target(
                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 ) {
@@ -1149,7 +1161,22 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                        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;
@@ -1598,7 +1625,10 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                        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;
@@ -2390,7 +2420,28 @@ asyncmeta_back_cf_gen( ConfigArgs *c )
                        }
                }
                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 );
index 1bfb674b78bd00d6beec2ab1f4d0d9d086428de5..d93fa4881f0ccf60a85aba061ac2b0b99cab9a9d 100644 (file)
@@ -43,7 +43,7 @@ asyncmeta_conn_alloc(
 {
        a_metaconn_t    *mc;
        int             ntargets = mi->mi_ntargets;
-
+       int j;
        assert( ntargets > 0 );
 
        /* malloc all in one */
@@ -56,6 +56,10 @@ asyncmeta_conn_alloc(
        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;
 }
 
@@ -120,13 +124,17 @@ asyncmeta_init_one_conn(
                        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;
@@ -176,6 +184,8 @@ asyncmeta_init_one_conn(
                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.
@@ -506,7 +516,8 @@ asyncmeta_getconn(
                        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;
@@ -518,6 +529,11 @@ asyncmeta_getconn(
        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;
@@ -613,6 +629,19 @@ asyncmeta_getconn(
                        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 ] );
@@ -714,6 +743,18 @@ asyncmeta_getconn(
                 */
                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
@@ -764,6 +805,19 @@ asyncmeta_getconn(
                                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 ] );
@@ -1055,6 +1109,10 @@ asyncmeta_clear_one_msc(
        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;
 }
 
index fc3e3f036afa2dcc10a65a97c7ee2e47086cb105..27ceeacb0b2dab495ef35b507435c88f62b0b9f1 100644 (file)
@@ -231,6 +231,7 @@ asyncmeta_target_finish(
                mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_NOANON;
        }
 
+       mt->msc_reset_time = slap_get_time();
        return 0;
 }
 
@@ -241,7 +242,7 @@ asyncmeta_back_db_open(
 {
        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 ) {
@@ -270,6 +271,9 @@ asyncmeta_back_db_open(
 
                        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;
                        }
@@ -277,7 +281,6 @@ asyncmeta_back_db_open(
                        mc->mc_info = mi;
                        LDAP_STAILQ_INIT( &mc->mc_om_list );
                }
-       
 
                ber_dupbv ( &mi->mi_suffix, &be->be_suffix[0] );
 
index 32b64d0eb5c8fd3fb85669181e7ac572da18ce5b..409e450f111de61ee510b270321b142aaa436fa4 100644 (file)
@@ -1656,15 +1656,44 @@ void asyncmeta_set_msc_time(a_metasingleconn_t *msc)
        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 ) {
@@ -1676,12 +1705,18 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
                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;
                        }
 
@@ -1703,7 +1738,8 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
                                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 );
@@ -1730,7 +1766,8 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
                                        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 );
@@ -1739,7 +1776,17 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
                                        }
                                }
                        }
+                       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) {
@@ -1827,9 +1874,36 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
                                }
                        }
                }
+               /* 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 );
index 0cc45575fccad54bb8e65b9e04038d18adc91b04..5662cf5199ddbac604a2fabdad3012bdf8dd8d29 100644 (file)
@@ -47,6 +47,7 @@ static AttributeDescription   *ad_olmDbNextConnectionGroup; /*mi_next_conn*/
 /* 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;
@@ -55,6 +56,8 @@ static AttributeDescription   *ad_olmCGPendingOps;
 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;
@@ -77,6 +80,7 @@ static struct {
        { 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 }
 };
 
@@ -204,6 +208,33 @@ static struct {
          "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 }
 };
 
@@ -235,6 +266,7 @@ static struct {
                "MAY ( "
                 "olmTgtURIList "
                 "$ olmTgtQuarantined "
+                "$ olmTgtConnLastReset "
                 "$ olmTgtTimeoutOps "
                        ") )",
                &oc_olmAsyncmetaTarget },
@@ -253,9 +285,11 @@ static struct {
                 "olmTargetConnLastUseTime "
                 "$ olmTargetConnBoundTime "
                 "$ olmTargetConnResultTime "
-                        "$ olmTargetConnFlags "
-                        "$ olmTargetConnURI "
-                        "$ olmTargetConnPeerAddress"
+                "$ olmTargetConnResetTime "
+                "$ olmTargetConnEstablishedTime "
+                "$ olmTargetConnFlags "
+                "$ olmTargetConnURI "
+                "$ olmTargetConnPeerAddress"
                        ") )",
                &oc_olmAsyncmetaTargetConnection },
 
@@ -431,6 +465,18 @@ asyncmeta_back_monitor_target_conn_update(
        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;
@@ -531,7 +577,7 @@ asyncmeta_back_monitor_target_conn_init(
                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 );
@@ -559,6 +605,14 @@ asyncmeta_back_monitor_target_conn_init(
 
                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 )
@@ -791,6 +845,12 @@ asyncmeta_back_monitor_targets_update(
        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;
 }
 
@@ -866,7 +926,7 @@ asyncmeta_back_monitor_targets_init(
                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 );
@@ -880,6 +940,10 @@ asyncmeta_back_monitor_targets_init(
                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 );
 
diff --git a/tests/data/asyncmeta.1.out b/tests/data/asyncmeta.1.out
new file mode 100644 (file)
index 0000000..3ac37af
--- /dev/null
@@ -0,0 +1,7 @@
+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
diff --git a/tests/data/asyncmeta.2.out b/tests/data/asyncmeta.2.out
new file mode 100644 (file)
index 0000000..eb1ff44
--- /dev/null
@@ -0,0 +1,7 @@
+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
diff --git a/tests/data/asyncmeta.allopen.out b/tests/data/asyncmeta.allopen.out
new file mode 100644 (file)
index 0000000..ddeb919
--- /dev/null
@@ -0,0 +1,31 @@
+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
diff --git a/tests/data/asyncmeta.closed.out b/tests/data/asyncmeta.closed.out
new file mode 100644 (file)
index 0000000..43fb3fe
--- /dev/null
@@ -0,0 +1,23 @@
+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
diff --git a/tests/data/slapd-asyncmeta-conttl.conf b/tests/data/slapd-asyncmeta-conttl.conf
new file mode 100644 (file)
index 0000000..2e716ee
--- /dev/null
@@ -0,0 +1,79 @@
+# 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
index 5a97fe3472cf58291df6f168c92b2a54c683761f..7d2cbedca32c94df52d4b8ea9d179c0c2c863c11 100755 (executable)
@@ -166,6 +166,7 @@ METACONF=$DATADIR/slapd-meta.conf
 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
diff --git a/tests/scripts/test090-asyncmeta-conttl b/tests/scripts/test090-asyncmeta-conttl
new file mode 100755 (executable)
index 0000000..62a970d
--- /dev/null
@@ -0,0 +1,295 @@
+#! /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