From 95b5abb6dd799fb8d1f6cb37f7dffb2969168baa Mon Sep 17 00:00:00 2001 From: Mark Noble Date: Mon, 5 Oct 2026 23:17:58 -0600 Subject: [PATCH 1/2] DIS-3017 use item prices from MARC records when recalculating cost savings - refactor calculation of cost savings so it can be used in new places - Improve memory management of recalculating historic cost savings --- .../cron/recalculateHistoricCostSavings.php | 67 +++++++++++++++---- code/web/release_notes/26.10.00/DIS-3017.yaml | 12 ++++ code/web/sys/User/Checkout.php | 50 +------------- code/web/sys/Utils/GroupingUtils.php | 50 ++++++++++++++ 4 files changed, 118 insertions(+), 61 deletions(-) create mode 100644 code/web/release_notes/26.10.00/DIS-3017.yaml diff --git a/code/web/cron/recalculateHistoricCostSavings.php b/code/web/cron/recalculateHistoricCostSavings.php index 3086fbe0670..1717e654a1a 100644 --- a/code/web/cron/recalculateHistoricCostSavings.php +++ b/code/web/cron/recalculateHistoricCostSavings.php @@ -10,6 +10,7 @@ require_once __DIR__ . '/../bootstrap_aspen.php'; set_time_limit(0); +ini_set('memory_limit', '2G'); require_once ROOT_DIR . '/sys/Administration/BackgroundProcess.php'; $backgroundProcess = null; @@ -35,11 +36,14 @@ require_once ROOT_DIR . '/sys/ReplacementCost.php'; $replacementCosts = ReplacementCost::getReplacementCostsByFormat(); +require_once ROOT_DIR . '/sys/Utils/GroupingUtils.php'; require_once ROOT_DIR . '/sys/ReadingHistoryEntry.php'; $readingHistoryEntry = new ReadingHistoryEntry(); if (!empty($format)) { $readingHistoryEntry->format = $format; } +//Optimize to load by source so we can initialize fewer record drivers +$readingHistoryEntry->orderBy(['source', 'sourceId']); $numEntriesToUpdate = $readingHistoryEntry->count(); if (!is_null($backgroundProcess)) { $backgroundProcess->addNote("Updating $numEntriesToUpdate reading history entries"); } @@ -47,26 +51,63 @@ $numUpdated = 0; $loggedZeroCostFormats = []; +global $indexingProfiles; + //Recalculate all reading history entries +$lastSource = ''; +$lastSourceId = ''; while ($readingHistoryEntry->fetch()) { + if ($lastSource != $readingHistoryEntry->source || $lastSourceId != $readingHistoryEntry->sourceId) { + $lastSource = $readingHistoryEntry->source; + $lastSourceId = $readingHistoryEntry->sourceId; + //Clear cache + RecordDriverFactory::$recordDrivers = []; + } $lowerFormat = strtolower($readingHistoryEntry->format); - if (array_key_exists($lowerFormat, $replacementCosts)) { - if ($replacementCosts[$lowerFormat] > 0) { - //Update the costSavings for the reading history entry and update the total cost savings for the user - $readingHistoryEntryToUpdate = new ReadingHistoryEntry(); - $readingHistoryEntryToUpdate->id = $readingHistoryEntry->id; - if ($readingHistoryEntryToUpdate->find(true)) { - $readingHistoryEntryToUpdate->costSavings = $replacementCosts[$lowerFormat]; - $readingHistoryEntryToUpdate->update(); - $numUpdated++; + //Update the costSavings for the reading history entry and update the total cost savings for the user + $readingHistoryEntryToUpdate = new ReadingHistoryEntry(); + $readingHistoryEntryToUpdate->id = $readingHistoryEntry->id; + if ($readingHistoryEntryToUpdate->find(true)) { + $useFormatMapLookup = true; + //We only have replacement costs at the item level for marc records from the ILS so don't bother creating drivers for anything else + if (array_key_exists($readingHistoryEntry->source, $indexingProfiles)) { + $recordDriver = RecordDriverFactory::initRecordDriverById($readingHistoryEntry->source . ':' . $readingHistoryEntry->sourceId); + if ($recordDriver != null) { + $replacementCost = getReplacementCost($recordDriver, $readingHistoryEntry->format, null, $readingHistoryEntry->barcode); + if ($replacementCost != 0) { + $readingHistoryEntryToUpdate->costSavings = $replacementCost; + $readingHistoryEntryToUpdate->update(); + $numUpdated++; + $useFormatMapLookup = false; + } + $recordDriver = null; } } - }else{ - if (!array_key_exists($lowerFormat, $loggedZeroCostFormats)) { - if (!is_null($backgroundProcess)) { $backgroundProcess->addNote("Skipping $readingHistoryEntry->format because no replacement cost was specified."); } - $loggedZeroCostFormats[$lowerFormat] = $lowerFormat; + if ($useFormatMapLookup) { + $foundCost = false; + if (array_key_exists($lowerFormat, $replacementCosts)) { + if ($replacementCosts[$lowerFormat] > 0) { + //Update the costSavings for the reading history entry and update the total cost savings for the user + $readingHistoryEntryToUpdate = new ReadingHistoryEntry(); + $readingHistoryEntryToUpdate->id = $readingHistoryEntry->id; + if ($readingHistoryEntryToUpdate->find(true)) { + $readingHistoryEntryToUpdate->costSavings = $replacementCosts[$lowerFormat]; + $readingHistoryEntryToUpdate->update(); + $numUpdated++; + $foundCost = true; + } + } + } + if (!$foundCost && !array_key_exists($lowerFormat, $loggedZeroCostFormats)) { + if (!is_null($backgroundProcess)) { + $backgroundProcess->addNote("Skipping $readingHistoryEntry->format because no replacement cost was specified."); + } + $loggedZeroCostFormats[$lowerFormat] = $lowerFormat; + } } } + $readingHistoryEntryToUpdate->__destruct(); + $readingHistoryEntryToUpdate = null; } //Now get the total cost savings for users that have checked something out in the format diff --git a/code/web/release_notes/26.10.00/DIS-3017.yaml b/code/web/release_notes/26.10.00/DIS-3017.yaml new file mode 100644 index 00000000000..623242d2459 --- /dev/null +++ b/code/web/release_notes/26.10.00/DIS-3017.yaml @@ -0,0 +1,12 @@ +Jira: DIS-3017 +Type: Enhancement +Developer: Mark Noble (MDN) +ContributingOrg: Grove For Libraries +Sponsor: Orange County Library System +GroveRelease: 26.10.00 +CommunityRelease: 26.11.00 +Component: User Account +Description: + - When recalculating historic cost savings, use item prices from MARC records when possible. +NewSettings: +NewPermissions: \ No newline at end of file diff --git a/code/web/sys/User/Checkout.php b/code/web/sys/User/Checkout.php index 239c9c3c2dc..acb79b86866 100644 --- a/code/web/sys/User/Checkout.php +++ b/code/web/sys/User/Checkout.php @@ -211,53 +211,7 @@ public function update(string $context = '') : int|bool { } public function getReplacementCost() : float { - require_once ROOT_DIR . '/sys/ReplacementCost.php'; - $replacementCosts = ReplacementCost::getReplacementCostsByFormat(); - - $replacementCostForCheckout = 0; - $useDefaultReplacementCost = true; - //Check to see if the title has a replacement cost in the record - $recordDriver = $this->getRecordDriver(); - if ($recordDriver instanceof MarcRecordDriver) { - $indexingProfile = $recordDriver->getIndexingProfile(); - if (!empty($indexingProfile->replacementCostSubfield) && $indexingProfile->replacementCostSubfield != ' ' && !empty($indexingProfile->itemRecordNumber) && $indexingProfile->itemRecordNumber != ' '){ - //We need the full MARC record to get all the data - $marcRecord = $recordDriver->getMarcRecord(); - if ($marcRecord) { - $itemFields = $marcRecord->getFields($indexingProfile->itemTag); - /** @var File_MARC_Data_Field $itemField */ - foreach ($itemFields as $itemField) { - $recordNumberMatches = false; - if (!empty($indexingProfile->itemRecordNumber)) { - $itemRecordNumber = $itemField->getSubfield($indexingProfile->itemRecordNumber); - $recordNumberMatches = (!empty($itemRecordNumber) && ($itemRecordNumber->getData() == $this->itemId)); - } - $barcodeMatches = false; - if (!empty($indexingProfile->barcode)) { - $itemBarcode = $itemField->getSubfield($indexingProfile->barcode); - $barcodeMatches = (!empty($itemBarcode) && ($itemBarcode->getData() == $this->barcode)); - } - $replacementCost = $itemField->getSubfield($indexingProfile->replacementCostSubfield); - if (!empty($replacementCost) && ($recordNumberMatches || $barcodeMatches)) { - $replacementCost = $replacementCost->getData(); - //Remove dollar signs if they are in the field. - require_once ROOT_DIR . '/sys/Utils/StringUtils.php'; - $replacementCost = str_replace(StringUtils::getCurrencySymbol(), '', $replacementCost); - if ($replacementCost > 0 && is_numeric($replacementCost)) { - $replacementCostForCheckout = $replacementCost; - $useDefaultReplacementCost = false; - } - break; - } - } - } - $marcRecord = null; - } - } - $lowerFormat = strtolower($this->format); - if ($useDefaultReplacementCost && array_key_exists($lowerFormat, $replacementCosts)) { - $replacementCostForCheckout = $replacementCosts[$lowerFormat]; - } - return $replacementCostForCheckout; + require_once ROOT_DIR . '/sys/Utils/GroupingUtils.php'; + return getReplacementCost($this->getRecordDriver(), $this->format, $this->itemId, $this->barcode); } } diff --git a/code/web/sys/Utils/GroupingUtils.php b/code/web/sys/Utils/GroupingUtils.php index 91b33a06717..b0cfc5baf5d 100644 --- a/code/web/sys/Utils/GroupingUtils.php +++ b/code/web/sys/Utils/GroupingUtils.php @@ -282,3 +282,53 @@ function getSortableDate(?string $str) : ?DateTime { $sortableDateCache[$str] = null; return null; } + +function getReplacementCost(GroupedWorkSubDriver $recordDriver, String $format, ?String $itemId = null, ?String $barcode = null) : float { + require_once ROOT_DIR . '/sys/ReplacementCost.php'; + $replacementCosts = ReplacementCost::getReplacementCostsByFormat(); + + $replacementCostForCheckout = 0; + $useDefaultReplacementCost = true; + //Check to see if the title has a replacement cost in the record + if ($recordDriver instanceof MarcRecordDriver) { + $indexingProfile = $recordDriver->getIndexingProfile(); + if (!empty($indexingProfile->replacementCostSubfield) && $indexingProfile->replacementCostSubfield != ' ' && ((!empty($indexingProfile->itemRecordNumber) && $indexingProfile->itemRecordNumber != ' ') || (!empty($indexingProfile->barcode) && $indexingProfile->barcode != ' '))){ + //We need the full MARC record to get all the data + $marcRecord = $recordDriver->getMarcRecord(); + if ($marcRecord) { + $itemFields = $marcRecord->getFields($indexingProfile->itemTag); + /** @var File_MARC_Data_Field $itemField */ + foreach ($itemFields as $itemField) { + $recordNumberMatches = false; + if (!empty($indexingProfile->itemRecordNumber) && !empty($itemId)) { + $itemRecordNumber = $itemField->getSubfield($indexingProfile->itemRecordNumber); + $recordNumberMatches = (!empty($itemRecordNumber) && ($itemRecordNumber->getData() == $itemId)); + } + $barcodeMatches = false; + if (!empty($indexingProfile->barcode) && !empty($barcode)) { + $itemBarcode = $itemField->getSubfield($indexingProfile->barcode); + $barcodeMatches = (!empty($itemBarcode) && ($itemBarcode->getData() == $barcode)); + } + $replacementCost = $itemField->getSubfield($indexingProfile->replacementCostSubfield); + if (!empty($replacementCost) && ($recordNumberMatches || $barcodeMatches)) { + $replacementCost = $replacementCost->getData(); + //Remove dollar signs if they are in the field. + require_once ROOT_DIR . '/sys/Utils/StringUtils.php'; + $replacementCost = str_replace(StringUtils::getCurrencySymbol(), '', $replacementCost); + if ($replacementCost > 0 && is_numeric($replacementCost)) { + $replacementCostForCheckout = $replacementCost; + $useDefaultReplacementCost = false; + } + break; + } + } + } + $marcRecord = null; + } + } + $lowerFormat = strtolower($format); + if ($useDefaultReplacementCost && array_key_exists($lowerFormat, $replacementCosts)) { + $replacementCostForCheckout = $replacementCosts[$lowerFormat]; + } + return $replacementCostForCheckout; +} From 9f11e425436166d95395454ea6e7cb6a58524eb1 Mon Sep 17 00:00:00 2001 From: Mark Noble Date: Tue, 6 Oct 2026 13:59:29 -0600 Subject: [PATCH 2/2] DIS-3017 Recalculate Cost Savings with Item Data - Improves performance of recalculating cost savings with item data - add logging if there is no background process - updates for caching of ils records and marc loader to minimize memory --- .../web/RecordDrivers/RecordDriverFactory.php | 8 ++ .../cron/recalculateHistoricCostSavings.php | 122 ++++++++++-------- code/web/sys/Indexing/IlsRecord.php | 5 + code/web/sys/MarcLoader.php | 5 +- code/web/sys/MemoryWatcher.php | 8 ++ 5 files changed, 89 insertions(+), 59 deletions(-) diff --git a/code/web/RecordDrivers/RecordDriverFactory.php b/code/web/RecordDrivers/RecordDriverFactory.php index 803a45fa54b..75466703433 100644 --- a/code/web/RecordDrivers/RecordDriverFactory.php +++ b/code/web/RecordDrivers/RecordDriverFactory.php @@ -192,4 +192,12 @@ public static function initAndReturnDriver($record, $driver, $path) { // If we got here, something went very wrong: return new AspenError("Problem loading record driver: {$driver}"); } + + public static function clearCachedDrivers() { + foreach (RecordDriverFactory::$recordDrivers as $recordDriver) { + $recordDriver->__destruct(); + $recordDriver = null; + } + RecordDriverFactory::$recordDrivers = []; + } } \ No newline at end of file diff --git a/code/web/cron/recalculateHistoricCostSavings.php b/code/web/cron/recalculateHistoricCostSavings.php index 1717e654a1a..b1fc710c1a2 100644 --- a/code/web/cron/recalculateHistoricCostSavings.php +++ b/code/web/cron/recalculateHistoricCostSavings.php @@ -11,6 +11,8 @@ set_time_limit(0); ini_set('memory_limit', '2G'); +/** @var MemoryWatcher $memoryWatcher */ +global $memoryWatcher; require_once ROOT_DIR . '/sys/Administration/BackgroundProcess.php'; $backgroundProcess = null; @@ -20,7 +22,7 @@ $backgroundProcess->id = $backgroundProcessId; if (!$backgroundProcess->find(true)) { $backgroundProcess = null; - echo ("Could not find the specified background process\n"); + if (is_null($backgroundProcess)) { echo ("Could not find the specified background process\n"); } die(); }else{ if (!$backgroundProcess->isRunning) { @@ -46,9 +48,11 @@ $readingHistoryEntry->orderBy(['source', 'sourceId']); $numEntriesToUpdate = $readingHistoryEntry->count(); if (!is_null($backgroundProcess)) { $backgroundProcess->addNote("Updating $numEntriesToUpdate reading history entries"); } +if (is_null($backgroundProcess)) { echo(date('Y M d H:i:s') . " There are $numEntriesToUpdate entries to update " . $memoryWatcher->getCurrentMemoryAllocation()); } $readingHistoryEntry->find(); $numUpdated = 0; +$numProcessed = 0; $loggedZeroCostFormats = []; global $indexingProfiles; @@ -56,90 +60,94 @@ //Recalculate all reading history entries $lastSource = ''; $lastSourceId = ''; +$recordDriver = null; +global $aspen_db; while ($readingHistoryEntry->fetch()) { if ($lastSource != $readingHistoryEntry->source || $lastSourceId != $readingHistoryEntry->sourceId) { + if (array_key_exists($lastSource, $indexingProfiles)) { + //Clear cache + RecordDriverFactory::clearCachedDrivers(); + $recordDriver = RecordDriverFactory::initRecordDriverById($readingHistoryEntry->source . ':' . $readingHistoryEntry->sourceId); + }else{ + $recordDriver = null; + } $lastSource = $readingHistoryEntry->source; $lastSourceId = $readingHistoryEntry->sourceId; - //Clear cache - RecordDriverFactory::$recordDrivers = []; } $lowerFormat = strtolower($readingHistoryEntry->format); //Update the costSavings for the reading history entry and update the total cost savings for the user - $readingHistoryEntryToUpdate = new ReadingHistoryEntry(); - $readingHistoryEntryToUpdate->id = $readingHistoryEntry->id; - if ($readingHistoryEntryToUpdate->find(true)) { - $useFormatMapLookup = true; - //We only have replacement costs at the item level for marc records from the ILS so don't bother creating drivers for anything else - if (array_key_exists($readingHistoryEntry->source, $indexingProfiles)) { - $recordDriver = RecordDriverFactory::initRecordDriverById($readingHistoryEntry->source . ':' . $readingHistoryEntry->sourceId); - if ($recordDriver != null) { - $replacementCost = getReplacementCost($recordDriver, $readingHistoryEntry->format, null, $readingHistoryEntry->barcode); - if ($replacementCost != 0) { - $readingHistoryEntryToUpdate->costSavings = $replacementCost; - $readingHistoryEntryToUpdate->update(); - $numUpdated++; - $useFormatMapLookup = false; - } - $recordDriver = null; + + $useFormatMapLookup = true; + //We only have replacement costs at the item level for marc records from the ILS so don't bother creating drivers for anything else + if (array_key_exists($readingHistoryEntry->source, $indexingProfiles)) { + if ($recordDriver != null) { + $replacementCost = getReplacementCost($recordDriver, $readingHistoryEntry->format, null, $readingHistoryEntry->barcode); + if ($replacementCost > 0) { + $aspen_db->exec("UPDATE user_reading_history_work SET costSavings = $replacementCost where id = $readingHistoryEntry->id"); + $numUpdated++; + $useFormatMapLookup = false; } + $recordDriver = null; } - if ($useFormatMapLookup) { - $foundCost = false; - if (array_key_exists($lowerFormat, $replacementCosts)) { - if ($replacementCosts[$lowerFormat] > 0) { - //Update the costSavings for the reading history entry and update the total cost savings for the user - $readingHistoryEntryToUpdate = new ReadingHistoryEntry(); - $readingHistoryEntryToUpdate->id = $readingHistoryEntry->id; - if ($readingHistoryEntryToUpdate->find(true)) { - $readingHistoryEntryToUpdate->costSavings = $replacementCosts[$lowerFormat]; - $readingHistoryEntryToUpdate->update(); - $numUpdated++; - $foundCost = true; - } - } + } + if ($useFormatMapLookup) { + $foundCost = false; + if (array_key_exists($lowerFormat, $replacementCosts)) { + if ($replacementCosts[$lowerFormat] > 0) { + //Update the costSavings for the reading history entry and update the total cost savings for the user + $replacementCost = $replacementCosts[$lowerFormat]; + $aspen_db->exec("UPDATE user_reading_history_work SET costSavings = $replacementCost where id = $readingHistoryEntry->id"); + $numUpdated++; + $foundCost = true; } - if (!$foundCost && !array_key_exists($lowerFormat, $loggedZeroCostFormats)) { - if (!is_null($backgroundProcess)) { - $backgroundProcess->addNote("Skipping $readingHistoryEntry->format because no replacement cost was specified."); - } - $loggedZeroCostFormats[$lowerFormat] = $lowerFormat; + } + if (!$foundCost && !array_key_exists($lowerFormat, $loggedZeroCostFormats)) { + if (!is_null($backgroundProcess)) { + $backgroundProcess->addNote("Skipping $readingHistoryEntry->format because no replacement cost was specified."); } + $loggedZeroCostFormats[$lowerFormat] = $lowerFormat; } } - $readingHistoryEntryToUpdate->__destruct(); - $readingHistoryEntryToUpdate = null; + + $numProcessed++; + if ($numProcessed % 1000 === 0) { + if (is_null($backgroundProcess)) { echo(date('Y M d H:i:s') . " Processed $numProcessed reading history entries " . $memoryWatcher->getCurrentMemoryAllocation() . "\n"); } + if (is_null($backgroundProcess)) { echo(date('Y M d H:i:s') . " - " . $memoryWatcher->getCurrentMemoryAllocation() . "\n"); } + ob_flush(); + } } //Now get the total cost savings for users that have checked something out in the format +if (is_null($backgroundProcess)) { echo(date('Y M d H:i:s') . " Updating total cost savings for users\n"); } +ob_flush(); $readingHistoryEntry = new ReadingHistoryEntry(); if (!empty($format)) { $readingHistoryEntry->format = $format; } +$readingHistoryEntry->whereAdd("costSavings > 0"); $readingHistoryEntry->selectAdd(); -$readingHistoryEntry->selectAdd("DISTINCT userId as userId"); +$readingHistoryEntry->selectAdd('userId'); +$readingHistoryEntry->selectAdd("sum(costSavings) as costSavings"); +$readingHistoryEntry->groupBy('userId'); + +$numUsersToUpdate = $readingHistoryEntry->count(); +if (is_null($backgroundProcess)) { echo(date('Y M d H:i:s') . " Updating total cost savings for $numUsersToUpdate users\n"); } +ob_flush(); $numUsersUpdated = 0; $readingHistoryEntry->find(); while ($readingHistoryEntry->fetch()) { - $userToUpdate = new User(); - $userToUpdate->id = $readingHistoryEntry->userId; - if ($userToUpdate->find(true)) { - $tmpReadingHistory = new ReadingHistoryEntry(); - $tmpReadingHistory->userId = $userToUpdate->id; - $tmpReadingHistory->selectAdd(); - $tmpReadingHistory->selectAdd("SUM(costSavings) as costSavings"); - if ($tmpReadingHistory->costSavings != $userToUpdate->totalCostSavings) { - if ($tmpReadingHistory->find(true)) { - $userToUpdate->__set('totalCostSavings', $tmpReadingHistory->costSavings); - } else { - $userToUpdate->__set('totalCostSavings', 0); - } - $userToUpdate->update(); - } - } + $aspen_db->exec("UPDATE user SET totalCostSavings = $readingHistoryEntry->costSavings where id = $readingHistoryEntry->userId"); $numUsersUpdated++; + if ($numUsersUpdated % 1000 == 0) { + if (is_null($backgroundProcess)) { echo(date('Y M d H:i:s') . " Updated $numUsersUpdated users\n"); } + ob_flush(); + } } +if (is_null($backgroundProcess)) { echo(date('Y M d H:i:s') . " Done!"); } +ob_flush(); + if (!is_null($backgroundProcess)) { $backgroundProcess->addNote(translate([ 'text' => 'Updated %1% historic cost savings.', diff --git a/code/web/sys/Indexing/IlsRecord.php b/code/web/sys/Indexing/IlsRecord.php index 14c8e08c659..853699b4bf8 100644 --- a/code/web/sys/Indexing/IlsRecord.php +++ b/code/web/sys/Indexing/IlsRecord.php @@ -72,6 +72,11 @@ static function getIlsRecordForId(string $type, string $identifier) : ?IlsRecord self::$preloadedIlsRecords[$type] = []; } if (!array_key_exists($identifier, self::$preloadedIlsRecords[$type])) { + while (count(self::$preloadedIlsRecords[$type]) >= 50) { + $firstKey = array_key_first(self::$preloadedIlsRecords[$type]); + unset(self::$preloadedIlsRecords[$type][$firstKey]); + } + $ilsRecord = new IlsRecord(); $ilsRecord->source = $type; $ilsRecord->ilsId = $identifier; diff --git a/code/web/sys/MarcLoader.php b/code/web/sys/MarcLoader.php index 339ff80c4c3..cc504ae782f 100644 --- a/code/web/sys/MarcLoader.php +++ b/code/web/sys/MarcLoader.php @@ -59,8 +59,9 @@ public static function loadMarcRecordByILSId(string $id, string $recordType = 'm } //Make sure not to use too much memory global $memoryWatcher; - if (count(MarcLoader::$loadedMarcRecords) > 50) { - array_shift(MarcLoader::$loadedMarcRecords); + while (count(MarcLoader::$loadedMarcRecords) >= 50) { + $firstKey = array_key_first(MarcLoader::$loadedMarcRecords); + unset(MarcLoader::$loadedMarcRecords[$firstKey]); $memoryWatcher->logMemory("Removed Cached MARC"); } $memoryWatcher->logMemory("Loaded MARC for $id"); diff --git a/code/web/sys/MemoryWatcher.php b/code/web/sys/MemoryWatcher.php index 762476855ad..7ef3eef6a09 100644 --- a/code/web/sys/MemoryWatcher.php +++ b/code/web/sys/MemoryWatcher.php @@ -53,6 +53,14 @@ public function writeMemory() { } } + public function getCurrentMemoryAllocation() { + $curMemory = memory_get_usage(false); + $memoryInUse = number_format(($curMemory) / 1024 /1024); + $curMemory = memory_get_usage(true); + $memoryReserved = number_format($curMemory / 1024 /1024); + return " $memoryInUse MB in use, $memoryReserved MB reserved"; + } + function __destruct() { if ($this->memoryLoggingEnabled) { global $logger;