소스 검색

Data Exchange - Improved handling of deletion (inc. the reporting of it)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@1099 a333f486-631f-4898-b8df-5754b55c2be0
romainq 14 년 전
부모
커밋
8766db7e7b
4개의 변경된 파일129개의 추가작업 그리고 78개의 파일을 삭제
  1. 2 1
      dictionaries/dictionary.itop.core.php
  2. 4 3
      synchro/synchro_exec.php
  3. 53 28
      synchro/synchro_import.php
  4. 70 46
      synchro/synchrodatasource.class.inc.php

+ 2 - 1
dictionaries/dictionary.itop.core.php

@@ -525,6 +525,7 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Core:Synchro:label_obj_deleted' => 'Deleted (%1$s)',
 	'Core:Synchro:label_obj_obsoleted' => 'Obsoleted (%1$s)',
 	'Core:Synchro:label_obj_disappeared_errors' => 'Errors (%1$s)',
+	'Core:Synchro:label_obj_disappeared_no_action' => 'No Action (%1$s)',
 	'Core:Synchro:label_obj_unchanged' => 'Unchanged (%1$s)',
 	'Core:Synchro:label_obj_updated' => 'Updated (%1$s)', 
 	'Core:Synchro:label_obj_updated_errors' => 'Errors (%1$s)',
@@ -534,7 +535,7 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Core:Synchro:label_obj_new_errors' => 'Errors (%1$s)',
 	'Core:Synchro:History' => 'Synchronization History',
 	'Core:SynchroLogTitle' => '%1$s - %2$s',
-	'Core:Synchro:Nb_Replica' => 'Replica: %1$s',
+	'Core:Synchro:Nb_Replica' => 'Replica processed: %1$s',
 	'Core:Synchro:Nb_Class:Objects' => '%1$s: %2$s',
 ));
 ?>

+ 4 - 3
synchro/synchro_exec.php

