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