Magento 2 - Programmatically recreate the missing Catalog Search index table

The story of a firefighting session after upgrading Magento

After a Magento upgrade, the mg3_catalogsearch_fulltext_scope1 table disappeared from the MySQL database of my Magento 2-based webshop. As a result, all product-related pages were returning HTTP 500 error code, and I could not create or update products in the admin panel.

SQLSTATE[42S02]: Base table or view not found: 1146 Table <...>.mg3_catalogsearch_fulltext_scope1' doesn't exist

TLDR

The original database was broken, so I’ve cloned it and used this script to recreate the missing index.

It was a dark and stormy night

Because I had no clue about the exact schema of the missing database table, I decided to recreate it programmatically using Magento’s built-in indexer service. Also, I could not use any command-line tools to fix the situation, as the webshop is running on a cheap host provider with no SSH access to the machine where Magento or the underlying MySQL database is hosted.

Access to Magento’s services

As a first step, I created a simple PHP script based on Magento’s index.php to have access to Magento’s classes and services.

try {
    require __DIR__ . '/app/bootstrap.php';
} catch (\Exception $e) {
    echo $e->getMessage();
    exit(1);
}

$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$app = $bootstrap->createApplication('Magento\Framework\App\Http');

Recreate the index table

Next, based on this Stack Exchange thread I’ve added a snippet to create the Catalog Search index table using IndexerHandlerFactory.

$indexerHandlerFactory = \Magento\Framework\App\ObjectManager::getInstance()->get("Magento\\CatalogSearch\\Model\\Indexer\\IndexerHandlerFactory");
$indexerConfig = \Magento\Framework\App\ObjectManager::getInstance()->get("Magento\\Framework\\Indexer\\ConfigInterface");
$dimensionFactory = \Magento\Framework\App\ObjectManager::getInstance()->get("Magento\\Framework\\Search\\Request\\DimensionFactory");

try {
    $dimensions = [
        $dimensionFactory->create(['name' => 'scope', 'value' => 'default'])
    ];
    $configData = $indexerConfig->getIndexer('catalogsearch_fulltext');
    $indexHandler = $indexerHandlerFactory->create(['data' => $configData]);
    $indexHandler->cleanIndex($dimensions);
} catch(Exception $e) {
    echo 'Error reindexing catalogsearch_fulltext<br/>';
    print($e->getMessage());
}

I’ve put this script next to the index.php, into the /home/<username>/public_html directory, hoping that after executing it, I get my indexes back.

Detour: what is a tablespace?

But then I faced with another issue: the upgrade only did a half-ass job deleting mg3_catalogsearch_fulltext_scope1. An attempt to recreate the table also failed:

Tablespace for table '`<...>`.`mg3_catalogsearch_fulltext_scope1`' exists. Please DISCARD the tablespace before IMPORT.

Trying to drop the said tablespace via phpMyAdmin also failed:

DROP TABLESPACE `<...>.mg3_catalogsearch_fulltext_scope1`;

# > Access denied; you need (at least one of) the CREATE TABLESPACE privilege(s) for this operation

According to Wikipedia, “a tablespace is a storage location where the actual data underlying database objects can be kept”. Based on this Stackoverflow discussion, in case of MySQL they can get “stuck”, and the best chance to remove them is to delete the corresponding file and restart MySQL. As I don’t have SSH access this was not an option.

So I decided to leave the original database behind and create a clone with phpMyAdmin, hoping that it copies the schema and the data, but not the faulty tablespaces.

Reindex products

After I reconfigured Magento to use the new database and ran the script, it indeed created the missing database table, and the website started to work again. However, I quickly realized that while all products are editable in the admin panel, they are not visible in the webshop UI. No wonder why: although now the index table is in place, it’s empty.

So I edited the PHP script to populate the indexes as well: (source)

$indexerFactory = \Magento\Framework\App\ObjectManager::getInstance()->get("Magento\Indexer\Model\IndexerFactory");
$indexerIds = array(
    'catalog_category_product',
    'catalog_product_category',
    'catalog_product_price',
    'catalog_product_attribute',
    'cataloginventory_stock',
    'catalogrule_product',
    'catalogsearch_fulltext',
);
foreach ($indexerIds as $indexerId) {
    try {
        echo " create index: ".$indexerId."\n";
        $indexer = $indexerFactory->create();
        $indexer->load($indexerId);
        $indexer->reindexAll();

    } catch (\Exception $e) {
        echo 'Error reindexing ' . $indexerId . '<br/>';
        echo $e->getMessage();
        echo "<br/>";
    }
}

End game

For some reason, the Category caches were not refreshed, so I had to save on one of the products in the admin panel to get the menus working again. Finally, I removed my PHP script from the server, and the Webshop lived happily ever after.

Despite I had no SSH access I could solve the problem by deploying custom PHP scripts. If you do so, make sure to clean up troubleshooting related scripts at the end.

The end.

17 September 2019