Commit 98dcc72c authored by Sebastian Kummer's avatar Sebastian Kummer

ZP-1122 Read configured memory limit once, check memory limit after each

message export and stop when limit is reached.

Released under the Affero GNU General Public License (AGPL) version 3.
parent fc985834
......@@ -25,6 +25,7 @@
************************************************/
class Request {
const MAXMEMORYUSAGE = 0.9; // use max. 90% of allowed memory when synching
const UNKNOWN = "unknown";
/**
......@@ -83,6 +84,7 @@ class Request {
static private $koeBuildDate;
static private $koeCapabilites;
static private $expectedConnectionTimeout;
static private $memoryLimit;
/**
* Initializes request data
......@@ -183,6 +185,11 @@ class Request {
if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) {
self::$authUser = Utils::GetLocalPartFromEmail(self::$authUser);
}
// get & convert configured memory limit
(int)preg_replace_callback('/(\-?\d+)(.?)/', function ($m) {
self::$memoryLimit = $m[1] * pow(1024, strpos('BKMG', $m[2])) * self::MAXMEMORYUSAGE;
}, strtoupper(ini_get('memory_limit')));
}
/**
......@@ -632,6 +639,18 @@ class Request {
return (time() - $_SERVER["REQUEST_TIME"]) >= self::GetExpectedConnectionTimeout();
}
/**
* Indicates if the memory usage limit is almost reached.
* Processing should stop then to prevent hard out-of-memory issues.
* The threshold is hardcoded at 90% in Request::MAXMEMORYUSAGE.
*
* @access public
* @return boolean
*/
static public function IsRequestMemoryLimitReached() {
return memory_get_peak_usage(true) >= self::$memoryLimit;
}
/**
* Checks the device type if it expects the globalobjid in meeting requests encoded as hex.
* If it's not the case, globalobjid will be base64 encoded.
......
......@@ -825,6 +825,12 @@ class Sync extends RequestProcessor {
$setupExporter = false;
}
// if max memory allocation is reached, stop processing other collections
if (Request::IsRequestMemoryLimitReached()) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as max memory allocatation reached, omitting output for collection.", $spa->GetFolderId()));
$setupExporter = false;
}
// ZP-907: never send changes of UNKNOWN folders to an Outlook client
if (Request::IsOutlook() && self::$deviceManager->GetFolderTypeFromCacheById($spa->GetFolderId()) == SYNC_FOLDER_TYPE_UNKNOWN && $spa->HasSyncKey()) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as type is UNKNOWN.", $spa->GetFolderId()));
......@@ -1213,7 +1219,7 @@ class Sync extends RequestProcessor {
}
}
if($n >= $windowSize || Request::IsRequestTimeoutReached()) {
if($n >= $windowSize || Request::IsRequestTimeoutReached() || Request::IsRequestMemoryLimitReached()) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Exported maxItems of messages: %d / %d", $n, $changecount));
break;
}
......@@ -1228,9 +1234,9 @@ class Sync extends RequestProcessor {
self::$encoder->endTag();
// log the request timeout
if (Request::IsRequestTimeoutReached()) {
ZLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Stopping export as maximum request timeout is almost reached!");
// Send a <MoreAvailable/> tag if we reached the request timeout, there are more changes and a moreavailable was not already send
if (Request::IsRequestTimeoutReached() || Request::IsRequestMemoryLimitReached()) {
ZLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Stopping export as limits of request timeout or available memory are almost reached!");
// Send a <MoreAvailable/> tag if we reached the request timeout or max memory, there are more changes and a moreavailable was not already send
if (!$moreAvailableSent && ($n > $windowSize)) {
self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
$spa->DelFolderStat();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment