Add support for --jobs in reindexdb
authorMichael Paquier <michael@paquier.xyz>
Sat, 27 Jul 2019 13:21:18 +0000 (22:21 +0900)
committerMichael Paquier <michael@paquier.xyz>
Sat, 27 Jul 2019 13:21:18 +0000 (22:21 +0900)
When doing a schema-level or a database-level operation, a list of
relations to build is created which gets processed in parallel using
multiple connections, based on the recent refactoring for parallel slots
in src/bin/scripts/.  System catalogs are processed first in a
serialized fashion to prevent deadlocks, followed by the rest done in
parallel.

This new option is not compatible with --system as reindexing system
catalogs in parallel can lead to deadlocks, and with --index as there is
no conflict handling for indexes rebuilt in parallel depending in the
same relation.

Author: Julien Rouhaud
Reviewed-by: Sergei Kornilov, Michael Paquier
Discussion: https://postgr.es/m/CAOBaU_YrnH_Jqo46NhaJ7uRBiWWEcS40VNRQxgFbqYo9kApUsg@mail.gmail.com

doc/src/sgml/ref/reindexdb.sgml
src/bin/scripts/Makefile
src/bin/scripts/reindexdb.c
src/bin/scripts/t/090_reindexdb.pl

index 25b5a72770571b79c192f3d85faa4a42e1adc772..5e21fbcc4e6e2f4407a273afff31cd494fdbb97b 100644 (file)
@@ -166,6 +166,29 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-j <replaceable class="parameter">njobs</replaceable></option></term>
+      <term><option>--jobs=<replaceable class="parameter">njobs</replaceable></option></term>
+      <listitem>
+       <para>
+        Execute the reindex commands in parallel by running
+        <replaceable class="parameter">njobs</replaceable>
+        commands simultaneously.  This option reduces the time of the
+        processing but it also increases the load on the database server.
+       </para>
+       <para>
+        <application>reindexdb</application> will open
+        <replaceable class="parameter">njobs</replaceable> connections to the
+        database, so make sure your <xref linkend="guc-max-connections"/>
+        setting is high enough to accommodate all connections.
+       </para>
+       <para>
+        Note that this option is incompatible with the <option>--index</option>
+        and <option>--system</option> options.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-q</option></term>
       <term><option>--quiet</option></term>
index 3cd793b13408a4f814a2b541df6ec36ea0c775f0..ede665090f773b4e3906af60b7c6a87d27a8ebd6 100644 (file)
@@ -29,7 +29,7 @@ dropdb: dropdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-
 dropuser: dropuser.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
 clusterdb: clusterdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
 vacuumdb: vacuumdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
-reindexdb: reindexdb.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
+reindexdb: reindexdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
 pg_isready: pg_isready.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils
 
 install: all installdirs
index 219a9a9211831f37f4920f0d8e56f18a1216ee95..b2c0400cb9c962350ec3567d95e2ff7dc8c719d9 100644 (file)
  */
 
 #include "postgres_fe.h"
+
+#include "catalog/pg_class_d.h"
 #include "common.h"
 #include "common/logging.h"
+#include "fe_utils/connect.h"
 #include "fe_utils/simple_list.h"
 #include "fe_utils/string_utils.h"
+#include "scripts_parallel.h"
 
 typedef enum ReindexType
 {
@@ -25,16 +29,26 @@ typedef enum ReindexType
 } ReindexType;
 
 
-static void reindex_one_database(const char *name, const char *dbname,
-                                ReindexType type, const char *host,
+static SimpleStringList *get_parallel_object_list(PGconn *conn,
+                                                 ReindexType type,
+                                                 SimpleStringList *user_list,
+                                                 bool echo);
+static void reindex_one_database(const char *dbname, ReindexType type,
+                                SimpleStringList *user_list, const char *host,
                                 const char *port, const char *username,
                                 enum trivalue prompt_password, const char *progname,
-                                bool echo, bool verbose, bool concurrently);
+                                bool echo, bool verbose, bool concurrently,
+                                int concurrentCons);
 static void reindex_all_databases(const char *maintenance_db,
                                  const char *host, const char *port,
                                  const char *username, enum trivalue prompt_password,
                                  const char *progname, bool echo,
-                                 bool quiet, bool verbose, bool concurrently);
+                                 bool quiet, bool verbose, bool concurrently,
+                                 int concurrentCons);
+static void run_reindex_command(PGconn *conn, ReindexType type,
+                               const char *name, bool echo, bool verbose,
+                               bool concurrently, bool async);
+
 static void help(const char *progname);
 
 int
