diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index f0821d2c336..aa092962a66 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -20,6 +20,7 @@ - Added `craft\helpers\Image::EXIF_IFD0_ROTATE_180_MIRRORED`. - Added `craft\helpers\Image::EXIF_IFD0_ROTATE_270_MIRRORED`. - Added `craft\helpers\Image::EXIF_IFD0_ROTATE_90_MIRRORED`. +- Added `craft\models\AssetIndexingSession::$forceStop`. ([#16435](https://github.com/craftcms/cms/pull/16435)) - `GuzzleHttp\Client` is now instantiated via `Craft::createObject()`. ([#16366](https://github.com/craftcms/cms/pull/16366)) - `craft\helpers\DateTimeHelper::humanDuration()` now has a `$language` argument. ([#16332](https://github.com/craftcms/cms/pull/16332)) @@ -31,4 +32,5 @@ - Fixed a bug where `craft\config\GeneralConfig::safeMode()` set Safe Mode to `false` by default. - Fixed a bug where Craft wasn’t auto-rotating or flipping images uploaded with a mirrored EXIF orientation. - Fixed a bug where elements weren’t getting returned in a consistent order when `orderBy` was set to `RAND(X)` across varying `limit` param values. ([#16432](https://github.com/craftcms/cms/issues/16432)) +- Fixed a bug where asset indexing could get stuck in an infinite loop if the index data was deleted. ([#16435](https://github.com/craftcms/cms/pull/16435)) - Updated Twig to 3.15. ([#16207](https://github.com/craftcms/cms/discussions/16207)) diff --git a/src/controllers/AssetIndexesController.php b/src/controllers/AssetIndexesController.php index 114d3621ff1..b20f07ade0c 100644 --- a/src/controllers/AssetIndexesController.php +++ b/src/controllers/AssetIndexesController.php @@ -151,6 +151,14 @@ public function actionProcessIndexingSession(): Response if (!$indexingSession->actionRequired) { $indexingSession = $assetIndexer->processIndexSession($indexingSession); + if ($indexingSession->forceStop) { + $assetIndexer->stopIndexingSession($indexingSession); + return $this->asFailure(data: [ + 'stop' => $sessionId, + 'message' => Craft::t('app', 'There was a problem indexing assets.'), + ]); + } + // If action is now required, we just processed the last entry // To save a round-trip, just pull the session review data if ($indexingSession->actionRequired) { diff --git a/src/models/AssetIndexingSession.php b/src/models/AssetIndexingSession.php index 6cdf0b73cc3..b270360df97 100644 --- a/src/models/AssetIndexingSession.php +++ b/src/models/AssetIndexingSession.php @@ -86,4 +86,10 @@ class AssetIndexingSession extends Model * @since 4.4.0 */ public bool $processIfRootEmpty = false; + + /** + * @var bool Whether we should stop processing the session because there was a problem. + * @since 4.14.0 + */ + public bool $forceStop = false; } diff --git a/src/services/AssetIndexer.php b/src/services/AssetIndexer.php index 6b5da101bd5..ae228f0847b 100644 --- a/src/services/AssetIndexer.php +++ b/src/services/AssetIndexer.php @@ -307,6 +307,26 @@ public function processIndexSession(AssetIndexingSession $indexingSession): Asse // The most likely scenario is that the last entry is being worked on. if (!$indexEntry && !$indexingSession->processIfRootEmpty) { + // if indexEntry is null here, we should also check if there's anything in the assetindexdata table at all + // (if not, it could have been deleted when clearing caches) + // if that table is empty, we'll get into an infinite loop, calling processIndexSession with the same data all the time + // (and it'll be very hard to discard the session via ui) + if ($indexingSession->processedEntries < $indexingSession->totalEntries) { + $count = (new Query()) + ->from([Table::ASSETINDEXDATA]) + ->where([ + 'sessionId' => $indexingSession->id, + 'completed' => false, + 'inProgress' => false, + ]) + ->count(); + + if ((int)$count === 0) { + Craft::warning('The assetindexdata table is empty; Can’t proceed with indexing.'); + $indexingSession->forceStop = true; + } + } + $mutex->release($lockName); return $indexingSession; } diff --git a/src/translations/en/app.php b/src/translations/en/app.php index 0fbadc4c2a8..fd7cf6ca3d9 100644 --- a/src/translations/en/app.php +++ b/src/translations/en/app.php @@ -1533,6 +1533,7 @@ 'There was a problem activating the user.' => 'There was a problem activating the user.', 'There was a problem deactivating the user.' => 'There was a problem deactivating the user.', 'There was a problem impersonating this user.' => 'There was a problem impersonating this user.', + 'There was a problem indexing assets.' => 'There was a problem indexing assets.', 'There was a problem saving your message.' => 'There was a problem saving your message.', 'There was a problem sending the password reset email.' => 'There was a problem sending the password reset email.', 'There was a problem with uploading the file.' => 'There was a problem with uploading the file.',