ITS#10358 Retry if entry changed (use assert control to detect this)

This commit is contained in:
Ondřej Kuzník 2025-12-08 12:50:44 +00:00 committed by Quanah Gibson-Mount
parent 3d94d11c75
commit b3821e772a

View file

@ -4017,6 +4017,7 @@ typedef struct dninfo {
int oldNcount; /* #values of old naming attr */
AttributeDescription *oldDesc; /* for renames */
AttributeDescription *newDesc; /* for renames */
char oldcsn[LDAP_PVT_CSNSTR_BUFSIZE];
} dninfo;
#define HASHUUID 1
@ -4133,15 +4134,24 @@ syncrepl_entry(
slap_callback cb = { NULL, NULL, NULL, NULL };
int syncuuid_inserted = 0;
BerElementBuffer berbuf;
BerElement *ber = (BerElement *)&berbuf;
LDAPControl c = { .ldctl_oid = LDAP_CONTROL_ASSERT, .ldctl_iscritical = 0 },
*ca[2] = { &c, NULL };
SlapReply rs_search = {REP_RESULT};
Filter f = {0};
AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
Filter f = {0}, csn_assertion = { .f_choice = LDAP_FILTER_EQUALITY };
AttributeAssertion ava = ATTRIBUTEASSERTION_INIT,
csnava = ATTRIBUTEASSERTION_INIT;
int rc = LDAP_SUCCESS;
struct berval pdn = BER_BVNULL;
struct berval filterstr, pdn = BER_BVNULL;
dninfo dni = {0};
int retry = 1;
int freecsn = 1;
int freecsn = 1, csn_queued = 0;
ber_init2( ber, NULL, LBER_USE_DER );
ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
Debug( LDAP_DEBUG_SYNC,
"syncrepl_entry: %s LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_%s) csn=%s tid %p\n",
@ -4180,25 +4190,64 @@ syncrepl_entry(
}
}
if ( syncuuid_inserted ) {
Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s inserted UUID %s\n",
si->si_ridtxt, syncUUID[1].bv_val );
}
filterstr.bv_len = STRLENOF( "(entryUUID=)" ) + syncUUID[1].bv_len;
filterstr.bv_val = (char *)slap_sl_malloc( filterstr.bv_len + 1,
op->o_tmpmemctx );
AC_MEMCPY( filterstr.bv_val, "(entryUUID=", STRLENOF( "(entryUUID=" ) );
AC_MEMCPY( &filterstr.bv_val[STRLENOF( "(entryUUID=" )],
syncUUID[1].bv_val, syncUUID[1].bv_len );
filterstr.bv_val[filterstr.bv_len - 1] = ')';
filterstr.bv_val[filterstr.bv_len] = '\0';
csnava.aa_desc = slap_schema.si_ad_entryCSN;
csn_assertion.f_ava = &csnava;
retry_diff:
/*
* ITS#10358: Another thread edited this entry changing its entryCSN, could
* have been another serverID with a CSN that's still older than ourselves
* so we looped back here: we have to reset our state and try again.
*
* Since ITS#9584, an entry can only be in process of being change by one
* consumer task, this leaves a race with actual clients. Luckily those can
* only generate a CSN that's newer than what we just received so we only
* retry once. We still accept this pending CSN, it's a modification that's
* been eclipsed, not rejected.
*/
if ( !freecsn ) {
BER_BVZERO( &op->o_csn );
freecsn = 1;
}
if ( !BER_BVISNULL( &dni.ndn ) ) {
op->o_tmpfree( dni.ndn.bv_val, op->o_tmpmemctx );
BER_BVZERO( &dni.ndn );
}
if ( !BER_BVISNULL( &dni.dn ) ) {
op->o_tmpfree( dni.dn.bv_val, op->o_tmpmemctx );
BER_BVZERO( &dni.dn );
}
dni.mods = NULL;
if ( *dni.oldcsn ) {
ber_reset( ber, 1 );
dni.oldcsn[0] = '\0';
op->o_ctrls = NULL;
op->o_assert = SLAP_CONTROL_NONE;
}
f.f_choice = LDAP_FILTER_EQUALITY;
f.f_ava = &ava;
ava.aa_desc = slap_schema.si_ad_entryUUID;
ava.aa_value = *syncUUID;
if ( syncuuid_inserted ) {
Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s inserted UUID %s\n",
si->si_ridtxt, syncUUID[1].bv_val );
}
op->ors_filter = &f;
op->ors_filterstr.bv_len = STRLENOF( "(entryUUID=)" ) + syncUUID[1].bv_len;
op->ors_filterstr.bv_val = (char *) slap_sl_malloc(
op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
AC_MEMCPY( op->ors_filterstr.bv_val, "(entryUUID=", STRLENOF( "(entryUUID=" ) );
AC_MEMCPY( &op->ors_filterstr.bv_val[STRLENOF( "(entryUUID=" )],
syncUUID[1].bv_val, syncUUID[1].bv_len );
op->ors_filterstr.bv_val[op->ors_filterstr.bv_len - 1] = ')';
op->ors_filterstr.bv_val[op->ors_filterstr.bv_len] = '\0';
op->ors_filterstr = filterstr;
op->o_tag = LDAP_REQ_SEARCH;
op->ors_scope = LDAP_SCOPE_SUBTREE;
@ -4239,8 +4288,22 @@ syncrepl_entry(
si->si_ridtxt, rc );
op->o_dont_replicate = 0;
if ( !BER_BVISNULL( &op->ors_filterstr ) ) {
slap_sl_free( op->ors_filterstr.bv_val, op->o_tmpmemctx );
BER_BVZERO( &op->ors_filterstr );
if ( *dni.oldcsn ) {
/*
* ITS#10358: We synthesize an assert control, have to create both
* versions in case this is push replication where the issue is
* also more likely to happen.
*/
ber_str2bv( dni.oldcsn, 0, 0, &csnava.aa_value );
ber_printf( ber, "t{OO}", LDAP_FILTER_EQUALITY,
&slap_schema.si_ad_entryCSN->ad_cname,
&csnava.aa_value );
ber_flatten2( ber, &c.ldctl_value, 0 );
op->o_ctrls = ca;
op->o_assertion = &csn_assertion;
op->o_assert = SLAP_CONTROL_NONCRITICAL;
}
cb.sc_response = syncrepl_null_callback;
@ -4256,9 +4319,10 @@ syncrepl_entry(
si->si_ridtxt, dni.dn.bv_val ? dni.dn.bv_val : "(null)" );
}
assert( BER_BVISNULL( &op->o_csn ) );
if ( syncCSN ) {
assert( csn_queued || BER_BVISNULL( &op->o_csn ) );
if ( !csn_queued && syncCSN ) {
slap_queue_csn( op, syncCSN );
csn_queued = 1;
}
#ifdef SLAP_CONTROL_X_LAZY_COMMIT
@ -4312,6 +4376,8 @@ retry_add:;
op->o_tag = LDAP_REQ_ADD;
op->ora_e = entry;
op->o_bd = si->si_wbe;
op->o_ctrls = NULL;
op->o_assert = SLAP_CONTROL_NONE;
rc = op->o_bd->be_add( op, &rs_add );
Debug( LDAP_DEBUG_SYNC,
@ -4607,11 +4673,16 @@ retry_modrdn:;
* has not been added yet (ITS#6472) */
if ( rc == LDAP_NO_SUCH_OBJECT && op->orr_nnewSup != NULL ) {
Operation op2 = *op;
op2.o_ctrls = NULL;
op2.o_assert = SLAP_CONTROL_NONE;
rc = syncrepl_add_glue_ancestors( &op2, entry );
if ( rc == LDAP_SUCCESS ) {
goto retry_modrdn;
}
}
if ( rc == LDAP_ASSERTION_FAILED ) {
goto retry_diff;
}
op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx );
op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx );
@ -4627,8 +4698,10 @@ retry_modrdn:;
/* Use CSN on the modify */
if ( just_rename )
syncCSN = NULL;
else if ( syncCSN )
else if ( syncCSN ) {
slap_queue_csn( op, syncCSN );
csn_queued = 1;
}
}
if ( dni.mods ) {
SlapReply rs_modify = {REP_RESULT};
@ -4644,7 +4717,9 @@ retry_modrdn:;
Debug( LDAP_DEBUG_SYNC,
"syncrepl_entry: %s be_modify %s (%d)\n",
si->si_ridtxt, op->o_req_dn.bv_val, rc );
if ( rs_modify.sr_err != LDAP_SUCCESS ) {
if ( rs_modify.sr_err == LDAP_ASSERTION_FAILED ) {
goto retry_diff;
} else if ( rs_modify.sr_err != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY,
"syncrepl_entry: %s be_modify failed (%d)\n",
si->si_ridtxt, rs_modify.sr_err );
@ -4670,6 +4745,7 @@ retry_modrdn:;
op->o_bd = si->si_wbe;
if ( !syncCSN && si->si_syncCookie.ctxcsn ) {
slap_queue_csn( op, si->si_syncCookie.ctxcsn );
csn_queued = 1;
}
rc = op->o_bd->be_delete( op, &rs_delete );
Debug( LDAP_DEBUG_SYNC,
@ -4706,6 +4782,9 @@ retry_modrdn:;
}
done:
op->o_ctrls = NULL;
op->o_assert = SLAP_CONTROL_NONE;
op->o_assertion = NULL;
slap_sl_free( syncUUID[1].bv_val, op->o_tmpmemctx );
BER_BVZERO( &syncUUID[1] );
if ( !BER_BVISNULL( &dni.ndn ) ) {
@ -4723,6 +4802,9 @@ done:
if ( !BER_BVISNULL( &op->o_csn ) && freecsn ) {
op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
}
if ( !BER_BVISNULL( &filterstr ) ) {
slap_sl_free( filterstr.bv_val, op->o_tmpmemctx );
}
BER_BVZERO( &op->o_csn );
return rc;
}
@ -5914,6 +5996,8 @@ dn_callback(
old->a_vals[0].bv_val );
return LDAP_SUCCESS;
}
memcpy( dni->oldcsn, old->a_vals[0].bv_val,
old->a_vals[0].bv_len+1 );
}
}