Skip to content

Commit 3836993

Browse files
authored
Merge pull request #10539 from nextcloud/feature-8642-memory-check
Adds a setup and cli check for the recommended memory limit
2 parents bac545e + 20839a4 commit 3836993

File tree

10 files changed

+314
-4
lines changed

10 files changed

+314
-4
lines changed

console.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,13 @@ function exceptionHandler($exception) {
8585
echo "The process control (PCNTL) extensions are required in case you want to interrupt long running commands - see http://php.net/manual/en/book.pcntl.php" . PHP_EOL;
8686
}
8787

88-
$application = new Application(\OC::$server->getConfig(), \OC::$server->getEventDispatcher(), \OC::$server->getRequest(), \OC::$server->getLogger());
88+
$application = new Application(
89+
\OC::$server->getConfig(),
90+
\OC::$server->getEventDispatcher(),
91+
\OC::$server->getRequest(),
92+
\OC::$server->getLogger(),
93+
\OC::$server->query(\OC\MemoryInfo::class)
94+
);
8995
$application->loadCommands(new ArgvInput(), new ConsoleOutput());
9096
$application->run();
9197
} catch (Exception $ex) {

core/js/setupchecks.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,17 @@
316316
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
317317
});
318318
}
319+
if (!data.isMemoryLimitSufficient) {
320+
messages.push({
321+
msg: t(
322+
'core',
323+
'The PHP memory limit is below the recommended value of 512MB.'
324+
),
325+
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
326+
})
327+
}
319328

320-
if(data.appDirsWithDifferentOwner.length > 0) {
329+
if(data.appDirsWithDifferentOwner && data.appDirsWithDifferentOwner.length > 0) {
321330
var appDirsWithDifferentOwner = data.appDirsWithDifferentOwner.reduce(
322331
function(appDirsWithDifferentOwner, directory) {
323332
return appDirsWithDifferentOwner + '<li>' + directory + '</li>';

core/js/tests/specs/setupchecksSpec.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ describe('OC.SetupChecks tests', function() {
171171
cronInfo: {
172172
diffInSeconds: 0
173173
},
174+
isMemoryLimitSufficient: true,
174175
appDirsWithDifferentOwner: []
175176
})
176177
);
@@ -219,6 +220,7 @@ describe('OC.SetupChecks tests', function() {
219220
cronInfo: {
220221
diffInSeconds: 0
221222
},
223+
isMemoryLimitSufficient: true,
222224
appDirsWithDifferentOwner: []
223225
})
224226
);
@@ -268,6 +270,7 @@ describe('OC.SetupChecks tests', function() {
268270
cronInfo: {
269271
diffInSeconds: 0
270272
},
273+
isMemoryLimitSufficient: true,
271274
appDirsWithDifferentOwner: []
272275
})
273276
);
@@ -315,6 +318,7 @@ describe('OC.SetupChecks tests', function() {
315318
cronInfo: {
316319
diffInSeconds: 0
317320
},
321+
isMemoryLimitSufficient: true,
318322
appDirsWithDifferentOwner: []
319323
})
320324
);
@@ -360,6 +364,7 @@ describe('OC.SetupChecks tests', function() {
360364
cronInfo: {
361365
diffInSeconds: 0
362366
},
367+
isMemoryLimitSufficient: true,
363368
appDirsWithDifferentOwner: []
364369
})
365370
);
@@ -405,6 +410,7 @@ describe('OC.SetupChecks tests', function() {
405410
cronInfo: {
406411
diffInSeconds: 0
407412
},
413+
isMemoryLimitSufficient: true,
408414
appDirsWithDifferentOwner: [
409415
'/some/path'
410416
]
@@ -452,6 +458,7 @@ describe('OC.SetupChecks tests', function() {
452458
cronInfo: {
453459
diffInSeconds: 0
454460
},
461+
isMemoryLimitSufficient: true,
455462
appDirsWithDifferentOwner: []
456463
})
457464
);
@@ -497,6 +504,7 @@ describe('OC.SetupChecks tests', function() {
497504
cronInfo: {
498505
diffInSeconds: 0
499506
},
507+
isMemoryLimitSufficient: true,
500508
appDirsWithDifferentOwner: []
501509
})
502510
);
@@ -510,6 +518,52 @@ describe('OC.SetupChecks tests', function() {
510518
});
511519
});
512520

