Back to index

d-push  2.0
Public Member Functions | Static Public Member Functions | Public Attributes | Protected Member Functions | Protected Attributes | Static Protected Attributes | Private Member Functions | Private Attributes | Static Private Attributes
LoopDetection Class Reference
Inheritance diagram for LoopDetection:
Inheritance graph
[legend]
Collaboration diagram for LoopDetection:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 LoopDetection ()
 Constructor.
 ProcessLoopDetectionInit ()
 PROCESS LOOP DETECTION.
 ProcessLoopDetectionTerminate ()
 Marks the process entry as termineted successfully on the process stack.
 ProcessLoopDetectionAddException ($exception)
 Adds an Exceptions to the process tracking.
 ProcessLoopDetectionAddStatus ($folderid, $status)
 Adds a folderid and connected status code to the process tracking.
 ProcessLoopDetectionIsHierarchyResyncRequired ()
 Indicates if a full Hierarchy Resync is necessary.
 ProcessLoopDetectionPreviousConnectionFailed ()
 Indicates if a previous process could not be terminated.
 ProcessLoopDetectionGetOutdatedSearchPID ()
 Gets the PID of an outdated search process.
 SetBrokenMessage ($folderid, $id)
 TRACKING OF BROKEN MESSAGES if a previousily ignored message is streamed again to the device it's tracked here.
 GetSyncedButBeforeIgnoredMessages ($folderid)
 Gets a list of all ids of a folder which were tracked and which were accepted by the device from the last sync.
 IsSyncStateObsolete ($folderid, $uuid, $counter)
 Checks if the given counter for a certain uuid+folderid was exported before.
 Detect ($folderid, $type, $uuid, $counter, $maxItems, $queuedMessages)
 MESSAGE LOOP DETECTION.
 IgnoreNextMessage ($markAsIgnored=true, $messageid=false, $folderid=false)
 Indicates if the next messages should be ignored (not be sent to the mobile!)
 ClearData ($user=false, $devid=false)
 Clears loop detection data.
 GetCachedData ($user, $devid)
 Returns loop detection data for a user and device.
 InterProcessData ()
 Constructor.
 InitializeParams ()
 Initializes internal parameters.
 ReInitSharedMem ()
 Reinitializes shared memory by removing, detaching and re-allocating it.
 Clean ()
 Cleans up the shared memory block.
 IsActive ()
 Indicates if the shared memory is active.

Static Public Member Functions

static GetProcessIdentifier ()
 Returns a unique identifier for the internal process tracking.
static GetProcessEntry ()
 Returns a unique entry with informations about the current process.

Public Attributes

const INTERPROCESSLD = "ipldkey"
const BROKENMSGS = "bromsgs"
const CLEANUPTIME = 1

Protected Member Functions

 blockMutex ()
 Blocks the class mutex Method blocks until mutex is available! ATTENTION: make sure that you always release a blocked mutex!
 releaseMutex ()
 Releases the class mutex After the release other processes are able to block the mutex themselfs.
 hasData ($id=2)
 Indicates if the requested variable is available in shared memory.
 getData ($id=2)
 Returns the requested variable from shared memory.
 setData ($data, $id=2)
 Writes the transmitted variable to shared memory Subclasses may never use an id < 2!

Protected Attributes

 $type
 $allocate

Static Protected Attributes

static $devid
static $pid
static $user
static $start

Private Member Functions

 updateProcessStack ()
 Inserts or updates the current process entry on the stack.
 getProcessStack ()
 Returns the current process stack.
 checkArrayStructure (&$loopdata, $folderid)
 Builds an array structure for the loop detection data.

Private Attributes

 $ignore_messageid
 $broken_message_uuid
 $broken_message_counter

Static Private Attributes

static $processident
static $processentry

Detailed Description

Definition at line 50 of file loopdetection.php.


Member Function Documentation

InterProcessData::blockMutex ( ) [protected, inherited]

Blocks the class mutex Method blocks until mutex is available! ATTENTION: make sure that you always release a blocked mutex!

protected

Returns:
boolean

Definition at line 200 of file interprocessdata.php.

                                    {
        if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false))
            return @sem_acquire($this->mutexid);

        return false;
    }

Here is the caller graph for this function:

LoopDetection::checkArrayStructure ( &$  loopdata,
folderid 
) [private]

Builds an array structure for the loop detection data.

Parameters:
array$loopdatareference to the topdata array

private

Returns:

Definition at line 863 of file loopdetection.php.

                                                                {
        if (!isset($loopdata) || !is_array($loopdata))
            $loopdata = array();

        if (!isset($loopdata[self::$devid]))
            $loopdata[self::$devid] = array();

        if (!isset($loopdata[self::$devid][self::$user]))
            $loopdata[self::$devid][self::$user] = array();

        if (!isset($loopdata[self::$devid][self::$user][$folderid]))
            $loopdata[self::$devid][self::$user][$folderid] = array();
    }

Here is the caller graph for this function:

InterProcessData::Clean ( ) [inherited]

Cleans up the shared memory block.

public

Returns:
boolean

Definition at line 166 of file interprocessdata.php.

                            {
        $stat = false;

        // exclusive block
        if ($this->blockMutex()) {
            $cleanuptime = ($this->hasData(1)) ? $this->getData(1) : false;

            // TODO implement Shared Memory cleanup

            $this->releaseMutex();
        }
        // end exclusive block

        return $stat;
    }

Here is the call graph for this function:

LoopDetection::ClearData ( user = false,
devid = false 
)

Clears loop detection data.

Parameters:
string$user(opt) user which data should be removed - user can not be specified without
string$devid(opt) device id which data to be removed
Returns:
boolean public

Definition at line 804 of file loopdetection.php.

                                                             {
        $stat = true;
        $ok = false;

        // exclusive block
        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();

            if ($user == false && $devid == false)
                $loopdata = array();
            elseif ($user == false && $devid != false)
                $loopdata[$devid] = array();
            elseif ($user != false && $devid != false)
                $loopdata[$devid][$user] = array();
            elseif ($user != false && $devid == false) {
                ZLog::Write(LOGLEVEL_WARN, sprintf("Not possible to reset loop detection data for user '%s' without a specifying a device id", $user));
                $stat = false;
            }

            if ($stat)
                $ok = $this->setData($loopdata);

            $this->releaseMutex();
        }
        // end exclusive block

        return $stat && $ok;
    }

Here is the call graph for this function:

LoopDetection::Detect ( folderid,
type,
uuid,
counter,
maxItems,
queuedMessages 
)

MESSAGE LOOP DETECTION.

Loop detection mechanism

  1. request counter is higher than the previous counter (somehow default) 1.1) standard situation -> do nothing 1.2) loop information exists 1.2.1) request counter < maxCounter AND no ignored data -> continue in loop mode 1.2.2) request counter < maxCounter AND ignored data -> we have already encountered issue, return to normal
  1. request counter is the same as the previous, but no data was sent on the last request (standard situation)
  1. request counter is the same as the previous and last time objects were sent (loop!) 3.1) no loop was detected before, entereing loop mode -> save loop data, loopcount = 1 3.2) loop was detected before, but are gone -> loop resolved 3.3) loop was detected before, continuing in loop mode -> this is probably the broken element,loopcount++, 3.3.1) item identified, loopcount >= 3 -> ignore item, set ignoredata flag
Parameters:
string$folderidthe current folder id to be worked on
string$typethe type of that folder (Email, Calendar, Contact, Task)
string$uuidthe synkkey
string$counterthe synckey counter
string$maxItemsthe current amount of items to be sent to the mobile
string$queuedMessagesthe amount of messages which were found by the exporter

public

Returns:
boolean when returning true if a loop has been identified