@@ -136,7 +136,7 @@ foreach(explode(',', $sDataSourcesList) as $iSDS)
 			}
 			foreach ($aResults as $sMessage)
 			{
-				$oP->p($sMessage);
+				$oP->p('#'.$sMessage);
 			}
 			if ($oStatLog->Get('status') == 'error')
 			{
@@ -152,9 +152,10 @@ foreach(explode(',', $sDataSourcesList) as $iSDS)
 			$oP->p("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors'));
 			$oP->p("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated'));
 			$oP->p("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors'));
-			$oP->p("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_new_updated'));
-			$oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_new_unchanged'));
+			$oP->p("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated'));
+			$oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged'));
 			$oP->p("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors'));
+			$oP->p("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action'));
 		}
 		catch(Exception $e)
 		{

+ 53 - 28
synchro/synchro_import.php

@@ -374,6 +374,9 @@ try
 	//
 	try
 	{
+		$oP->add_comment('Load--------------');
+		$oP->add_comment('------------------');
+
 		if ($bSimulate)
 		{
 			CMDBSource::Query('START TRANSACTION');
@@ -444,19 +447,62 @@ try
 			}
 		}
 	
+		if (($sOutput == "summary") || ($sOutput == 'details'))
+		{
+			$oP->add_comment("Data Source: ".$iDataSourceId);
+			$oP->add_comment("Synchronize: ".($bSynchronize ? '1' : '0'));
+			$oP->add_comment("Class: ".$sClass);
+			$oP->add_comment("Separator: ".$sSep);
+			$oP->add_comment("Qualifier: ".$sQualifier);
+			$oP->add_comment("Charset Encoding:".$sCharSet);
+			$oP->add_comment("Data Size: ".strlen($sCSVData));
+			$oP->add_comment("Data Lines: ".$iLineCount);
+			$oP->add_comment("Columns: ".implode(', ', $aInputColumns));
+			$oP->add_comment("Output format: ".$sOutput);
+	//		$oP->add_comment("Report level: ".$sReportLevel);
+			$oP->add_comment("Simulate: ".($bSimulate ? '1' : '0'));
+			$oP->add_comment("Change tracking comment: ".$sComment);
+			$oP->add_comment("Issues (before synchro): ".$iCountErrors);
+	//		$oP->add_comment("Warnings: ".$iCountWarnings);
+			$oP->add_comment("Created (before synchro): ".$iCountCreations);
+			$oP->add_comment("Updated (before synchro): ".$iCountUpdates);
+		}
+
 		//////////////////////////////////////////////////
 		//
 		// Synchronize
 		//
 		if ($bSynchronize)
 		{
-			$aTraces = array();
-			$oStatLog = $oDataSource->Synchronize($aTraces, $oLoadStartDate);
-			//echo "#@# Synchronize() returned :<br/>\n";
-			//echo "<pre>\n";
-			//print_r($aTraces);
-			//print_r($oStatLog);
-			//echo "</pre>\n";
+			$aResults = array();
+			$oStatLog = $oDataSource->Synchronize($aResults, $oLoadStartDate);
+			$oP->add_comment('Synchronization---');
+			$oP->add_comment('------------------');
+			if ($sOutput == 'details')
+			{
+				foreach ($aResults as $sMessage)
+				{
+					$oP->add_comment($sMessage);
+				}
+			}
+			if ($oStatLog->Get('status') == 'error')
+			{
+				$oP->p("ERROR: ".$oStatLog->Get('last_error'));
+			}
+			$oP->add_comment("Replicas: ".$oStatLog->Get('stats_nb_replica_total'));
+			$oP->add_comment("Replicas touched since last synchro: ".$oStatLog->Get('stats_nb_replica_seen'));
+			$oP->add_comment("Objects deleted: ".$oStatLog->Get('stats_nb_obj_deleted'));
+			$oP->add_comment("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors'));
+			$oP->add_comment("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted'));
+			$oP->add_comment("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors'));
+			$oP->add_comment("Objects created: ".$oStatLog->Get('stats_nb_obj_created'));
+			$oP->add_comment("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors'));
+			$oP->add_comment("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated'));
+			$oP->add_comment("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors'));
+			$oP->add_comment("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated'));
+			$oP->add_comment("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged'));
+			$oP->add_comment("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors'));
+			$oP->add_comment("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action'));
 		}
 	}
 	catch(Exception $e)
@@ -480,27 +526,6 @@ try
 	{
 		$oP->add($iCountErrors);
 	}
-
-	if (($sOutput == "summary") || ($sOutput == 'details'))
-	{
-		$oP->add_comment("Data Source: ".$iDataSourceId);
-		$oP->add_comment("Synchronize: ".($bSynchronize ? '1' : '0'));
-		$oP->add_comment("Class: ".$sClass);
-		$oP->add_comment("Separator: ".$sSep);
-		$oP->add_comment("Qualifier: ".$sQualifier);
-		$oP->add_comment("Charset Encoding:".$sCharSet);
-		$oP->add_comment("Data Size: ".strlen($sCSVData));
-		$oP->add_comment("Data Lines: ".$iLineCount);
-		$oP->add_comment("Columns: ".implode(', ', $aInputColumns));
-		$oP->add_comment("Output format: ".$sOutput);
-//		$oP->add_comment("Report level: ".$sReportLevel);
-		$oP->add_comment("Simulate: ".($bSimulate ? '1' : '0'));
-		$oP->add_comment("Change tracking comment: ".$sComment);
-		$oP->add_comment("Issues: ".$iCountErrors);
-//		$oP->add_comment("Warnings: ".$iCountWarnings);
-		$oP->add_comment("Created: ".$iCountCreations);
-		$oP->add_comment("Updated: ".$iCountUpdates);
-	}
 }
 catch(ExchangeException $e)
 {

+ 70 - 46
synchro/synchrodatasource.class.inc.php

@@ -228,7 +228,9 @@ EOF
 );
 			$oPage->add($this->HtmlBox('repl_ignored', $aData, '#999').'<td colspan="2">&nbsp;</td>');
 			$oPage->add("</tr>\n<tr>");
-			$oPage->add($this->HtmlBox('repl_disappeared', $aData, '#630', 'rowspan="3"').'<td rowspan="3" class="arrow">=&gt;</td>'.$this->HtmlBox('obj_deleted', $aData, '#000'));
+			$oPage->add($this->HtmlBox('repl_disappeared', $aData, '#630', 'rowspan="4"').'<td rowspan="4" class="arrow">=&gt;</td>'.$this->HtmlBox('obj_disappeared_no_action', $aData, '#333'));
+			$oPage->add("</tr>\n<tr>");
+			$oPage->add($this->HtmlBox('obj_deleted', $aData, '#000'));
 			$oPage->add("</tr>\n<tr>");
 			$oPage->add($this->HtmlBox('obj_obsoleted', $aData, '#630'));
 			$oPage->add("</tr>\n<tr>");
@@ -271,6 +273,7 @@ EOF
 			'obj_deleted' => $oLastLog->Get('stats_nb_obj_deleted'),
 			'obj_obsoleted' => $oLastLog->Get('stats_nb_obj_obsoleted'),
 			'obj_disappeared_errors' => $oLastLog->Get('stats_nb_obj_obsoleted_errors') + $oLastLog->Get('stats_nb_obj_deleted_errors'),
+			'obj_disappeared_no_action' => $oLastLog->Get('stats_nb_replica_disappeared_no_action'),
 			'obj_updated' => $oLastLog->Get('stats_nb_obj_updated'),
 			'obj_updated_errors' => $oLastLog->Get('stats_nb_obj_updated_errors'),
 			'obj_new_updated' => $oLastLog->Get('stats_nb_obj_new_updated'),
@@ -279,7 +282,7 @@ EOF
 			'obj_created_errors' => $oLastLog->Get('stats_nb_obj_created_errors'),
 		);
 		$iReconciledErrors = $oLastLog->Get('stats_nb_replica_reconciled_errors');
-		$iDisappeared = $aData['obj_disappeared_errors'] + $aData['obj_obsoleted'] + $aData['obj_deleted'];
+		$iDisappeared = $aData['obj_disappeared_errors'] + $aData['obj_obsoleted'] + $aData['obj_deleted'] + $aData['obj_disappeared_no_action'];
 		$aData['repl_disappeared'] = $iDisappeared;
 		$iNewErrors = $aData['obj_created_errors'] + $oLastLog->Get('stats_nb_replica_reconciled_errors');
 		$aData['obj_new_errors'] = $iNewErrors;
@@ -499,6 +502,7 @@ EOF
 		$oStatLog->Set('stats_nb_obj_updated_errors', 0);
 //		$oStatLog->Set('stats_nb_replica_reconciled', 0);
 		$oStatLog->Set('stats_nb_replica_reconciled_errors', 0);
+		$oStatLog->Set('stats_nb_replica_disappeared_no_action', 0);
 		$oStatLog->Set('stats_nb_obj_new_updated', 0);
 		$oStatLog->Set('stats_nb_obj_new_unchanged',0);
 		
@@ -545,6 +549,8 @@ EOF
 			throw new SynchroExceptionNotStarted(Dict::S('Core:SyncDataSourceAccessRestriction'));
 		}
 
+		$sDeletePolicy = $this->Get('delete_policy');
+
 		// Count the replicas
 		$sSelectAll  = "SELECT SynchroReplica WHERE sync_source_id = :source_id";
 		$oSetAll = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAll), array() /* order by*/, array('source_id' => $this->GetKey()));
@@ -584,10 +590,10 @@ EOF
 		} 
 		while($oReplica = $oSetToObsolete->Fetch())
 		{
-			// TO DO: take the appropriate action based on the 'delete_policy' field
-			$sUpdateOnObsolete = $this->Get('delete_policy');
-			if ( ($sUpdateOnObsolete == 'update') || ($sUpdateOnObsolete == 'update_then_delete') )
+			switch ($sDeletePolicy)
 			{
+			case 'update':
+			case 'update_then_delete':
 				$aTraces[] = "Destination object: (dest_id:".$oReplica->Get('dest_id').") to be updated";
 				$aToUpdate = array();
 				$aToUpdate = explode(';', $this->Get('delete_policy_update')); //ex: 'status:obsolete;description:stopped',
@@ -601,10 +607,16 @@ EOF
 						$aToUpdate[$sAttCode] = $sValue;
 					}
 				}
-				$oReplica->UpdateDestObject($aToUpdate, $oMyChange, $oStatLog, $aTraces, 'stats_nb_obj_obsoleted');
+				$oReplica->UpdateDestObject($aToUpdate, $oMyChange, $oStatLog, $aTraces);
+				$oReplica->Set('status', 'obsolete');
+				$oReplica->DBUpdateTracked($oMyChange);
+				break;
+
+         case 'delete':
+         default:
+				$aTraces[] = "Destination object: (dest_id:".$oReplica->Get('dest_id').") to be DELETED";
+				$oReplica->DeleteDestObject($oMyChange, $oStatLog, $aTraces);
 			}
-			$oReplica->Set('status', 'obsolete');
-			$oReplica->DBUpdateTracked($oMyChange);
 		}
 
 		//Count "seen" objects
@@ -674,37 +686,34 @@ EOF
 		
 		// Get all the replicas that are to be deleted
 		//
-		$oDeletionDate = $oLastFullLoadStartDate;
-		$sDeleteRetention = trim($this->Get('delete_policy_retention'));
-		if (strlen($sDeleteRetention) > 0)
+		if ($sDeletePolicy == 'update_then_delete')
 		{
-			$sInterval = '-'.$sDeleteRetention;
-			// Note: the PHP doc states that Modify return FALSE in case of error
-			//       but, this is actually NOT the case
-			//       Therefore, I do compare before and after, considering that the
-			//       format is incorrect when the datetime remains unchanged
-			$sBefore = $oDeletionDate->Format('Y-m-d H:i:s');
-			$oDeletionDate->Modify($sInterval);
-			$sAfter = $oDeletionDate->Format('Y-m-d H:i:s');
-			if ($sBefore == $sAfter)
+			$oDeletionDate = $oLastFullLoadStartDate;
+			$sDeleteRetention = trim($this->Get('delete_policy_retention')); // MUST NOT BE NULL
+			if (strlen($sDeleteRetention) > 0)
 			{
-				throw new SynchroExceptionNotStarted("Data exchange: Wrong interval specification", array('interval' => $sInterval, 'source_id' => $this->GetKey()));
+				$sInterval = '-'.$sDeleteRetention;
+				// Note: the PHP doc states that Modify return FALSE in case of error
+				//       but, this is actually NOT the case
+				//       Therefore, I do compare before and after, considering that the
+				//       format is incorrect when the datetime remains unchanged
+				$sBefore = $oDeletionDate->Format('Y-m-d H:i:s');
+				$oDeletionDate->Modify($sInterval);
+				$sAfter = $oDeletionDate->Format('Y-m-d H:i:s');
+				if ($sBefore == $sAfter)
+				{
+					throw new SynchroExceptionNotStarted("Data exchange: Wrong interval specification", array('interval' => $sInterval, 'source_id' => $this->GetKey()));
+				}
 			}
-		}
-		$sDeletionDate = $oDeletionDate->Format('Y-m-d H:i:s');	
-		$aTraces[] = "sDeletionDate: $sDeletionDate";
-		$sSelectToDelete  = "SELECT SynchroReplica WHERE sync_source_id = :source_id AND status IN ('obsolete') AND status_last_seen < :last_import";
-		$oSetToDelete = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToDelete), array() /* order by*/, array('source_id' => $this->GetKey(), 'last_import' => $sDeletionDate));
-		while($oReplica = $oSetToDelete->Fetch())
-		{
-			$sUpdateOnObsolete = $this->Get('delete_policy');
-			if ( ($sUpdateOnObsolete == 'delete') || ($sUpdateOnObsolete == 'update_then_delete') )
+			$sDeletionDate = $oDeletionDate->Format('Y-m-d H:i:s');	
+			$aTraces[] = "Deletion date: $sDeletionDate";
+			$sSelectToDelete  = "SELECT SynchroReplica WHERE sync_source_id = :source_id AND status IN ('obsolete') AND status_last_seen < :last_import";
+			$oSetToDelete = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToDelete), array() /* order by*/, array('source_id' => $this->GetKey(), 'last_import' => $sDeletionDate));
+			while($oReplica = $oSetToDelete->Fetch())
 			{
 				$aTraces[] = "Destination object: (dest_id:".$oReplica->Get('dest_id').") to be DELETED";
 				$oReplica->DeleteDestObject($oMyChange, $oStatLog, $aTraces);
 			}
-			$aTraces[] = "Replica id:".$oReplica->GetKey()." (dest_id:".$oReplica->Get('dest_id').") to be deleted";
-			$oReplica->DBDeleteTracked($oMyChange);
 		}
 	}
 	
@@ -924,6 +933,8 @@ class SynchroLog extends cmdbAbstractObject
 		MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
 //		MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
 		MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled_errors", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_disappeared_no_action", array("allowed_values"=>null, "sql"=>"stats_nb_replica_disappeared_no_action", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
+
 		MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_updated", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_updated", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
 		MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_unchanged", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_unchanged", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
 
@@ -931,7 +942,7 @@ class SynchroLog extends cmdbAbstractObject
 
 		// Display lists
 		MetaModel::Init_SetZListItems('details', array('sync_source_id', 'start_date', 'end_date', 'status', 'stats_nb_replica_total', 'stats_nb_replica_seen', 'stats_nb_obj_created', /*'stats_nb_replica_reconciled',*/ 'stats_nb_obj_updated', 'stats_nb_obj_obsoleted', 'stats_nb_obj_deleted',
-														'stats_nb_obj_created_errors', 'stats_nb_replica_reconciled_errors', 'stats_nb_obj_updated_errors', 'stats_nb_obj_obsoleted_errors', 'stats_nb_obj_deleted_errors', 'stats_nb_obj_new_unchanged', 'stats_nb_obj_new_updated')); // Attributes to be displayed for the complete details
+														'stats_nb_obj_created_errors', 'stats_nb_replica_reconciled_errors', 'stats_nb_replica_disappeared_no_action', 'stats_nb_obj_updated_errors', 'stats_nb_obj_obsoleted_errors', 'stats_nb_obj_deleted_errors', 'stats_nb_obj_new_unchanged', 'stats_nb_obj_new_updated')); // Attributes to be displayed for the complete details
 		MetaModel::Init_SetZListItems('list', array('sync_source_id', 'start_date', 'end_date', 'status', 'stats_nb_replica_seen')); // Attributes to be displayed for a list
 		// Search criteria
 //		MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
@@ -1032,6 +1043,7 @@ class SynchroReplica extends DBObject
 		switch($this->Get('status'))
 		{
 			case 'new':
+			$this->Set('status_dest_creator', false);
 			// If needed, construct the query used for the reconciliation
 			if (!isset(self::$aSearches[$oDataSource->GetKey()]))
 			{
@@ -1092,10 +1104,7 @@ class SynchroReplica extends DBObject
 					$oDestObj = $oDestSet->Fetch();
 					$this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, $aTraces, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors');
 					$this->Set('dest_id', $oDestObj->GetKey());
-					$this->Set('status_dest_creator', false);
 					$this->Set('dest_class', get_class($oDestObj));
-	
-					$oStatLog->Inc('stats_nb_replica_reconciled'); //@@@
 				}
 				else
 				{
@@ -1124,7 +1133,6 @@ class SynchroReplica extends DBObject
 					$oDestObj = $oDestSet->Fetch();
 					$this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, $aTraces, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors');
 					$this->Set('dest_id', $oDestObj->GetKey());
-					$this->Set('status_dest_creator', false);
 					$this->Set('dest_class', get_class($oDestObj));
 				}
 			}
@@ -1230,23 +1238,31 @@ class SynchroReplica extends DBObject
 	/**
 	 * Update the destination object with given values
 	 */	
-	public function UpdateDestObject($aValues, $oChange, &$oStatLog, &$aTraces, $sStatCode)
+	public function UpdateDestObject($aValues, $oChange, &$oStatLog, &$aTraces)
 	{
 		try
 		{
-			$oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id'));
-			foreach($aValues as $sAttCode => $value)
+			if ($this->Get('dest_class') == '')
 			{
-				$oDestObj->Set($sAttCode, $value);
+				$this->SetLastError('No destination object to update');
+				$oStatLog->Inc('stats_nb_obj_obsoleted_errors');
+			}
+			else
+			{
+				$oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id'));
+				foreach($aValues as $sAttCode => $value)
+				{
+					$oDestObj->Set($sAttCode, $value);
+				}
+				$oDestObj->DBUpdateTracked($oChange);
+				$aTraces[] = "Replica id:".$this->GetKey()." (dest_id:".$this->Get('dest_id').") marked as obsolete";
+				$oStatLog->Inc('stats_nb_obj_obsoleted');
 			}
-			$oDestObj->DBUpdateTracked($oChange);
-			$aTraces[] = "Replica id:".$this->GetKey()." (dest_id:".$this->Get('dest_id').") marked as obsolete";
-			$oStatLog->Inc($sStatCode);
 		}
 		catch(Exception $e)
 		{
 			$this->SetLastError('Unable to update the destination object: ', $e);
-			$oStatLog->Inc($sStatCode.'_errors');
+			$oStatLog->Inc('stats_nb_obj_obsoleted_errors');
 		}
 	}
 
@@ -1257,18 +1273,26 @@ class SynchroReplica extends DBObject
 	{
 		if($this->Get('status_dest_creator'))
 		{
-			$oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id'));
 			try
 			{
+				$oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id'));
 				$oDestObj->DBDeleteTracked($oChange);
+				$this->DBDeleteTracked($oChange);
 				$oStatLog->Inc('stats_nb_obj_deleted');
 			}
 			catch(Exception $e)
 			{
 				$this->SetLastError('Unable to delete the destination object: ', $e);
+				$this->Set('status', 'obsolete');
+				$this->DBUpdateTracked($oChange);
 				$oStatLog->Inc('stats_nb_obj_deleted_errors');
 			}
 		}
+		else
+		{
+			$this->DBDeleteTracked($oChange);
+			$oStatLog->Inc('stats_nb_replica_disappeared_no_action');
+		}
 	}
 
 	/**