521+
it('should return a warning if the memory limit is below the recommended value', function(done) {
522+
var async = OC.SetupChecks.checkSetup();
523+
524+
suite.server.requests[0].respond(
525+
200,
526+
{
527+
'Content-Type': 'application/json',
528+
},
529+
JSON.stringify({
530+
hasFileinfoInstalled: true,
531+
isGetenvServerWorking: true,
532+
isReadOnlyConfig: false,
533+
hasWorkingFileLocking: true,
534+
hasValidTransactionIsolationLevel: true,
535+
suggestedOverwriteCliURL: '',
536+
isUrandomAvailable: true,
537+
serverHasInternetConnection: true,
538+
isMemcacheConfigured: true,
539+
forwardedForHeadersWorking: true,
540+
reverseProxyDocs: 'https://docs.owncloud.org/foo/bar.html',
541+
isCorrectMemcachedPHPModuleInstalled: true,
542+
hasPassedCodeIntegrityCheck: true,
543+
isOpcacheProperlySetup: true,
544+
hasOpcacheLoaded: true,
545+
isSettimelimitAvailable: true,
546+
hasFreeTypeSupport: true,
547+
missingIndexes: [],
548+
outdatedCaches: [],
549+
cronErrors: [],
550+
cronInfo: {
551+
diffInSeconds: 0
552+
},
553+
appDirsWithDifferentOwner: [],
554+
isMemoryLimitSufficient: false
555+
})
556+
);
557+
558+
async.done(function( data, s, x ){
559+
expect(data).toEqual([{
560+
msg: 'The PHP memory limit is below the recommended value of 512MB.',
561+
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
562+
}]);
563+
done();
564+
});
565+
});
566+
513567
it('should return an error if the response has no statuscode 200', function(done) {
514568
var async = OC.SetupChecks.checkSetup();
515569

@@ -563,6 +617,7 @@ describe('OC.SetupChecks tests', function() {
563617
cronInfo: {
564618
diffInSeconds: 0
565619
},
620+
isMemoryLimitSufficient: true,
566621
appDirsWithDifferentOwner: []
567622
})
568623
);
@@ -609,6 +664,7 @@ describe('OC.SetupChecks tests', function() {
609664
cronInfo: {
610665
diffInSeconds: 0
611666
},
667+
isMemoryLimitSufficient: true,
612668
appDirsWithDifferentOwner: []
613669
})
614670
);
@@ -655,6 +711,7 @@ describe('OC.SetupChecks tests', function() {
655711
cronInfo: {
656712
diffInSeconds: 0
657713
},
714+
isMemoryLimitSufficient: true,
658715
appDirsWithDifferentOwner: []
659716
})
660717
);
@@ -701,6 +758,7 @@ describe('OC.SetupChecks tests', function() {
701758
cronInfo: {
702759
diffInSeconds: 0
703760
},
761+
isMemoryLimitSufficient: true,
704762
appDirsWithDifferentOwner: []
705763
})
706764
);

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@
830830
'OC\\Memcache\\Memcached' => $baseDir . '/lib/private/Memcache/Memcached.php',
831831
'OC\\Memcache\\NullCache' => $baseDir . '/lib/private/Memcache/NullCache.php',
832832
'OC\\Memcache\\Redis' => $baseDir . '/lib/private/Memcache/Redis.php',
833+
'OC\\MemoryInfo' => $baseDir . '/lib/private/MemoryInfo.php',
833834
'OC\\Migration\\BackgroundRepair' => $baseDir . '/lib/private/Migration/BackgroundRepair.php',
834835
'OC\\Migration\\ConsoleOutput' => $baseDir . '/lib/private/Migration/ConsoleOutput.php',
835836
'OC\\Migration\\SimpleOutput' => $baseDir . '/lib/private/Migration/SimpleOutput.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
860860
'OC\\Memcache\\Memcached' => __DIR__ . '/../../..' . '/lib/private/Memcache/Memcached.php',
861861
'OC\\Memcache\\NullCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/NullCache.php',
862862
'OC\\Memcache\\Redis' => __DIR__ . '/../../..' . '/lib/private/Memcache/Redis.php',
863+
'OC\\MemoryInfo' => __DIR__ . '/../../..' . '/lib/private/MemoryInfo.php',
863864
'OC\\Migration\\BackgroundRepair' => __DIR__ . '/../../..' . '/lib/private/Migration/BackgroundRepair.php',
864865
'OC\\Migration\\ConsoleOutput' => __DIR__ . '/../../..' . '/lib/private/Migration/ConsoleOutput.php',
865866
'OC\\Migration\\SimpleOutput' => __DIR__ . '/../../..' . '/lib/private/Migration/SimpleOutput.php',

lib/private/Console/Application.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
*/
3030
namespace OC\Console;
3131

