From 0759b72bec6a4395cf3b0eae9ab8f34bff5d464f Mon Sep 17 00:00:00 2001
From: Ludwig Krispenz <lkrispen@redhat.com>
Date: Thu, 17 Nov 2016 15:23:37 +0100
Subject: [PATCH] Ticket 49020 - do not treat missing csn as fatal

---
 ldap/schema/01core389.ldif                         |   3 +-
 ldap/servers/plugins/replication/cl5_api.c         |  16 ++-
 ldap/servers/plugins/replication/cl5_clcache.c     |  43 ++++----
 ldap/servers/plugins/replication/cl5_clcache.h     |   2 +-
 ldap/servers/plugins/replication/repl5.h           |   4 +
 ldap/servers/plugins/replication/repl5_agmt.c      | 114 +++++++++++++++++++++
 ldap/servers/plugins/replication/repl5_agmtlist.c  |  13 +++
 .../plugins/replication/repl5_inc_protocol.c       |  14 +--
 ldap/servers/plugins/replication/repl_globals.c    |   1 +
 9 files changed, 172 insertions(+), 38 deletions(-)

diff --git a/ldap/schema/01core389.ldif b/ldap/schema/01core389.ldif
index ab07a0b..dfa4729 100644
--- a/ldap/schema/01core389.ldif
+++ b/ldap/schema/01core389.ldif
@@ -298,6 +298,7 @@ attributeTypes: ( 2.16.840.1.113730.3.1.2330 NAME 'nsslapd-logging-backend' DESC
 attributeTypes: ( 2.16.840.1.113730.3.1.2331 NAME 'nsslapd-logging-hr-timestamps-enabled' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.2332 NAME 'allowWeakDHParam' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.2333 NAME 'nsds5ReplicaReleaseTimeout' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
+attributeTypes: ( 2.16.840.1.113730.3.1.2335 NAME 'nsds5ReplicaIgnoreMissingChange' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
 #
 # objectclasses
 #
@@ -309,7 +310,7 @@ objectClasses: ( 2.16.840.1.113730.3.2.110 NAME 'nsMappingTree' DESC 'Netscape d
 objectClasses: ( 2.16.840.1.113730.3.2.104 NAME 'nsContainer' DESC 'Netscape defined objectclass' SUP top  MUST ( CN ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.108 NAME 'nsDS5Replica' DESC 'Netscape defined objectclass' SUP top  MUST ( nsDS5ReplicaRoot $  nsDS5ReplicaId ) MAY (cn $ nsds5ReplicaPreciseTombstonePurging $ nsds5ReplicaCleanRUV $ nsds5ReplicaAbortCleanRUV $ nsDS5ReplicaType $ nsDS5ReplicaBindDN $ nsState $ nsDS5ReplicaName $ nsDS5Flags $ nsDS5Task $ nsDS5ReplicaReferral $ nsDS5ReplicaAutoReferral $ nsds5ReplicaPurgeDelay $ nsds5ReplicaTombstonePurgeInterval $ nsds5ReplicaChangeCount $ nsds5ReplicaLegacyConsumer $ nsds5ReplicaProtocolTimeout $ nsds5ReplicaBackoffMin $ nsds5ReplicaBackoffMax $ nsds5ReplicaReleaseTimeout ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.113 NAME 'nsTombstone' DESC 'Netscape defined objectclass' SUP top MAY ( nstombstonecsn $ nsParentUniqueId $ nscpEntryDN ) X-ORIGIN 'Netscape Directory Server' )
-objectClasses: ( 2.16.840.1.113730.3.2.103 NAME 'nsDS5ReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsds5ReplicaCleanRUVNotified $ nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicatedAttributeListTotal $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5ReplicaEnabled $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5ReplicaStripAttrs $ nsds5replicaSessionPauseTime $ nsds5ReplicaProtocolTimeout $ nsds5ReplicaFlowControlWindow $ nsds5ReplicaFlowControlPause $ nsDS5ReplicaWaitForAsyncResults ) X-ORIGIN 'Netscape Directory Server' )
+objectClasses: ( 2.16.840.1.113730.3.2.103 NAME 'nsDS5ReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsds5ReplicaCleanRUVNotified $ nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicatedAttributeListTotal $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5ReplicaEnabled $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5ReplicaStripAttrs $ nsds5replicaSessionPauseTime $ nsds5ReplicaProtocolTimeout $ nsds5ReplicaFlowControlWindow $ nsds5ReplicaFlowControlPause $ nsDS5ReplicaWaitForAsyncResults $ nsds5ReplicaIgnoreMissingChange) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.39 NAME 'nsslapdConfig' DESC 'Netscape defined objectclass' SUP top MAY ( cn ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.317 NAME 'nsSaslMapping' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsSaslMapRegexString $ nsSaslMapBaseDNTemplate $ nsSaslMapFilterTemplate ) MAY ( nsSaslMapPriority ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.43 NAME 'nsSNMP' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsSNMPEnabled ) MAY ( nsSNMPOrganization $ nsSNMPLocation $ nsSNMPContact $ nsSNMPDescription $ nsSNMPName $ nsSNMPMasterHost $ nsSNMPMasterPort ) X-ORIGIN 'Netscape Directory Server' )
diff --git a/ldap/servers/plugins/replication/cl5_api.c b/ldap/servers/plugins/replication/cl5_api.c
index 19ab1be..b41a3dc 100644
--- a/ldap/servers/plugins/replication/cl5_api.c
+++ b/ldap/servers/plugins/replication/cl5_api.c
@@ -310,7 +310,7 @@ static int _cl5WriteBervals (struct berval **bv, char** buff, u_int32_t *size);
 static PRBool _cl5ValidReplayIterator (const CL5ReplayIterator *iterator);
 #endif
 static int _cl5PositionCursorForReplay (ReplicaId consumerRID, const RUV *consumerRuv,
-			Object *replica, Object *fileObject, CL5ReplayIterator **iterator);
+		Object *replica, Object *fileObject, CL5ReplayIterator **iterator, int *continue_on_missing);
 static int _cl5CheckMissingCSN (const CSN *minCsn, const RUV *supplierRUV, CL5DBFile *file);
 
 /* changelog trimming */
@@ -1536,7 +1536,7 @@ int cl5CreateReplayIteratorEx (Private_Repl_Protocol *prp, const RUV *consumerRu
     	/* iterate through the ruv in csn order to find first master for which 
 	       we can replay changes */		    
 		
-		rc = _cl5PositionCursorForReplay (consumerRID, consumerRuv, replica, obj, iterator);
+		rc = _cl5PositionCursorForReplay (consumerRID, consumerRuv, replica, obj, iterator, NULL);
 	}
 	else
 	{
@@ -1597,7 +1597,13 @@ int cl5CreateReplayIterator (Private_Repl_Protocol *prp, const RUV *consumerRuv,
     	/* iterate through the ruv in csn order to find first master for which 
 	       we can replay changes */		    
 		ReplicaId consumerRID = agmt_get_consumer_rid ( prp->agmt, prp->conn );
-		rc = _cl5PositionCursorForReplay (consumerRID, consumerRuv, replica, obj, iterator);
+		int continue_on_missing = agmt_get_ignoremissing ( prp->agmt);
+		int save_cont_miss = continue_on_missing;
+		rc = _cl5PositionCursorForReplay (consumerRID, consumerRuv, replica, obj, iterator, &continue_on_missing);
+		if (save_cont_miss == 1 && continue_on_missing ==0) {
+			/* the option to continue once on a missing csn was used, rest */
+			agmt_set_ignoremissing ( prp->agmt, 0);
+		}
 	}
 	else
 	{
@@ -5549,7 +5555,7 @@ struct replica_hash_entry
 
 
 static int _cl5PositionCursorForReplay (ReplicaId consumerRID, const RUV *consumerRuv,
-		Object *replica, Object *fileObj, CL5ReplayIterator **iterator)
+		Object *replica, Object *fileObj, CL5ReplayIterator **iterator, int *continue_on_missing)
 {
 	CLC_Buffer *clcache = NULL;
 	CL5DBFile *file;
@@ -5593,7 +5599,7 @@ static int _cl5PositionCursorForReplay (ReplicaId consumerRID, const RUV *consum
 	rc = clcache_get_buffer ( &clcache, file->db, consumerRID, consumerRuv, supplierRuv );
 	if ( rc != 0 ) goto done;
 
-	rc = clcache_load_buffer (clcache, &startCSN);
+	rc = clcache_load_buffer (clcache, &startCSN, continue_on_missing);
 
         if (rc == 0) {
 		haveChanges = PR_TRUE;
diff --git a/ldap/servers/plugins/replication/cl5_clcache.c b/ldap/servers/plugins/replication/cl5_clcache.c
index 13bbc21..bf8c471 100644
--- a/ldap/servers/plugins/replication/cl5_clcache.c
+++ b/ldap/servers/plugins/replication/cl5_clcache.c
@@ -324,7 +324,7 @@ clcache_return_buffer ( CLC_Buffer **buf )
  *		       historic reason.
  */
 int
-clcache_load_buffer ( CLC_Buffer *buf, CSN **anchorCSN )
+clcache_load_buffer ( CLC_Buffer *buf, CSN **anchorCSN, int *continue_on_miss )
 {
 	int rc = 0;
         int flag = DB_NEXT;
@@ -345,6 +345,22 @@ clcache_load_buffer ( CLC_Buffer *buf, CSN **anchorCSN )
 		if (anchorCSN) *anchorCSN = buf->buf_current_csn;
 		rc = clcache_load_buffer_bulk ( buf, flag );
 
+		if (rc == DB_NOTFOUND && continue_on_miss && *continue_on_miss) {
+			/* make replication going using next best startcsn */
+			slapi_log_err(SLAPI_LOG_ERR, buf->buf_agmt_name,
+					"clcache_load_buffer - Can't load changelog buffer starting at CSN %s with flag(%s). "
+					"Trying to use an alterantive start CSN.\n",
+					(char*)buf->buf_key.data,
+					flag==DB_NEXT?"DB_NEXT":"DB_SET" );
+			rc = clcache_load_buffer_bulk ( buf, DB_SET_RANGE );
+			if (rc == 0) {
+				slapi_log_err(SLAPI_LOG_ERR, buf->buf_agmt_name,
+					"clcache_load_buffer - Using alternative start iteration csn: %s \n",
+					(char*)buf->buf_key.data);
+			}
+			/* the use of alternative start csns can be limited, record its usage */
+			(*continue_on_miss)--;
+		}
 		/* Reset some flag variables */
 		if ( rc == 0 ) {
 			int i;
@@ -408,23 +424,6 @@ retry:
 								 & buf->buf_key,
 								 & buf->buf_data,
 								 DB_SET );
-			if (rc == DB_NOTFOUND) {
-				/* the start position in the changelog is not found
-				 * 1. log an error
-				 * 2. try to find another starting position as close
-				 *    as possible
-				 */
-				slapi_log_err(SLAPI_LOG_ERR, buf->buf_agmt_name, "clcache_load_buffer_bulk - "
-							"changelog record with csn (%s) not found for DB_NEXT\n",
-							(char *)buf->buf_key.data );
-				rc = cursor->c_get ( cursor, & buf->buf_key, & buf->buf_data,
-							 DB_SET_RANGE );
-				/* this moves the cursor ahead of the tageted csn,
-				 * so we achieved what was intended with DB_SET/DB_NEXT
-				 * continute at this csn.
-				 */
-				use_flag = DB_CURRENT;
-			}
 		}
 
 		/*
@@ -433,12 +432,6 @@ retry:
 		 */
 		if ( 0 == rc || DB_BUFFER_SMALL == rc ) {
 			rc = clcache_cursor_get ( cursor, buf, use_flag );
-			if ( rc == DB_NOTFOUND && use_flag == DB_SET) {
-				slapi_log_err(SLAPI_LOG_ERR, buf->buf_agmt_name, "clcache_load_buffer_bulk - "
-							"changelog record with csn (%s) not found for DB_SET\n",
-							(char *)buf->buf_key.data );
-				rc = clcache_cursor_get ( cursor, buf, DB_SET_RANGE );
-			}
 		}
 
 	}
@@ -512,7 +505,7 @@ clcache_get_next_change ( CLC_Buffer *buf, void **key, size_t *keylen, void **da
 		 * We're done with the current buffer. Now load the next chunk.
 		 */
 		if ( NULL == *key && CLC_STATE_READY == buf->buf_state ) {
-			rc = clcache_load_buffer ( buf, NULL );
+			rc = clcache_load_buffer ( buf, NULL, NULL );
 			if ( 0 == rc && buf->buf_record_ptr ) {
 				DB_MULTIPLE_KEY_NEXT ( buf->buf_record_ptr, &buf->buf_data,
 								   *key, *keylen, *data, *datalen );
diff --git a/ldap/servers/plugins/replication/cl5_clcache.h b/ldap/servers/plugins/replication/cl5_clcache.h
index 4a029bb..9b6bbc8 100644
--- a/ldap/servers/plugins/replication/cl5_clcache.h
+++ b/ldap/servers/plugins/replication/cl5_clcache.h
@@ -23,7 +23,7 @@ typedef struct clc_buffer CLC_Buffer;
 int	 clcache_init ( DB_ENV **dbenv );
 void clcache_set_config(void);
 int	 clcache_get_buffer ( CLC_Buffer **buf, DB *db, ReplicaId consumer_rid, const RUV *consumer_ruv, const RUV *local_ruv );
-int	 clcache_load_buffer ( CLC_Buffer *buf, CSN **anchorCSN );
+int	 clcache_load_buffer ( CLC_Buffer *buf, CSN **anchorCSN, int *continue_on_miss );
 void clcache_return_buffer ( CLC_Buffer **buf );
 int	 clcache_get_next_change ( CLC_Buffer *buf, void **key, size_t *keylen, void **data, size_t *datalen, CSN **csn );
 void clcache_destroy(void);
diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h
index c163dcf..529b218 100644
--- a/ldap/servers/plugins/replication/repl5.h
+++ b/ldap/servers/plugins/replication/repl5.h
@@ -154,6 +154,7 @@ extern const char *type_replicaReleaseTimeout;
 extern const char *type_replicaBackoffMin;
 extern const char *type_replicaBackoffMax;
 extern const char *type_replicaPrecisePurge;
+extern const char *type_replicaIgnoreMissingChange;
 
 /* Attribute names for windows replication agreements */
 extern const char *type_nsds7WindowsReplicaArea;
@@ -317,6 +318,7 @@ long agmt_get_busywaittime(const Repl_Agmt *ra);
 long agmt_get_pausetime(const Repl_Agmt *ra);
 long agmt_get_flowcontrolwindow(const Repl_Agmt *ra);
 long agmt_get_flowcontrolpause(const Repl_Agmt *ra);
+long agmt_get_ignoremissing(const Repl_Agmt *ra);
 int agmt_start(Repl_Agmt *ra);
 int windows_agmt_start(Repl_Agmt *ra); 
 int agmt_stop(Repl_Agmt *ra);
@@ -339,6 +341,7 @@ int agmt_set_schedule_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
 int agmt_set_timeout_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
 int agmt_set_flowcontrolwindow_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
 int agmt_set_flowcontrolpause_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
+int agmt_set_ignoremissing_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
 int agmt_set_busywaittime_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
 int agmt_set_pausetime_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
 int agmt_set_credentials_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
@@ -385,6 +388,7 @@ int agmt_set_enabled_from_entry(Repl_Agmt *ra, Slapi_Entry *e, char *returntext)
 char **agmt_get_attrs_to_strip(Repl_Agmt *ra);
 int agmt_set_attrs_to_strip(Repl_Agmt *ra, Slapi_Entry *e);
 int agmt_set_timeout(Repl_Agmt *ra, long timeout);
+int agmt_set_ignoremissing(Repl_Agmt *ra, long ignoremissing);
 void agmt_update_done(Repl_Agmt *ra, int is_total);
 PRUint64 agmt_get_protocol_timeout(Repl_Agmt *agmt);
 void agmt_set_protocol_timeout(Repl_Agmt *agmt, PRUint64 timeout);
diff --git a/ldap/servers/plugins/replication/repl5_agmt.c b/ldap/servers/plugins/replication/repl5_agmt.c
index 4ce1dbc..067bf39 100644
--- a/ldap/servers/plugins/replication/repl5_agmt.c
+++ b/ldap/servers/plugins/replication/repl5_agmt.c
@@ -124,6 +124,7 @@ typedef struct repl5agmt {
 	long flowControlPause; /* When nb of not acknowledged entries overpass totalUpdateWindow
 	                        * This is the duration (in msec) that the RA will pause before sending the next entry
 	                        */
+	long ignoreMissingChange;	/* if set replication will try to continue even if change cannot be found in changelog */
 	Slapi_RWLock *attr_lock; /* RW lock for all the stripped attrs */
 	int WaitForAsyncResults; /* Pass to DS_Sleep(PR_MillisecondsToInterval(WaitForAsyncResults))
 	                          * in repl5_inc_waitfor_async_results */
@@ -137,6 +138,7 @@ static int get_agmt_status(Slapi_PBlock *pb, Slapi_Entry* e,
 static int agmt_set_bind_method_no_lock(Repl_Agmt *ra, const Slapi_Entry *e);
 static int agmt_set_transportinfo_no_lock(Repl_Agmt *ra, const Slapi_Entry *e);
 static ReplicaId agmt_maxcsn_get_rid(char *maxcsn);
+static void agmt_replica_reset_ignoremissing (const Repl_Agmt *agmt);
 
 /*
 Schema for replication agreement:
@@ -349,6 +351,21 @@ agmt_new_from_entry(Slapi_Entry *e)
 		}
 	}
 
+	/* continue on missing change ? */
+	ra->ignoreMissingChange = 0;
+	tmpstr = slapi_entry_attr_get_charptr(e, type_replicaIgnoreMissingChange);
+	if (NULL != tmpstr)
+	{
+		if (strcasecmp(tmpstr,"off") == 0 || strcasecmp(tmpstr,"never") == 0) {
+			ra->ignoreMissingChange = 0;
+		} else if (strcasecmp(tmpstr,"on") == 0 || strcasecmp(tmpstr,"once") == 0) {
+			ra->ignoreMissingChange = 1;
+		} else if (strcasecmp(tmpstr,"always") == 0) {
+			ra->ignoreMissingChange = -1;
+		}
+		slapi_ch_free_string(&tmpstr);
+	}
+
 	/* DN of entry at root of replicated area */
 	tmpstr = slapi_entry_attr_get_charptr(e, type_nsds5ReplicaRoot);
 	if (NULL != tmpstr)
@@ -1131,6 +1148,16 @@ agmt_get_flowcontrolpause(const Repl_Agmt *ra)
 	PR_Unlock(ra->lock);
 	return return_value;
 }
+long
+agmt_get_ignoremissing(const Repl_Agmt *ra)
+{
+	long return_value;
+	PR_ASSERT(NULL != ra);
+	PR_Lock(ra->lock);
+	return_value = ra->ignoreMissingChange;
+	PR_Unlock(ra->lock);
+	return return_value;
+}
 /*
  * Warning - reference to the long name of the agreement is returned.
  * The long name of an agreement is the DN of the agreement entry,
@@ -1996,6 +2023,48 @@ agmt_set_flowcontrolpause_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
 	}
 	return return_value;
 }
+/* add comment here */
+int
+agmt_set_ignoremissing_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+	Slapi_Attr *sattr = NULL;
+	int return_value = -1;
+
+	PR_ASSERT(NULL != ra);
+	PR_Lock(ra->lock);
+	if (ra->stop_in_progress)
+	{
+		PR_Unlock(ra->lock);
+		return return_value;
+	}
+
+	slapi_entry_attr_find(e, type_replicaIgnoreMissingChange, &sattr);
+	if (NULL != sattr)
+	{
+		Slapi_Value *sval = NULL;
+		slapi_attr_first_value(sattr, &sval);
+		if (NULL != sval)
+		{
+			const char *tmpval = slapi_value_get_string(sval);
+			if (strcasecmp(tmpval,"off") == 0 || strcasecmp(tmpval,"never") == 0) {
+				ra->ignoreMissingChange = 0;
+				return_value = 0;
+			} else if (strcasecmp(tmpval,"on") == 0 || strcasecmp(tmpval,"once") == 0) {
+				ra->ignoreMissingChange = 1;
+				return_value = 0;
+			} else if (strcasecmp(tmpval,"always") == 0) {
+				ra->ignoreMissingChange = -1;
+				return_value = 0;
+			}
+		}
+	}
+	PR_Unlock(ra->lock);
+	if (return_value == 0)
+	{
+		prot_notify_agmt_changed(ra->protocol, ra->long_name);
+	}
+	return return_value;
+}
 
 int
 agmt_set_timeout(Repl_Agmt *ra, long timeout)
@@ -2036,6 +2105,20 @@ agmt_set_flowcontrolpause(Repl_Agmt *ra, long pause)
 
     return 0;
 }
+int
+agmt_set_ignoremissing(Repl_Agmt *ra, long ignoremissing)
+{
+    PR_Lock(ra->lock);
+    if (ra->stop_in_progress){
+        PR_Unlock(ra->lock);
+        return -1;
+    }
+    ra->ignoreMissingChange = ignoremissing;
+    PR_Unlock(ra->lock);
+    /* if reset to 0 update the entry */
+    agmt_replica_reset_ignoremissing(ra);
+    return 0;
+}
 
 /*
  * Set or reset the busywaittime
@@ -2272,6 +2355,37 @@ agmt_replica_init_done (const Repl_Agmt *agmt)
     slapi_pblock_destroy (pb);
 }
 
+
+/* delete nsds5replicaIgnoreMissingChange attribute */
+static void
+agmt_replica_reset_ignoremissing (const Repl_Agmt *agmt)
+{
+    int rc;
+    Slapi_PBlock *pb = slapi_pblock_new ();
+    LDAPMod *mods [2];
+    LDAPMod mod;
+
+    mods[0] = &mod;
+    mods[1] = NULL;
+    mod.mod_op = LDAP_MOD_DELETE | LDAP_MOD_BVALUES;
+    mod.mod_type = (char*)type_replicaIgnoreMissingChange;
+    mod.mod_bvalues = NULL;
+
+    slapi_modify_internal_set_pb_ext(pb, agmt->dn, mods, NULL/* controls */,
+          NULL/* uniqueid */, repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0/* flags */);
+    slapi_modify_internal_pb (pb);
+
+    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+    if (rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_ATTRIBUTE)
+    {
+        slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "agmt_replica_ignoremissing: "
+                        "failed to remove (%s) attribute from (%s) entry; LDAP error - %d\n",
+                        type_replicaIgnoreMissingChange, slapi_sdn_get_ndn (agmt->dn), rc);
+    }
+
+    slapi_pblock_destroy (pb);
+}
+
 /* Agreement object is acquired on behalf of the caller.
    The caller is responsible for releasing the object
    when it is no longer used */
diff --git a/ldap/servers/plugins/replication/repl5_agmtlist.c b/ldap/servers/plugins/replication/repl5_agmtlist.c
index b7963b5..b4dd032 100644
--- a/ldap/servers/plugins/replication/repl5_agmtlist.c
+++ b/ldap/servers/plugins/replication/repl5_agmtlist.c
@@ -399,6 +399,19 @@ agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry
 			}
 		}
 		else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+					type_replicaIgnoreMissingChange))
+		{
+			/* New replica timeout */
+			if (agmt_set_ignoremissing_from_entry(agmt, e) != 0)
+			{
+				slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "agmtlist_modify_callback - "
+						"Failed to update the ignorMissingChange attribute for agreement %s\n",
+						agmt_get_long_name(agmt));
+				*returncode = LDAP_OPERATIONS_ERROR;
+				rc = SLAPI_DSE_CALLBACK_ERROR;
+			}
+		}
+		else if (slapi_attr_types_equivalent(mods[i]->mod_type,
 					type_nsds5ReplicaBusyWaitTime))
 		{
 			/* New replica busywaittime */
diff --git a/ldap/servers/plugins/replication/repl5_inc_protocol.c b/ldap/servers/plugins/replication/repl5_inc_protocol.c
index eb4b965..a5ae885 100644
--- a/ldap/servers/plugins/replication/repl5_inc_protocol.c
+++ b/ldap/servers/plugins/replication/repl5_inc_protocol.c
@@ -1704,20 +1704,22 @@ send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *nu
 		case CL5_PURGED_DATA:    /* requested data has been purged */
 			slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
 				"send_updates - %s: Data required to update replica has been purged from the changelog. "
-				"The replica must be reinitialized.\n",
+				"If the error persists the replica must be reinitialized.\n",
 				agmt_get_long_name(prp->agmt));
 			agmt_set_last_update_status(prp->agmt, 0, NSDS50_REPL_CL_ERROR,
 				"Data required to update replica has been purged from the changelog. "
-				"The replica must be reinitialized.");
-			return_value = UPDATE_FATAL_ERROR;
+				"If the error persists the replica must be reinitialized.");
+			return_value = UPDATE_TRANSIENT_ERROR;
 			break;
 		case CL5_MISSING_DATA:   /* data should be in the changelog, but is missing */
 			slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
-				"send_updates - %s: Missing data encountered\n",
+				"send_updates - %s: Missing data encountered. "
+				"If the error persists the replica must be reinitialized.\n",
 				agmt_get_long_name(prp->agmt));
 			agmt_set_last_update_status(prp->agmt, 0, NSDS50_REPL_CL_ERROR,
-				"Changelog data is missing");
-			return_value = UPDATE_FATAL_ERROR;
+				"Changelog data is missing. "
+				"If the error persists the replica must be reinitialized.");
+			return_value = UPDATE_TRANSIENT_ERROR;
 			break;
 		case CL5_UNKNOWN_ERROR:   /* unclassified error */
 			slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
diff --git a/ldap/servers/plugins/replication/repl_globals.c b/ldap/servers/plugins/replication/repl_globals.c
index 8b891fb..ab85e7f 100644
--- a/ldap/servers/plugins/replication/repl_globals.c
+++ b/ldap/servers/plugins/replication/repl_globals.c
@@ -114,6 +114,7 @@ const char *type_nsds5ReplicaStripAttrs = "nsds5ReplicaStripAttrs";
 const char* type_nsds5ReplicaFlowControlWindow = "nsds5ReplicaFlowControlWindow";
 const char* type_nsds5ReplicaFlowControlPause = "nsds5ReplicaFlowControlPause";
 const char *type_nsds5WaitForAsyncResults = "nsds5ReplicaWaitForAsyncResults";
+const char* type_replicaIgnoreMissingChange = "nsds5ReplicaIgnoreMissingChange";
 
 /* windows sync specific attributes */
 const char *type_nsds7WindowsReplicaArea = "nsds7WindowsReplicaSubtree";
-- 
2.4.3

