]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/syncrepl.c
Partially revert prev commit, leave rs->sr_err == SLAPD_ABANDON
[openldap] / servers / slapd / syncrepl.c
index acb2ebf606d92a5f7a7e8b8d307175007d8eadd3..6282013598b927df02897dadb30d78c02d3e89de 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2003-2008 The OpenLDAP Foundation.
+ * Copyright 2003-2009 The OpenLDAP Foundation.
  * Portions Copyright 2003 by IBM Corporation.
  * Portions Copyright 2003-2008 by Howard Chu, Symas Corporation.
  * All rights reserved.
@@ -41,6 +41,7 @@ typedef struct cookie_state {
        ldap_pvt_thread_mutex_t cs_mutex;
        int     cs_num;
        int cs_age;
+       int cs_ref;
        struct berval *cs_vals;
        int *cs_sids;
 } cookie_state;
@@ -343,7 +344,7 @@ ldap_sync_search(
 {
        BerElementBuffer berbuf;
        BerElement *ber = (BerElement *)&berbuf;
-       LDAPControl c[2], *ctrls[3];
+       LDAPControl c[3], *ctrls[4];
        int rc;
        int rhint;
        char *base;
@@ -417,14 +418,19 @@ ldap_sync_search(
        c[0].ldctl_iscritical = si->si_type < 0;
        ctrls[0] = &c[0];
 
+       c[1].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
+       BER_BVZERO( &c[1].ldctl_value );
+       c[1].ldctl_iscritical = 1;
+       ctrls[1] = &c[1];
+
        if ( !BER_BVISNULL( &si->si_bindconf.sb_authzId ) ) {
-               c[1].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
-               c[1].ldctl_value = si->si_bindconf.sb_authzId;
-               c[1].ldctl_iscritical = 1;
-               ctrls[1] = &c[1];
-               ctrls[2] = NULL;
+               c[2].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+               c[2].ldctl_value = si->si_bindconf.sb_authzId;
+               c[2].ldctl_iscritical = 1;
+               ctrls[2] = &c[2];
+               ctrls[3] = NULL;
        } else {
-               ctrls[1] = NULL;
+               ctrls[2] = NULL;
        }
 
        rc = ldap_search_ext( si->si_ld, base, scope, filter, attrs, attrsonly,
@@ -583,6 +589,8 @@ do_syncrep1(
        rc = LDAP_DEREF_NEVER;  /* actually could allow DEREF_FINDING */
        ldap_set_option( si->si_ld, LDAP_OPT_DEREF, &rc );
 
+       ldap_set_option( si->si_ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
+
        si->si_syncCookie.rid = si->si_rid;
 
        /* whenever there are multiple data sources possible, advertise sid */
@@ -856,6 +864,7 @@ do_syncrep2(
                                                        }
                                                }
                                        }
+                                       op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
                                }
                        }
                        rc = 0;
@@ -962,6 +971,7 @@ do_syncrep2(
                                        if ( !BER_BVISNULL( &syncCookie.octet_str ) )
                                        {
                                                slap_parse_sync_cookie( &syncCookie, NULL );
+                                               op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
                                        }
                                }
                                if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES )
@@ -1030,6 +1040,18 @@ do_syncrep2(
                                                "LDAP_RES_INTERMEDIATE", 
                                                "NEW_COOKIE" );
                                        ber_scanf( ber, "tm", &tag, &cookie );
+                                       Debug( LDAP_DEBUG_SYNC,
+                                               "do_syncrep2: %s NEW_COOKIE: %s\n",
+                                               si->si_ridtxt,
+                                               cookie.bv_val, 0);
+                                       if ( !BER_BVISNULL( &cookie ) ) {
+                                               ch_free( syncCookie.octet_str.bv_val );
+                                               ber_dupbv( &syncCookie.octet_str, &cookie );
+                                       }
+                                       if (!BER_BVISNULL( &syncCookie.octet_str ) ) {
+                                               slap_parse_sync_cookie( &syncCookie, NULL );
+                                               op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
+                                       }
                                        break;
                                case LDAP_TAG_SYNC_REFRESH_DELETE:
                                case LDAP_TAG_SYNC_REFRESH_PRESENT:
@@ -1059,6 +1081,7 @@ do_syncrep2(
                                                if ( !BER_BVISNULL( &syncCookie.octet_str ) )
                                                {
                                                        slap_parse_sync_cookie( &syncCookie, NULL );
+                                                       op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
                                                }
                                        }
                                        /* Defaults to TRUE */
@@ -1094,6 +1117,7 @@ do_syncrep2(
                                                if ( !BER_BVISNULL( &syncCookie.octet_str ) )
                                                {
                                                        slap_parse_sync_cookie( &syncCookie, NULL );
+                                                       op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
                                                        compare_csns( &syncCookie_req, &syncCookie, &m );
                                                }
                                        }
@@ -1142,6 +1166,7 @@ do_syncrep2(
 
                                if ( match < 0 ) {
                                        if ( si->si_refreshPresent == 1 &&
+                                               si_tag != LDAP_TAG_SYNC_NEW_COOKIE &&
                                                syncCookie_req.numcsns == syncCookie.numcsns ) {
                                                syncrepl_del_nonpresent( op, si, NULL,
                                                        &syncCookie, m );
@@ -1234,13 +1259,23 @@ do_syncrepl(
        int i, defer = 1, fail = 0;
        Backend *be;
 
-       Debug( LDAP_DEBUG_TRACE, "=>do_syncrepl %s\n", si->si_ridtxt, 0, 0 );
-
        if ( si == NULL )
                return NULL;
+       if ( slapd_shutdown )
+               return NULL;
 
-       /* There will never be more than one instance active */
-       ldap_pvt_thread_mutex_lock( &si->si_mutex );
+       Debug( LDAP_DEBUG_TRACE, "=>do_syncrepl %s\n", si->si_ridtxt, 0, 0 );
+
+       /* Don't get stuck here while a pause is initiated */
+       while ( ldap_pvt_thread_mutex_trylock( &si->si_mutex )) {
+               if ( slapd_shutdown )
+                       return NULL;
+               if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
+                       ldap_pvt_thread_yield();
+       }
+
+       if ( !si->si_ctype )
+               goto deleted;
 
        switch( abs( si->si_type ) ) {
        case LDAP_SYNC_REFRESH_ONLY:
@@ -1267,10 +1302,6 @@ do_syncrepl(
        connection_fake_init( &conn, &opbuf, ctx );
        op = &opbuf.ob_op;
 
-       /* use global malloc for now */
-       op->o_tmpmemctx = NULL;
-       op->o_tmpmfuncs = &ch_mfuncs;
-
        op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
        be = si->si_be;
 
@@ -1329,6 +1360,7 @@ reload:
                        goto reload;
                }
 
+deleted:
                /* We got deleted while running on cn=config */
                if ( !si->si_ctype ) {
                        if ( si->si_conn )
@@ -1399,7 +1431,10 @@ reload:
 
                if ( !si->si_ctype
                        || !si->si_retrynum || si->si_retrynum[i] == RETRYNUM_TAIL ) {
-                       ldap_pvt_runqueue_remove( &slapd_rq, rtask );
+                       if ( si->si_re ) {
+                               ldap_pvt_runqueue_remove( &slapd_rq, rtask );
+                               si->si_re = NULL;
+                       }
                        fail = RETRYNUM_TAIL;
                } else if ( RETRYNUM_VALID( si->si_retrynum[i] ) ) {
                        if ( si->si_retrynum[i] > 0 )
@@ -1417,37 +1452,22 @@ reload:
        if ( rc ) {
                if ( fail == RETRYNUM_TAIL ) {
                        Debug( LDAP_DEBUG_ANY,
-                               "do_syncrepl: %s quitting\n",
-                               si->si_ridtxt, 0, 0 );
+                               "do_syncrepl: %s rc %d quitting\n",
+                               si->si_ridtxt, rc, 0 );
                } else if ( fail > 0 ) {
                        Debug( LDAP_DEBUG_ANY,
-                               "do_syncrepl: %s retrying (%d retries left)\n",
-                               si->si_ridtxt, fail, 0 );
+                               "do_syncrepl: %s rc %d retrying (%d retries left)\n",
+                               si->si_ridtxt, rc, fail );
                } else {
                        Debug( LDAP_DEBUG_ANY,
-                               "do_syncrepl: %s retrying\n",
-                               si->si_ridtxt, 0, 0 );
+                               "do_syncrepl: %s rc %d retrying\n",
+                               si->si_ridtxt, rc, 0 );
                }
        }
 
        /* Do final delete cleanup */
        if ( !si->si_ctype ) {
-               cookie_state *cs = NULL;
-               syncinfo_t **sip;
-
-               cs = be->be_syncinfo->si_cookieState;
-               for ( sip = &be->be_syncinfo; *sip != si; sip = &(*sip)->si_next );
-               *sip = si->si_next;
                syncinfo_free( si, 0 );
-               if ( !be->be_syncinfo ) {
-                       SLAP_DBFLAGS( be ) &= ~(SLAP_DBFLAG_SHADOW|SLAP_DBFLAG_SYNC_SHADOW);
-                       if ( cs ) {
-                               ch_free( cs->cs_sids );
-                               ber_bvarray_free( cs->cs_vals );
-                               ldap_pvt_thread_mutex_destroy( &cs->cs_mutex );
-                               ch_free( cs );
-                       }
-               }
        }
        return NULL;
 }
@@ -1569,6 +1589,7 @@ syncrepl_message_to_op(
                prdn = BER_BVNULL, nrdn = BER_BVNULL,
                psup = BER_BVNULL, nsup = BER_BVNULL;
        int             rc, deleteOldRdn = 0, freeReqDn = 0;
+       int             do_graduate = 0;
 
        if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) {
                Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: %s "
@@ -1601,7 +1622,16 @@ syncrepl_message_to_op(
 
                if ( !ber_bvstrcasecmp( &bv, &ls->ls_dn ) ) {
                        bdn = bvals[0];
-                       dnPrettyNormal( NULL, &bdn, &dn, &ndn, op->o_tmpmemctx );
+                       rc = dnPrettyNormal( NULL, &bdn, &dn, &ndn, op->o_tmpmemctx );
+                       if ( rc != LDAP_SUCCESS ) {
+                               Debug( LDAP_DEBUG_ANY,
+                                       "syncrepl_message_to_op: %s "
+                                       "dn \"%s\" normalization failed (%d)",
+                                       si->si_ridtxt, bdn.bv_val, rc );
+                               rc = -1;
+                               ch_free( bvals );
+                               goto done;
+                       }
                        ber_dupbv( &op->o_req_dn, &dn );
                        ber_dupbv( &op->o_req_ndn, &ndn );
                        slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
@@ -1637,6 +1667,7 @@ syncrepl_message_to_op(
                        &slap_schema.si_ad_entryCSN->ad_cname ) )
                {
                        slap_queue_csn( op, bvals );
+                       do_graduate = 1;
                }
                ch_free( bvals );
        }
@@ -1681,6 +1712,7 @@ syncrepl_message_to_op(
                                Debug( LDAP_DEBUG_SYNC,
                                        "syncrepl_message_to_op: %s be_add %s (%d)\n", 
                                        si->si_ridtxt, op->o_req_dn.bv_val, rc );
+                               do_graduate = 0;
                        }
                        if ( e == op->ora_e )
                                be_entry_release_w( op, op->ora_e );
@@ -1693,6 +1725,7 @@ syncrepl_message_to_op(
                                "syncrepl_message_to_op: %s be_modify %s (%d)\n", 
                                si->si_ridtxt, op->o_req_dn.bv_val, rc );
                        op->o_bd = si->si_be;
+                       do_graduate = 0;
                }
                break;
        case LDAP_REQ_MODRDN:
@@ -1736,16 +1769,19 @@ syncrepl_message_to_op(
                Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
                        "syncrepl_message_to_op: %s be_modrdn %s (%d)\n", 
                        si->si_ridtxt, op->o_req_dn.bv_val, rc );
+               do_graduate = 0;
                break;
        case LDAP_REQ_DELETE:
                rc = op->o_bd->be_delete( op, &rs );
                Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
                        "syncrepl_message_to_op: %s be_delete %s (%d)\n", 
                        si->si_ridtxt, op->o_req_dn.bv_val, rc );
+               do_graduate = 0;
                break;
        }
 done:
-       slap_graduate_commit_csn( op );
+       if ( do_graduate )
+               slap_graduate_commit_csn( op );
        op->o_bd = si->si_be;
        op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
        BER_BVZERO( &op->o_csn );
@@ -1827,7 +1863,21 @@ syncrepl_message_to_entry(
                return -1;
        }
 
-       dnPrettyNormal( NULL, &bdn, &dn, &ndn, op->o_tmpmemctx );
+       rc = dnPrettyNormal( NULL, &bdn, &dn, &ndn, op->o_tmpmemctx );
+       if ( rc != LDAP_SUCCESS ) {
+               /* One of the things that could happen is that the schema
+                * is not lined-up; this could result in unknown attributes.
+                * A value non conformant to the syntax should be unlikely,
+                * except when replicating between different versions
+                * of the software, or when syntax validation bugs are fixed
+                */
+               Debug( LDAP_DEBUG_ANY,
+                       "syncrepl_message_to_entry: "
+                       "%s dn \"%s\" normalization failed (%d)",
+                       si->si_ridtxt, bdn.bv_val, rc );
+               return rc;
+       }
+
        ber_dupbv( &op->o_req_dn, &dn );
        ber_dupbv( &op->o_req_ndn, &ndn );
        slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
@@ -2086,6 +2136,7 @@ syncrepl_entry(
        op->o_time = slap_get_time();
        op->ors_tlimit = SLAP_NO_LIMIT;
        op->ors_slimit = 1;
+       op->ors_limit = NULL;
 
        op->ors_attrs = slap_anlist_all_attributes;
        op->ors_attrsonly = 0;
@@ -2097,12 +2148,10 @@ syncrepl_entry(
        dni.new_entry = entry;
        dni.modlist = modlist;
 
-       if ( limits_check( op, &rs_search ) == 0 ) {
-               rc = be->be_search( op, &rs_search );
-               Debug( LDAP_DEBUG_SYNC,
-                               "syncrepl_entry: %s be_search (%d)\n", 
-                               si->si_ridtxt, rc, 0 );
-       }
+       rc = be->be_search( op, &rs_search );
+       Debug( LDAP_DEBUG_SYNC,
+                       "syncrepl_entry: %s be_search (%d)\n", 
+                       si->si_ridtxt, rc, 0 );
 
        if ( !BER_BVISNULL( &op->ors_filterstr ) ) {
                slap_sl_free( op->ors_filterstr.bv_val, op->o_tmpmemctx );
@@ -2156,8 +2205,8 @@ retry_add:;
 
                        rc = op->o_bd->be_add( op, &rs_add );
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: %s be_add (%d)\n", 
-                                       si->si_ridtxt, rc, 0 );
+                                       "syncrepl_entry: %s be_add %s (%d)\n", 
+                                       si->si_ridtxt, op->o_req_dn.bv_val, rc );
                        switch ( rs_add.sr_err ) {
                        case LDAP_SUCCESS:
                                if ( op->ora_e == entry ) {
@@ -2219,10 +2268,11 @@ retry_add:;
 
                        default:
                                Debug( LDAP_DEBUG_ANY,
-                                       "syncrepl_entry: %s be_add failed (%d)\n",
-                                       si->si_ridtxt, rs_add.sr_err, 0 );
+                                       "syncrepl_entry: %s be_add %s failed (%d)\n",
+                                       si->si_ridtxt, op->o_req_dn.bv_val, rs_add.sr_err );
                                break;
                        }
+                       syncCSN = NULL;
                        op->o_bd = be;
                        goto done;
                }
@@ -2313,6 +2363,12 @@ retry_add:;
                                                                break;
                                                        }
                                                }