Definition at line 557 of file loopdetection.php.

                                                                                          {
        $this->broken_message_uuid = $uuid;
        $this->broken_message_counter = $counter;

        // if an incoming loop is already detected, do nothing
        if ($maxItems === 0 && $queuedMessages > 0) {
            ZPush::GetTopCollector()->AnnounceInformation("Incoming loop!", true);
            return true;
        }

        // initialize params
        $this->InitializeParams();

        $loop = false;

        // exclusive block
        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();

            // check and initialize the array structure
            $this->checkArrayStructure($loopdata, $folderid);

            $current = $loopdata[self::$devid][self::$user][$folderid];

            // completely new/unknown UUID
            if (empty($current))
                $current = array("type" => $type, "uuid" => $uuid, "count" => $counter-1, "queued" => $queuedMessages);

            // old UUID in cache - the device requested a new state!!
            else if (isset($current['type']) && $current['type'] == $type && isset($current['uuid']) && $current['uuid'] != $uuid ) {
                ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): UUID changed for folder");

                // some devices (iPhones) may request new UUIDs after broken items were sent several times
                if (isset($current['queued']) && $current['queued'] > 0 &&
                    (isset($current['maxCount']) && $current['count']+1 < $current['maxCount'] || $counter == 1)) {

                    ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): UUID changed and while items where sent to device - forcing loop mode");
                    $loop = true; // force loop mode
                    $current['queued'] = $queuedMessages;
                }
                else {
                    $current['queued'] = 0;
                }

                // set new data, unset old loop information
                $current["uuid"] = $uuid;
                $current['count'] = $counter;
                unset($current['loopcount']);
                unset($current['ignored']);
                unset($current['maxCount']);
                unset($current['potential']);
            }

            // see if there are values
            if (isset($current['uuid']) && $current['uuid'] == $uuid &&
                isset($current['type']) && $current['type'] == $type &&
                isset($current['count'])) {

                // case 1 - standard, during loop-resolving & resolving
                if ($current['count'] < $counter) {

                    // case 1.1
                    $current['count'] = $counter;
                    $current['queued'] = $queuedMessages;

                    // case 1.2
                    if (isset($current['maxCount'])) {
                        ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2 detected");

                        // case 1.2.1
                        // broken item not identified yet
                        if (!isset($current['ignored']) && $counter < $current['maxCount']) {
                            $loop = true; // continue in loop-resolving
                            ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2.1 detected");
                        }
                        // case 1.2.2 - if there were any broken items they should be gone, return to normal
                        else {
                            ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2.2 detected");
                            unset($current['loopcount']);
                            unset($current['ignored']);
                            unset($current['maxCount']);
                            unset($current['potential']);
                        }
                    }
                }

                // case 2 - same counter, but there were no changes before and are there now
                else if ($current['count'] == $counter && $current['queued'] == 0 && $queuedMessages > 0) {
                    $current['queued'] = $queuedMessages;
                }

                // case 3 - same counter, changes sent before, hanging loop and ignoring
                else if ($current['count'] == $counter && $current['queued'] > 0) {

                    if (!isset($current['loopcount'])) {
                        // case 3.1) we have just encountered a loop!
                        ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.1 detected - loop detected, init loop mode");
                        $current['loopcount'] = 1;
                        // the MaxCount is the max number of messages exported before
                        $current['maxCount'] = $counter + (($maxItems < $queuedMessages)? $maxItems: $queuedMessages);
                        $loop = true;   // loop mode!!
                    }
                    else if ($queuedMessages == 0) {
                        // case 3.2) there was a loop before but now the changes are GONE
                        ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.2 detected - changes gone - clearing loop data");
                        $current['queued'] = 0;
                        unset($current['loopcount']);
                        unset($current['ignored']);
                        unset($current['maxCount']);
                        unset($current['potential']);
                    }
                    else {
                        // case 3.3) still looping the same message! Increase counter
                        ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.3 detected - in loop mode, increase loop counter");
                        $current['loopcount']++;

                        // case 3.3.1 - we got our broken item!
                        if ($current['loopcount'] >= 3 && isset($current['potential'])) {
                            ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->Detect(): case 3.3.1 detected - broken item should be next, attempt to ignore it - id '%s'", $current['potential']));
                            $this->ignore_messageid = $current['potential'];
                        }
                        $current['maxCount'] = $counter + $queuedMessages;
                        $loop = true;   // loop mode!!
                    }
                }

            }
            if (isset($current['loopcount']))
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->Detect(): loop data: loopcount(%d), maxCount(%d), queued(%d), ignored(%s)", $current['loopcount'], $current['maxCount'], $current['queued'], (isset($current['ignored'])?$current['ignored']:'false')));

            // update loop data
            $loopdata[self::$devid][self::$user][$folderid] = $current;
            $ok = $this->setData($loopdata);

            $this->releaseMutex();
        }
        // end exclusive block

        if ($loop == true && $this->ignore_messageid == false) {
            ZPush::GetTopCollector()->AnnounceInformation("Loop detection", true);
        }

        return $loop;
    }

Here is the call graph for this function:

LoopDetection::GetCachedData ( user,
devid 
)

