package com.constellio.data.dao.services.transactionLog.replay; import java.io.IOException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; import org.apache.solr.client.solrj.SolrServerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.constellio.data.dao.dto.records.RecordsFlushing; import com.constellio.data.dao.services.DataLayerLogger; import com.constellio.data.dao.services.bigVault.solr.BigVaultException; import com.constellio.data.dao.services.bigVault.solr.BigVaultServer; import com.constellio.data.dao.services.bigVault.solr.BigVaultServerTransaction; import com.constellio.data.utils.ThreadList; public class TransactionsLogImportHandler { private static final Logger LOGGER = LoggerFactory.getLogger(TransactionsLogImportHandler.class); private DataLayerLogger dataLayerLogger; private LinkedBlockingQueue<ReplayedTransactionQueueItem> transactions; private AtomicInteger executedTransactions = new AtomicInteger(); private ThreadList<TransactionsLogImportHandlerThread> threads = new ThreadList<>(); private int parallelism; private Semaphore availableThreads; private BigVaultServer bigVaultServer; public TransactionsLogImportHandler(BigVaultServer bigVaultServer, DataLayerLogger dataLayerLogger, int parallelism) { this.dataLayerLogger = dataLayerLogger; this.bigVaultServer = bigVaultServer; this.parallelism = parallelism; } public void start() { transactions = new LinkedBlockingQueue<>(parallelism); for (int i = 0; i < parallelism; i++) { threads.addAndStart(new TransactionsLogImportHandlerThread()); } availableThreads = new Semaphore(parallelism); } public void join() { for (int i = 0; i < parallelism; i++) { try { transactions.put(new ReplayedTransactionQueueItem(null)); } catch (InterruptedException e) { throw new RuntimeException(e); } } try { threads.joinAll(); } catch (InterruptedException e) { throw new RuntimeException(e); } try { bigVaultServer.softCommit(); } catch (IOException e) { throw new RuntimeException(e); } catch (SolrServerException e) { throw new RuntimeException(e); } } public void pushTransaction(BigVaultServerTransaction transaction) { try { transactions.put(new ReplayedTransactionQueueItem(transaction)); } catch (InterruptedException e) { throw new RuntimeException(e); } } private class TransactionsLogImportHandlerThread extends Thread { @Override public void run() { while (true) { BigVaultServerTransaction transaction; try { synchronized (TransactionsLogImportHandler.this) { try { transaction = transactions.take().transaction; if (transaction != null) { transaction.setRecordsFlushing(RecordsFlushing.LATER()); } } catch (InterruptedException e) { throw new RuntimeException(e); } if (transaction != null) { transaction.setRecordsFlushing(RecordsFlushing.LATER()); if (!transaction.isParallelisable()) { availableThreads.acquire(parallelism); availableThreads.release(parallelism); } availableThreads.acquire(); } } } catch (InterruptedException e) { throw new RuntimeException(e); } if (transaction == null) { break; } else { try { dataLayerLogger.logTransaction(transaction); int executedTransactionsCount = executedTransactions.incrementAndGet(); if (executedTransactionsCount > 9) { LOGGER.info("Replaying transactions - write #" + (++executedTransactionsCount)); } bigVaultServer.addAll(transaction); } catch (BigVaultException e) { throw new RuntimeException(e); } finally { availableThreads.release(); } } } } } private static class ReplayedTransactionQueueItem { BigVaultServerTransaction transaction; private ReplayedTransactionQueueItem(BigVaultServerTransaction transaction) { this.transaction = transaction; } } }