struct berval li_uuid;
int li_success;
log_base *li_bases;
+ BerVarray li_mincsn;
+ int *li_sids, li_numcsns;
ldap_pvt_thread_mutex_t li_op_rmutex;
ldap_pvt_thread_mutex_t li_log_mutex;
} log_info;
*ad_reqScope, *ad_reqFilter, *ad_reqAttr, *ad_reqEntries,
*ad_reqSizeLimit, *ad_reqTimeLimit, *ad_reqAttrsOnly, *ad_reqData,
*ad_reqId, *ad_reqMessage, *ad_reqVersion, *ad_reqDerefAliases,
- *ad_reqReferral, *ad_reqOld, *ad_auditContext, *ad_reqEntryUUID;
+ *ad_reqReferral, *ad_reqOld, *ad_auditContext, *ad_reqEntryUUID,
+ *ad_minCSN;
static int
logSchemaControlValidate(
"ORDERING UUIDOrderingMatch "
"SYNTAX 1.3.6.1.1.16.1 "
"SINGLE-VALUE )", &ad_reqEntryUUID },
+
+ /*
+ * ITS#8486
+ */
+ { "( " LOG_SCHEMA_AT ".32 NAME 'minCSN' "
+ "DESC 'CSN set that the logs are recorded from' "
+ "EQUALITY CSNMatch "
+ "ORDERING CSNOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_minCSN },
{ NULL, NULL }
};
#define PURGE_INCREMENT 100
typedef struct purge_data {
+ struct log_info *li;
int slots;
int used;
+ int mincsn_updated;
BerVarray dn;
BerVarray ndn;
- struct berval csn; /* an arbitrary old CSN */
} purge_data;
static int
log_old_lookup( Operation *op, SlapReply *rs )
{
purge_data *pd = op->o_callback->sc_private;
+ struct log_info *li = pd->li;
Attribute *a;
if ( rs->sr_type != REP_SEARCH) return 0;
if ( slapd_shutdown ) return 0;
- /* Remember max CSN: should always be the last entry
- * seen, since log entries are ordered chronologically...
- */
+ /* Update minCSN */
a = attr_find( rs->sr_entry->e_attrs,
slap_schema.si_ad_entryCSN );
if ( a ) {
ber_len_t len = a->a_nvals[0].bv_len;
+ int i, sid;
+
+ /* Find the correct sid */
+ sid = slap_parse_csn_sid( &a->a_nvals[0] );
+ for ( i=0; i < li->li_numcsns; i++ ) {
+ if ( sid <= li->li_sids[i] ) break;
+ }
+ if ( i >= li->li_numcsns || sid != li->li_sids[i] ) {
+ Debug( LDAP_DEBUG_ANY, "log_old_lookup: "
+ "csn=%s with sid not in minCSN set!\n",
+ a->a_nvals[0].bv_val );
+ }
+
/* Paranoid len check, normalized CSNs are always the same length */
- if ( len > LDAP_PVT_CSNSTR_BUFSIZE )
- len = LDAP_PVT_CSNSTR_BUFSIZE;
- if ( memcmp( a->a_nvals[0].bv_val, pd->csn.bv_val, len ) > 0 ) {
- AC_MEMCPY( pd->csn.bv_val, a->a_nvals[0].bv_val, len );
- pd->csn.bv_len = len;
+ if ( len > li->li_mincsn[i].bv_len )
+ len = li->li_mincsn[i].bv_len;
+ if ( ber_bvcmp( &li->li_mincsn[i], &a->a_nvals[0] ) < 0 ) {
+ pd->mincsn_updated = 1;
+ AC_MEMCPY( li->li_mincsn[i].bv_val, a->a_nvals[0].bv_val, len );
}
}
if ( pd->used >= pd->slots ) {
slap_callback cb = { NULL, log_old_lookup, NULL, NULL, NULL };
Filter f;
AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
- purge_data pd = {0};
+ purge_data pd = { .li = li };
char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
time_t old = slap_get_time();
op->ors_attrs = slap_anlist_no_attrs;
op->ors_attrsonly = 1;
- pd.csn.bv_len = sizeof( csnbuf );
- pd.csn.bv_val = csnbuf;
- csnbuf[0] = '\0';
cb.sc_private = &pd;
op->o_bd->be_search( op, &rs );
if ( pd.used ) {
int i;
- /* delete the expired entries */
- op->o_tag = LDAP_REQ_DELETE;
op->o_callback = &nullsc;
- op->o_csn = pd.csn;
op->o_dont_replicate = 1;
- for (i=0; i<pd.used; i++) {
- op->o_req_dn = pd.dn[i];
- op->o_req_ndn = pd.ndn[i];
- if ( !slapd_shutdown ) {
- rs_reinit( &rs, REP_RESULT );
- op->o_bd->be_delete( op, &rs );
- }
- ch_free( pd.ndn[i].bv_val );
- ch_free( pd.dn[i].bv_val );
- ldap_pvt_thread_pool_pausecheck( &connection_pool );
- }
- ch_free( pd.ndn );
- ch_free( pd.dn );
-
- {
+ if ( pd.mincsn_updated ) {
Modifications mod;
- struct berval bv[2];
- rs_reinit( &rs, REP_RESULT );
- /* update context's entryCSN to reflect oldest CSN */
- mod.sml_numvals = 1;
- mod.sml_values = bv;
- bv[0] = pd.csn;
- BER_BVZERO(&bv[1]);
+ /* update context's minCSN to reflect oldest CSN */
+ mod.sml_numvals = li->li_numcsns;
+ mod.sml_values = li->li_mincsn;
mod.sml_nvalues = NULL;
- mod.sml_desc = slap_schema.si_ad_entryCSN;
+ mod.sml_desc = ad_minCSN;
mod.sml_op = LDAP_MOD_REPLACE;
mod.sml_flags = SLAP_MOD_INTERNAL;
mod.sml_next = NULL;
op->o_req_ndn = li->li_db->be_nsuffix[0];
op->o_no_schema_check = 1;
op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
- op->o_bd->be_modify( op, &rs );
- if ( mod.sml_next ) {
- slap_mods_free( mod.sml_next, 1 );
+ if ( !slapd_shutdown ) {
+ Debug( LDAP_DEBUG_SYNC, "accesslog_purge: "
+ "updating minCSN with %d values\n",
+ li->li_numcsns );
+ op->o_bd->be_modify( op, &rs );
}
}
+
+ /* delete the expired entries */
+ op->o_tag = LDAP_REQ_DELETE;
+ for (i=0; i<pd.used; i++) {
+ op->o_req_dn = pd.dn[i];
+ op->o_req_ndn = pd.ndn[i];
+ if ( !slapd_shutdown ) {
+ rs_reinit( &rs, REP_RESULT );
+ op->o_bd->be_delete( op, &rs );
+ }
+ ch_free( pd.ndn[i].bv_val );
+ ch_free( pd.dn[i].bv_val );
+ ldap_pvt_thread_pool_pausecheck( &connection_pool );
+ }
+ ch_free( pd.ndn );
+ ch_free( pd.dn );
}
ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
if ( e == op2.ora_e ) entry_free( e );
e = NULL;
+ /* TODO: What to do about minCSN when we have an op without a CSN? */
+ if ( !BER_BVISEMPTY( &op->o_csn ) ) {
+ Modifications mod;
+ int i, sid = slap_parse_csn_sid( &op->o_csn );
+
+ for ( i=0; i < li->li_numcsns; i++ ) {
+ if ( sid <= li->li_sids[i] ) break;
+ }
+ if ( i >= li->li_numcsns || sid != li->li_sids[i] ) {
+ /* SID not in minCSN set, add */
+ struct berval bv[2];
+
+ Debug( LDAP_DEBUG_TRACE, "accesslog_response: "
+ "adding minCSN %s\n",
+ op->o_csn.bv_val );
+ slap_insert_csn_sids( (struct sync_cookie *)&li->li_mincsn, i,
+ sid, &op->o_csn );
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_req_dn = li->li_db->be_suffix[0];
+ op2.o_req_ndn = li->li_db->be_nsuffix[0];
+
+ bv[0] = op->o_csn;
+ BER_BVZERO( &bv[1] );
+
+ mod.sml_numvals = 1;
+ mod.sml_values = bv;
+ mod.sml_nvalues = bv;
+ mod.sml_desc = ad_minCSN;
+ mod.sml_op = LDAP_MOD_ADD;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ op2.orm_modlist = &mod;
+ op2.orm_no_opattrs = 1;
+
+ Debug( LDAP_DEBUG_SYNC, "accesslog_response: "
+ "adding a new csn=%s into minCSN\n",
+ bv[0].bv_val );
+ rs_reinit( &rs2, REP_RESULT );
+ op2.o_bd->be_modify( &op2, &rs2 );
+ if ( rs2.sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_SYNC, "accesslog_response: "
+ "got result 0x%x adding minCSN %s\n",
+ rs2.sr_err, op->o_csn.bv_val );
+ }
+ } else if ( ber_bvcmp( &op->o_csn, &li->li_mincsn[i] ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "accesslog_response: "
+ "csn=%s older than existing minCSN csn=%s for this sid\n",
+ op->o_csn.bv_val, li->li_mincsn[i].bv_val );
+ }
+ }
+
done:
if ( lo->mask & LOG_OP_WRITES )
ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
return LDAP_SUCCESS;
}
-/* Create the logdb's root entry if it's missing */
+/* Create the logdb's root entry if it's missing, load mincsn */
static void *
accesslog_db_root(
void *ctx,
rc = be_entry_get_rw( op, li->li_db->be_nsuffix, NULL, NULL, 0, &e );
if ( e ) {
+ Attribute *a = attr_find( e->e_attrs, ad_minCSN );
+ if ( !a ) {
+ /* TODO: find the lowest CSN we are safe to put in */
+ a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
+ if ( a ) {
+ SlapReply rs = {REP_RESULT};
+ Modifications mod;
+ BackendDB db = *li->li_db;
+
+ op->o_bd = &db;
+
+ mod.sml_numvals = a->a_numvals;
+ mod.sml_values = a->a_vals;
+ mod.sml_nvalues = a->a_nvals;
+ mod.sml_desc = ad_minCSN;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_req_dn = e->e_name;
+ op->o_req_ndn = e->e_nname;
+ op->o_callback = &nullsc;
+ SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
+
+ Debug( LDAP_DEBUG_SYNC, "accesslog_db_root: "
+ "setting up minCSN with %d values\n",
+ a->a_numvals );
+
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ rc = op->o_bd->be_modify( op, &rs );
+ }
+ }
+ if ( a ) {
+ ber_bvarray_dup_x( &li->li_mincsn, a->a_vals, NULL );
+ li->li_numcsns = a->a_numvals;
+ li->li_sids = slap_parse_csn_sids( li->li_mincsn, li->li_numcsns, NULL );
+ slap_sort_csn_sids( li->li_mincsn, li->li_sids, li->li_numcsns, NULL );
+ }
be_entry_release_rw( op, e, 0 );
-
} else {
SlapReply rs = {REP_RESULT};
struct berval rdn, nrdn, attr;
attr_merge_one( e, slap_schema.si_ad_entryCSN,
&a->a_vals[0], &a->a_nvals[0] );
attr_merge( e, a->a_desc, a->a_vals, a->a_nvals );
+ attr_merge( e, ad_minCSN, a->a_vals, a->a_nvals );
}
be_entry_release_rw( op, e_ctx, 0 );
}