Returns loop detection data for a user and device.

Parameters:
string$user
string$devid
Returns:
array/boolean returns false if data not available public

Definition at line 842 of file loopdetection.php.

                                                 {
        // exclusive block
        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();
            $this->releaseMutex();
        }
        // end exclusive block
        if (isset($loopdata) && isset($loopdata[$devid]) && isset($loopdata[$devid][$user]))
            return $loopdata[$devid][$user];

        return false;
    }

Here is the call graph for this function:

InterProcessData::getData ( id = 2) [protected, inherited]

Returns the requested variable from shared memory.

Parameters:
int$idint indicating the variable

protected

Returns:
mixed

Definition at line 249 of file interprocessdata.php.

                                        {
        if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false))
            return @shm_get_var($this->memid, $id);

        return ;
    }

Here is the caller graph for this function:

static LoopDetection::GetProcessEntry ( ) [static]

Returns a unique entry with informations about the current process.

public

Returns:
array

Definition at line 122 of file loopdetection.php.

                                             {
        if (!isset(self::$processentry)) {
            self::$processentry = array();
            self::$processentry['id'] = self::GetProcessIdentifier();
            self::$processentry['pid'] = self::$pid;
            self::$processentry['time'] = self::$start;
            self::$processentry['cc'] = Request::GetCommandCode();
        }

        return self::$processentry;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

Returns a unique identifier for the internal process tracking.

public

Returns:
string

Definition at line 109 of file loopdetection.php.

                                                  {
        if (!isset(self::$processident))
            self::$processident = sprintf('%04x%04', mt_rand(0, 0xffff), mt_rand(0, 0xffff));

        return self::$processident;
    }

Here is the caller graph for this function:

Returns the current process stack.

private

Returns:
array

Definition at line 341 of file loopdetection.php.

                                       {
        // initialize params
        $this->InitializeParams();
        $stack = array();

        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();

            // check and initialize the array structure
            $this->checkArrayStructure($loopdata, self::INTERPROCESSLD);

            $stack = $loopdata[self::$devid][self::$user][self::INTERPROCESSLD];

            $this->releaseMutex();
        }
        // end exclusive block

        return $stack;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

Gets a list of all ids of a folder which were tracked and which were accepted by the device from the last sync.

Parameters:
string$folderidthe parent folder of the message
string$idthe id of the message

public

Returns:
array

Definition at line 423 of file loopdetection.php.

                                                                 {
        if ($folderid == false || !isset($this->broken_message_uuid) || !isset($this->broken_message_counter) || $this->broken_message_uuid == false || $this->broken_message_counter == false)
            return array();

        $brokenkey = self::BROKENMSGS ."-". $folderid;
        $removeIds = array();
        $okIds = array();

        // initialize params
        $this->InitializeParams();
        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();

            // check and initialize the array structure
            $this->checkArrayStructure($loopdata, $brokenkey);

            $brokenmsgs = $loopdata[self::$devid][self::$user][$brokenkey];

            if (!empty($brokenmsgs)) {
                foreach ($brokenmsgs as $id => $data) {
                    // previously broken message was sucessfully synced!
                    if ($data['uuid'] == $this->broken_message_uuid && $data['counter'] < $this->broken_message_counter) {
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): message '%s' was successfully synchronized", $folderid, $id));
                        $okIds[] = $id;
                    }

                    // if the uuid has changed this is old data which should also be removed
                    if ($data['uuid'] != $this->broken_message_uuid) {
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): stored message id '%s' for uuid '%s' is obsolete", $folderid, $id, $data['uuid']));
                        $removeIds[] = $id;
                    }
                }

                // remove data
                foreach (array_merge($okIds,$removeIds) as $id) {
                    unset($brokenmsgs[$id]);
                }

                if (empty($brokenmsgs) && isset($loopdata[self::$devid][self::$user][$brokenkey])) {
                    unset($loopdata[self::$devid][self::$user][$brokenkey]);
                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): removed folder from tracking of ignored messages", $folderid));
                }
                else {
                    // update data
                    $loopdata[self::$devid][self::$user][$brokenkey] = $brokenmsgs;
                }
                $ok = $this->setData($loopdata);
            }

            $this->releaseMutex();
        }
        // end exclusive block

        return $okIds;
    }

Here is the call graph for this function:

