Closed mattgrayisok closed 3 years ago
So I'm explicitly using ->each() here to avoid the situation you're discussing:
https://github.com/nystudio107/craft-imageoptimize/blob/v1/src/jobs/ResaveOptimizedImages.php#L83
It was added in to fix the situation you're describing, and some time ago... so I'm intrigued that this is coming up
From my understanding of how ->each() works, it should be iterating through it in batches, loading in at most the default of 100 rows at a time, but handling the "pagination" and such for me
https://www.yiiframework.com/doc/guide/2.0/en/db-query-builder#batch-query
Looks like a limitation of the MySQL PDO driver which will just buffer the whole thing anyway: https://www.yiiframework.com/doc/guide/2.0/en/db-query-builder#batch-query-mysql
So this is pretty interesting:
https://github.com/yiisoft/yii2/issues/8420
It looks like the TL;DR on this is ->each()
and ->batch()
simply don't work as advertised using the MySQL PDO extension unless you jump through hoops doing this:
$unbufferedDb = new \yii\db\Connection([
'dsn' => Yii::$app->db->dsn,
'username' => Yii::$app->db->username,
'password' => Yii::$app->db->password,
'charset' => Yii::$app->db->charset,
]);
$unbufferedDb->open();
$unbufferedDb->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
foreach ($query->batch(1000, $unbufferedDb) as $batch) {
//...
}
The interesting thing is I see Craft using ->each()
in a number of places, I'll loop them in in case they are unaware of the issue
This post in that thread summarizes the potential solutions:
https://github.com/yiisoft/yii2/issues/8420#issuecomment-612742289
Super-relavent, and also embarassingly, a discussion involving me:
https://twitter.com/markhuot/status/1240379989603897345
https://forum.yiiframework.com/t/large-amount-data-with-less-memory/81519/3
It looks like I could be using ActiveDataProvider here:
https://www.yiiframework.com/doc/api/2.0/yii-data-activedataprovider
$provider = new ActiveDataProvider([
'query' => Post::find(),
'pagination' => [
'pageSize' => 20,
],
]);
// get the posts in the current page
$posts = $provider->getModels();
Addressed in: https://github.com/nystudio107/craft-imageoptimize/commit/296e1e3512f2a1ddb08e86e26e131876fd91bf51
You can try it now by setting your semver in your composer.json
to look like this:
"nystudio107/craft-imageoptimize": "dev-develop as 1.6.21",
Then do a composer update
So I refactored it (again) to use craft\db\Paginator
in https://github.com/nystudio107/craft-imageoptimize/commit/c3e2a5b1111c6f94065b75c9e3f3b17b6142e109
You can try it now by setting your semver in your composer.json
to look like this:
"nystudio107/craft-imageoptimize": "dev-develop as 1.6.21",
Then do a composer update
Brandon has also addressed this in Craft 3.6: https://github.com/craftcms/cms/issues/7338
But I felt like I should fix it here as well. lmk!
Describe the bug
Speculative diagnosis after brief investigation
If a user uses a single assets volume for all images, and has lots of content, the resave optimized images background task can OOM PHP by trying to pull the entire contents of Craft's content table into memory.
Experienced this with a couple of clients, one was fixed by bumping the memory limit up to 512 MB, the other has a very large content table so raising the memory limit isn't feasible.
I believe it's this query:
https://github.com/nystudio107/craft-imageoptimize/blob/bd6b351851dda437af6553a4427aaceb12a2a521/src/jobs/ResaveOptimizedImages.php#L66
With these criteria:
https://github.com/nystudio107/craft-imageoptimize/blob/bd6b351851dda437af6553a4427aaceb12a2a521/src/services/OptimizedImages.php#L326
Causing an SQL statement which looks like this:
Which you can probably tell is a bit dangerous! If all elements have an associated image, and they're all stored in a single volume, this will try to load the entire
content
table into memory.Resulting in:
Perhaps rather than setting the limit and offset values to
null
when executing this query the results could be chunked into groups of 50 or something similar which should resolve it.To reproduce
Steps to reproduce the behaviour:
Expected behaviour
No explosions
Versions