@@ -54,6 +68,7 @@ main(int argc, char *argv[])
        {"system", no_argument, NULL, 's'},
        {"table", required_argument, NULL, 't'},
        {"index", required_argument, NULL, 'i'},
+       {"jobs", required_argument, NULL, 'j'},
        {"verbose", no_argument, NULL, 'v'},
        {"concurrently", no_argument, NULL, 1},
        {"maintenance-db", required_argument, NULL, 2},
@@ -79,6 +94,7 @@ main(int argc, char *argv[])
    SimpleStringList indexes = {NULL, NULL};
    SimpleStringList tables = {NULL, NULL};
    SimpleStringList schemas = {NULL, NULL};
+   int         concurrentCons = 1;
 
    pg_logging_init(argv[0]);
    progname = get_progname(argv[0]);
@@ -87,7 +103,7 @@ main(int argc, char *argv[])
    handle_help_version_opts(argc, argv, "reindexdb", help);
 
    /* process command-line options */
-   while ((c = getopt_long(argc, argv, "h:p:U:wWeqS:d:ast:i:v", long_options, &optindex)) != -1)
+   while ((c = getopt_long(argc, argv, "h:p:U:wWeqS:d:ast:i:j:v", long_options, &optindex)) != -1)
    {
        switch (c)
        {
@@ -130,6 +146,20 @@ main(int argc, char *argv[])
            case 'i':
                simple_string_list_append(&indexes, optarg);
                break;
+           case 'j':
+               concurrentCons = atoi(optarg);
+               if (concurrentCons <= 0)
+               {
+                   pg_log_error("number of parallel jobs must be at least 1");
+                   exit(1);
+               }
+               if (concurrentCons > FD_SETSIZE - 1)
+               {
+                   pg_log_error("too many parallel jobs requested (maximum: %d)",
+                                FD_SETSIZE - 1);
+                   exit(1);
+               }
+               break;
            case 'v':
                verbose = true;
                break;
@@ -194,7 +224,8 @@ main(int argc, char *argv[])
        }
 
        reindex_all_databases(maintenance_db, host, port, username,
-                             prompt_password, progname, echo, quiet, verbose, concurrently);
+                             prompt_password, progname, echo, quiet, verbose,
+                             concurrently, concurrentCons);
    }
    else if (syscatalog)
    {
@@ -214,6 +245,12 @@ main(int argc, char *argv[])
            exit(1);
        }
 
+       if (concurrentCons > 1)
+       {
+           pg_log_error("cannot use multiple jobs to reindex system catalogs");
+           exit(1);
+       }
+
        if (dbname == NULL)
        {
            if (getenv("PGDATABASE"))
@@ -224,12 +261,23 @@ main(int argc, char *argv[])
                dbname = get_user_name_or_exit(progname);
        }
 