InterProcessData::hasData ( id = 2) [protected, inherited]

Indicates if the requested variable is available in shared memory.

Parameters:
int$idint indicating the variable

protected

Returns:
boolean

Definition at line 229 of file interprocessdata.php.

                                        {
        if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false)) {
            if (function_exists("shm_has_var"))
                return @shm_has_var($this->memid, $id);
            else {
                $some = $this->getData($id);
                return isset($some);
            }
        }
        return false;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

LoopDetection::IgnoreNextMessage ( markAsIgnored = true,
messageid = false,
folderid = false 
)

Indicates if the next messages should be ignored (not be sent to the mobile!)

Parameters:
string$messageid(opt) id of the message which is to be exported next
string$folderid(opt) parent id of the message
boolean$markAsIgnored(opt) to peek without setting the next message to be ignored, set this value to false public
Returns:
boolean

Definition at line 712 of file loopdetection.php.

                                                                                                    {
        // as the next message id is not available at all point this method is called, we use different indicators.
        // potentialbroken indicates that we know that the broken message should be exported next,
        // alltho we do not know for sure as it's export message orders can change
        // if the $messageid is available and matches then we are sure and only then really ignore it

        $potentialBroken = false;
        $realBroken = false;
        if (Request::GetCommandCode() == ZPush::COMMAND_SYNC && $this->ignore_messageid !== false)
            $potentialBroken = true;

        if ($messageid !== false && $this->ignore_messageid == $messageid)
            $realBroken = true;

        // this call is just to know what should be happening
        // no further actions necessary
        if ($markAsIgnored === false) {
            return $potentialBroken;
        }

        // we should really do something here

        // first we check if we are in the loop mode, if so,
        // we update the potential broken id message so we loop count the same message

        $changedData = false;
        // exclusive block
        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();

            // check and initialize the array structure
            $this->checkArrayStructure($loopdata, $folderid);

            $current = $loopdata[self::$devid][self::$user][$folderid];

            // we found our broken message!
            if ($realBroken) {
                $this->ignore_messageid = false;
                $current['ignored'] = $messageid;
                $changedData = true;

                // check if this message was broken before - here we know that it still is and remove it from the tracking
                $brokenkey = self::BROKENMSGS ."-". $folderid;
                if (isset($loopdata[self::$devid][self::$user][$brokenkey]) && isset($loopdata[self::$devid][self::$user][$brokenkey][$messageid])) {
                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): previously broken message '%s' is still broken and will not be tracked anymore", $messageid));
                    unset($loopdata[self::$devid][self::$user][$brokenkey][$messageid]);
                }
            }
            // not the broken message yet
            else {
                // update potential id if looping on an item
                if (isset($current['loopcount'])) {
                    $current['potential'] = $messageid;

                    // this message should be the broken one, but is not!!
                    // we should reset the loop count because this is certainly not the broken one
                    if ($potentialBroken) {
                        $current['loopcount'] = 1;
                        ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IgnoreNextMessage(): this should be the broken one, but is not! Resetting loop count.");
                    }

                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): Loop mode, potential broken message id '%s'", $current['potential']));

                    $changedData = true;
                }
            }

            // update loop data
            if ($changedData == true) {
                $loopdata[self::$devid][self::$user][$folderid] = $current;
                $ok = $this->setData($loopdata);
            }

            $this->releaseMutex();
        }
        // end exclusive block

        if ($realBroken)
            ZPush::GetTopCollector()->AnnounceInformation("Broken message ignored", true);

        return $realBroken;
    }

Here is the call graph for this function:

Initializes internal parameters.

public

Returns:
boolean

Definition at line 76 of file interprocessdata.php.

                                       {
        if (!isset(self::$devid)) {
            self::$devid = Request::GetDeviceID();
            self::$pid = @getmypid();
            self::$user = Request::GetAuthUser();
            self::$start = time();
        }
        return true;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

Constructor.

public

Definition at line 62 of file interprocessdata.php.

                                       {
        if (!isset($this->type) || !isset($this->allocate))
            throw new FatalNotImplementedException(sprintf("Class InterProcessData can not be initialized. Subclass %s did not initialize type and allocable memory.", get_class($this)));

        if ($this->InitSharedMem())
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("%s(): Initialized mutexid %s and memid %s.", get_class($this), $this->mutexid, $this->memid));
    }

