001/* 002 * The contents of this file are subject to the license and copyright 003 * detailed in the LICENSE and NOTICE files at the root of the source 004 * tree. 005 */ 006package org.fcrepo.persistence.ocfl.impl; 007 008import static org.slf4j.LoggerFactory.getLogger; 009 010import java.util.List; 011import java.util.concurrent.TimeUnit; 012 013import org.fcrepo.common.db.DbTransactionExecutor; 014import org.fcrepo.kernel.api.Transaction; 015import org.fcrepo.kernel.api.TransactionManager; 016 017import org.fcrepo.persistence.api.exceptions.ObjectExistsInOcflIndexException; 018import org.slf4j.Logger; 019 020import com.google.common.base.Stopwatch; 021 022/** 023 * A reindexing worker thread. 024 * @author whikloj 025 */ 026public class ReindexWorker implements Runnable { 027 028 private static final Logger LOGGER = getLogger(ReindexWorker.class); 029 030 private static final long REPORTING_INTERVAL_SECS = 30; 031 032 private Thread t; 033 private ReindexManager manager; 034 private ReindexService service; 035 private boolean running = true; 036 private boolean failOnError; 037 private TransactionManager txManager; 038 private DbTransactionExecutor dbTransactionExecutor; 039 040 /** 041 * Basic Constructor 042 * @param name the name of the worker -- used in logging 043 * @param reindexManager the manager service. 044 * @param reindexService the reindexing service. 045 * @param transactionManager a transaction manager to generate 046 * @param dbTransactionExecutor manages db transactions 047 * @param failOnError whether the thread should fail on an error or log and continue. 048 */ 049 public ReindexWorker(final String name, 050 final ReindexManager reindexManager, 051 final ReindexService reindexService, 052 final TransactionManager transactionManager, 053 final DbTransactionExecutor dbTransactionExecutor, 054 final boolean failOnError) { 055 manager = reindexManager; 056 service = reindexService; 057 txManager = transactionManager; 058 this.dbTransactionExecutor = dbTransactionExecutor; 059 this.failOnError = failOnError; 060 t = new Thread(this, name); 061 } 062 063 /** 064 * Join the thread. 065 * @throws InterruptedException if the current thread is interrupted. 066 */ 067 public void join() throws InterruptedException { 068 t.join(); 069 } 070 071 /** 072 * Start the thread with this Runnable 073 */ 074 public void start() { 075 t.start(); 076 } 077 078 @Override 079 public void run() { 080 final var stopwatch = Stopwatch.createStarted(); 081 while (running) { 082 final List<String> ids = manager.getIds(); 083 if (ids.isEmpty()) { 084 LOGGER.debug("No more objects found to process. Stopping..."); 085 stopThread(); 086 break; 087 } 088 089 int completed = 0; 090 int errors = 0; 091 int skipped = 0; 092 093 for (final var id : ids) { 094 if (!running) { 095 break; 096 } 097 098 final Transaction tx = txManager.create(); 099 tx.suppressEvents(); 100 tx.setShortLived(true); 101 if (stopwatch.elapsed(TimeUnit.SECONDS) > REPORTING_INTERVAL_SECS) { 102 manager.updateComplete(completed, errors, skipped); 103 completed = 0; 104 errors = 0; 105 stopwatch.reset().start(); 106 } 107 try { 108 dbTransactionExecutor.doInTxWithRetry(() -> { 109 service.indexOcflObject(tx, id); 110 tx.commit(); 111 }); 112 completed += 1; 113 } catch (final ObjectExistsInOcflIndexException e) { 114 tx.rollback(); 115 LOGGER.debug(e.getMessage()); 116 skipped += 1; 117 } catch (final Exception e) { 118 LOGGER.error("Reindexing of OCFL id {} failed", id, e); 119 tx.rollback(); 120 errors += 1; 121 if (failOnError) { 122 manager.updateComplete(completed, errors, skipped); 123 manager.stop(); 124 service.cleanupSession(tx.getId()); 125 throw e; 126 } 127 } 128 service.cleanupSession(tx.getId()); 129 } 130 manager.updateComplete(completed, errors, skipped); 131 } 132 } 133 134 /** 135 * Stop this thread from running once it has completed its current batch. 136 */ 137 public void stopThread() { 138 this.running = false; 139 } 140 141}