-       reindex_one_database(NULL, dbname, REINDEX_SYSTEM, host,
+       reindex_one_database(dbname, REINDEX_SYSTEM, NULL, host,
                             port, username, prompt_password, progname,
-                            echo, verbose, concurrently);
+                            echo, verbose, concurrently, 1);
    }
    else
    {
+       /*
+        * Index-level REINDEX is not supported with multiple jobs as we
+        * cannot control the concurrent processing of multiple indexes
+        * depending on the same relation.
+        */
+       if (concurrentCons > 1 && indexes.head != NULL)
+       {
+           pg_log_error("cannot use multiple jobs to reindex indexes");
+           exit(1);
+       }
+
        if (dbname == NULL)
        {
            if (getenv("PGDATABASE"))
@@ -241,61 +289,49 @@ main(int argc, char *argv[])
        }
 
        if (schemas.head != NULL)
-       {
-           SimpleStringListCell *cell;
-
-           for (cell = schemas.head; cell; cell = cell->next)
-           {
-               reindex_one_database(cell->val, dbname, REINDEX_SCHEMA, host,
-                                    port, username, prompt_password, progname,
-                                    echo, verbose, concurrently);
-           }
-       }
+           reindex_one_database(dbname, REINDEX_SCHEMA, &schemas, host,
+                                port, username, prompt_password, progname,
+                                echo, verbose, concurrently, concurrentCons);
 
        if (indexes.head != NULL)
-       {
-           SimpleStringListCell *cell;
+           reindex_one_database(dbname, REINDEX_INDEX, &indexes, host,
+                                port, username, prompt_password, progname,
+                                echo, verbose, concurrently, 1);
 
-           for (cell = indexes.head; cell; cell = cell->next)
-           {
-               reindex_one_database(cell->val, dbname, REINDEX_INDEX, host,
-                                    port, username, prompt_password, progname,
-                                    echo, verbose, concurrently);
-           }
-       }
        if (tables.head != NULL)
-       {
-           SimpleStringListCell *cell;
-
-           for (cell = tables.head; cell; cell = cell->next)
-           {
-               reindex_one_database(cell->val, dbname, REINDEX_TABLE, host,
-                                    port, username, prompt_password, progname,
-                                    echo, verbose, concurrently);
-           }
-       }
+           reindex_one_database(dbname, REINDEX_TABLE, &tables, host,
+                                port, username, prompt_password, progname,
+                                echo, verbose, concurrently,
+                                concurrentCons);
 
        /*
         * reindex database only if neither index nor table nor schema is
         * specified
         */
        if (indexes.head == NULL && tables.head == NULL && schemas.head == NULL)
-           reindex_one_database(NULL, dbname, REINDEX_DATABASE, host,
+           reindex_one_database(dbname, REINDEX_DATABASE, NULL, host,
                                 port, username, prompt_password, progname,
-                                echo, verbose, concurrently);
+                                echo, verbose, concurrently, concurrentCons);
    }
 
    exit(0);
 }
 
 static void