+                                               if ( !mod->sml_numvals ) {
+                                                       /* Drop this op */
+                                                       *ml = mod->sml_next;
+                                                       mod->sml_next = NULL;
+                                                       slap_mods_free( mod, 1 );
+                                               }
                                                break;
                                        }
                                }
@@ -2352,7 +2408,7 @@ retry_add:;
                                        }
                                }
                        }
-                                       
+
                        /* RDNs must be NUL-terminated for back-ldap */
                        noldp = op->orr_newrdn;
                        ber_dupbv_x( &op->orr_newrdn, &noldp, op->o_tmpmemctx );
@@ -2409,14 +2465,16 @@ retry_add:;
 
                        slap_mods_free( op->orr_modlist, 1 );
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: %s be_modrdn (%d)\n", 
-                                       si->si_ridtxt, rc, 0 );
+                                       "syncrepl_entry: %s be_modrdn %s (%d)\n", 
+                                       si->si_ridtxt, op->o_req_dn.bv_val, rc );
                        op->o_bd = be;
                        /* Renamed entries may still have other mods so just fallthru */
                        op->o_req_dn = entry->e_name;
                        op->o_req_ndn = entry->e_nname;
                        /* Use CSN on the modify */
-                       if ( syncCSN && !just_rename )
+                       if ( just_rename )
+                               syncCSN = NULL;
+                       else if ( syncCSN )
                                slap_queue_csn( op, syncCSN );
                }
                if ( dni.mods ) {
@@ -2429,18 +2487,23 @@ retry_add:;
                        slap_mods_free( op->orm_modlist, 1 );
                        op->orm_no_opattrs = 0;
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: %s be_modify (%d)\n", 
-                                       si->si_ridtxt, rc, 0 );
+                                       "syncrepl_entry: %s be_modify %s (%d)\n", 
+                                       si->si_ridtxt, op->o_req_dn.bv_val, rc );
                        if ( rs_modify.sr_err != LDAP_SUCCESS ) {
                                Debug( LDAP_DEBUG_ANY,
                                        "syncrepl_entry: %s be_modify failed (%d)\n",
                                        si->si_ridtxt, rs_modify.sr_err, 0 );
                        }
