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.setShortLived(true); 098 if (stopwatch.elapsed(TimeUnit.SECONDS) > REPORTING_INTERVAL_SECS) { 099 manager.updateComplete(completed, errors); 100 completed = 0; 101 errors = 0; 102 stopwatch.reset().start(); 103 } 104 try { 105 dbTransactionExecutor.doInTxWithRetry(() -> { 106 service.indexOcflObject(tx, id); 107 tx.commit(); 108 }); 109 completed += 1; 110 } catch (final Exception e) { 111 LOGGER.error("Reindexing of OCFL id {} failed", id, e); 112 tx.rollback(); 113 errors += 1; 114 if (failOnError) { 115 manager.updateComplete(completed, errors); 116 manager.stop(); 117 service.cleanupSession(tx.getId()); 118 throw e; 119 } 120 } 121 service.cleanupSession(tx.getId()); 122 } 123 manager.updateComplete(completed, errors); 124 } 125 } 126 127 /** 128 * Stop this thread from running once it has completed its current batch. 129 */ 130 public void stopThread() { 131 this.running = false; 132 } 133 134}