Here is the call graph for this function:

InterProcessData::IsActive ( ) [inherited]

Indicates if the shared memory is active.

public

Returns:
boolean

Definition at line 188 of file interprocessdata.php.

                               {
        return ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false));
    }

Here is the caller graph for this function:

LoopDetection::IsSyncStateObsolete ( folderid,
uuid,
counter 
)

Checks if the given counter for a certain uuid+folderid was exported before.

Returns also true if the counter are the same but previously there were changes to be exported.

Parameters:
string$folderidfolder id
string$uuidsynkkey
string$countersynckey counter

public

Returns:
boolean indicating if an uuid+counter were exported (with changes) before

Definition at line 491 of file loopdetection.php.

                                                                    {
        // initialize params
        $this->InitializeParams();

        $obsolete = false;

        // exclusive block
        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();
            $this->releaseMutex();
            // end exclusive block

            // check and initialize the array structure
            $this->checkArrayStructure($loopdata, $folderid);

            $current = $loopdata[self::$devid][self::$user][$folderid];

            if (!empty($current)) {
                if ($current["uuid"] != $uuid) {
                    ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IsSyncStateObsolete(): yes, uuid changed");
                    $obsolete = true;
                }
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IsSyncStateObsolete(): check uuid counter: %d - last known counter: %d with %d queued objects", $counter, $current["count"], $current["queued"]));

                if ($current["uuid"] == $uuid && ($current["count"] > $counter || ($current["count"] == $counter && $current["queued"] > 0))) {
                    ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IsSyncStateObsolete(): yes, counter already processed");
                    $obsolete = true;
                }
            }

        }

        return $obsolete;
    }

Here is the call graph for this function:

Constructor.

public

Definition at line 65 of file loopdetection.php.

                                    {
        // initialize super parameters
        $this->allocate = 204800; // 200 KB
        $this->type = 1337;
        parent::__construct();

        $this->ignore_messageid = false;
    }

Adds an Exceptions to the process tracking.

Parameters:
Exception$exceptionpublic
Returns:
boolean

Definition at line 142 of file loopdetection.php.

                                                                 {
        // generate entry if not already there
        self::GetProcessEntry();

        if (!isset(self::$processentry['stat']))
            self::$processentry['stat'] = array();

        self::$processentry['stat'][get_class($exception)] = $exception->getCode();

        $this->updateProcessStack();
        return true;
    }

Here is the call graph for this function:

LoopDetection::ProcessLoopDetectionAddStatus ( folderid,
status 
)

Adds a folderid and connected status code to the process tracking.

Parameters:
string$folderid
int$statuspublic
Returns:
boolean

Definition at line 164 of file loopdetection.php.

                                                                      {
        // generate entry if not already there
        self::GetProcessEntry();

        if ($folderid === false)
            $folderid = "hierarchy";

        if (!isset(self::$processentry['stat']))
            self::$processentry['stat'] = array();

        self::$processentry['stat'][$folderid] = $status;

        $this->updateProcessStack();

        return true;
    }

Here is the call graph for this function:

Gets the PID of an outdated search process.

Returns false if there isn't any process

public

Returns:
boolean

Definition at line 275 of file loopdetection.php.

                                                               {
        $stack = $this->getProcessStack();
        if (count($stack) > 1) {
            $se = $stack[0];
            if ($se['cc'] == ZPush::COMMAND_SEARCH) {
                return $se['pid'];
            }
        }
        return false;
    }

Here is the call graph for this function:

PROCESS LOOP DETECTION.

Adds the process entry to the process stack

public

Returns:
boolean

Definition at line 84 of file loopdetection.php.

                                               {
        return $this->updateProcessStack();
    }

Here is the call graph for this function:

Indicates if a full Hierarchy Resync is necessary.

In some occasions the mobile tries to sync a folder with an invalid/not-existing ID. In these cases a status exception like SYNC_STATUS_FOLDERHIERARCHYCHANGED is returned so the mobile executes a FolderSync expecting that some action is taken on that folder (e.g. remove).

If the FolderSync is not doing anything relevant, then the Sync is attempted again resulting in the same error and looping between these two processes.

This method checks if in the last process stack a Sync and FolderSync were triggered to catch the loop at the 2nd interaction (Sync->FolderSync->Sync->FolderSync => ReSync) Ticket: https://jira.zarafa.com/browse/ZP-5