-reindex_one_database(const char *name, const char *dbname, ReindexType type,
-                    const char *host, const char *port, const char *username,
+reindex_one_database(const char *dbname, ReindexType type,
+                    SimpleStringList *user_list, const char *host,
+                    const char *port, const char *username,
                     enum trivalue prompt_password, const char *progname, bool echo,
-                    bool verbose, bool concurrently)
+                    bool verbose, bool concurrently, int concurrentCons)
 {
-   PQExpBufferData sql;
    PGconn     *conn;
+   SimpleStringListCell *cell;
+   bool        parallel = concurrentCons > 1;
+   SimpleStringList *process_list = user_list;
+   ReindexType process_type = type;
+   ParallelSlot *slots;
+   bool        failed = false;
+   int         items_count = 0;
 
    conn = connectDatabase(dbname, host, port, username, prompt_password,
                           progname, echo, false, false);
@@ -308,6 +344,151 @@ reindex_one_database(const char *name, const char *dbname, ReindexType type,
        exit(1);
    }
 
+   if (!parallel)
+   {
+       switch (process_type)
+       {
+           case REINDEX_DATABASE:
+           case REINDEX_SYSTEM:
+
+               /*
+                * Database and system reindexes only need to work on the
+                * database itself, so build a list with a single entry.
+                */
+               Assert(user_list == NULL);
+               process_list = pg_malloc0(sizeof(SimpleStringList));
+               simple_string_list_append(process_list, PQdb(conn));
+               break;
+
+           case REINDEX_INDEX:
+           case REINDEX_SCHEMA:
+           case REINDEX_TABLE:
+               Assert(user_list != NULL);
+               break;
+       }
+   }
+   else
+   {
+       switch (process_type)
+       {
+           case REINDEX_DATABASE:
+
+               /*
+                * Database-wide parallel reindex requires special processing.
+                * If multiple jobs were asked, we have to reindex system
+                * catalogs first as they cannot be processed in parallel.
+                */
+               if (concurrently)
+                   pg_log_warning("cannot reindex system catalogs concurrently, skipping all");
+               else
+                   run_reindex_command(conn, REINDEX_SYSTEM, PQdb(conn), echo,
+                                       verbose, concurrently, false);
+
+               /* Build a list of relations from the database */
+               process_list = get_parallel_object_list(conn, process_type,
+                                                       user_list, echo);
+               process_type = REINDEX_TABLE;
+
+               /* Bail out if nothing to process */
+               if (process_list == NULL)
+                   return;
+               break;
+
+           case REINDEX_SCHEMA:
+               Assert(user_list != NULL);
+
+               /* Build a list of relations from all the schemas */
+               process_list = get_parallel_object_list(conn, process_type,
+                                                       user_list, echo);
+               process_type = REINDEX_TABLE;
+
+               /* Bail out if nothing to process */
+               if (process_list == NULL)
+                   return;
+               break;
+
+           case REINDEX_SYSTEM:
+           case REINDEX_INDEX:
+               /* not supported */
+               Assert(false);
+               break;
+
+           case REINDEX_TABLE:
+
+               /*
+                * Fall through.  The list of items for tables is already
+                * created.
+                */
+               break;
+       }
+   }
+
+   /*
+    * Adjust the number of concurrent connections depending on the items in
+    * the list.  We choose the minimum between the number of concurrent
+    * connections and the number of items in the list.
+    */
+   for (cell = process_list->head; cell; cell = cell->next)
+   {
+       items_count++;
+
+       /* no need to continue if there are more elements than jobs */
+       if (items_count >= concurrentCons)
+           break;
+   }
+   concurrentCons = Min(concurrentCons, items_count);
+   Assert(concurrentCons > 0);
+
+   Assert(process_list != NULL);
+
+   slots = ParallelSlotsSetup(dbname, host, port, username, prompt_password,
+                              progname, echo, conn, concurrentCons);
+
+   cell = process_list->head;
+   do
+   {
+       const char *objname = cell->val;
+       ParallelSlot *free_slot = NULL;
+
+       if (CancelRequested)
+       {
+           failed = true;
+           goto finish;
+       }
+
+       free_slot = ParallelSlotsGetIdle(slots, concurrentCons);
+       if (!free_slot)
+       {
+           failed = true;
+           goto finish;
+       }
+
+       run_reindex_command(free_slot->connection, process_type, objname,
+                           echo, verbose, concurrently, true);
+
+       cell = cell->next;
+   } while (cell != NULL);
+
+   if (!ParallelSlotsWaitCompletion(slots, concurrentCons))
+       failed = true;
+
+finish:
+   ParallelSlotsTerminate(slots, concurrentCons);
+   pfree(slots);
+
+   if (failed)
+       exit(1);
+}
+
+static void
+run_reindex_command(PGconn *conn, ReindexType type, const char *name,
+                   bool echo, bool verbose, bool concurrently, bool async)
+{
+   PQExpBufferData sql;
+   bool        status;
+
+   Assert(name);
+
    /* build the REINDEX query */
    initPQExpBuffer(&sql);
 
@@ -344,7 +525,7 @@ reindex_one_database(const char *name, const char *dbname, ReindexType type,
    {
        case REINDEX_DATABASE:
        case REINDEX_SYSTEM:
-           appendPQExpBufferStr(&sql, fmtId(PQdb(conn)));
+           appendPQExpBufferStr(&sql, fmtId(name));
            break;
        case REINDEX_INDEX:
        case REINDEX_TABLE:
@@ -358,7 +539,17 @@ reindex_one_database(const char *name, const char *dbname, ReindexType type,
    /* finish the query */
    appendPQExpBufferChar(&sql, ';');
 
-   if (!executeMaintenanceCommand(conn, sql.data, echo))
+   if (async)
+   {
+       if (echo)
+           printf("%s\n", sql.data);
+
+       status = PQsendQuery(conn, sql.data) == 1;
+   }
+   else
+       status = executeMaintenanceCommand(conn, sql.data, echo);
+
+   if (!status)
    {
        switch (type)
        {
@@ -383,20 +574,141 @@ reindex_one_database(const char *name, const char *dbname, ReindexType type,
                             name, PQdb(conn), PQerrorMessage(conn));
                break;
        }
-       PQfinish(conn);
-       exit(1);
+       if (!async)
+       {
+           PQfinish(conn);
+           exit(1);
+       }
    }
 
-   PQfinish(conn);
    termPQExpBuffer(&sql);
 }
 
+/*
+ * Prepare the list of objects to process by querying the catalogs.
+ *
+ * This function will return a SimpleStringList object containing the entire
+ * list of tables in the given database that should be processed by a parallel
+ * database-wide reindex (excluding system tables), or NULL if there's no such
+ * table.
+ */
+static SimpleStringList *
+get_parallel_object_list(PGconn *conn, ReindexType type,
+                        SimpleStringList *user_list, bool echo)
+{
+   PQExpBufferData catalog_query;
+   PQExpBufferData buf;
+   PGresult   *res;
+   SimpleStringList *tables;
+   int         ntups,
+               i;
+
+   initPQExpBuffer(&catalog_query);
+
+   /*
+    * The queries here are using a safe search_path, so there's no need to
+    * fully qualify everything.
+    */
+   switch (type)
+   {
+       case REINDEX_DATABASE:
+           Assert(user_list == NULL);
+           appendPQExpBuffer(&catalog_query,
+                             "SELECT c.relname, ns.nspname\n"
+                             " FROM pg_catalog.pg_class c\n"
+                             " JOIN pg_catalog.pg_namespace ns"
+                             " ON c.relnamespace = ns.oid\n"
+                             " WHERE ns.nspname != 'pg_catalog'\n"
+                             "   AND c.relkind IN ("
+                             CppAsString2(RELKIND_RELATION) ", "
+                             CppAsString2(RELKIND_MATVIEW) ")\n"
+                             " ORDER BY c.relpages DESC;");
+           break;
+
+       case REINDEX_SCHEMA:
+           {
+               SimpleStringListCell *cell;
+               bool        nsp_listed = false;
+
+               Assert(user_list != NULL);
+
+               /*
+                * All the tables from all the listed schemas are grabbed at
+                * once.
+                */
+               appendPQExpBuffer(&catalog_query,
+                                 "SELECT c.relname, ns.nspname\n"
+                                 " FROM pg_catalog.pg_class c\n"
+                                 " JOIN pg_catalog.pg_namespace ns"
+                                 " ON c.relnamespace = ns.oid\n"
+                                 " WHERE c.relkind IN ("
+                                 CppAsString2(RELKIND_RELATION) ", "
+                                 CppAsString2(RELKIND_MATVIEW) ")\n"
+                                 " AND ns.nspname IN (");
+
+               for (cell = user_list->head; cell; cell = cell->next)
+               {
+                   const char *nspname = cell->val;
+
+                   if (nsp_listed)
+                       appendPQExpBuffer(&catalog_query, ", ");
+                   else
+                       nsp_listed = true;
+
+                   appendStringLiteralConn(&catalog_query, nspname, conn);
+               }
+
+               appendPQExpBuffer(&catalog_query, ")\n"
+                                 " ORDER BY c.relpages DESC;");
+           }
+           break;
+
+       case REINDEX_SYSTEM:
+       case REINDEX_INDEX:
+       case REINDEX_TABLE:
+           Assert(false);
+           break;
+   }
+
+   res = executeQuery(conn, catalog_query.data, echo);
+   termPQExpBuffer(&catalog_query);
+
+   /*
+    * If no rows are returned, there are no matching tables, so we are done.
+    */
+   ntups = PQntuples(res);
+   if (ntups == 0)
+   {
+       PQclear(res);
+       PQfinish(conn);
+       return NULL;
+   }
+
+   tables = pg_malloc0(sizeof(SimpleStringList));
+
+   /* Build qualified identifiers for each table */
+   initPQExpBuffer(&buf);
+   for (i = 0; i < ntups; i++)
+   {
+       appendPQExpBufferStr(&buf,
+                            fmtQualifiedId(PQgetvalue(res, i, 1),
+                                           PQgetvalue(res, i, 0)));
+
+       simple_string_list_append(tables, buf.data);
+       resetPQExpBuffer(&buf);
+   }
+   termPQExpBuffer(&buf);
+   PQclear(res);
+
+   return tables;
+}
+
 static void
 reindex_all_databases(const char *maintenance_db,
                      const char *host, const char *port,
                      const char *username, enum trivalue prompt_password,
                      const char *progname, bool echo, bool quiet, bool verbose,
-                     bool concurrently)
+                     bool concurrently, int concurrentCons)
 {
    PGconn     *conn;
    PGresult   *result;
@@ -423,9 +735,10 @@ reindex_all_databases(const char *maintenance_db,
        appendPQExpBufferStr(&connstr, "dbname=");
        appendConnStrVal(&connstr, dbname);
 
-       reindex_one_database(NULL, connstr.data, REINDEX_DATABASE, host,
+       reindex_one_database(connstr.data, REINDEX_DATABASE, NULL, host,
                             port, username, prompt_password,
-                            progname, echo, verbose, concurrently);
+                            progname, echo, verbose, concurrently,
+                            concurrentCons);
    }
    termPQExpBuffer(&connstr);
 
@@ -444,6 +757,7 @@ help(const char *progname)
    printf(_("  -d, --dbname=DBNAME       database to reindex\n"));
    printf(_("  -e, --echo                show the commands being sent to the server\n"));
    printf(_("  -i, --index=INDEX         recreate specific index(es) only\n"));
+   printf(_("  -j, --jobs=NUM            use this many concurrent connections to reindex\n"));
    printf(_("  -q, --quiet               don't write any messages\n"));
    printf(_("  -s, --system              reindex system catalogs\n"));
    printf(_("  -S, --schema=SCHEMA       reindex specific schema(s) only\n"));
index 1af8ab70ad91e86110beda2eac4c08d159440fc2..c20ffbd505cc3a241ae6e558ec4ec9b8c8994518 100644 (file)
@@ -3,7 +3,7 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 34;
+use Test::More tests => 44;
 
 program_help_ok('reindexdb');
 program_version_ok('reindexdb');
@@ -77,3 +77,45 @@ $node->command_ok(
 $node->command_ok(
    [qw(reindexdb --echo --system dbname=template1)],
    'reindexdb system with connection string');
+
+# parallel processing
+$node->safe_psql(
+   'postgres', q|
+   CREATE SCHEMA s1;
+   CREATE TABLE s1.t1(id integer);
+   CREATE INDEX ON s1.t1(id);
+   CREATE SCHEMA s2;
+   CREATE TABLE s2.t2(id integer);
+   CREATE INDEX ON s2.t2(id);
+   -- empty schema
+   CREATE SCHEMA s3;
+|);
+
+$node->command_fails(
+   [ 'reindexdb', '-j', '2', '-s', 'postgres' ],
+   'parallel reindexdb cannot process system catalogs');
+$node->command_fails(
+   [ 'reindexdb', '-j', '2', '-i', 'i1', 'postgres' ],
+   'parallel reindexdb cannot process indexes');
+$node->issues_sql_like(
+   [ 'reindexdb', '-j', '2', 'postgres' ],
+   qr/statement:\ REINDEX SYSTEM postgres;
+.*statement:\ REINDEX TABLE public\.test1/s,
+   'parallel reindexdb for database issues REINDEX SYSTEM first');
+# Note that the ordering of the commands is not stable, so the second
+# command for s2.t2 is not checked after.
+$node->issues_sql_like(
+   [ 'reindexdb', '-j', '2', '-S', 's1', '-S', 's2', 'postgres' ],
+   qr/statement:\ REINDEX TABLE s1.t1;/,
+   'parallel reindexdb for schemas does a per-table REINDEX');
+$node->command_ok(
+   ['reindexdb', '-j', '2', '-S', 's3'],
+   'parallel reindexdb with empty schema');
+$node->command_checks_all(
+   [ 'reindexdb', '-j', '2', '--concurrently', '-d', 'postgres' ],
+   0,
+   [qr/^$/],
+   [
+       qr/^reindexdb: warning: cannot reindex system catalogs concurrently, skipping all/s
+   ],
+   'parallel reindexdb for system with --concurrently skips catalogs');