32+
use OC\MemoryInfo;
3233
use OC\NeedsUpdateException;
3334
use OC_App;
3435
use OCP\AppFramework\QueryException;
@@ -52,20 +53,28 @@ class Application {
5253
private $request;
5354
/** @var ILogger */
5455
private $logger;
56+
/** @var MemoryInfo */
57+
private $memoryInfo;
5558

5659
/**
5760
* @param IConfig $config
5861
* @param EventDispatcherInterface $dispatcher
5962
* @param IRequest $request
6063
* @param ILogger $logger
64+
* @param MemoryInfo $memoryInfo
6165
*/
62-
public function __construct(IConfig $config, EventDispatcherInterface $dispatcher, IRequest $request, ILogger $logger) {
66+
public function __construct(IConfig $config,
67+
EventDispatcherInterface $dispatcher,
68+
IRequest $request,
69+
ILogger $logger,
70+
MemoryInfo $memoryInfo) {
6371
$defaults = \OC::$server->getThemingDefaults();
6472
$this->config = $config;
6573
$this->application = new SymfonyApplication($defaults->getName(), \OC_Util::getVersionString());
6674
$this->dispatcher = $dispatcher;
6775
$this->request = $request;
6876
$this->logger = $logger;
77+
$this->memoryInfo = $memoryInfo;
6978
}
7079

7180
/**
@@ -97,6 +106,14 @@ public function loadCommands(
97106
if ($input->getOption('no-warnings')) {
98107
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
99108
}
109+
110+
if ($this->memoryInfo->isMemoryLimitSufficient() === false) {
111+
$output->getErrorOutput()->writeln(
112+
'<comment>The current PHP memory limit ' .
113+
'is below the recommended value of 512MB.</comment>'
114+
);
115+
}
116+
100117
try {
101118
require_once __DIR__ . '/../../../core/register_command.php';
102119
if ($this->config->getSystemValue('installed', false)) {

lib/private/MemoryInfo.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* @copyright Copyright (c) 2018, Michael Weimann (<mail@michael-weimann.eu>)
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as
11+
* published by the Free Software Foundation, either version 3 of the
12+
* License, or (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Affero General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Affero General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
24+
namespace OC;
25+
26+
/**
27+
* Helper class that covers memory info.
28+
*/
29+
class MemoryInfo {
30+
31+
const RECOMMENDED_MEMORY_LIMIT = 512 * 1024 * 1024;
32+
33+
/**
34+
* Tests if the memory limit is greater or equal the recommended value.
35+
*
36+
* @return bool
37+
*/
38+
public function isMemoryLimitSufficient(): bool {
39+
$memoryLimit = $this->getMemoryLimit();
40+
return $memoryLimit === -1 || $memoryLimit >= self::RECOMMENDED_MEMORY_LIMIT;
41+
}
42+
43+
/**
44+
* Returns the php memory limit.
45+
*
46+
* @return int The memory limit in bytes.
47+
*/
48+
public function getMemoryLimit(): int {
49+
$iniValue = trim(ini_get('memory_limit'));
50+
if ($iniValue === '-1') {
51+
return -1;
52+
} else if (is_numeric($iniValue) === true) {
53+
return (int)$iniValue;
54+
} else {
55+
return $this->memoryLimitToBytes($iniValue);
56+
}
57+
}
58+
59+
/**
60+
* Converts the ini memory limit to bytes.
61+
*
62+
* @param string $memoryLimit The "memory_limit" ini value
63+
* @return int
64+
*/
65+
private function memoryLimitToBytes(string $memoryLimit): int {
66+
$last = strtolower(substr($memoryLimit, -1));
67+
$memoryLimit = (int)substr($memoryLimit, 0, -1);
68+
69+
// intended fall trough
70+
switch($last) {
71+
case 'g':
72+
$memoryLimit *= 1024;
73+
case 'm':
74+
$memoryLimit *= 1024;
75+
case 'k':
76+
$memoryLimit *= 1024;
77+
}
78+
79+
return $memoryLimit;
80+
}
81+
}

settings/Controller/CheckSetupController.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
use OC\DB\MissingIndexInformation;
4242
use OC\IntegrityCheck\Checker;
4343
use OC\Lock\NoopLockingProvider;
44+
use OC\MemoryInfo;
4445
use OCP\AppFramework\Controller;
4546
use OCP\AppFramework\Http\DataDisplayResponse;
4647
use OCP\AppFramework\Http\DataResponse;
@@ -83,6 +84,8 @@ class CheckSetupController extends Controller {
8384
private $lockingProvider;
8485
/** @var IDateTimeFormatter */
8586
private $dateTimeFormatter;
87+
/** @var MemoryInfo */
88+
private $memoryInfo;
8689

8790
public function __construct($AppName,
8891
IRequest $request,
@@ -96,7 +99,8 @@ public function __construct($AppName,
9699
EventDispatcherInterface $dispatcher,
97100
IDBConnection $db,
98101
ILockingProvider $lockingProvider,
99-
IDateTimeFormatter $dateTimeFormatter) {
102+
IDateTimeFormatter $dateTimeFormatter,
103+
MemoryInfo $memoryInfo) {
100104
parent::__construct($AppName, $request);
101105
$this->config = $config;
102106
$this->clientService = $clientService;
@@ -109,6 +113,7 @@ public function __construct($AppName,
109113
$this->db = $db;
110114
$this->lockingProvider = $lockingProvider;
111115
$this->dateTimeFormatter = $dateTimeFormatter;
116+
$this->memoryInfo = $memoryInfo;
112117
}
113118

114119
/**
@@ -616,6 +621,7 @@ public function check() {
616621
'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
617622
'isPhpMailerUsed' => $this->isPhpMailerUsed(),
618623
'mailSettingsDocumentation' => $this->urlGenerator->getAbsoluteURL('index.php/settings/admin'),
624+
'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(),
619625
'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
620626
]
621627
);

0 commit comments

Comments
 (0)