public

Returns:
boolean

Definition at line 199 of file loopdetection.php.

                                                                    {
        $seenFailed = array();
        $seenFolderSync = false;

        $lookback = self::$start - 600; // look at the last 5 min
        foreach ($this->getProcessStack() as $se) {
            if ($se['time'] > $lookback && $se['time'] < (self::$start-1)) {
                // look for sync command
                if (isset($se['stat']) && ($se['cc'] == ZPush::COMMAND_SYNC || $se['cc'] == ZPush::COMMAND_PING)) {
                    foreach($se['stat'] as $key => $value) {
                        if (!isset($seenFailed[$key]))
                            $seenFailed[$key] = 0;
                        $seenFailed[$key]++;
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): seen command with Exception or folderid '%s' and code '%s'", $key, $value ));
                    }
                }
                // look for FolderSync command with previous failed commands
                if ($se['cc'] == ZPush::COMMAND_FOLDERSYNC && !empty($seenFailed) && $se['id'] != self::GetProcessIdentifier()) {
                    // a full folderresync was already triggered
                    if (isset($se['stat']) && isset($se['stat']['hierarchy']) && $se['stat']['hierarchy'] == SYNC_FSSTATUS_SYNCKEYERROR) {
                        ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): a full FolderReSync was already requested. Resetting fail counter.");
                        $seenFailed = array();
                    }
                    else {
                        $seenFolderSync = true;
                        if (!empty($seenFailed))
                            ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): seen FolderSync after other failing command");
                    }
                }
            }
        }

        $filtered = array();
        foreach ($seenFailed as $k => $count) {
            if ($count>1)
                $filtered[] = $k;
        }

        if ($seenFolderSync && !empty($filtered)) {
            ZLog::Write(LOGLEVEL_INFO, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): Potential loop detected. Full hierarchysync indicated.");
            return true;
        }

        return false;
    }

Here is the call graph for this function:

Indicates if a previous process could not be terminated.

Checks if there is an end time for the last entry on the stack

public

Returns:
boolean

Definition at line 254 of file loopdetection.php.

                                                                   {
        $stack = $this->getProcessStack();
        if (count($stack) > 1) {
            $se = $stack[0];
            if (!isset($se['end']) && $se['cc'] != ZPush::COMMAND_PING) {
                // there is no end time
                ZLog::Write(LOGLEVEL_ERROR, sprintf("LoopDetection->ProcessLoopDetectionPreviousConnectionFailed(): Command '%s' at %s with pid '%d' terminated unexpectedly or is still running.", Utils::GetCommandFromCode($se['cc']), Utils::GetFormattedTime($se['time']), $se['pid']));
                ZLog::Write(LOGLEVEL_ERROR, "Please check your logs for this PID and errors like PHP-Fatals or Apache segmentation faults and report your results to the Z-Push dev team.");
            }
        }
    }

Here is the call graph for this function:

Marks the process entry as termineted successfully on the process stack.

public

Returns:
boolean

Definition at line 94 of file loopdetection.php.

                                                    {
        // just to be sure that the entry is there
        self::GetProcessEntry();

        self::$processentry['end'] = time();
        ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionTerminate()");
        return $this->updateProcessStack();
    }

Here is the call graph for this function:

Reinitializes shared memory by removing, detaching and re-allocating it.

public

Returns:
boolean

Definition at line 156 of file interprocessdata.php.

                                      {
        return ($this->RemoveSharedMem() && $this->InitSharedMem());
    }

Here is the call graph for this function:

InterProcessData::releaseMutex ( ) [protected, inherited]

Releases the class mutex After the release other processes are able to block the mutex themselfs.

protected

Returns:
boolean

Definition at line 214 of file interprocessdata.php.

                                      {
        if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false))
            return @sem_release($this->mutexid);

        return false;
    }

Here is the caller graph for this function:

LoopDetection::SetBrokenMessage ( folderid,
id 
)

TRACKING OF BROKEN MESSAGES if a previousily ignored message is streamed again to the device it's tracked here.

There are two outcomes:

  • next uuid counter is higher than current -> message is fixed and successfully synchronized
  • next uuid counter is the same or uuid changed -> message is still broken Adds a message to the tracking of broken messages Being tracked means that a broken message was streamed to the device. We save the latest uuid and counter so if on the next sync the counter is higher the message was accepted by the device.
