--- a/src/plugins/fts-flatcurve/fts-backend-flatcurve-xapian.cc +++ b/src/plugins/fts-flatcurve/fts-backend-flatcurve-xapian.cc @@ -102,6 +102,7 @@ #define FLATCURVE_DBW_LOCK_RETRY_SECS 1 #define FLATCURVE_DBW_LOCK_RETRY_MAX 60 #define FLATCURVE_MANUAL_OPTIMIZE_COMMIT_LIMIT 500 +#define FLATCURVE_OPTIMIZE_SKIP_LOG_LIMIT 10 /* Lock: needed to ensure we don't run into race conditions when * manipulating current directory. */ @@ -1837,20 +1838,41 @@ Xapian::MSetIterator iter = mset.begin(); unsigned int updates = 0; + unsigned int skipped = 0; for (iter = mset.begin(); iter != mset.end(); ++iter) { - Xapian::Document doc = iter.get_document(); try { - xdb->dbw->replace_document(doc.get_docid(), doc); + Xapian::Document doc = iter.get_document(); + xdb->dbw->replace_document(doc.get_docid(), doc); if (++updates > FLATCURVE_MANUAL_OPTIMIZE_COMMIT_LIMIT) { xdb->dbw->commit(); updates = 0; } - } catch (Xapian::Error &e) { + } catch (Xapian::DocNotFoundError &e) { + /* Document is in the MSet but its termlist cannot be + read. Skip it rather than abandon the rebuild, which + would leave every shard in place. Usually the message + is already gone, but corruption looks the same here, + so log it -- capped, since there may be many. */ + if (skipped < FLATCURVE_OPTIMIZE_SKIP_LOG_LIMIT) + e_warning(backend->event, "Optimize: skipping " + "unreadable document; %s", + e.get_description().c_str()); + ++skipped; + /* commit() is inside the try, so the throw may arrive + with documents counted but not committed. */ + updates = 0; + continue; + } catch (Xapian::Error &e) { *error_r = t_strdup(e.get_description().c_str()); return -1; } } + if (skipped > 0) + e_warning(backend->event, "Optimize: skipped %u unreadable " + "document(s); reindex needed if the messages still " + "exist", skipped); + return fts_flatcurve_xapian_close_db( backend, xdb, FLATCURVE_XAPIAN_DB_CLOSE_WDB, error_r); }