From 22b48d989e767e1e7a209c2465e3ea452280b9a4 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Thu, 25 Jul 2024 16:13:26 +0200 Subject: [PATCH] Issue #3463797 by mkalkbrenner: Avoid standard deletes when reindexing if the index is empty --- src/Commands/SearchApiSolrCommands.php | 23 +++++++++++++++++++ src/Entity/Index.php | 13 +++++++++++ .../backend/SearchApiSolrBackend.php | 5 ++++ src/Utility/SolrCommandHelper.php | 17 ++++++++++++-- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/Commands/SearchApiSolrCommands.php b/src/Commands/SearchApiSolrCommands.php index e4d2714a..1bb31ede 100644 --- a/src/Commands/SearchApiSolrCommands.php +++ b/src/Commands/SearchApiSolrCommands.php @@ -270,6 +270,29 @@ public function indexParallel($indexId = NULL, array $options = ['threads' => NU } sleep(2); } + + $this->commandHelper->resetEmptyIndexState([$indexId]); + } + + /** + * Reset empty index state to FALSE. + * + * Important if drush search-api-solr:index-parallel crashed or has been + * interrupted. That might cause to block deletes on an index for one hour + * unless you run this command. + * + * @param string $indexId + * (optional) A search index ID, or NULL to index items for all enabled + * indexes. + * @param array $options + * (optional) An array of options. + * + * @command search-api-solr:reset-empty-index-state + * + * @default $options [] + */ + public function resetEmptyIndexState($indexId = NULL, array $options = []) { + $this->commandHelper->resetEmptyIndexState([$indexId]); } } diff --git a/src/Entity/Index.php b/src/Entity/Index.php index 9088772c..6c64151d 100644 --- a/src/Entity/Index.php +++ b/src/Entity/Index.php @@ -6,6 +6,8 @@ class Index extends SearchApiIndex { + const KEEP_EMPTY_INDEX_STATE_SECONDS = 3600; + /** * {@inheritdoc} */ @@ -19,4 +21,15 @@ public function getLockId(): string { return parent::getLockId(); } + public function isIndexingEmptyIndex(): bool { + $key = "search_api.index.{$this->id()}.indexing_empty"; + $timestamp = \Drupal::state()->get($key, 0); + return (\Drupal::time()->getRequestTime() - $timestamp) < self::KEEP_EMPTY_INDEX_STATE_SECONDS; + } + + public function setIndexingEmptyIndex(bool $state): void { + $key = "search_api.index.{$this->id()}.indexing_empty"; + \Drupal::state()->set($key, $state ? \Drupal::time()->getRequestTime() : 0); + } + } diff --git a/src/Plugin/search_api/backend/SearchApiSolrBackend.php b/src/Plugin/search_api/backend/SearchApiSolrBackend.php index e2317e79..6adb79ac 100644 --- a/src/Plugin/search_api/backend/SearchApiSolrBackend.php +++ b/src/Plugin/search_api/backend/SearchApiSolrBackend.php @@ -1467,6 +1467,11 @@ public function getDocuments(IndexInterface $index, array $items, UpdateQuery $u * @throws \Drupal\search_api\SearchApiException */ public function deleteItems(IndexInterface $index, array $ids) { + /** @var \Drupal\search_api_solr\Entity\Index $index */ + if ($index->isIndexingEmptyIndex()) { + return; + } + try { $index_id = $this->getTargetedIndexId($index); $site_hash = $this->getTargetedSiteHash($index); diff --git a/src/Utility/SolrCommandHelper.php b/src/Utility/SolrCommandHelper.php index e7f1d555..001c2781 100644 --- a/src/Utility/SolrCommandHelper.php +++ b/src/Utility/SolrCommandHelper.php @@ -216,12 +216,14 @@ public function indexParallelCommand(array $indexIds = NULL, $threads = 2, $batc $ids = []; + /** @var \Drupal\search_api_solr\Entity\Index $index */ foreach ($indexes as $index) { if (!$index->status() || $index->isReadOnly()) { continue; } $tracker = $index->getTrackerInstance(); - $remaining = $tracker->getTotalItemsCount() - $tracker->getIndexedItemsCount(); + $indexed = (int) $tracker->getIndexedItemsCount(); + $remaining = $tracker->getTotalItemsCount() - $indexed; if (!$remaining) { $this->logSuccess($this->t("The index @index is up to date.", ['@index' => $index->label()])); @@ -258,12 +260,15 @@ public function indexParallelCommand(array $indexIds = NULL, $threads = 2, $batc $currentThreads = 1; } + $index->setIndexingEmptyIndex($indexed === 0); + $arguments = [ '@index' => $index->label(), '@threads' => $currentThreads, '@batch_size' => $currentBatchSize, + '@empty' => $index->isIndexingEmptyIndex() ? $this->t('empty') : $this->t('not empty'), ]; - $this->logSuccess($this->t("Indexing parallel with @threads threads (@batch_size items per batch run) for the index '@index'.", $arguments)); + $this->logSuccess($this->t("Indexing parallel with @threads threads (@batch_size items per batch run) for the index '@index'. The index is @empty", $arguments)); // Create the batch. try { @@ -298,5 +303,13 @@ public function indexParallelCommand(array $indexIds = NULL, $threads = 2, $batc return $shuffled_ids; } + public function resetEmptyIndexState(array $indexIds): void { + if ($indexes = $this->loadIndexes($indexIds)) { + /** @var \Drupal\search_api_solr\Entity\Index $index */ + foreach ($indexes as $index) { + $index->setIndexingEmptyIndex(FALSE); + } + } + } }