Parameters:
string$folderidthe parent folder of the message
string$idthe id of the message

public

Returns:
boolean

Definition at line 382 of file loopdetection.php.

                                                     {
        if ($folderid == false || !isset($this->broken_message_uuid) || !isset($this->broken_message_counter) || $this->broken_message_uuid == false || $this->broken_message_counter == false)
            return false;

        $ok = false;
        $brokenkey = self::BROKENMSGS ."-". $folderid;

        // initialize params
        $this->InitializeParams();
        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();

            // check and initialize the array structure
            $this->checkArrayStructure($loopdata, $brokenkey);

            $brokenmsgs = $loopdata[self::$devid][self::$user][$brokenkey];

            $brokenmsgs[$id] = array('uuid' => $this->broken_message_uuid, 'counter' => $this->broken_message_counter);
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->SetBrokenMessage('%s', '%s'): tracking broken message", $folderid, $id));

            // update data
            $loopdata[self::$devid][self::$user][$brokenkey] = $brokenmsgs;
            $ok = $this->setData($loopdata);

            $this->releaseMutex();
        }
        // end exclusive block

        return $ok;
    }

Here is the call graph for this function:

InterProcessData::setData ( data,
id = 2 
) [protected, inherited]

Writes the transmitted variable to shared memory Subclasses may never use an id < 2!

Parameters:
mixed$datadata which should be saved into shared memory
int$idint indicating the variable (bigger than 2!)

protected

Returns:
boolean

Definition at line 266 of file interprocessdata.php.

                                               {
        if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false))
            return @shm_put_var($this->memid, $id, $data);

        return false;
    }

Here is the caller graph for this function:

Inserts or updates the current process entry on the stack.

private

Returns:
boolean

Definition at line 292 of file loopdetection.php.

                                          {
        // initialize params
        $this->InitializeParams();
        if ($this->blockMutex()) {
            $loopdata = ($this->hasData()) ? $this->getData() : array();

            // check and initialize the array structure
            $this->checkArrayStructure($loopdata, self::INTERPROCESSLD);

            $stack = $loopdata[self::$devid][self::$user][self::INTERPROCESSLD];

            // insert/update current process entry
            $nstack = array();
            $updateentry = self::GetProcessEntry();
            $found = false;

            foreach ($stack as $entry) {
                if ($entry['id'] != $updateentry['id']) {
                    $nstack[] = $entry;
                }
                else {
                    $nstack[] = $updateentry;
                    $found = true;
                }
            }

            if (!$found)
                $nstack[] = $updateentry;

            if (count($nstack) > 10)
                $nstack = array_slice($nstack, -10, 10);

            // update loop data
            $loopdata[self::$devid][self::$user][self::INTERPROCESSLD] = $nstack;
            $ok = $this->setData($loopdata);

            $this->releaseMutex();
        }
        // end exclusive block

        return true;
    }

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

InterProcessData::$allocate [protected, inherited]

Definition at line 53 of file interprocessdata.php.

Definition at line 57 of file loopdetection.php.

Definition at line 56 of file loopdetection.php.

InterProcessData::$devid [static, protected, inherited]

Definition at line 48 of file interprocessdata.php.

Definition at line 55 of file loopdetection.php.

InterProcessData::$pid [static, protected, inherited]

Definition at line 49 of file interprocessdata.php.

LoopDetection::$processentry [static, private]

Definition at line 54 of file loopdetection.php.

LoopDetection::$processident [static, private]

Definition at line 53 of file loopdetection.php.

InterProcessData::$start [static, protected, inherited]

Definition at line 51 of file interprocessdata.php.

InterProcessData::$type [protected, inherited]

Definition at line 52 of file interprocessdata.php.

InterProcessData::$user [static, protected, inherited]

Definition at line 50 of file interprocessdata.php.

const LoopDetection::BROKENMSGS = "bromsgs"

Definition at line 52 of file loopdetection.php.

const InterProcessData::CLEANUPTIME = 1 [inherited]

Definition at line 46 of file interprocessdata.php.

const LoopDetection::INTERPROCESSLD = "ipldkey"

Definition at line 51 of file loopdetection.php.


The documentation for this class was generated from the following file: