Optimize SyncML performance

This commit is contained in:
Jörg Lehrke 2009-12-01 11:21:13 +00:00
parent fd884940f8
commit a1ee7d5cbf
7 changed files with 198 additions and 234 deletions

View File

@ -174,119 +174,111 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
// Determine sync type and status response code. // Determine sync type and status response code.
Horde::logMessage("SyncML: Alert " . $this->_alert, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: Alert " . $this->_alert, __FILE__, __LINE__, PEAR_LOG_DEBUG);
switch ($this->_alert) { switch ($this->_alert) {
case ALERT_NEXT_MESSAGE: case ALERT_NEXT_MESSAGE:
$state->setAlert222Received(true); $state->setAlert222Received(true);
case ALERT_RESULT_ALERT: case ALERT_RESULT_ALERT:
case ALERT_NO_END_OF_DATA: case ALERT_NO_END_OF_DATA:
// Nothing to do on our side // Nothing to do on our side
$status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Alert'); $status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Alert');
$status->setCmdRef($this->_cmdID); $status->setCmdRef($this->_cmdID);
if ($this->_sourceLocURI != null) { if ($this->_sourceLocURI != null) {
$status->setSourceRef($this->_sourceLocURI); $status->setSourceRef($this->_sourceLocURI);
} }
if ($this->_targetLocURI != null) { if ($this->_targetLocURI != null) {
$status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI)); $status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI));
} }
if ($this->_alert == ALERT_NEXT_MESSAGE) { if ($this->_alert == ALERT_NEXT_MESSAGE) {
if ($this->_sourceLocURI != null) { if ($this->_sourceLocURI != null) {
$status->setItemSourceLocURI($this->_sourceLocURI); $status->setItemSourceLocURI($this->_sourceLocURI);
} }
if ($this->_targetLocURI != null) { if ($this->_targetLocURI != null) {
$status->setItemTargetLocURI(isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI); $status->setItemTargetLocURI(isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI);
} }
} }
$currentCmdID = $status->output($currentCmdID, $output); $currentCmdID = $status->output($currentCmdID, $output);
return $currentCmdID; return $currentCmdID;
case ALERT_TWO_WAY: case ALERT_TWO_WAY:
if ($anchormatch) { if ($anchormatch) {
$synctype = ALERT_TWO_WAY; $synctype = ALERT_TWO_WAY;
$response = RESPONSE_OK; $response = RESPONSE_OK;
} else { } else {
$synctype = ALERT_SLOW_SYNC; $synctype = ALERT_SLOW_SYNC;
$response = RESPONSE_REFRESH_REQUIRED; $response = RESPONSE_REFRESH_REQUIRED;
} }
break; break;
case ALERT_SLOW_SYNC: case ALERT_SLOW_SYNC:
$synctype = ALERT_SLOW_SYNC; $synctype = ALERT_SLOW_SYNC;
$response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED; $response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED;
break; break;
case ALERT_ONE_WAY_FROM_CLIENT: case ALERT_ONE_WAY_FROM_CLIENT:
if ($anchormatch) { if ($anchormatch) {
$synctype = ALERT_ONE_WAY_FROM_CLIENT; $synctype = ALERT_ONE_WAY_FROM_CLIENT;
$response = RESPONSE_OK; $response = RESPONSE_OK;
} else { } else {
$synctype = ALERT_REFRESH_FROM_CLIENT; $synctype = ALERT_REFRESH_FROM_CLIENT;
$response = RESPONSE_REFRESH_REQUIRED; $response = RESPONSE_REFRESH_REQUIRED;
} }
break; break;
case ALERT_REFRESH_FROM_CLIENT: case ALERT_REFRESH_FROM_CLIENT:
$synctype = ALERT_REFRESH_FROM_CLIENT; $synctype = ALERT_REFRESH_FROM_CLIENT;
$response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED; $response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED;
// We will erase the current server content, // We will erase the current server content,
// then we can add the client's contents. // then we can add the client's contents.
$hordeType = $state->getHordeType($this->_targetLocURI); $hordeType = $state->getHordeType($this->_targetLocURI);
$state->setTargetURI($this->_targetLocURI); $state->setTargetURI($this->_targetLocURI);
$deletes = $state->getClientItems(); $deletes = $state->getClientItems();
if (is_array($deletes)) { if (is_array($deletes)) {
foreach ($deletes as $delete) { foreach ($deletes as $delete) {
$registry->call($hordeType . '/delete', array($delete)); $registry->call($hordeType . '/delete', array($delete));
} }
Horde::logMessage("SyncML: RefreshFromClient " . count($deletes) . " entries deleted for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: RefreshFromClient " . count($deletes) . " entries deleted for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
} }
$anchormatch = false; $anchormatch = false;
break; break;
case ALERT_ONE_WAY_FROM_SERVER: case ALERT_ONE_WAY_FROM_SERVER:
if ($anchormatch) { if ($anchormatch) {
$synctype = ALERT_ONE_WAY_FROM_SERVER; $synctype = ALERT_ONE_WAY_FROM_SERVER;
$response = RESPONSE_OK; $response = RESPONSE_OK;
} else { } else {
$synctype = ALERT_REFRESH_FROM_SERVER; $synctype = ALERT_REFRESH_FROM_SERVER;
$response = RESPONSE_REFRESH_REQUIRED; $response = RESPONSE_REFRESH_REQUIRED;
} }
break; break;
case ALERT_REFRESH_FROM_SERVER: case ALERT_REFRESH_FROM_SERVER:
$synctype = ALERT_REFRESH_FROM_SERVER; $synctype = ALERT_REFRESH_FROM_SERVER;
$response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED; $response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED;
break; break;
case ALERT_RESUME: case ALERT_RESUME:
// @TODO: Suspend and Resume is not supported yet // @TODO: Suspend and Resume is not supported yet
$synctype = ALERT_SLOW_SYNC; $synctype = ALERT_SLOW_SYNC;
$response = RESPONSE_REFRESH_REQUIRED; $response = RESPONSE_REFRESH_REQUIRED;
break; break;
default: default:
// We can't handle this one // We can't handle this one
Horde::logMessage('SyncML: Unknown sync type ' . $this->_alert, Horde::logMessage('SyncML: Unknown sync type ' . $this->_alert,
__FILE__, __LINE__, PEAR_LOG_ERR); __FILE__, __LINE__, PEAR_LOG_ERR);
$status = new Horde_SyncML_Command_Status(RESPONSE_BAD_REQUEST, 'Alert'); $status = new Horde_SyncML_Command_Status(RESPONSE_BAD_REQUEST, 'Alert');
$status->setCmdRef($this->_cmdID); $status->setCmdRef($this->_cmdID);
if ($this->_sourceLocURI != null) { if ($this->_sourceLocURI != null) {
$status->setSourceRef($this->_sourceLocURI); $status->setSourceRef($this->_sourceLocURI);
} }
if ($this->_targetLocURI != null) { if ($this->_targetLocURI != null) {
$status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI)); $status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI));
} }
$currentCmdID = $status->output($currentCmdID, $output); $currentCmdID = $status->output($currentCmdID, $output);
return $currentCmdID; return $currentCmdID;
} }
// Store client's Next Anchor in State and
// set server's Next Anchor. After successful sync
// this is then written to persistence for negotiation of
// further syncs.
$state->setClientAnchorNext($type, $this->_metaAnchorNext);
$serverAnchorNext = time();
$state->setServerAnchorNext($type, $serverAnchorNext);
// Now set interval to retrieve server changes from, defined by // Now set interval to retrieve server changes from, defined by
// ServerAnchor [Last,Next] // ServerAnchor [Last,Next]
if ($synctype != ALERT_TWO_WAY && if ($synctype != ALERT_TWO_WAY &&
@ -305,7 +297,7 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
. $this->_targetLocURI . '; sync type ' . $synctype, . $this->_targetLocURI . '; sync type ' . $synctype,
__FILE__, __LINE__, PEAR_LOG_DEBUG); __FILE__, __LINE__, PEAR_LOG_DEBUG);
$sync = &Horde_SyncML_Sync::factory($synctype); $sync = &Horde_SyncML_Sync::factory($synctype);
$state->clearConflictItems($this->_targetLocURI); $state->clearConflictItems($this->_targetLocURI);
} }
$sync->setTargetLocURI($this->_targetLocURI); $sync->setTargetLocURI($this->_targetLocURI);
$sync->setSourceLocURI($this->_sourceLocURI); $sync->setSourceLocURI($this->_sourceLocURI);
@ -313,6 +305,21 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
$sync->setsyncType($synctype); $sync->setsyncType($synctype);
$sync->setFilterExpression($this->_filterExpression); $sync->setFilterExpression($this->_filterExpression);
$state->setSync($this->_targetLocURI, $sync); $state->setSync($this->_targetLocURI, $sync);
$hordeType = $state->getHordeType($this->_targetLocURI);
$changes =& $registry->call($hordeType. '/listBy',
array('action' => 'modify',
'timestamp' => $serverAnchorLast,
'type' => $this->_targetLocURI,
'filter' => $this->_filterExpression));
$state->setChangedItems($this->_targetLocURI, $changes);
// Store client's Next Anchor in State and
// set server's Next Anchor. After successful sync
// this is then written to persistence for negotiation of
// further syncs.
$state->setClientAnchorNext($type, $this->_metaAnchorNext);
$serverAnchorNext = time();
$state->setServerAnchorNext($type, $serverAnchorNext);
$status = new Horde_SyncML_Command_Status($response, 'Alert'); $status = new Horde_SyncML_Command_Status($response, 'Alert');
$status->setCmdRef($this->_cmdID); $status->setCmdRef($this->_cmdID);

View File

@ -127,87 +127,92 @@ class Horde_SyncML_Command_Sync extends Horde_SyncML_Command {
function syncToClient($currentCmdID, &$output) function syncToClient($currentCmdID, &$output)
{ {
Horde::logMessage('SyncML: starting sync to client', __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML: starting sync to client',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
if($state->getSyncStatus() >= CLIENT_SYNC_FINNISHED && $state->getSyncStatus() < SERVER_SYNC_FINNISHED) if ($state->getSyncStatus() >= CLIENT_SYNC_FINNISHED
{ && $state->getSyncStatus() < SERVER_SYNC_FINNISHED)
$deviceInfo = $state->getClientDeviceInfo(); {
$targets = $state->getTargets(); $deviceInfo = $state->getClientDeviceInfo();
foreach($targets as $target) if (($targets = $state->getTargets())) {
{ foreach ($targets as $target)
$sync = &$state->getSync($target);
Horde::logMessage('SyncML['. session_id() .']: sync alerttype '. $sync->_syncType .' found for target ' . $target, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($sync->_syncType == ALERT_ONE_WAY_FROM_CLIENT ||
$sync->_syncType == ALERT_REFRESH_FROM_CLIENT) {
Horde::logMessage('SyncML['. session_id() .']: From client Sync, no sync of '. $target .' to client', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->clearSync($target);
} else if ($state->getSyncStatus() >= CLIENT_SYNC_ACKNOWLEDGED) {
Horde::logMessage("SyncML: starting sync to client $target", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$attrs = array();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
$output->startElement($state->getURI(), 'Sync', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$output->characters($currentCmdID);
$currentCmdID++;
$output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = $sync->_sourceLocURI;
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = (isset($sync->_targetLocURIParameters) ? $sync->_targetLocURI.'?/'.$sync->_targetLocURIParameters : $sync->_targetLocURI);
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source');
if(!$sync->_syncDataLoaded)
{ {
$numberOfItems = $sync->loadData(); $sync = &$state->getSync($target);
if($deviceInfo['supportNumberOfChanges']) Horde::logMessage('SyncML[' . session_id() . ']: sync alerttype ' .
{ $sync->_syncType . ' found for target ' . $target,
$output->startElement($state->getURI(), 'NumberOfChanges', $attrs); __FILE__, __LINE__, PEAR_LOG_DEBUG);
$output->characters($numberOfItems); if ($sync->_syncType == ALERT_ONE_WAY_FROM_CLIENT ||
$output->endElement($state->getURI(), 'NumberOfChanges'); $sync->_syncType == ALERT_REFRESH_FROM_CLIENT) {
} Horde::logMessage('SyncML[' . session_id() .
']: From client Sync, no sync of ' . $target .
' to client', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->clearSync($target);
} elseif ($state->getSyncStatus() >= CLIENT_SYNC_ACKNOWLEDGED) {
Horde::logMessage("SyncML: starting sync to client $target",
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$attrs = array();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
$output->startElement($state->getURI(), 'Sync', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$output->characters($currentCmdID);
$currentCmdID++;
$output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = $sync->_sourceLocURI;
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = (isset($sync->_targetLocURIParameters) ? $sync->_targetLocURI.'?/'.$sync->_targetLocURIParameters : $sync->_targetLocURI);
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source');
if(!$sync->_syncDataLoaded)
{
$numberOfItems = $sync->loadData();
if($deviceInfo['supportNumberOfChanges'])
{
$output->startElement($state->getURI(), 'NumberOfChanges', $attrs);
$output->characters($numberOfItems);
$output->endElement($state->getURI(), 'NumberOfChanges');
}
}
$currentCmdID = $sync->endSync($currentCmdID, $output);
$output->endElement($state->getURI(), 'Sync');
if (isset($state->curSyncItem) ||
$state->getNumberOfElements() === false) {
break;
}
} else {
Horde::logMessage("SyncML: Waiting for client ACKNOWLEDGE for $target",
__FILE__, __LINE__, PEAR_LOG_DEBUG);
}
} }
}
$currentCmdID = $sync->endSync($currentCmdID, $output); // no syncs left
if ($state->getTargets() === false &&
$output->endElement($state->getURI(), 'Sync'); !isset($state->curSyncItem)) {
$state->setSyncStatus(SERVER_SYNC_FINNISHED);
if (isset($state->curSyncItem) || }
$state->getNumberOfElements() === false) { Horde::logMessage('SyncML: syncStatus(syncToClient) = ' .
break; $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
} }
} else { return $currentCmdID;
Horde::logMessage("SyncML: Waiting for client ACKNOWLEDGE for $target", __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
}
// no syncs left
if($state->getTargets() === FALSE &&
!isset($state->curSyncItem)) {
$state->setSyncStatus(SERVER_SYNC_FINNISHED);
}
Horde::logMessage('SyncML: syncStatus(syncToClient) = '. $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
return $currentCmdID;
} }
function endElement($uri, $element) function endElement($uri, $element)

View File

@ -612,7 +612,7 @@ class Horde_SyncML_State {
function getTargets() function getTargets()
{ {
if(count($this->_syncs) < 1) if(count($this->_syncs) < 1)
return FALSE; return false;
foreach($this->_syncs as $target => $sync) foreach($this->_syncs as $target => $sync)
{ {

View File

@ -213,8 +213,9 @@ class EGW_SyncML_State extends Horde_SyncML_State
/** /**
* returns GUIDs of all client items * returns GUIDs of all client items
*/ */
function getClientItems() { function getClientItems($type=false) {
$mapID = $this->_locName . $this->_sourceURI . $this->_targetURI; if (!$type) $type = $this->_targetURI;
$mapID = $this->_locName . $this->_sourceURI . $type;
$guids = array(); $guids = array();
foreach($GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', array( foreach($GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', array(

View File

@ -177,14 +177,8 @@ class Horde_SyncML_Sync {
$sourceURI = $state->getSourceURI(); $sourceURI = $state->getSourceURI();
$hordeType = $state->getHordeType($type); $hordeType = $state->getHordeType($type);
$serverAnchorLast = $state->getServerAnchorLast($type); $serverAnchorLast = $state->getServerAnchorLast($type);
$state->setTargetURI($type);
$changes = array(); $changes = array();
// First we get all changes done after the previous sync start foreach($state->getChangedItems($type) as $change) {
foreach ($registry->call($hordeType. '/listBy',
array('action' => 'modify',
'timestamp' => $serverAnchorLast,
'type' => $type,
'filter' => $this->_filterExpression)) as $change) {
// now we have to remove the ones // now we have to remove the ones
// that came from the last sync with this client // that came from the last sync with this client
$guid_ts = $state->getSyncTSforAction($change, 'modify'); $guid_ts = $state->getSyncTSforAction($change, 'modify');

View File

@ -308,21 +308,11 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$hordeType = $state->getHordeType($syncType); $hordeType = $state->getHordeType($syncType);
$state->setTargetURI($syncType); $state->setTargetURI($syncType);
$future = $state->getServerAnchorNext($syncType); $future = $state->getServerAnchorNext($syncType);
$delta_add = 0;
Horde::logMessage("SyncML: reading added items from database for $hordeType",
__FILE__, __LINE__, PEAR_LOG_DEBUG);
/* The items, which now match the filter criteria are show here, too
$delta_add = count($registry->call($hordeType. '/listBy',
array('action' => 'add',
'timestamp' => $future,
'type' => $syncType,
'filter' => $this->_filterExpression)));
*/
$state->mergeAddedItems($syncType, $registry->call($hordeType. '/list', array('filter' => $this->_filterExpression))); $state->mergeAddedItems($syncType, $registry->call($hordeType. '/list', array('filter' => $this->_filterExpression)));
$this->_syncDataLoaded = TRUE; $this->_syncDataLoaded = TRUE;
return count($state->getAddedItems($syncType)) - $delta_add + count($state->getConflictItems($syncType)); return count($state->getAddedItems($syncType)) + count($state->getConflictItems($syncType));
} }
} }

View File

@ -409,27 +409,8 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$state = & $_SESSION['SyncML.state']; $state = & $_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI; $syncType = $this->_targetLocURI;
$hordeType = $state->getHordeType($syncType); $hordeType = $state->getHordeType($syncType);
$state->setTargetURI($syncType);
$refts = $state->getServerAnchorLast($syncType); $refts = $state->getServerAnchorLast($syncType);
$future = $state->getServerAnchorNext($syncType); $future = $state->getServerAnchorNext($syncType);
$delta_mod = 0;
$delta_add = 0;
Horde :: logMessage("SyncML: reading changed items from database for $hordeType",
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$delta_mod = count($registry->call($hordeType . '/listBy', array (
'action' => 'modify',
'timestamp' => $future,
'type' => $syncType,
'filter' => $this->_filterExpression
)));
$changedItems =& $registry->call($hordeType . '/listBy', array (
'action' => 'modify',
'timestamp' => $refts,
'type' => $syncType,
'filter' => $this->_filterExpression
));
$addedItems =& $registry->call($hordeType . '/listBy', array ( $addedItems =& $registry->call($hordeType . '/listBy', array (
'action' => 'add', 'action' => 'add',
@ -438,12 +419,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
'filter' => $this->_filterExpression 'filter' => $this->_filterExpression
)); ));
// added items may show up as changed, too $state->setAddedItems($syncType, $addedItems);
$changedItems = array_diff($changedItems, $addedItems);
$state->mergeChangedItems($syncType, $changedItems);
$state->mergeAddedItems($syncType, $addedItems);
$state->setDeletedItems($syncType, $registry->call($hordeType . '/listBy', array ( $state->setDeletedItems($syncType, $registry->call($hordeType . '/listBy', array (
'action' => 'delete', 'action' => 'delete',
@ -452,17 +428,8 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
'filter' => $this->_filterExpression 'filter' => $this->_filterExpression
))); )));
/* The items, which now match the filter criteria are show here, too
$delta_add = count($registry->call($hordeType . '/listBy', array (
'action' => 'add',
'timestamp' => $future,
'type' => $syncType,
'filter' => $this->_filterExpression
)));
*/
$this->_syncDataLoaded = TRUE; $this->_syncDataLoaded = TRUE;
return count($state->getChangedItems($syncType)) - $delta_mod + count($state->getDeletedItems($syncType)) + count($state->getAddedItems($syncType)) - $delta_add + count($state->getConflictItems($syncType)); return count($state->getChangedItems($syncType)) + count($state->getDeletedItems($syncType)) + count($state->getAddedItems($syncType)) + count($state->getConflictItems($syncType));
} }
} }