]> git.sur5r.net Git - openldap/commitdiff
Preliminary multi-context support for syncrepl. Passes all single-master
authorHoward Chu <hyc@openldap.org>
Mon, 5 Feb 2007 04:50:07 +0000 (04:50 +0000)
committerHoward Chu <hyc@openldap.org>
Mon, 5 Feb 2007 04:50:07 +0000 (04:50 +0000)
tests, needs multi-master testing.

servers/slapd/bconfig.c
servers/slapd/ldapsync.c
servers/slapd/overlays/syncprov.c
servers/slapd/proto-slap.h
servers/slapd/schema_prep.c
servers/slapd/slap.h
servers/slapd/syncrepl.c

index 3a0373babb774c6078249fa307755e42f8f39d61..9ec4b7f10ba52a0ebb69c38179206d8ada26aba3 100644 (file)
@@ -555,7 +555,7 @@ static ConfigTable config_back_cf_table[] = {
                        "SYNTAX OMsDN )", NULL, NULL },
        { "syncrepl", NULL, 0, 0, 0, ARG_DB|ARG_MAGIC,
                &syncrepl_config, "( OLcfgDbAt:0.11 NAME 'olcSyncrepl' "
-                       "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+                       "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
        { "threads", "count", 2, 2, 0,
 #ifdef NO_THREADS
                ARG_IGNORED, NULL,
@@ -760,8 +760,6 @@ typedef struct ServerID {
        int si_num;
 } ServerID;
 
-#define SERVERID_MAX   4095
-
 static ServerID *sid_list;
 
 static int
@@ -1574,7 +1572,7 @@ config_generic(ConfigArgs *c) {
                                ServerID *si, **sip;
                                LDAPURLDesc *lud;
                                int num = atoi( c->argv[1] );
-                               if ( num < 0 || num > SERVERID_MAX ) {
+                               if ( num < 0 || num > SLAP_SYNC_SID_MAX ) {
                                        snprintf( c->msg, sizeof( c->msg ),
                                                "<%s> illegal server ID", c->argv[0] );
                                        Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
@@ -1636,7 +1634,7 @@ config_generic(ConfigArgs *c) {
                                                for ( i=0; l[i]; i++ ) {
                                                        LDAPURLDesc *lu2;
                                                        int isMe = 0;
-                                                       ldap_url_parse( &l[i]->sl_url, &lu2 );
+                                                       ldap_url_parse( l[i]->sl_url.bv_val, &lu2 );
                                                        do {
                                                                if ( strcasecmp( lud->lud_scheme,
                                                                        lu2->lud_scheme ))
index f1e6f318dfa44637d3d6724063d5d1f43f350cf1..652622d0c9f6faa6df18b479a2972fe31e6f4ff0 100644 (file)
@@ -34,34 +34,47 @@ void
 slap_compose_sync_cookie(
        Operation *op,
        struct berval *cookie,
-       struct berval *csn,
+       BerVarray csn,
        int rid )
 {
-       char cookiestr[ LDAP_LUTIL_CSNSTR_BUFSIZE + 20 ];
-       int len;
+       int len, numcsn = 0;
 
-       if ( BER_BVISNULL( csn )) {
+       if ( csn ) {
+               for (; !BER_BVISEMPTY( &csn[numcsn] ); numcsn++);
+       }
+
+       if ( numcsn == 0 || rid == -1 ) {
+               char cookiestr[ LDAP_LUTIL_CSNSTR_BUFSIZE + 20 ];
                if ( rid == -1 ) {
                        cookiestr[0] = '\0';
                        len = 0;
                } else {
-                       len = snprintf( cookiestr, LDAP_LUTIL_CSNSTR_BUFSIZE + 20,
+                       len = snprintf( cookiestr, sizeof( cookiestr ),
                                        "rid=%03d", rid );
                }
+               ber_str2bv_x( cookiestr, len, 1, cookie, 
+                       op ? op->o_tmpmemctx : NULL );
        } else {
-               char *end = cookiestr + sizeof(cookiestr);
-               char *ptr = lutil_strcopy( cookiestr, "csn=" );
-               len = csn->bv_len;
-               if ( ptr + len >= end )
-                       len = end - ptr;
-               ptr = lutil_strncopy( ptr, csn->bv_val, len );
-               if ( rid != -1 && ptr < end - STRLENOF(",rid=xxx") ) {
-                       ptr += sprintf( ptr, ",rid=%03d", rid );
+               char *ptr;
+               int i;
+
+               len = 0;
+               for ( i=0; i<numcsn; i++)
+                       len += csn[i].bv_len + 1;
+
+               len += STRLENOF("rid=123,csn=");
+               cookie->bv_val = slap_sl_malloc( len, op ? op->o_tmpmemctx : NULL );
+
+               len = sprintf( cookie->bv_val, "rid=%03d,csn=", rid );
+               ptr = cookie->bv_val + len;
+               for ( i=0; i<numcsn; i++) {
+                       ptr = lutil_strncopy( ptr, csn->bv_val, csn->bv_len );
+                       *ptr++ = ';';
                }
-               len = ptr - cookiestr;
+               ptr--;
+               *ptr = '\0';
+               cookie->bv_len = ptr - cookie->bv_val;
        }
-       ber_str2bv_x( cookiestr, len, 1, cookie, 
-               op ? op->o_tmpmemctx : NULL );
 }
 
 void
@@ -73,11 +86,16 @@ slap_sync_cookie_free(
        if ( cookie == NULL )
                return;
 
-       if ( !BER_BVISNULL( &cookie->ctxcsn )) {
-               ch_free( cookie->ctxcsn.bv_val );
-               BER_BVZERO( &cookie->ctxcsn );
+       if ( cookie->sids ) {
+               ch_free( cookie->sids );
+               cookie->sids = NULL;
        }
 
+       if ( cookie->ctxcsn ) {
+               ber_bvarray_free( cookie->ctxcsn );
+               cookie->ctxcsn = NULL;
+       }
+       cookie->numcsns = 0;
        if ( !BER_BVISNULL( &cookie->octet_str )) {
                ch_free( cookie->octet_str.bv_val );
                BER_BVZERO( &cookie->octet_str );
@@ -90,6 +108,37 @@ slap_sync_cookie_free(
        return;
 }
 
+int
+slap_parse_csn_sid( struct berval *csn )
+{
+       char *p, *q;
+       int i;
+
+       p = memchr( csn->bv_val, '#', csn->bv_len );
+       if ( p )
+               p = strchr( p+1, '#' );
+       if ( !p )
+               return -1;
+       p++;
+       i = strtoul( p, &q, 10 );
+       if ( p == q || i > SLAP_SYNC_SID_MAX )
+               i = -1;
+       return i;
+}
+
+int *
+slap_parse_csn_sids( BerVarray csns, int numcsns )
+{
+       int i, *ret;
+       char *p, *q;
+
+       ret = ch_malloc( numcsns * sizeof(int) );
+       for ( i=0; i<numcsns; i++ ) {
+               ret[i] = slap_parse_csn_sid( &csns[i] );
+       }
+       return ret;
+}
+
 int
 slap_parse_sync_cookie(
        struct sync_cookie *cookie,
@@ -99,10 +148,10 @@ slap_parse_sync_cookie(
        char *csn_ptr;
        char *csn_str;
        int csn_str_len;
-       int valid = 0;
        char *rid_ptr;
        char *cval;
-       char *next;
+       char *next, *end;
+       AttributeDescription *ad = slap_schema.si_ad_modifyTimestamp;
 
        if ( cookie == NULL )
                return -1;
@@ -111,60 +160,76 @@ slap_parse_sync_cookie(
                return -1;
 
        cookie->rid = -1;
-       /* FIXME: may read past end of cookie->octet_str.bv_val */
-       rid_ptr = strstr( cookie->octet_str.bv_val, "rid=" );
-       if ( rid_ptr == NULL 
-               || rid_ptr > &cookie->octet_str.bv_val[ cookie->octet_str.bv_len - STRLENOF( "rid=" ) ] )
-       {
-               return -1;
-       }
-
-       if ( rid_ptr[ STRLENOF( "rid=" ) ] == '-' ) {
-               return -1;
-       }
-       cookie->rid = strtoul( &rid_ptr[ STRLENOF( "rid=" ) ], &next, 10 );
-       if ( next == &rid_ptr[ STRLENOF( "rid=" ) ] || ( next[ 0 ] != ',' && next[ 0 ] != '\0' ) ) {
-               return -1;
-       }
-
-       while (( csn_ptr = strstr( cookie->octet_str.bv_val, "csn=" )) != NULL ) {
-               AttributeDescription *ad = slap_schema.si_ad_modifyTimestamp;
-               slap_syntax_validate_func *validate;
-               struct berval stamp;
-
-               /* This only happens when called from main */
-               if ( ad == NULL )
-                       break;
-
-               if ( csn_ptr >= &cookie->octet_str.bv_val[ cookie->octet_str.bv_len - STRLENOF( "csn=" ) ] ) {
-                       return -1;
+       cookie->ctxcsn = NULL;
+       cookie->sids = NULL;
+       cookie->numcsns = 0;
+
+       end = cookie->octet_str.bv_val + cookie->octet_str.bv_len;
+
+       for ( next=cookie->octet_str.bv_val; next < end; ) {
+               if ( !strncmp( next, "rid=", STRLENOF("rid=") )) {
+                       rid_ptr = next;
+                       cookie->rid = strtoul( &rid_ptr[ STRLENOF( "rid=" ) ], &next, 10 );
+                       if ( next == rid_ptr || next > end || *next != ',' ) {
+                               return -1;
+                       }
+                       if ( *next == ',' ) {
+                               next++;
+                       }
+                       if ( !ad ) {
+                               break;
+                       }
+                       continue;
                }
-
-               csn_str = csn_ptr + STRLENOF("csn=");
-               cval = strchr( csn_str, ',' );
-               if ( cval && cval < &cookie->octet_str.bv_val[ cookie->octet_str.bv_len ] )
-                       csn_str_len = cval - csn_str;
-               else
-                       csn_str_len = 0;
-
-               /* FIXME use csnValidate when it gets implemented */
-               csn_ptr = strchr( csn_str, '#' );
-               if ( !csn_ptr || csn_str >= &cookie->octet_str.bv_val[ cookie->octet_str.bv_len ] ) break;
-
-               stamp.bv_val = csn_str;
-               stamp.bv_len = csn_ptr - csn_str;
-               validate = ad->ad_type->sat_syntax->ssyn_validate;
-               if ( validate( ad->ad_type->sat_syntax, &stamp ) != LDAP_SUCCESS )
-                       break;
-               valid = 1;
-               break;
+               if ( !strncmp( next, "csn=", STRLENOF("csn=") )) {
+                       slap_syntax_validate_func *validate;
+                       struct berval stamp;
+
+                       csn_str = next + STRLENOF("csn=");
+                       while ( next < end ) {
+                               /* FIXME use csnValidate when it gets implemented */
+                               csn_ptr = strchr( csn_str, '#' );
+                               if ( !csn_ptr || csn_ptr > end )
+                                       break;
+                               /* ad will be NULL when called from main. we just
+                                * want to parse the rid then. But we still iterate
+                                * through the string to find the end.
+                                */
+                               if ( ad ) {
+                                       stamp.bv_val = csn_str;
+                                       stamp.bv_len = csn_ptr - csn_str;
+                                       validate = ad->ad_type->sat_syntax->ssyn_validate;
+                                       if ( validate( ad->ad_type->sat_syntax, &stamp )
+                                               != LDAP_SUCCESS )
+                                               break;
+                               }
+                               cval = strchr( csn_ptr, ';' );
+                               if ( !cval )
+                                       cval = strchr(csn_ptr, ',' );
+                               if ( cval )
+                                       stamp.bv_len = cval - csn_str;
+                               else
+                                       stamp.bv_len = end - csn_str;
+                               if ( ad ) {
+                                       value_add_one( &cookie->ctxcsn, &stamp );
+                                       cookie->numcsns++;
+                               }
+                               if ( cval ) {
+                                       next = cval + 1;
+                                       if ( *cval != ';' )
+                                               break;
+                               } else {
+                                       next = end;
+                                       break;
+                               }
+                       }
+                       continue;
+               }
+               next++;
        }
-       if ( valid ) {
-               ber_str2bv_x( csn_str, csn_str_len, 1, &cookie->ctxcsn, memctx );
-       } else {
-               BER_BVZERO( &cookie->ctxcsn );
+       if ( cookie->numcsns ) {
+               cookie->sids = slap_parse_csn_sids( cookie->ctxcsn, cookie->numcsns );
        }
-
        return 0;
 }
 
@@ -189,7 +254,9 @@ slap_init_sync_cookie_ctxcsn(
 
        ctxcsn.bv_val = octet_str.bv_val + 4;
        ctxcsn.bv_len = octet_str.bv_len - 4;
-       ber_dupbv( &cookie->ctxcsn, &ctxcsn );
+       cookie->ctxcsn = NULL;
+       value_add_one( &cookie->ctxcsn, &ctxcsn );
+       cookie->numcsns = 1;
 
        return 0;
 }
@@ -201,14 +268,16 @@ slap_dup_sync_cookie(
 )
 {
        struct sync_cookie *new;
+       int i;
 
        if ( src == NULL )
                return NULL;
 
        if ( dst ) {
-               ch_free( dst->ctxcsn.bv_val );
+               ber_bvarray_free( dst->ctxcsn );
+               dst->ctxcsn = NULL;
+               dst->sids = NULL;
                ch_free( dst->octet_str.bv_val );
-               BER_BVZERO( &dst->ctxcsn );
                BER_BVZERO( &dst->octet_str );
                new = dst;
        } else {
@@ -217,9 +286,18 @@ slap_dup_sync_cookie(
        }
 
        new->rid = src->rid;
-
-       if ( !BER_BVISNULL( &src->ctxcsn )) {
-               ber_dupbv( &new->ctxcsn, &src->ctxcsn );
+       new->numcsns = src->numcsns;
+
+       if ( src->numcsns ) {
+               if ( ber_bvarray_dup_x( &new->ctxcsn, src->ctxcsn, NULL )) {
+                       if ( !dst ) {
+                               ch_free( new );
+                       }
+                       return NULL;
+               }
+               new->sids = ch_malloc( src->numcsns * sizeof(int) );
+               for (i=0; i<src->numcsns; i++)
+                       new->sids[i] = src->sids[i];
        }
 
        if ( !BER_BVISNULL( &src->octet_str )) {
index cda82aa0fb400b16e8a3990facd558ab73e72e96..c7ad1417b88d4c3d7af1bb5f2ab30115a2105c10 100644 (file)
@@ -103,6 +103,7 @@ typedef struct slog_entry {
        struct slog_entry *se_next;
        struct berval se_uuid;
        struct berval se_csn;
+       int     se_sid;
        ber_tag_t       se_tag;
 } slog_entry;
 
@@ -118,7 +119,9 @@ typedef struct sessionlog {
 /* The main state for this overlay */
 typedef struct syncprov_info_t {
        syncops         *si_ops;
-       struct berval   si_ctxcsn;      /* ldapsync context */
+       BerVarray       si_ctxcsn;      /* ldapsync context */
+       int             *si_sids;
+       int             si_numcsns;
        int             si_chkops;      /* checkpointing info */
        int             si_chktime;
        int             si_numops;      /* number of ops since last checkpoint */
@@ -130,7 +133,6 @@ typedef struct syncprov_info_t {
        ldap_pvt_thread_mutex_t si_csn_mutex;
        ldap_pvt_thread_mutex_t si_ops_mutex;
        ldap_pvt_thread_mutex_t si_mods_mutex;
-       char            si_ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
 } syncprov_info_t;
 
 typedef struct opcookie {
@@ -590,13 +592,10 @@ syncprov_findcsn( Operation *op, find_csn_t mode )
        sync_control *srs = NULL;
        struct slap_limits_set fc_limits;
        int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
+       int maxid = 0;
 
        if ( mode != FIND_MAXCSN ) {
                srs = op->o_controls[slap_cids.sc_LDAPsync];
-
-               if ( srs->sr_state.ctxcsn.bv_len >= LDAP_LUTIL_CSNSTR_BUFSIZE ) {
-                       return LDAP_OTHER;
-               }
        }
 
        fop = *op;
@@ -606,6 +605,7 @@ syncprov_findcsn( Operation *op, find_csn_t mode )
 
        cf.f_ava = &eq;
        cf.f_av_desc = slap_schema.si_ad_entryCSN;
+       BER_BVZERO( &cf.f_av_value );
        cf.f_next = NULL;
 
        fop.o_callback = &cb;
@@ -618,7 +618,14 @@ again:
        switch( mode ) {
        case FIND_MAXCSN:
                cf.f_choice = LDAP_FILTER_GE;
-               cf.f_av_value = si->si_ctxcsn;
+               cf.f_av_value = si->si_ctxcsn[0];
+               /* If there are multiple CSNs, use the largest */
+               for ( i=1; i<si->si_numcsns; i++) {
+                       if ( ber_bvcmp( &cf.f_av_value, &si->si_ctxcsn[i] ) < 0 ) {
+                               cf.f_av_value = si->si_ctxcsn[i];
+                               maxid = i;
+                       }
+               }
                fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN>=%s)",
                        cf.f_av_value.bv_val );
                fop.ors_attrsonly = 0;
@@ -626,12 +633,21 @@ again:
                fop.ors_slimit = SLAP_NO_LIMIT;
                cb.sc_private = &maxcsn;
                cb.sc_response = findmax_cb;
-               strcpy( cbuf, si->si_ctxcsn.bv_val );
+               strcpy( cbuf, cf.f_av_value.bv_val );
                maxcsn.bv_val = cbuf;
-               maxcsn.bv_len = si->si_ctxcsn.bv_len;
+               maxcsn.bv_len = cf.f_av_value.bv_len;
                break;
        case FIND_CSN:
-               cf.f_av_value = srs->sr_state.ctxcsn;
+               if ( BER_BVISEMPTY( &cf.f_av_value )) {
+                       cf.f_av_value = srs->sr_state.ctxcsn[0];
+                       /* If there are multiple CSNs, use the smallest */
+                       for ( i=1; i<srs->sr_state.numcsns; i++ ) {
+                               if ( ber_bvcmp( &cf.f_av_value, &srs->sr_state.ctxcsn[i] )
+                                       > 0 ) {
+                                       cf.f_av_value = srs->sr_state.ctxcsn[i];
+                               }
+                       }
+               }
                /* Look for exact match the first time */
                if ( findcsn_retry ) {
                        cf.f_choice = LDAP_FILTER_EQUALITY;
@@ -681,8 +697,10 @@ again:
 
        switch( mode ) {
        case FIND_MAXCSN:
-               strcpy( si->si_ctxcsnbuf, maxcsn.bv_val );
-               si->si_ctxcsn.bv_len = maxcsn.bv_len;
+               if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
+                       ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
+                       si->si_numops++;        /* ensure a checkpoint */
+               }
                break;
        case FIND_CSN:
                /* If matching CSN was not found, invalidate the context. */
@@ -741,7 +759,7 @@ syncprov_sendresp( Operation *op, opcookie *opc, syncops *so,
 
        SlapReply rs = { REP_SEARCH };
        LDAPControl *ctrls[2];
-       struct berval cookie;
+       struct berval cookie, csns[2];
        Entry e_uuid = {0};
        Attribute a_uuid = {0};
 
@@ -749,7 +767,9 @@ syncprov_sendresp( Operation *op, opcookie *opc, syncops *so,
                return SLAPD_ABANDON;
 
        ctrls[1] = NULL;
-       slap_compose_sync_cookie( op, &cookie, &opc->sctxcsn, so->s_rid );
+       csns[0] = opc->sctxcsn;
+       BER_BVZERO( &csns[1] );
+       slap_compose_sync_cookie( op, &cookie, csns, so->s_rid );
 
        e_uuid.e_attrs = &a_uuid;
        a_uuid.a_desc = slap_schema.si_ad_entryUUID;
@@ -1256,23 +1276,15 @@ syncprov_op_cleanup( Operation *op, SlapReply *rs )
 }
 
 static void
-syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on,
-       struct berval *csn )
+syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on )
 {
+    syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
        Modifications mod;
        Operation opm;
        SlapReply rsm = { 0 };
-       struct berval bv[2];
        slap_callback cb = {0};
 
-       /* If ctxcsn is empty, delete it */
-       if ( BER_BVISEMPTY( csn )) {
-               mod.sml_values = NULL;
-       } else {
-               mod.sml_values = bv;
-               bv[1].bv_val = NULL;
-               bv[0] = *csn;
-       }
+       mod.sml_values = si->si_ctxcsn;
        mod.sml_nvalues = NULL;
        mod.sml_desc = slap_schema.si_ad_contextCSN;
        mod.sml_op = LDAP_MOD_REPLACE;
@@ -1284,13 +1296,12 @@ syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on,
        opm.o_tag = LDAP_REQ_MODIFY;
        opm.o_callback = &cb;
        opm.orm_modlist = &mod;
+       opm.orm_no_opattrs = 1;
        opm.o_req_dn = op->o_bd->be_suffix[0];
        opm.o_req_ndn = op->o_bd->be_nsuffix[0];
        opm.o_bd->bd_info = on->on_info->oi_orig;
        opm.o_managedsait = SLAP_CONTROL_NONCRITICAL;
-       SLAP_DBFLAGS( opm.o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
        opm.o_bd->be_modify( &opm, &rsm );
-       SLAP_DBFLAGS( opm.o_bd ) ^= SLAP_DBFLAG_NOLASTMOD;
        if ( mod.sml_next != NULL ) {
                slap_mods_free( mod.sml_next, 1 );
        }
@@ -1321,6 +1332,7 @@ syncprov_add_slog( Operation *op )
                AC_MEMCPY( se->se_csn.bv_val, op->o_csn.bv_val, op->o_csn.bv_len );
                se->se_csn.bv_val[op->o_csn.bv_len] = '\0';
                se->se_csn.bv_len = op->o_csn.bv_len;
+               se->se_sid = slap_parse_csn_sid( &se->se_csn );
 
                ldap_pvt_thread_mutex_lock( &sl->sl_mutex );
                if ( sl->sl_head ) {
@@ -1355,14 +1367,14 @@ playlog_cb( Operation *op, SlapReply *rs )
 /* enter with sl->sl_mutex locked, release before returning */
 static void
 syncprov_playlog( Operation *op, SlapReply *rs, sessionlog *sl,
-       sync_control *srs, struct berval *ctxcsn )
+       sync_control *srs, BerVarray ctxcsn, int numcsns, int *sids )
 {
        slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
        slog_entry *se;
        int i, j, ndel, num, nmods, mmods;
        char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
        BerVarray uuids;
-       struct berval delcsn;
+       struct berval delcsn[2];
 
        if ( !sl->sl_num ) {
                ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
@@ -1377,35 +1389,47 @@ syncprov_playlog( Operation *op, SlapReply *rs, sessionlog *sl,
                num * UUID_LEN, op->o_tmpmemctx );
        uuids[0].bv_val = (char *)(uuids + num + 1);
 
-       delcsn.bv_len = 0;
-       delcsn.bv_val = cbuf;
+       delcsn[0].bv_len = 0;
+       delcsn[0].bv_val = cbuf;
+       BER_BVZERO(&delcsn[1]);
 
        /* Make a copy of the relevant UUIDs. Put the Deletes up front
         * and everything else at the end. Do this first so we can
         * unlock the list mutex.
         */
-        Debug( LDAP_DEBUG_SYNC, "srs csn %s\n", srs-> sr_state.ctxcsn.bv_val, 0, 0 );
+       Debug( LDAP_DEBUG_SYNC, "srs csn %s\n",
+               srs->sr_state.ctxcsn[0].bv_val, 0, 0 );
        for ( se=sl->sl_head; se; se=se->se_next ) {
-                Debug( LDAP_DEBUG_SYNC, "log csn %s\n", se-> se_csn.bv_val,
-0, 0 );
-                ndel = ber_bvcmp( &se->se_csn, &srs->sr_state.ctxcsn );
-                if ( ndel <= 0 ) {
-                        Debug( LDAP_DEBUG_SYNC, "cmp %d, too old\n", ndel,
-0, 0 );
-                        continue;
-                }
-                ndel = ber_bvcmp( &se->se_csn, ctxcsn );
-                if ( ndel > 0 ) {
-                        Debug( LDAP_DEBUG_SYNC, "cmp %d, too new\n", ndel,
-0, 0 );
-                        break;
-                }
+               int k;
+               Debug( LDAP_DEBUG_SYNC, "log csn %s\n", se->se_csn.bv_val, 0, 0 );
+               ndel = 1;
+               for ( k=0; k<srs->sr_state.numcsns; k++ ) {
+                       if ( se->se_sid == srs->sr_state.sids[k] ) {
+                               ndel = ber_bvcmp( &se->se_csn, &srs->sr_state.ctxcsn[k] );
+                               break;
+                       }
+               }
+               if ( ndel <= 0 ) {
+                       Debug( LDAP_DEBUG_SYNC, "cmp %d, too old\n", ndel, 0, 0 );
+                       continue;
+               }
+               ndel = 0;
+               for ( k=0; k<numcsns; k++ ) {
+                       if ( se->se_sid == sids[k] ) {
+                               ndel = ber_bvcmp( &se->se_csn, &ctxcsn[k] );
+                               break;
+                       }
+               }
+               if ( ndel > 0 ) {
+                       Debug( LDAP_DEBUG_SYNC, "cmp %d, too new\n", ndel, 0, 0 );
+                       break;
+               }
                if ( se->se_tag == LDAP_REQ_DELETE ) {
                        j = i;
                        i++;
                        AC_MEMCPY( cbuf, se->se_csn.bv_val, se->se_csn.bv_len );
-                       delcsn.bv_len = se->se_csn.bv_len;
-                       delcsn.bv_val[delcsn.bv_len] = '\0';
+                       delcsn[0].bv_len = se->se_csn.bv_len;
+                       delcsn[0].bv_val[delcsn[0].bv_len] = '\0';
                } else {
                        nmods++;
                        j = num - nmods;
@@ -1499,7 +1523,7 @@ syncprov_playlog( Operation *op, SlapReply *rs, sessionlog *sl,
        if ( ndel ) {
                struct berval cookie;
 
-               slap_compose_sync_cookie( op, &cookie, &delcsn, srs->sr_state.rid );
+               slap_compose_sync_cookie( op, &cookie, delcsn, srs->sr_state.rid );
                uuids[ndel].bv_val = NULL;
                syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, &cookie, 0, uuids, 1 );
                op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
@@ -1526,10 +1550,24 @@ syncprov_op_response( Operation *op, SlapReply *rs )
                ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
                slap_get_commit_csn( op, &maxcsn );
                if ( !BER_BVISNULL( &maxcsn ) ) {
+                       int i, sid;
                        strcpy( cbuf, maxcsn.bv_val );
-                       if ( ber_bvcmp( &maxcsn, &si->si_ctxcsn ) > 0 ) {
-                               strcpy( si->si_ctxcsnbuf, cbuf );
-                               si->si_ctxcsn.bv_len = maxcsn.bv_len;
+                       sid = slap_parse_csn_sid( &maxcsn );
+                       for ( i=0; i<si->si_numcsns; i++ ) {
+                               if ( sid == si->si_sids[i] ) {
+                                       if ( ber_bvcmp( &maxcsn, &si->si_ctxcsn[i] ) > 0 ) {
+                                               ber_bvreplace( &si->si_ctxcsn[i], &maxcsn );
+                                       }
+                                       break;
+                               }
+                       }
+                       /* It's a new SID for us */
+                       if ( i == si->si_numcsns ) {
+                               value_add_one( &si->si_ctxcsn, &maxcsn );
+                               si->si_numcsns++;
+                               si->si_sids = ch_realloc( si->si_sids, si->si_numcsns *
+                                       sizeof(int));
+                               si->si_sids[i] = sid;
                        }
                }
 
@@ -1558,7 +1596,7 @@ syncprov_op_response( Operation *op, SlapReply *rs )
                opc->sctxcsn.bv_val = cbuf;
 
                if ( do_check ) {
-                       syncprov_checkpoint( op, rs, on, &opc->sctxcsn );
+                       syncprov_checkpoint( op, rs, on );
                }
 
                /* Handle any persistent searches */
@@ -1608,20 +1646,18 @@ syncprov_op_compare( Operation *op, SlapReply *rs )
        {
                Entry e = {0};
                Attribute a = {0};
-               struct berval bv[2];
 
                e.e_name = op->o_bd->be_suffix[0];
                e.e_nname = op->o_bd->be_nsuffix[0];
-
-               BER_BVZERO( &bv[1] );
-               bv[0] = si->si_ctxcsn;
+               e.e_attrs = &a;
 
                a.a_desc = slap_schema.si_ad_contextCSN;
-               a.a_vals = bv;
-               a.a_nvals = a.a_vals;
 
                ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
 
+               a.a_vals = si->si_ctxcsn;
+               a.a_nvals = a.a_vals;
+
                rs->sr_err = access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
                        &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
                if ( ! rs->sr_err ) {
@@ -1748,9 +1784,10 @@ syncprov_op_extended( Operation *op, SlapReply *rs )
 typedef struct searchstate {
        slap_overinst *ss_on;
        syncops *ss_so;
+       BerVarray ss_ctxcsn;
+       int *ss_sids;
+       int ss_numcsns;
        int ss_present;
-       struct berval ss_ctxcsn;
-       char ss_csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
 } searchstate;
 
 static int
@@ -1879,23 +1916,36 @@ syncprov_search_response( Operation *op, SlapReply *rs )
                        a = attr_find( rs->sr_operational_attrs, slap_schema.si_ad_entryCSN );
                }
                if ( a ) {
+                       int i, sid;
+                       sid = slap_parse_csn_sid( &a->a_nvals[0] );
+
                        /* Make sure entry is less than the snapshot'd contextCSN */
-                       if ( ber_bvcmp( &a->a_nvals[0], &ss->ss_ctxcsn ) > 0 ) {
-                               Debug( LDAP_DEBUG_SYNC, "Entry %s CSN %s greater than snapshot %s\n",
-                                       rs->sr_entry->e_name.bv_val,
-                                       a->a_nvals[0].bv_val,
-                                       ss->ss_ctxcsn.bv_val );
-                               return LDAP_SUCCESS;
+                       for ( i=0; i<ss->ss_numcsns; i++ ) {
+                               if ( sid == ss->ss_sids[i] && ber_bvcmp( &a->a_nvals[0],
+                                       &ss->ss_ctxcsn[i] ) > 0 ) {
+                                       Debug( LDAP_DEBUG_SYNC,
+                                               "Entry %s CSN %s greater than snapshot %s\n",
+                                               rs->sr_entry->e_name.bv_val,
+                                               a->a_nvals[0].bv_val,
+                                               ss->ss_ctxcsn[i].bv_val );
+                                       return LDAP_SUCCESS;
+                               }
                        }
 
-                       /* Don't send the ctx entry twice */
-                       if ( !BER_BVISNULL( &srs->sr_state.ctxcsn ) &&
-                               bvmatch( &a->a_nvals[0], &srs->sr_state.ctxcsn ) ) {
-                               Debug( LDAP_DEBUG_SYNC, "Entry %s CSN %s matches ctx %s\n",
-                                       rs->sr_entry->e_name.bv_val,
-                                       a->a_nvals[0].bv_val,
-                                       srs->sr_state.ctxcsn.bv_val );
-                               return LDAP_SUCCESS;
+                       /* Don't send old entries twice */
+                       if ( srs->sr_state.ctxcsn ) {
+                               for ( i=0; i<srs->sr_state.numcsns; i++ ) {
+                                       if ( sid == srs->sr_state.sids[i] &&
+                                               ber_bvcmp( &a->a_nvals[0],
+                                                       &srs->sr_state.ctxcsn[i] )<= 0 ) {
+                                               Debug( LDAP_DEBUG_SYNC,
+                                                       "Entry %s CSN %s older or equal to ctx %s\n",
+                                                       rs->sr_entry->e_name.bv_val,
+                                                       a->a_nvals[0].bv_val,
+                                                       srs->sr_state.ctxcsn[i].bv_val );
+                                               return LDAP_SUCCESS;
+                                       }
+                               }
                        }
                }
                rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
@@ -1906,7 +1956,7 @@ syncprov_search_response( Operation *op, SlapReply *rs )
        } else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
                struct berval cookie;
 
-               slap_compose_sync_cookie( op, &cookie, &ss->ss_ctxcsn,
+               slap_compose_sync_cookie( op, &cookie, ss->ss_ctxcsn,
                        srs->sr_state.rid );
 
                /* Is this a regular refresh? */
@@ -1955,8 +2005,9 @@ syncprov_op_search( Operation *op, SlapReply *rs )
        syncops *sop = NULL;
        searchstate *ss;
        sync_control *srs;
-       struct berval ctxcsn;
-       char csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
+       BerVarray ctxcsn;
+       int i, *sids, numcsns;
+       struct berval mincsn;
 
        if ( !(op->o_sync_mode & SLAP_SYNC_REFRESH) ) return SLAP_CB_CONTINUE;
 
@@ -2010,34 +2061,77 @@ syncprov_op_search( Operation *op, SlapReply *rs )
 
        /* snapshot the ctxcsn */
        ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
-       strcpy( csnbuf, si->si_ctxcsnbuf );
-       ctxcsn.bv_len = si->si_ctxcsn.bv_len;
+       ber_bvarray_dup_x( &ctxcsn, si->si_ctxcsn, op->o_tmpmemctx );
+       numcsns = si->si_numcsns;
+       sids = op->o_tmpalloc( numcsns * sizeof(int), op->o_tmpmemctx );
+       for ( i=0; i<numcsns; i++ )
+               sids[i] = si->si_sids[i];
        ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
-       ctxcsn.bv_val = csnbuf;
        
        /* If we have a cookie, handle the PRESENT lookups */
-       if ( !BER_BVISNULL( &srs->sr_state.ctxcsn )) {
+       if ( srs->sr_state.ctxcsn ) {
                sessionlog *sl;
+               int i, j;
 
-               /* The cookie was validated when it was parsed, just use it */
+               /* If there are SIDs we don't recognize in the cookie, drop them */
+               for (i=0; i<srs->sr_state.numcsns; ) {
+                       for (j=0; j<numcsns; j++) {
+                               if ( srs->sr_state.sids[i] == sids[j] ) {
+                                       break;
+                               }
+                       }
+                       /* not found */
+                       if ( j == numcsns ) {
+                               struct berval tmp = srs->sr_state.ctxcsn[i];
+                               j = srs->sr_state.numcsns - 1;
+                               srs->sr_state.ctxcsn[i] = srs->sr_state.ctxcsn[j];
+                               tmp.bv_len = 0;
+                               srs->sr_state.ctxcsn[j] = tmp;
+                               srs->sr_state.numcsns = j;
+                               srs->sr_state.sids[i] = srs->sr_state.sids[j];
+                               continue;
+                       }
+                       i++;
+               }
 
-               /* If just Refreshing and nothing has changed, shortcut it */
-               if ( bvmatch( &srs->sr_state.ctxcsn, &ctxcsn )) {
-                       nochange = 1;
-                       if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
-                               LDAPControl     *ctrls[2];
+               /* Find the smallest CSN */
+               mincsn = srs->sr_state.ctxcsn[0];
+               for ( i=1; i<srs->sr_state.numcsns; i++ ) {
+                       if ( ber_bvcmp( &mincsn, &srs->sr_state.ctxcsn[i] ) > 0 )
+                               mincsn = srs->sr_state.ctxcsn[i];
+               }
 
-                               ctrls[0] = NULL;
-                               ctrls[1] = NULL;
-                               syncprov_done_ctrl( op, rs, ctrls, 0, 0,
-                                       NULL, LDAP_SYNC_REFRESH_DELETES );
-                               rs->sr_ctrls = ctrls;
-                               rs->sr_err = LDAP_SUCCESS;
-                               send_ldap_result( op, rs );
-                               rs->sr_ctrls = NULL;
-                               return rs->sr_err;
+               /* If nothing has changed, shortcut it */
+               if ( srs->sr_state.numcsns == numcsns ) {
+                       int i, j, changed = 0;
+                       for ( i=0; i<srs->sr_state.numcsns; i++ ) {
+                               for ( j=0; j<numcsns; j++ ) {
+                                       if ( srs->sr_state.sids[i] != sids[j] )
+                                               continue;
+                                       if ( !bvmatch( &srs->sr_state.ctxcsn[i], &ctxcsn[j] ))
+                                               changed = 1;
+                                       break;
+                               }
+                               if ( changed )
+                                       break;
+                       }
+                       if ( !changed ) {
+                               nochange = 1;
+                               if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
+                                       LDAPControl     *ctrls[2];
+
+                                       ctrls[0] = NULL;
+                                       ctrls[1] = NULL;
+                                       syncprov_done_ctrl( op, rs, ctrls, 0, 0,
+                                               NULL, LDAP_SYNC_REFRESH_DELETES );
+                                       rs->sr_ctrls = ctrls;
+                                       rs->sr_err = LDAP_SUCCESS;
+                                       send_ldap_result( op, rs );
+                                       rs->sr_ctrls = NULL;
+                                       return rs->sr_err;
+                               }
+                               goto shortcut;
                        }
-                       goto shortcut;
                }
                /* Do we have a sessionlog for this search? */
                sl=si->si_logs;
@@ -2046,10 +2140,10 @@ syncprov_op_search( Operation *op, SlapReply *rs )
                        /* Are there any log entries, and is the consumer state
                         * present in the session log?
                         */
-                       if ( sl->sl_num > 0 && ber_bvcmp( &srs->sr_state.ctxcsn, &sl->sl_mincsn ) >= 0 ) {
+                       if ( sl->sl_num > 0 && ber_bvcmp( &mincsn, &sl->sl_mincsn ) >= 0 ) {
                                do_present = 0;
                                /* mutex is unlocked in playlog */
-                               syncprov_playlog( op, rs, sl, srs, &ctxcsn );
+                               syncprov_playlog( op, rs, sl, srs, ctxcsn, numcsns, sids );
                        } else {
                                ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
                        }
@@ -2096,7 +2190,7 @@ shortcut:
 #ifdef LDAP_COMP_MATCH
                fava->f_ava->aa_cf = NULL;
 #endif
-               ber_dupbv_x( &fava->f_ava->aa_value, &srs->sr_state.ctxcsn, op->o_tmpmemctx );
+               ber_dupbv_x( &fava->f_ava->aa_value, &mincsn, op->o_tmpmemctx );
                fava->f_next = op->ors_filter;
                op->ors_filter = fand;
                filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
@@ -2110,19 +2204,15 @@ shortcut:
        ss->ss_on = on;
        ss->ss_so = sop;
        ss->ss_present = do_present;
-       ss->ss_ctxcsn.bv_len = ctxcsn.bv_len;
-       ss->ss_ctxcsn.bv_val = ss->ss_csnbuf;
-       strcpy( ss->ss_ctxcsn.bv_val, ctxcsn.bv_val );
+       ss->ss_ctxcsn = ctxcsn;
+       ss->ss_numcsns = numcsns;
+       ss->ss_sids = sids;
        cb->sc_response = syncprov_search_response;
        cb->sc_cleanup = syncprov_search_cleanup;
        cb->sc_private = ss;
        cb->sc_next = op->o_callback;
        op->o_callback = cb;
 
-#if 0  /* I don't think we need to shortcircuit back-bdb any more */
-       op->o_sync_mode &= SLAP_CONTROL_MASK;
-#endif
-
        /* If this is a persistent search and no changes were reported during
         * the refresh phase, just invoke the response callback to transition
         * us into persist phase
@@ -2156,21 +2246,28 @@ syncprov_operational(
                                        break;
                        }
 
-                       if ( !a ) {
-                               for ( ap = &rs->sr_operational_attrs; *ap; ap=&(*ap)->a_next );
+                       ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
+                       if ( si->si_ctxcsn ) {
+                               if ( !a ) {
+                                       for ( ap = &rs->sr_operational_attrs; *ap;
+                                               ap=&(*ap)->a_next );
 
-                               a = attr_alloc( slap_schema.si_ad_contextCSN );
-                               a->a_vals = ch_malloc( 2 * sizeof(struct berval));
-                               a->a_vals[1].bv_val = NULL;
-                               a->a_nvals = a->a_vals;
-                               *ap = a;
-                       }
+                                       a = attr_alloc( slap_schema.si_ad_contextCSN );
+                                       *ap = a;
+                               }
 
-                       ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
-                       if ( !ap ) {
-                               strcpy( a->a_vals[0].bv_val, si->si_ctxcsnbuf );
-                       } else {
-                               ber_dupbv( &a->a_vals[0], &si->si_ctxcsn );
+                               if ( !ap ) {
+                                       if ( !rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
+                                               rs->sr_entry = entry_dup( rs->sr_entry );
+                                               rs->sr_flags |=
+                                                       REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+                                               a = attr_find( rs->sr_entry->e_attrs,
+                                                       slap_schema.si_ad_contextCSN );
+                                       }
+                                       free( a->a_vals );
+                               }
+                               ber_bvarray_dup_x( &a->a_vals, si->si_ctxcsn, NULL );
+                               a->a_nvals = a->a_vals;
                        }
                        ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
                }
@@ -2379,7 +2476,6 @@ syncprov_db_open(
 
        Connection conn = { 0 };
        OperationBuffer opbuf = { 0 };
-       char ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
        Operation *op = (Operation *) &opbuf;
        Entry *e;
        Attribute *a;
@@ -2407,8 +2503,6 @@ syncprov_db_open(
        op->o_dn = be->be_rootdn;
        op->o_ndn = be->be_rootndn;
 
-       ctxcsnbuf[0] = '\0';
-
        op->o_bd->bd_info = on->on_info->oi_orig;
        rc = be_entry_get_rw( op, be->be_nsuffix, NULL,
                slap_schema.si_ad_contextCSN, 0, &e );
@@ -2418,16 +2512,14 @@ syncprov_db_open(
 
                a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
                if ( a ) {
-                       si->si_ctxcsn.bv_len = a->a_nvals[0].bv_len;
-                       if ( si->si_ctxcsn.bv_len >= sizeof(si->si_ctxcsnbuf ))
-                               si->si_ctxcsn.bv_len = sizeof(si->si_ctxcsnbuf)-1;
-                       strncpy( si->si_ctxcsnbuf, a->a_nvals[0].bv_val,
-                               si->si_ctxcsn.bv_len );
-                       si->si_ctxcsnbuf[si->si_ctxcsn.bv_len] = '\0';
-                       strcpy( ctxcsnbuf, si->si_ctxcsnbuf );
+                       int i;
+                       ber_bvarray_dup_x( &si->si_ctxcsn, a->a_vals, NULL );
+                       for ( i=0; !BER_BVISEMPTY( &a->a_vals[i] ); i++ );
+                       si->si_numcsns = i;
+                       si->si_sids = slap_parse_csn_sids( si->si_ctxcsn, i );
                }
                be_entry_release_rw( op, e, 0 );
-               if ( !BER_BVISEMPTY( &si->si_ctxcsn ) ) {
+               if ( si->si_ctxcsn ) {
                        op->o_bd->bd_info = (BackendInfo *)on;
                        op->o_req_dn = be->be_suffix[0];
                        op->o_req_ndn = be->be_nsuffix[0];
@@ -2437,22 +2529,27 @@ syncprov_db_open(
                }
        }
 
-       if ( BER_BVISEMPTY( &si->si_ctxcsn ) ) {
-               if ( SLAP_SYNC_SHADOW( op->o_bd )) {
-               /* If we're also a consumer, and we didn't get a contextCSN,
+       /* Didn't find a contextCSN, should we generate one? */
+       if ( !si->si_ctxcsn ) {
+               char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
+               struct berval csn;
+
+               if ( SLAP_SYNC_SHADOW( op->o_bd ) && SLAP_SINGLE_SHADOW( op->o_bd )) {
+               /* If we're also a consumer, and we're not multimaster,
                 * then don't generate anything, wait for our provider to send it
                 * to us.
                 */
                        goto out;
                }
-               si->si_ctxcsn.bv_len = sizeof( si->si_ctxcsnbuf );
-               slap_get_csn( op, &si->si_ctxcsn, 0 );
-       }
+               csn.bv_val = csnbuf;
+               csn.bv_len = sizeof( csnbuf );
+               slap_get_csn( op, &csn, 0 );
+               value_add_one( &si->si_ctxcsn, &csn );
+               si->si_numcsns = 1;
+               si->si_sids = ch_malloc( sizeof(int) );
+               si->si_sids[0] = slap_serverID;
 
-       /* If our ctxcsn is different from what was read from the root
-        * entry, make sure we do a checkpoint on close
-        */
-       if ( strcmp( si->si_ctxcsnbuf, ctxcsnbuf )) {
+               /* make sure we do a checkpoint on close */
                si->si_numops++;
        }
 
@@ -2486,7 +2583,7 @@ syncprov_db_close(
                op->o_bd = be;
                op->o_dn = be->be_rootdn;
                op->o_ndn = be->be_rootndn;
-               syncprov_checkpoint( op, &rs, on, &si->si_ctxcsn );
+               syncprov_checkpoint( op, &rs, on );
        }
 
     return 0;
@@ -2512,7 +2609,6 @@ syncprov_db_init(
        ldap_pvt_thread_mutex_init( &si->si_csn_mutex );
        ldap_pvt_thread_mutex_init( &si->si_ops_mutex );
        ldap_pvt_thread_mutex_init( &si->si_mods_mutex );
-       si->si_ctxcsn.bv_val = si->si_ctxcsnbuf;
 
        csn_anlist[0].an_desc = slap_schema.si_ad_entryCSN;
        csn_anlist[0].an_name = slap_schema.si_ad_entryCSN->ad_cname;
index 772500add1472cca4fab8ecace2a0d0082c7d2e4..d04794faf395b8e0c04556c55d150817b87c5859 100644 (file)
@@ -1016,9 +1016,13 @@ LDAP_SLAPD_V (char *)    slap_known_controls[];
  * ldapsync.c
  */
 LDAP_SLAPD_F (void) slap_compose_sync_cookie LDAP_P((
-                               Operation *, struct berval *, struct berval *, int ));
+                               Operation *, struct berval *, BerVarray, int ));
 LDAP_SLAPD_F (void) slap_sync_cookie_free LDAP_P((
                                struct sync_cookie *, int free_cookie ));
+LDAP_SLAPD_F (int) slap_parse_csn_sid LDAP_P((
+                               struct berval * ));
+LDAP_SLAPD_F (int *) slap_parse_csn_sids LDAP_P((
+                               BerVarray, int ));
 LDAP_SLAPD_F (int) slap_parse_sync_cookie LDAP_P((
                                struct sync_cookie *, void *memctx ));
 LDAP_SLAPD_F (int) slap_init_sync_cookie_ctxcsn LDAP_P((
index cad035f1bf0572f07f1ef462c8af6505c6d9763f..43eb1be76687933f097de5ac0881260e74ef5ead 100644 (file)
@@ -603,7 +603,7 @@ static struct slap_schema_ad_map {
                        "EQUALITY CSNMatch "
                        "ORDERING CSNOrderingMatch "
                        "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} "
-                       "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
+                       "NO-USER-MODIFICATION USAGE dSAOperation )",
                NULL, SLAP_AT_HIDE,
                NULL, NULL,
                NULL, NULL, NULL, NULL, NULL,
index 3acd543545278e513e7759b5f3243cfbd38657a5..720046d25c55463e8058e159d4b3805758307416 100644 (file)
@@ -1650,14 +1650,17 @@ typedef BackendDB Backend;
 struct syncinfo_s;
 
 #define SLAP_SYNC_RID_SIZE     3
+#define SLAP_SYNC_SID_MAX      4095    /* based on liblutil/csn.c field width */
 #define SLAP_SYNCUUID_SET_SIZE 256
 
 #define        SLAP_SYNC_UPDATE_MSGID  1
 
 struct sync_cookie {
-       struct berval ctxcsn;
+       struct berval *ctxcsn;
        struct berval octet_str;
-       long rid;
+       int rid;
+       int numcsns;
+       int *sids;
        LDAP_STAILQ_ENTRY(sync_cookie) sc_next;
 };
 
index 75fcc6662aa8422c3f5e878be5536f1cbc27668f..41c45f953258cc958249677ffd5d7b8be2d01825 100644 (file)
@@ -37,6 +37,14 @@ struct nonpresent_entry {
        LDAP_LIST_ENTRY(nonpresent_entry) npe_link;
 };
 
+typedef struct cookie_state {
+       ldap_pvt_thread_mutex_t cs_mutex;
+       int     cs_num;
+       int cs_age;
+       struct berval *cs_vals;
+       int *cs_sids;
+} cookie_state;
+       
 #define        SYNCDATA_DEFAULT        0       /* entries are plain LDAP entries */
 #define        SYNCDATA_ACCESSLOG      1       /* entries are accesslog format */
 #define        SYNCDATA_CHANGELOG      2       /* entries are changelog format */
@@ -50,9 +58,11 @@ struct nonpresent_entry {
 #define RETRYNUM_FINITE(n)     ((n) > RETRYNUM_FOREVER)        /* not forever */
 
 typedef struct syncinfo_s {
+       struct syncinfo_s       *si_next;
        struct slap_backend_db *si_be;
        struct re_s                     *si_re;
-       long                            si_rid;
+       int                                     si_rid;
+       char                            si_ridtxt[8];
        slap_bindconf           si_bindconf;
        struct berval           si_base;
        struct berval           si_logbase;
@@ -75,6 +85,8 @@ typedef struct syncinfo_s {
        int                                     *si_retrynum_init;
        int                                     *si_retrynum;
        struct sync_cookie      si_syncCookie;
+       cookie_state            *si_cookieState;
+       int                                     si_cookieAge;
        int                                     si_manageDSAit;
        int                                     si_slimit;
        int                                     si_tlimit;
@@ -100,8 +112,7 @@ static int syncrepl_message_to_entry(
 static int syncrepl_entry(
                                        syncinfo_t *, Operation*, Entry*,
                                        Modifications**,int, struct berval*,
-                                       struct sync_cookie *,
-                                       struct berval * );
+                                       struct sync_cookie * );
 static int syncrepl_updateCookie(
                                        syncinfo_t *, Operation *, struct berval *,
                                        struct sync_cookie * );
@@ -330,7 +341,7 @@ ldap_sync_search(
        /* If we're using a log but we have no state, then fallback to
         * normal mode for a full refresh.
         */
-       if ( si->si_syncdata && BER_BVISEMPTY( &si->si_syncCookie.ctxcsn ) ) {
+       if ( si->si_syncdata && !si->si_syncCookie.numcsns ) {
                si->si_logstate = SYNCLOG_FALLBACK;
        }
 
@@ -417,13 +428,10 @@ do_syncrep1(
        int cmdline_cookie_found = 0;
 
        struct sync_cookie      *sc = NULL;
-       struct berval   *psub;
 #ifdef HAVE_TLS
        void    *ssl;
 #endif
 
-       psub = &si->si_be->be_nsuffix[0];
-
        rc = slap_client_connect( &si->si_ld, &si->si_bindconf );
        if ( rc != LDAP_SUCCESS ) {
                goto done;
@@ -447,21 +455,43 @@ do_syncrep1(
 
 
        if ( BER_BVISNULL( &si->si_syncCookie.octet_str ) ) {
-               /* get contextCSN shadow replica from database */
-               BerVarray csn = NULL;
-
-               assert( si->si_rid < 1000 );
-               op->o_req_ndn = op->o_bd->be_nsuffix[0];
-               op->o_req_dn = op->o_req_ndn;
+               int i;
 
-               /* try to read stored contextCSN */
-               backend_attribute( op, NULL, &op->o_req_ndn,
-                       slap_schema.si_ad_contextCSN, &csn, ACL_READ );
-               if ( csn ) {
-                       ch_free( si->si_syncCookie.ctxcsn.bv_val );
-                       ber_dupbv( &si->si_syncCookie.ctxcsn, csn );
-                       ber_bvarray_free_x( csn, op->o_tmpmemctx );
+               ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+               if ( !si->si_cookieState->cs_num ) {
+                       /* get contextCSN shadow replica from database */
+                       BerVarray csn = NULL;
+                       void *ctx = op->o_tmpmemctx;
+
+                       op->o_req_ndn = op->o_bd->be_nsuffix[0];
+                       op->o_req_dn = op->o_req_ndn;
+
+                       /* try to read stored contextCSN */
+                       op->o_tmpmemctx = NULL;
+                       backend_attribute( op, NULL, &op->o_req_ndn,
+                               slap_schema.si_ad_contextCSN, &csn, ACL_READ );
+                       op->o_tmpmemctx = ctx;
+                       if ( csn ) {
+                               si->si_cookieState->cs_vals = csn;
+                               for (i=0; !BER_BVISNULL( &csn[i] ); i++);
+                               si->si_cookieState->cs_num = i;
+                               si->si_cookieState->cs_sids = slap_parse_csn_sids( csn, i );
+                       }
+               }
+               if ( si->si_cookieState->cs_num ) {
+                       ber_bvarray_free( si->si_syncCookie.ctxcsn );
+                       if ( ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
+                               si->si_cookieState->cs_vals, NULL )) {
+                               rc = LDAP_NO_MEMORY;
+                               goto done;
+                       }
+                       si->si_syncCookie.numcsns = si->si_cookieState->cs_num;
+                       si->si_syncCookie.sids = ch_malloc( si->si_cookieState->cs_num *
+                               sizeof(int) );
+                       for ( i=0; i<si->si_syncCookie.numcsns; i++ )
+                               si->si_syncCookie.sids[i] = si->si_cookieState->cs_sids[i];
                }
+               ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
 
                si->si_syncCookie.rid = si->si_rid;
 
@@ -479,7 +509,7 @@ do_syncrep1(
 
                        /* ctxcsn wasn't parsed yet, do it now */
                        slap_parse_sync_cookie( sc, op->o_tmpmemctx );
-                       if ( BER_BVISNULL( &sc->ctxcsn ) ) {
+                       if ( !sc->ctxcsn ) {
                                /* if cmdline cookie does not have ctxcsn */
                                /* component, set it to an initial value */
                                slap_init_sync_cookie_ctxcsn( sc );
@@ -490,15 +520,41 @@ do_syncrep1(
                }
 
                slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
-                       &si->si_syncCookie.ctxcsn, si->si_syncCookie.rid );
+                       si->si_syncCookie.ctxcsn, si->si_syncCookie.rid );
+       } else {
+               ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+               /* match SIDs */
+               if ( si->si_cookieState->cs_num > 1 && si->si_cookieAge !=
+                       si->si_cookieState->cs_age ) {
+                       int i, j;
+
+                       for (i=0; !BER_BVISNULL( &si->si_syncCookie.ctxcsn[i] ); i++) {
+                               /* bogus, just dup everything */
+                               if ( si->si_syncCookie.sids[i] == -1 ) {
+                                       ber_bvarray_free( si->si_syncCookie.ctxcsn );
+                                       ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
+                                               si->si_cookieState->cs_vals, NULL );
+                                       break;
+                               }
+                               for (j=0; j<si->si_cookieState->cs_num; j++) {
+                                       if ( si->si_syncCookie.sids[i] !=
+                                               si->si_cookieState->cs_sids[j] )
+                                               continue;
+                                       ber_bvreplace( &si->si_syncCookie.ctxcsn[i],
+                                               &si->si_cookieState->cs_vals[j] );
+                                       break;
+                               }
+                       }
+               }
+               ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
        }
 
        rc = ldap_sync_search( si, op->o_tmpmemctx );
 
        if( rc != LDAP_SUCCESS ) {
-               Debug( LDAP_DEBUG_ANY, "do_syncrep1: rid %03ld "
+               Debug( LDAP_DEBUG_ANY, "do_syncrep1: %s "
                        "ldap_search_ext: %s (%d)\n",
-                       si->si_rid, ldap_err2string( rc ), rc );
+                       si->si_ridtxt, ldap_err2string( rc ), rc );
        }
 
 done:
@@ -512,6 +568,32 @@ done:
        return rc;
 }
 
+static int
+compare_csns( struct sync_cookie *sc1, struct sync_cookie *sc2, int *which )
+{
+       int i, j, match = 0;
+       const char *text;
+
+       *which = 0;
+
+       for (i=0; !BER_BVISNULL( &sc1->ctxcsn[i] ); i++) {
+               for (j=0; !BER_BVISNULL( &sc2->ctxcsn[j] ); j++) {
+                       if ( sc1->sids[i] != sc2->sids[j] )
+                               continue;
+                       value_match( &match, slap_schema.si_ad_entryCSN,
+                               slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
+                               SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+                               &sc1->ctxcsn[i], &sc2->ctxcsn[i], &text );
+                       if ( match < 0 ) {
+                               *which = j;
+                               return match;
+                       }
+                       break;
+               }
+       }
+       return match;
+}
+
 static int
 do_syncrep2(
        Operation *op,
@@ -533,8 +615,8 @@ do_syncrep2(
 
        int             syncstate;
        struct berval   syncUUID = BER_BVNULL;
-       struct sync_cookie      syncCookie = { BER_BVNULL };
-       struct sync_cookie      syncCookie_req = { BER_BVNULL };
+       struct sync_cookie      syncCookie = { NULL };
+       struct sync_cookie      syncCookie_req = { NULL };
        struct berval           cookie = BER_BVNULL;
 
        int     rc, err;
@@ -543,8 +625,7 @@ do_syncrep2(
        struct berval   *psub;
        Modifications   *modlist = NULL;
 
-       const char              *text;
-       int                             match;
+       int                             match, m;
 
        struct timeval *tout_p = NULL;
        struct timeval tout = { 0, 0 };
@@ -562,7 +643,7 @@ do_syncrep2(
        ber_init2( ber, NULL, LBER_USE_DER );
        ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
 
-       Debug( LDAP_DEBUG_TRACE, "=>do_syncrep2 rid %03ld\n", si->si_rid, 0, 0 );
+       Debug( LDAP_DEBUG_TRACE, "=>do_syncrep2 %s\n", si->si_ridtxt, 0, 0 );
 
        psub = &si->si_be->be_nsuffix[0];
 
@@ -601,9 +682,9 @@ do_syncrep2(
                                        rctrlp = ldap_find_control( LDAP_CONTROL_SYNC_STATE, rctrls );
                                }
                                if ( rctrlp == NULL ) {
-                                       Debug( LDAP_DEBUG_ANY, "do_syncrep2: rid %03ld "
+                                       Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
                                                "got search entry without "
-                                               "Sync State control\n", si->si_rid, 0, 0 );
+                                               "Sync State control\n", si->si_ridtxt, 0, 0 );
                                        rc = -1;
                                        goto done;
                                }
@@ -612,8 +693,8 @@ do_syncrep2(
                                /* FIXME: what if syncUUID is NULL or empty?
                                 * (happens with back-sql...) */
                                if ( BER_BVISEMPTY( &syncUUID ) ) {
-                                       Debug( LDAP_DEBUG_ANY, "do_syncrep2: rid %03ld "
-                                               "got empty syncUUID\n", si->si_rid, 0, 0 );
+                                       Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+                                               "got empty syncUUID\n", si->si_ridtxt, 0, 0 );
                                        ldap_controls_free( rctrls );
                                        rc = -1;
                                        goto done;
@@ -633,7 +714,7 @@ do_syncrep2(
                                if ( si->si_syncdata && si->si_logstate == SYNCLOG_LOGGING ) {
                                        modlist = NULL;
                                        if ( ( rc = syncrepl_message_to_op( si, op, msg ) ) == LDAP_SUCCESS &&
-                                               !BER_BVISNULL( &syncCookie.ctxcsn ) )
+                                               syncCookie.ctxcsn )
                                        {
                                                rc = syncrepl_updateCookie( si, op, psub, &syncCookie );
                                        }
@@ -641,9 +722,8 @@ do_syncrep2(
                                        &modlist, &entry, syncstate ) ) == LDAP_SUCCESS )
                                {
                                        if ( ( rc = syncrepl_entry( si, op, entry, &modlist,
-                                               syncstate, &syncUUID, &syncCookie_req,
-                                               &syncCookie.ctxcsn ) ) == LDAP_SUCCESS &&
-                                               !BER_BVISNULL( &syncCookie.ctxcsn ) )
+                                               syncstate, &syncUUID, &syncCookie_req ) ) == LDAP_SUCCESS &&
+                                               syncCookie.ctxcsn )
                                        {
                                                rc = syncrepl_updateCookie( si, op, psub, &syncCookie );
                                        }
@@ -658,14 +738,14 @@ do_syncrep2(
 
                        case LDAP_RES_SEARCH_REFERENCE:
                                Debug( LDAP_DEBUG_ANY,
-                                       "do_syncrep2: rid %03ld reference received error\n",
-                                       si->si_rid, 0, 0 );
+                                       "do_syncrep2: %s reference received error\n",
+                                       si->si_ridtxt, 0, 0 );
                                break;
 
                        case LDAP_RES_SEARCH_RESULT:
                                Debug( LDAP_DEBUG_SYNC,
-                                       "do_syncrep2: rid %03ld LDAP_RES_SEARCH_RESULT\n",
-                                       si->si_rid, 0, 0 );
+                                       "do_syncrep2: %s LDAP_RES_SEARCH_RESULT\n",
+                                       si->si_ridtxt, 0, 0 );
                                ldap_parse_result( si->si_ld, msg, &err, NULL, NULL, NULL,
                                        &rctrls, 0 );
 #ifdef LDAP_X_SYNC_REFRESH_REQUIRED
@@ -703,16 +783,12 @@ do_syncrep2(
                                        }
                                        ber_scanf( ber, /*"{"*/ "}" );
                                }
-                               if ( BER_BVISNULL( &syncCookie_req.ctxcsn ) ) {
+                               if ( !syncCookie_req.ctxcsn ) {
                                        match = -1;
-                               } else if ( BER_BVISNULL( &syncCookie.ctxcsn ) ) {
+                               } else if ( !syncCookie.ctxcsn ) {
                                        match = 1;
                                } else {
-                                       value_match( &match, slap_schema.si_ad_entryCSN,
-                                               slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
-                                               SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
-                                               &syncCookie_req.ctxcsn, &syncCookie.ctxcsn,
-                                               &text );
+                                       match = compare_csns( &syncCookie_req, &syncCookie, &m );
                                }
                                if ( rctrls ) {
                                        ldap_controls_free( rctrls );
@@ -725,14 +801,14 @@ do_syncrep2(
                                        if ( refreshDeletes == 0 && match < 0 &&
                                                err == LDAP_SUCCESS )
                                        {
-                                               syncrepl_del_nonpresent( op, si, NULL, &syncCookie.ctxcsn );
+                                               syncrepl_del_nonpresent( op, si, NULL,
+                                                       &syncCookie.ctxcsn[m] );
                                        } else {
                                                avl_free( si->si_presentlist, avl_ber_bvfree );
                                                si->si_presentlist = NULL;
                                        }
                                }
-                               if ( !BER_BVISNULL( &syncCookie.ctxcsn ) &&
-                                       match < 0 && err == LDAP_SUCCESS )
+                               if ( syncCookie.ctxcsn && match < 0 && err == LDAP_SUCCESS )
                                {
                                        rc = syncrepl_updateCookie( si, op, psub, &syncCookie );
                                }
@@ -756,8 +832,8 @@ do_syncrep2(
                                        ber_tag_t tag;
                                        case LDAP_TAG_SYNC_NEW_COOKIE:
                                                Debug( LDAP_DEBUG_SYNC,
-                                                       "do_syncrep2: rid %03ld %s - %s\n", 
-                                                       si->si_rid,
+                                                       "do_syncrep2: %s %s - %s\n", 
+                                                       si->si_ridtxt,
                                                        "LDAP_RES_INTERMEDIATE", 
                                                        "NEW_COOKIE" );
                                                ber_scanf( ber, "tm", &tag, &cookie );
@@ -765,8 +841,8 @@ do_syncrep2(
                                        case LDAP_TAG_SYNC_REFRESH_DELETE:
                                        case LDAP_TAG_SYNC_REFRESH_PRESENT:
                                                Debug( LDAP_DEBUG_SYNC,
-                                                       "do_syncrep2: rid %03ld %s - %s\n", 
-                                                       si->si_rid,
+                                                       "do_syncrep2: %s %s - %s\n", 
+                                                       si->si_ridtxt,
                                                        "LDAP_RES_INTERMEDIATE", 
                                                        si_tag == LDAP_TAG_SYNC_REFRESH_PRESENT ?
                                                        "REFRESH_PRESENT" : "REFRESH_DELETE" );
@@ -797,8 +873,8 @@ do_syncrep2(
                                                break;
                                        case LDAP_TAG_SYNC_ID_SET:
                                                Debug( LDAP_DEBUG_SYNC,
-                                                       "do_syncrep2: rid %03ld %s - %s\n", 
-                                                       si->si_rid,
+                                                       "do_syncrep2: %s %s - %s\n", 
+                                                       si->si_ridtxt,
                                                        "LDAP_RES_INTERMEDIATE", 
                                                        "SYNC_ID_SET" );
                                                ber_scanf( ber, "t{" /*"}"*/, &tag );
@@ -813,6 +889,7 @@ do_syncrep2(
                                                        if ( !BER_BVISNULL( &syncCookie.octet_str ) )
                                                        {
                                                                slap_parse_sync_cookie( &syncCookie, NULL );
+                                                               compare_csns( &syncCookie_req, &syncCookie, &m );
                                                        }
                                                }
                                                if ( ber_peek_tag( ber, &len ) ==
@@ -824,7 +901,7 @@ do_syncrep2(
                                                ber_scanf( ber, /*"{"*/ "}" );
                                                if ( refreshDeletes ) {
                                                        syncrepl_del_nonpresent( op, si, syncUUIDs,
-                                                               &syncCookie.ctxcsn );
+                                                               &syncCookie.ctxcsn[m] );
                                                        ber_bvarray_free_x( syncUUIDs, op->o_tmpmemctx );
                                                } else {
                                                        int i;
@@ -845,31 +922,28 @@ do_syncrep2(
                                                break;
                                        default:
                                                Debug( LDAP_DEBUG_ANY,
-                                                       "do_syncrep2: rid %03ld unknown syncinfo tag (%ld)\n",
-                                                       si->si_rid, (long) si_tag, 0 );
+                                                       "do_syncrep2: %s unknown syncinfo tag (%ld)\n",
+                                                       si->si_ridtxt, (long) si_tag, 0 );
                                                ldap_memfree( retoid );
                                                ber_bvfree( retdata );
                                                continue;
                                        }
 
-                                       if ( BER_BVISNULL( &syncCookie_req.ctxcsn ) ) {
+                                       if ( !syncCookie_req.ctxcsn ) {
                                                match = -1;
-                                       } else if ( BER_BVISNULL( &syncCookie.ctxcsn ) ) {
+                                       } else if ( !syncCookie.ctxcsn ) {
                                                match = 1;
                                        } else {
-                                               value_match( &match, slap_schema.si_ad_entryCSN,
-                                                       slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
-                                                       SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
-                                                       &syncCookie_req.ctxcsn,
-                                                       &syncCookie.ctxcsn, &text );
+                                               match = compare_csns( &syncCookie_req, &syncCookie, &m );
                                        }
 
                                        if ( match < 0 ) {
                                                if ( si->si_refreshPresent == 1 ) {
-                                                       syncrepl_del_nonpresent( op, si, NULL, &syncCookie.ctxcsn );
+                                                       syncrepl_del_nonpresent( op, si, NULL,
+                                                               &syncCookie.ctxcsn[m] );
                                                }
 
-                                               if ( !BER_BVISNULL( &syncCookie.ctxcsn ) )
+                                               if ( syncCookie.ctxcsn )
                                                {
                                                        rc = syncrepl_updateCookie( si, op, psub, &syncCookie);
                                                }
@@ -880,9 +954,9 @@ do_syncrep2(
                                        break;
 
                                } else {
-                                       Debug( LDAP_DEBUG_ANY, "do_syncrep2: rid %03ld "
+                                       Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
                                                "unknown intermediate response (%d)\n",
-                                               si->si_rid, rc, 0 );
+                                               si->si_ridtxt, rc, 0 );
                                        ldap_memfree( retoid );
                                        ber_bvfree( retdata );
                                        break;
@@ -890,8 +964,8 @@ do_syncrep2(
                                break;
 
                        default:
-                               Debug( LDAP_DEBUG_ANY, "do_syncrep2: rid %03ld "
-                                       "unknown message\n", si->si_rid, 0, 0 );
+                               Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+                                       "unknown message\n", si->si_ridtxt, 0, 0 );
                                break;
 
                        }
@@ -912,7 +986,7 @@ do_syncrep2(
                errstr = ldap_err2string( rc );
                
                Debug( LDAP_DEBUG_ANY,
-                       "do_syncrep2: rid %03ld %s\n", si->si_rid, errstr, 0 );
+                       "do_syncrep2: %s %s\n", si->si_ridtxt, errstr, 0 );
        }
 
 done:
@@ -951,7 +1025,7 @@ do_syncrepl(
        int i, defer = 1;
        Backend *be;
 
-       Debug( LDAP_DEBUG_TRACE, "=>do_syncrepl rid %03ld\n", si->si_rid, 0, 0 );
+       Debug( LDAP_DEBUG_TRACE, "=>do_syncrepl %s\n", si->si_ridtxt, 0, 0 );
 
        if ( si == NULL )
                return NULL;
@@ -1201,9 +1275,9 @@ syncrepl_message_to_op(
        int             rc, deleteOldRdn = 0, freeReqDn = 0;
 
        if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) {
-               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: rid %03ld "
+               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: %s "
                        "Message type should be entry (%d)",
-                       si->si_rid, ldap_msgtype( msg ), 0 );
+                       si->si_ridtxt, ldap_msgtype( msg ), 0 );
                return -1;
        }
 
@@ -1216,8 +1290,8 @@ syncrepl_message_to_op(
 
        if ( rc != LDAP_SUCCESS ) {
                Debug( LDAP_DEBUG_ANY,
-                       "syncrepl_message_to_op: rid %03ld dn get failed (%d)",
-                       si->si_rid, rc, 0 );
+                       "syncrepl_message_to_op: %s dn get failed (%d)",
+                       si->si_ridtxt, rc, 0 );
                return rc;
        }
 
@@ -1240,8 +1314,8 @@ syncrepl_message_to_op(
                        int i = verb_to_mask( bvals[0].bv_val, modops );
                        if ( i < 0 ) {
                                Debug( LDAP_DEBUG_ANY,
-                                       "syncrepl_message_to_op: rid %03ld unknown op %s",
-                                       si->si_rid, bvals[0].bv_val, 0 );
+                                       "syncrepl_message_to_op: %s unknown op %s",
+                                       si->si_ridtxt, bvals[0].bv_val, 0 );
                                ch_free( bvals );
                                rc = -1;
                                goto done;
@@ -1288,9 +1362,9 @@ syncrepl_message_to_op(
                rc = slap_mods_check( op, modlist, &text, txtbuf, textlen, NULL );
 
                if ( rc != LDAP_SUCCESS ) {
-                       Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: rid %03ld "
+                       Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: %s "
                                "mods check (%s)\n",
-                               si->si_rid, text, 0 );
+                               si->si_ridtxt, text, 0 );
                        goto done;
                }
 
@@ -1302,14 +1376,14 @@ syncrepl_message_to_op(
                        freeReqDn = 0;
                        rc = slap_mods2entry( modlist, &op->ora_e, 1, 0, &text, txtbuf, textlen);
                        if( rc != LDAP_SUCCESS ) {
-                               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: rid %03ld "
+                               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: %s "
                                "mods2entry (%s)\n",
-                                       si->si_rid, text, 0 );
+                                       si->si_ridtxt, text, 0 );
                        } else {
                                rc = op->o_bd->be_add( op, &rs );
                                Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_message_to_op: rid %03ld be_add %s (%d)\n", 
-                                       si->si_rid, op->o_req_dn.bv_val, rc );
+                                       "syncrepl_message_to_op: %s be_add %s (%d)\n", 
+                                       si->si_ridtxt, op->o_req_dn.bv_val, rc );
                        }
                        if ( e == op->ora_e )
                                be_entry_release_w( op, op->ora_e );
@@ -1317,8 +1391,8 @@ syncrepl_message_to_op(
                        op->orm_modlist = modlist;
                        rc = op->o_bd->be_modify( op, &rs );
                        Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
-                               "syncrepl_message_to_op: rid %03ld be_modify %s (%d)\n", 
-                               si->si_rid, op->o_req_dn.bv_val, rc );
+                               "syncrepl_message_to_op: %s be_modify %s (%d)\n", 
+                               si->si_ridtxt, op->o_req_dn.bv_val, rc );
                }
                break;
        case LDAP_REQ_MODRDN:
@@ -1360,14 +1434,14 @@ syncrepl_message_to_op(
                rc = op->o_bd->be_modrdn( op, &rs );
                slap_mods_free( op->orr_modlist, 1 );
                Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
-                       "syncrepl_message_to_op: rid %03ld be_modrdn %s (%d)\n", 
-                       si->si_rid, op->o_req_dn.bv_val, rc );
+                       "syncrepl_message_to_op: %s be_modrdn %s (%d)\n", 
+                       si->si_ridtxt, op->o_req_dn.bv_val, rc );
                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: rid %03ld be_delete %s (%d)\n", 
-                       si->si_rid, op->o_req_dn.bv_val, rc );
+                       "syncrepl_message_to_op: %s be_delete %s (%d)\n", 
+                       si->si_ridtxt, op->o_req_dn.bv_val, rc );
                break;
        }
 done:
@@ -1425,9 +1499,9 @@ syncrepl_message_to_entry(
        *modlist = NULL;
 
        if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) {
-               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: rid %03ld "
+               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: %s "
                        "Message type should be entry (%d)",
-                       si->si_rid, ldap_msgtype( msg ), 0 );
+                       si->si_ridtxt, ldap_msgtype( msg ), 0 );
                return -1;
        }
 
@@ -1436,8 +1510,8 @@ syncrepl_message_to_entry(
        rc = ldap_get_dn_ber( si->si_ld, msg, &ber, &bdn );
        if ( rc != LDAP_SUCCESS ) {
                Debug( LDAP_DEBUG_ANY,
-                       "syncrepl_message_to_entry: rid %03ld dn get failed (%d)",
-                       si->si_rid, rc, 0 );
+                       "syncrepl_message_to_entry: %s dn get failed (%d)",
+                       si->si_ridtxt, rc, 0 );
                return rc;
        }
 
@@ -1485,8 +1559,8 @@ syncrepl_message_to_entry(
        }
 
        if ( *modlist == NULL ) {
-               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: rid %03ld no attributes\n",
-                       si->si_rid, 0, 0 );
+               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: %s no attributes\n",
+                       si->si_ridtxt, 0, 0 );
                rc = -1;
                goto done;
        }
@@ -1494,8 +1568,8 @@ syncrepl_message_to_entry(
        rc = slap_mods_check( op, *modlist, &text, txtbuf, textlen, NULL );
 
        if ( rc != LDAP_SUCCESS ) {
-               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: rid %03ld mods check (%s)\n",
-                       si->si_rid, text, 0 );
+               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: %s mods check (%s)\n",
+                       si->si_ridtxt, text, 0 );
                goto done;
        }
 
@@ -1527,8 +1601,8 @@ syncrepl_message_to_entry(
 
        rc = slap_mods2entry( *modlist, &e, 1, 1, &text, txtbuf, textlen);
        if( rc != LDAP_SUCCESS ) {
-               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: rid %03ld mods2entry (%s)\n",
-                       si->si_rid, text, 0 );
+               Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: %s mods2entry (%s)\n",
+                       si->si_ridtxt, text, 0 );
        }
 
 done:
@@ -1583,8 +1657,7 @@ syncrepl_entry(
        Modifications** modlist,
        int syncstate,
        struct berval* syncUUID,
-       struct sync_cookie* syncCookie_req,
-       struct berval* syncCSN )
+       struct sync_cookie* syncCookie_req )
 {
        Backend *be = op->o_bd;
        slap_callback   cb = { NULL, NULL, NULL, NULL };
@@ -1610,28 +1683,28 @@ syncrepl_entry(
 
        switch( syncstate ) {
        case LDAP_SYNC_PRESENT:
-               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: rid %03ld %s\n",
-                                       si->si_rid,
+               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s %s\n",
+                                       si->si_ridtxt,
                                        "LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_PRESENT)", 0 );
                break;
        case LDAP_SYNC_ADD:
-               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: rid %03ld %s\n",
-                                       si->si_rid,
+               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s %s\n",
+                                       si->si_ridtxt,
                                        "LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_ADD)", 0 );
                break;
        case LDAP_SYNC_DELETE:
-               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: rid %03ld %s\n",
-                                       si->si_rid,
+               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s %s\n",
+                                       si->si_ridtxt,
                                        "LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_DELETE)", 0 );
                break;
        case LDAP_SYNC_MODIFY:
-               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: rid %03ld %s\n",
-                                       si->si_rid,
+               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s %s\n",
+                                       si->si_ridtxt,
                                        "LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_MODIFY)", 0 );
                break;
        default:
-               Debug( LDAP_DEBUG_ANY, "syncrepl_entry: rid %03ld %s\n",
-                                       si->si_rid,
+               Debug( LDAP_DEBUG_ANY, "syncrepl_entry: %s %s\n",
+                                       si->si_ridtxt,
                                        "LDAP_RES_SEARCH_ENTRY(UNKNOWN syncstate)", 0 );
        }
 
@@ -1681,8 +1754,8 @@ syncrepl_entry(
        ava.aa_value = *syncUUID;
 
        if ( syncuuid_bv ) {
-               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: rid %03ld inserted UUID %s\n",
-                       si->si_rid, syncUUID_strrep.bv_val, 0 );
+               Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s inserted UUID %s\n",
+                       si->si_ridtxt, syncUUID_strrep.bv_val, 0 );
        }
        op->ors_filter = &f;
 
@@ -1720,8 +1793,8 @@ syncrepl_entry(
        if ( limits_check( op, &rs_search ) == 0 ) {
                rc = be->be_search( op, &rs_search );
                Debug( LDAP_DEBUG_SYNC,
-                               "syncrepl_entry: rid %03ld be_search (%d)\n", 
-                               si->si_rid, rc, 0 );
+                               "syncrepl_entry: %s be_search (%d)\n", 
+                               si->si_ridtxt, rc, 0 );
        }
 
        if ( !BER_BVISNULL( &op->ors_filterstr ) ) {
@@ -1733,12 +1806,12 @@ syncrepl_entry(
 
        if ( entry && !BER_BVISNULL( &entry->e_name ) ) {
                Debug( LDAP_DEBUG_SYNC,
-                               "syncrepl_entry: rid %03ld %s\n",
-                               si->si_rid, entry->e_name.bv_val, 0 );
+                               "syncrepl_entry: %s %s\n",
+                               si->si_ridtxt, entry->e_name.bv_val, 0 );
        } else {
                Debug( LDAP_DEBUG_SYNC,
-                               "syncrepl_entry: rid %03ld %s\n",
-                               si->si_rid, dni.dn.bv_val ? dni.dn.bv_val : "(null)", 0 );
+                               "syncrepl_entry: %s %s\n",
+                               si->si_ridtxt, dni.dn.bv_val ? dni.dn.bv_val : "(null)", 0 );
        }
 
        /* Don't save the contextCSN on the inooming context entry,
@@ -1785,8 +1858,8 @@ retry_add:;
 
                        rc = be->be_add( op, &rs_add );
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: rid %03ld be_add (%d)\n", 
-                                       si->si_rid, rc, 0 );
+                                       "syncrepl_entry: %s be_add (%d)\n", 
+                                       si->si_ridtxt, rc, 0 );
                        switch ( rs_add.sr_err ) {
                        case LDAP_SUCCESS:
                                if ( op->ora_e == entry ) {
@@ -1847,8 +1920,8 @@ retry_add:;
 
                        default:
                                Debug( LDAP_DEBUG_ANY,
-                                       "syncrepl_entry: rid %03ld be_add failed (%d)\n",
-                                       si->si_rid, rs_add.sr_err, 0 );
+                                       "syncrepl_entry: %s be_add failed (%d)\n",
+                                       si->si_ridtxt, rs_add.sr_err, 0 );
                                break;
                        }
                        goto done;
@@ -1923,8 +1996,8 @@ retry_add:;
 
                        slap_mods_free( op->orr_modlist, 1 );
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: rid %03ld be_modrdn (%d)\n", 
-                                       si->si_rid, rc, 0 );
+                                       "syncrepl_entry: %s be_modrdn (%d)\n", 
+                                       si->si_ridtxt, rc, 0 );
                        goto done;
                }
                if ( dni.mods ) {
@@ -1935,17 +2008,17 @@ retry_add:;
                        rc = be->be_modify( op, &rs_modify );
                        slap_mods_free( op->orm_modlist, 1 );
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: rid %03ld be_modify (%d)\n", 
-                                       si->si_rid, rc, 0 );
+                                       "syncrepl_entry: %s be_modify (%d)\n", 
+                                       si->si_ridtxt, rc, 0 );
                        if ( rs_modify.sr_err != LDAP_SUCCESS ) {
                                Debug( LDAP_DEBUG_ANY,
-                                       "syncrepl_entry: rid %03ld be_modify failed (%d)\n",
-                                       si->si_rid, rs_modify.sr_err, 0 );
+                                       "syncrepl_entry: %s be_modify failed (%d)\n",
+                                       si->si_ridtxt, rs_modify.sr_err, 0 );
                        }
                } else {
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: rid %03ld entry unchanged, ignored (%s)\n", 
-                                       si->si_rid, op->o_req_dn.bv_val, 0 );
+                                       "syncrepl_entry: %s entry unchanged, ignored (%s)\n", 
+                                       si->si_ridtxt, op->o_req_dn.bv_val, 0 );
                }
                goto done;
        case LDAP_SYNC_DELETE :
@@ -1955,8 +2028,8 @@ retry_add:;
                        op->o_tag = LDAP_REQ_DELETE;
                        rc = be->be_delete( op, &rs_delete );
                        Debug( LDAP_DEBUG_SYNC,
-                                       "syncrepl_entry: rid %03ld be_delete (%d)\n", 
-                                       si->si_rid, rc, 0 );
+                                       "syncrepl_entry: %s be_delete (%d)\n", 
+                                       si->si_ridtxt, rc, 0 );
 
                        while ( rs_delete.sr_err == LDAP_SUCCESS
                                && op->o_delete_glue_parent ) {
@@ -1978,7 +2051,7 @@ retry_add:;
 
        default :
                Debug( LDAP_DEBUG_ANY,
-                       "syncrepl_entry: rid %03ld unknown syncstate\n", si->si_rid, 0, 0 );
+                       "syncrepl_entry: %s unknown syncstate\n", si->si_ridtxt, 0, 0 );
                goto done;
        }
 
@@ -2095,7 +2168,7 @@ syncrepl_del_nonpresent(
                if ( cookiecsn && !BER_BVISNULL( cookiecsn ) ) {
                        csn = *cookiecsn;
                } else {
-                       csn = si->si_syncCookie.ctxcsn;
+                       csn = si->si_syncCookie.ctxcsn[0];
                }
 
                slap_queue_csn( op, &csn );
@@ -2113,8 +2186,8 @@ syncrepl_del_nonpresent(
                        op->o_req_ndn = *np_prev->npe_nname;
                        rc = op->o_bd->be_delete( op, &rs_delete );
                        Debug( LDAP_DEBUG_SYNC,
-                               "syncrepl_del_nonpresent: rid %03ld be_delete %s (%d)\n", 
-                               si->si_rid, op->o_req_dn.bv_val, rc );
+                               "syncrepl_del_nonpresent: %s be_delete %s (%d)\n", 
+                               si->si_ridtxt, op->o_req_dn.bv_val, rc );
 
                        if ( rs_delete.sr_err == LDAP_NOT_ALLOWED_ON_NONLEAF ) {
                                Modifications mod1, mod2;
@@ -2329,27 +2402,57 @@ syncrepl_updateCookie(
        struct sync_cookie *syncCookie )
 {
        Backend *be = op->o_bd;
-       Modifications mod = { { 0 } };
-       struct berval vals[ 2 ];
+       Modifications mod[2];
+       struct berval first = BER_BVNULL;
 
-       int rc, flags;
+       int rc, i, j;
 
        slap_callback cb = { NULL };
        SlapReply       rs_modify = {REP_RESULT};
 
-       mod.sml_op = LDAP_MOD_REPLACE;
-       mod.sml_desc = slap_schema.si_ad_contextCSN;
-       mod.sml_type = mod.sml_desc->ad_cname;
-       mod.sml_values = vals;
-       vals[0] = syncCookie->ctxcsn;
-       BER_BVZERO( &vals[1] );
-
-       slap_queue_csn( op, &syncCookie->ctxcsn );
+       mod[0].sml_op = LDAP_MOD_DELETE;
+       mod[0].sml_desc = slap_schema.si_ad_contextCSN;
+       mod[0].sml_type = mod[0].sml_desc->ad_cname;
+       mod[0].sml_values = NULL;
+       mod[0].sml_nvalues = NULL;
+       mod[0].sml_next = &mod[1];
+
+       mod[1].sml_op = LDAP_MOD_ADD;
+       mod[1].sml_desc = slap_schema.si_ad_contextCSN;
+       mod[1].sml_type = mod[0].sml_desc->ad_cname;
+       mod[1].sml_values = NULL;
+       mod[1].sml_nvalues = NULL;
+       mod[1].sml_next = NULL;
+
+       ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+
+       for ( i=0; i<syncCookie->numcsns; i++ ) {
+               for ( j=0; j<si->si_cookieState->cs_num; j++ ) {
+                       if ( syncCookie->sids[i] != si->si_cookieState->cs_sids[j] )
+                               continue;
+                       if ( ber_bvcmp( &syncCookie->ctxcsn[i],
+                               &si->si_cookieState->cs_vals[j] ) > 0 ) {
+                               ber_bvarray_add_x( &mod[0].sml_values,
+                                       &si->si_cookieState->cs_vals[j], op->o_tmpmemctx );
+                               ber_bvarray_add_x( &mod[1].sml_values,
+                                       &syncCookie->ctxcsn[i], op->o_tmpmemctx );
+                               if ( BER_BVISNULL( &first ))
+                                       first = syncCookie->ctxcsn[i];
+                       }
+                       break;
+               }
+               /* there was no match for this SID, it's a new CSN */
+               if ( j == si->si_cookieState->cs_num ) {
+                       ber_bvarray_add_x( &mod[1].sml_values,
+                               &syncCookie->ctxcsn[i], op->o_tmpmemctx );
+                       if ( BER_BVISNULL( &first ))
+                               first = syncCookie->ctxcsn[i];
+               }
+       }
+       slap_queue_csn( op, &first );
 
        op->o_tag = LDAP_REQ_MODIFY;
 
-       assert( si->si_rid < 1000 );
-
        cb.sc_response = null_callback;
        cb.sc_private = si;
 
@@ -2359,26 +2462,55 @@ syncrepl_updateCookie(
 
        /* update contextCSN */
        op->o_msgid = SLAP_SYNC_UPDATE_MSGID;
-       op->orm_modlist = &mod;
-       flags = SLAP_DBFLAGS( op->o_bd );
-       SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
+
+       if ( mod[0].sml_values )
+               op->orm_modlist = mod;
+       else
+               op->orm_modlist = &mod[1];
+
+       op->orm_no_opattrs = 1;
        rc = be->be_modify( op, &rs_modify );
-       SLAP_DBFLAGS( op->o_bd ) = flags;
        op->o_msgid = 0;
 
        if ( rs_modify.sr_err == LDAP_SUCCESS ) {
                slap_sync_cookie_free( &si->si_syncCookie, 0 );
                slap_dup_sync_cookie( &si->si_syncCookie, syncCookie );
+               /* If we replaced any old values */
+               if ( mod[0].sml_values ) {
+                       for ( i=0; !BER_BVISNULL( &mod[0].sml_values[i] ); i++ ) {
+                               for ( j=0; j<si->si_cookieState->cs_num; j++ ) {
+                                       if ( mod[0].sml_values[i].bv_val !=
+                                               si->si_cookieState->cs_vals[j].bv_val )
+                                               continue;
+                                       ber_bvreplace( &si->si_cookieState->cs_vals[j],
+                                               &mod[1].sml_values[i] );
+                                       break;
+                               }
+                       }
+               } else {
+                       /* Else we just added */
+                       si->si_cookieState->cs_num += syncCookie->numcsns;
+                       value_add( &si->si_cookieState->cs_vals, syncCookie->ctxcsn );
+                       free( si->si_cookieState->cs_sids );
+                       si->si_cookieState->cs_sids = slap_parse_csn_sids(
+                               si->si_cookieState->cs_vals, si->si_cookieState->cs_num );
+               }
+
+               si->si_cookieState->cs_age++;
+               si->si_cookieAge = si->si_cookieState->cs_age;
        } else {
                Debug( LDAP_DEBUG_ANY,
-                       "syncrepl_updateCookie: rid %03ld be_modify failed (%d)\n",
-                       si->si_rid, rs_modify.sr_err, 0 );
+                       "syncrepl_updateCookie: %s be_modify failed (%d)\n",
+                       si->si_ridtxt, rs_modify.sr_err, 0 );
        }
+       ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
 
        slap_graduate_commit_csn( op );
        op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
        BER_BVZERO( &op->o_csn );
-       if ( mod.sml_next ) slap_mods_free( mod.sml_next, 1 );
+       if ( mod[1].sml_next ) slap_mods_free( mod[1].sml_next, 1 );
+       op->o_tmpfree( mod[1].sml_values, op->o_tmpmemctx );
+       op->o_tmpfree( mod[0].sml_values, op->o_tmpmemctx );
 
        return rc;
 }
@@ -2662,7 +2794,7 @@ nonpresent_callback(
 
        } else if ( rs->sr_type == REP_SEARCH ) {
                if ( !( si->si_refreshDelete & NP_DELETE_ONE ) ) {
-                       char buf[sizeof("000 not")];
+                       char buf[sizeof("rid=000 not")];
 
                        a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
 
@@ -2672,11 +2804,11 @@ nonpresent_callback(
                        }
 
                        if ( slap_debug & LDAP_DEBUG_SYNC ) {
-                               sprintf( buf, "%03ld %s", si->si_rid,
+                               sprintf( buf, "%s %s", si->si_ridtxt,
                                        present_uuid ? "got" : "not" );
                        }
 
-                       Debug( LDAP_DEBUG_SYNC, "nonpresent_callback: rid %s UUID %s, dn %s\n",
+                       Debug( LDAP_DEBUG_SYNC, "nonpresent_callback: %s UUID %s, dn %s\n",
                                buf, a ? a->a_vals[0].bv_val : "<missing>", rs->sr_entry->e_name.bv_val );
 
                        if ( a == NULL ) return 0;
@@ -2992,6 +3124,7 @@ parse_syncrepl_line(
                                return -1;
                        }
                        si->si_rid = tmp;
+                       sprintf( si->si_ridtxt, IDSTR "=%03d", si->si_rid );
                        gots |= GOT_ID;
                } else if ( !strncasecmp( c->argv[ i ], PROVIDERSTR "=",
                                        STRLENOF( PROVIDERSTR "=" ) ) )
@@ -3468,7 +3601,7 @@ add_syncrepl(
                                init_syncrepl( si );
                                si->si_re = ldap_pvt_runqueue_insert( &slapd_rq,
                                        si->si_interval, do_syncrepl, si, "do_syncrepl",
-                                       c->be->be_suffix[0].bv_val );
+                                       si->si_ridtxt );
                                if ( si->si_re )
                                        rc = config_sync_shadow( c ) ? -1 : 0;
                                else
@@ -3493,6 +3626,13 @@ add_syncrepl(
                if ( !si->si_schemachecking ) {
                        SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
                }
+               if ( c->be->be_syncinfo ) {
+                       si->si_cookieState = c->be->be_syncinfo->si_cookieState;
+               } else {
+                       si->si_cookieState = ch_calloc( 1, sizeof( cookie_state ));
+                       ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_mutex );
+               }
+               si->si_next = c->be->be_syncinfo;
                c->be->be_syncinfo = si;
                return 0;
        }
@@ -3667,20 +3807,43 @@ syncrepl_config( ConfigArgs *c )
        if (c->op == SLAP_CONFIG_EMIT) {
                if ( c->be->be_syncinfo ) {
                        struct berval bv;
-                       syncrepl_unparse( c->be->be_syncinfo, &bv ); 
-                       ber_bvarray_add( &c->rvalue_vals, &bv );
+                       syncinfo_t *si;
+
+                       for ( si = c->be->be_syncinfo; si; si=si->si_next ) {
+                               syncrepl_unparse( si, &bv ); 
+                               ber_bvarray_add( &c->rvalue_vals, &bv );
+                       }
                        return 0;
                }
                return 1;
        } else if ( c->op == LDAP_MOD_DELETE ) {
+               cookie_state *cs = NULL;
                if ( c->be->be_syncinfo ) {
-                       syncinfo_free( c->be->be_syncinfo );
-                       c->be->be_syncinfo = NULL;
+                       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 ) {
+                                       *sip = si->si_next;
+                                       syncinfo_free( si );
+                               } else {
+                                       sip = &si->si_next;
+                               }
+                       }
+               }
+               if ( !c->be->be_syncinfo ) {
+                       SLAP_DBFLAGS( c->be ) &= ~(SLAP_DBFLAG_SHADOW|SLAP_DBFLAG_SYNC_SHADOW);
+                       if ( cs ) {
+                               ber_bvarray_free( cs->cs_vals );
+                               ldap_pvt_thread_mutex_destroy( &cs->cs_mutex );
+                               ch_free( cs );
+                       }
                }
-               SLAP_DBFLAGS( c->be ) &= ~(SLAP_DBFLAG_SHADOW|SLAP_DBFLAG_SYNC_SHADOW);
                return 0;
        }
-       if ( SLAP_SHADOW( c->be ) ) {
+       if ( SLAP_SLURP_SHADOW( c->be ) ) {
                Debug(LDAP_DEBUG_ANY, "%s: "
                        "syncrepl: database already shadowed.\n",
                        c->log, 0, 0);