+                       syncCSN = NULL;
                        op->o_bd = be;
                } else if ( !dni.renamed ) {
                        Debug( LDAP_DEBUG_SYNC,
                                        "syncrepl_entry: %s entry unchanged, ignored (%s)\n", 
                                        si->si_ridtxt, op->o_req_dn.bv_val, 0 );
+                       if ( syncCSN ) {
+                               slap_graduate_commit_csn( op );
+                               syncCSN = NULL;
+                       }
                }
                goto done;
        case LDAP_SYNC_DELETE :
@@ -2451,8 +2514,8 @@ retry_add:;
                        op->o_bd = si->si_wbe;
                        rc = op->o_bd->be_delete( op, &rs_delete );
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: %s be_delete (%d)\n", 
-                                       si->si_ridtxt, rc, 0 );
+                                       "syncrepl_entry: %s be_delete %s (%d)\n", 
+                                       si->si_ridtxt, op->o_req_dn.bv_val, rc );
 
                        while ( rs_delete.sr_err == LDAP_SUCCESS
                                && op->o_delete_glue_parent ) {
@@ -2469,6 +2532,7 @@ retry_add:;
                                        break;
                                }
                        }
+                       syncCSN = NULL;
                        op->o_bd = be;
                }
                goto done;
@@ -2571,40 +2635,43 @@ syncrepl_del_nonpresent(
                si->si_refreshDelete ^= NP_DELETE_ONE;
        } else {
                Filter *cf, *of;
+               Filter mmf[2];
+               AttributeAssertion mmaa;
 
                memset( &an[0], 0, 2 * sizeof( AttributeName ) );
                an[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
                an[0].an_desc = slap_schema.si_ad_entryUUID;
                op->ors_attrs = an;
                op->ors_slimit = SLAP_NO_LIMIT;
+               op->ors_tlimit = SLAP_NO_LIMIT;
+               op->ors_limit = NULL;
                op->ors_attrsonly = 0;
                op->ors_filter = str2filter_x( op, si->si_filterstr.bv_val );
                /* In multimaster, updates can continue to arrive while
                 * we're searching. Limit the search result to entries
-                * older than all of our cookie CSNs.
+                * older than our newest cookie CSN.
                 */
                if ( SLAP_MULTIMASTER( op->o_bd )) {
                        Filter *f;
                        int i;
-                       cf = op->o_tmpalloc( (sc->numcsns+1) * sizeof(Filter) +
-                               sc->numcsns * sizeof(AttributeAssertion), op->o_tmpmemctx );
-                       f = cf;
+
+                       f = mmf;
                        f->f_choice = LDAP_FILTER_AND;
-                       f->f_next = NULL;
+                       f->f_next = op->ors_filter;
                        f->f_and = f+1;
                        of = f->f_and;
+                       f = of;
+                       f->f_choice = LDAP_FILTER_LE;
+                       f->f_ava = &mmaa;
+                       f->f_av_desc = slap_schema.si_ad_entryCSN;
+                       f->f_next = NULL;
+                       BER_BVZERO( &f->f_av_value );
                        for ( i=0; i<sc->numcsns; i++ ) {
-                               f = of;
-                               f->f_choice = LDAP_FILTER_LE;
-                               f->f_ava = (AttributeAssertion *)(f+1);
-                               f->f_av_desc = slap_schema.si_ad_entryCSN;
-                               f->f_av_value = sc->ctxcsn[i];
-                               f->f_next = (Filter *)(f->f_ava+1);
-                               of = f->f_next;
+                               if ( ber_bvcmp( &sc->ctxcsn[i], &f->f_av_value ) > 0 )
+                                       f->f_av_value = sc->ctxcsn[i];
                        }
-                       f->f_next = op->ors_filter;
                        of = op->ors_filter;
-                       op->ors_filter = cf;
+                       op->ors_filter = mmf;
                        filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
                } else {
                        cf = NULL;
@@ -2612,11 +2679,9 @@ syncrepl_del_nonpresent(
                }
                op->o_nocaching = 1;
 
-               if ( limits_check( op, &rs_search ) == 0 ) {
-                       rc = be->be_search( op, &rs_search );
-               }
+
+               rc = be->be_search( op, &rs_search );
                if ( SLAP_MULTIMASTER( op->o_bd )) {
-                       op->o_tmpfree( cf, op->o_tmpmemctx );
                        op->ors_filter = of;
                }
                if ( op->ors_filter ) filter_free_x( op, op->ors_filter, 1 );
@@ -2879,6 +2944,9 @@ syncrepl_updateCookie(
        Backend *be = op->o_bd;
        Modifications mod;
        struct berval first = BER_BVNULL;
+#ifdef CHECK_CSN
+       Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
+#endif
 
        int rc, i, j;
        ber_len_t len;
@@ -2895,6 +2963,15 @@ syncrepl_updateCookie(
 
        ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
 
+#ifdef CHECK_CSN
+       for ( i=0; i<syncCookie->numcsns; i++ ) {
+               assert( !syn->ssyn_validate( syn, syncCookie->ctxcsn+i ));
+       }
+       for ( i=0; i<si->si_cookieState->cs_num; i++ ) {
+               assert( !syn->ssyn_validate( syn, si->si_cookieState->cs_vals+i ));
+       }
+#endif
+
        /* clone the cookieState CSNs so we can Replace the whole thing */
        mod.sml_numvals = si->si_cookieState->cs_num;
        mod.sml_values = op->o_tmpalloc(( mod.sml_numvals+1 )*sizeof(struct berval), op->o_tmpmemctx );
@@ -2997,6 +3074,12 @@ syncrepl_updateCookie(
        if ( mod.sml_next ) slap_mods_free( mod.sml_next, 1 );
        op->o_tmpfree( mod.sml_values, op->o_tmpmemctx );
 
+#ifdef CHECK_CSN
+       for ( i=0; i<si->si_cookieState->cs_num; i++ ) {
+               assert( !syn->ssyn_validate( syn, si->si_cookieState->cs_vals+i ));
+       }
+#endif
+
        return rc;
 }
 
@@ -3162,7 +3245,7 @@ dn_callback(
                         * We compare the non-normalized values so that cosmetic changes
                         * in the provider are always propagated.
                         */
-                       if ( dni->new_entry && !is_entry_glue( rs->sr_entry )) {
+                       if ( dni->new_entry ) {
                                Modifications **modtail, **ml;
                                Attribute *old, *new;
                                struct berval old_rdn, new_rdn;
@@ -3173,8 +3256,6 @@ dn_callback(
                                 * is actually newer than old entry
                                 */
                                if ( !is_entry_glue( rs->sr_entry )) {
-                                       old = attr_find( rs->sr_entry->e_attrs,
-                                               slap_schema.si_ad_objectClass );
                                        old = attr_find( rs->sr_entry->e_attrs,
                                                slap_schema.si_ad_entryCSN );
                                        new = attr_find( dni->new_entry->e_attrs,
@@ -3327,8 +3408,8 @@ dn_callback(
                                         * stays co-located with the other mod opattrs. But only
                                         * if we know there are other valid mods.
                                         */
-                                       if ( old->a_desc == slap_schema.si_ad_modifiersName &&
-                                               dni->mods )
+                                       if ( dni->mods && ( old->a_desc == slap_schema.si_ad_modifiersName ||
+                                               old->a_desc == slap_schema.si_ad_modifyTimestamp ))
                                                attr_cmp( op, NULL, new, &modtail, &ml );
                                        else
                                                attr_cmp( op, old, new, &modtail, &ml );
@@ -3539,12 +3620,9 @@ syncinfo_free( syncinfo_t *sie, int free_all )
 {
        syncinfo_t *si_next;
 
-       if ( free_all && sie->si_cookieState ) {
-               ch_free( sie->si_cookieState->cs_sids );
-               ber_bvarray_free( sie->si_cookieState->cs_vals );
-               ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_mutex );
-               ch_free( sie->si_cookieState );
-       }
+       Debug( LDAP_DEBUG_TRACE, "syncinfo_free: %s\n",
+               sie->si_ridtxt, 0, 0 );
+
        do {
                si_next = sie->si_next;
 
@@ -3556,20 +3634,21 @@ syncinfo_free( syncinfo_t *sie, int free_all )
                        ldap_unbind_ext( sie->si_ld, NULL, NULL );
                }
        
-               /* re-fetch it, in case it was already removed */
-               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
-               sie->si_re = ldap_pvt_runqueue_find( &slapd_rq, do_syncrepl, sie );
                if ( sie->si_re ) {
-                       if ( ldap_pvt_runqueue_isrunning( &slapd_rq, sie->si_re ) )
-                               ldap_pvt_runqueue_stoptask( &slapd_rq, sie->si_re );
-                       ldap_pvt_runqueue_remove( &slapd_rq, sie->si_re );
+                       struct re_s             *re = sie->si_re;
+                       sie->si_re = NULL;
+
+                       ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+                       if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
+                               ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+                       ldap_pvt_runqueue_remove( &slapd_rq, re );
+                       ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
                }
-       
-               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
-               ldap_pvt_thread_mutex_destroy( &sie->si_mutex );
-       
+
+               ldap_pvt_thread_mutex_destroy( &sie->si_mutex );
+
                bindconf_free( &sie->si_bindconf );
-       
+
                if ( sie->si_filterstr.bv_val ) {
                        ch_free( sie->si_filterstr.bv_val );
                }
@@ -3645,6 +3724,13 @@ syncinfo_free( syncinfo_t *sie, int free_all )
                        }
                        ch_free( npe );
                }
+               sie->si_cookieState->cs_ref--;
+               if ( !sie->si_cookieState->cs_ref ) {
+                       ch_free( sie->si_cookieState->cs_sids );
+                       ber_bvarray_free( sie->si_cookieState->cs_vals );
+                       ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_mutex );
+                       ch_free( sie->si_cookieState );
+               }
                ch_free( sie );
                sie = si_next;
        } while ( free_all && si_next );
@@ -4310,13 +4396,23 @@ add_syncrepl(
                        BER_BVISNULL( &si->si_bindconf.sb_uri ) ?
                        "(null)" : si->si_bindconf.sb_uri.bv_val, 0, 0 );
                if ( c->be->be_syncinfo ) {
+                       syncinfo_t *sip;
+
                        si->si_cookieState = c->be->be_syncinfo->si_cookieState;
+
+                       /* add new syncrepl to end of list (same order as when deleting) */
+                       for ( sip = c->be->be_syncinfo; sip->si_next; sip = sip->si_next );
+                       sip->si_next = si;
                } else {
                        si->si_cookieState = ch_calloc( 1, sizeof( cookie_state ));
                        ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_mutex );
+
+                       c->be->be_syncinfo = si;
                }
-               si->si_next = c->be->be_syncinfo;
-               c->be->be_syncinfo = si;
+               si->si_cookieState->cs_ref++;
+
+               si->si_next = NULL;
+
                return 0;
        }
 }
@@ -4512,28 +4608,46 @@ syncrepl_config( ConfigArgs *c )
                }
                return 1;
        } else if ( c->op == LDAP_MOD_DELETE ) {
-               cookie_state *cs = NULL;
+               int isrunning = 0;
                if ( c->be->be_syncinfo ) {
                        syncinfo_t *si, **sip;
                        int i;
 
-                       cs = c->be->be_syncinfo->si_cookieState;
                        for ( sip = &c->be->be_syncinfo, i=0; *sip; i++ ) {
                                si = *sip;
                                if ( c->valx == -1 || i == c->valx ) {
-                                       int isrunning = 0;
                                        *sip = si->si_next;
                                        /* If the task is currently active, we have to leave
                                         * it running. It will exit on its own. This will only
                                         * happen when running on the cn=config DB.
                                         */
                                        if ( si->si_re ) {
-                                               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
-                                               isrunning = ldap_pvt_runqueue_isrunning( &slapd_rq, si->si_re );
-                                               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+                                               if ( ldap_pvt_thread_mutex_trylock( &si->si_mutex )) {
+                                                       isrunning = 1;
+                                               } else {
+                                                       if ( si->si_conn ) {
+                                                               isrunning = 1;
+                                                               /* If there's a persistent connection, we don't
+                                                                * know if it's already got a thread queued.
+                                                                * so defer the free, but reschedule the task.
+                                                                * If there's a connection thread queued, it
+                                                                * will cleanup as necessary. If not, then the
+                                                                * runqueue task will cleanup.
+                                                                */
+                                                               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+                                                               if ( !ldap_pvt_runqueue_isrunning( &slapd_rq, si->si_re )) {
+                                                                       si->si_re->interval.tv_sec = 0;
+                                                                       ldap_pvt_runqueue_resched( &slapd_rq, si->si_re, 0 );
+                                                                       si->si_re->interval.tv_sec = si->si_interval;
+                                                               }
+                                                               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+                                                       }
+                                                       ldap_pvt_thread_mutex_unlock( &si->si_mutex );
+                                               }
                                        }
-                                       if ( si->si_re && isrunning ) {
+                                       if ( isrunning ) {
                                                si->si_ctype = 0;
+                                               si->si_next = NULL;
                                        } else {
                                                syncinfo_free( si, 0 );
                                        }
@@ -4546,11 +4660,6 @@ syncrepl_config( ConfigArgs *c )
                }
                if ( !c->be->be_syncinfo ) {
                        SLAP_DBFLAGS( c->be ) &= ~SLAP_DBFLAG_SHADOW_MASK;
-                       if ( cs ) {
-                               ber_bvarray_free( cs->cs_vals );
-                               ldap_pvt_thread_mutex_destroy( &cs->cs_mutex );
-                               ch_free( cs );
-                       }
                }
                return 0;
        }