package com.constellio.model.services.batch.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.data.dao.services.bigVault.solr.SolrUtils;
import com.constellio.data.threads.ConstellioThread;
import com.constellio.data.utils.BatchBuilderIterator;
import com.constellio.model.entities.batchprocess.BatchProcess;
import com.constellio.model.entities.records.Record;
import com.constellio.model.services.batch.manager.BatchProcessesManager;
import com.constellio.model.services.batch.state.BatchProcessProgressionServices;
import com.constellio.model.services.batch.state.InMemoryBatchProcessProgressionServices;
import com.constellio.model.services.batch.state.StoredBatchProcessPart;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.search.iterators.RecordSearchResponseIterator;
public class BatchProcessControllerThread extends ConstellioThread {
private static final String RESOURCE_NAME = "BatchProcessControllerThread";
private static final Logger LOGGER = LoggerFactory.getLogger(BatchProcessControllerThread.class);
private final int numberOfRecordsPerTask;
private final BatchProcessesManager batchProcessesManager;
private final RecordServices recordServices;
private final MetadataSchemasManager schemasManager;
private final SearchServices searchServices;
private final ModelLayerFactory modelLayerFactory;
private boolean stopRequested;
private Semaphore newEventSemaphore;
private AtomicLong completed = new AtomicLong();
public BatchProcessControllerThread(ModelLayerFactory modelLayerFactory, int numberOfRecordsPerTask) {
super(modelLayerFactory.toResourceName(RESOURCE_NAME));
this.modelLayerFactory = modelLayerFactory;
this.batchProcessesManager = modelLayerFactory.getBatchProcessesManager();
this.recordServices = modelLayerFactory.newRecordServices();
this.numberOfRecordsPerTask = numberOfRecordsPerTask;
this.schemasManager = modelLayerFactory.getMetadataSchemasManager();
this.searchServices = modelLayerFactory.newSearchServices();
this.newEventSemaphore = new Semaphore(1);
}
@Override
public void execute() {
while (!isStopRequested()) {
try {
process();
} catch (Throwable t) {
LOGGER.error("Error while batch processing", t);
}
}
}
void process()
throws Exception {
BatchProcess batchProcess = batchProcessesManager.getCurrentBatchProcess();
if (batchProcess != null) {
try {
if (batchProcess.getRecords() != null) {
processFromIds(batchProcess);
} else {
processFromQuery(batchProcess);
}
} catch (Exception e) {
batchProcessesManager.markAsFinished(batchProcess, 1);
throw e;
}
}
//newEventSemaphore.release();
waitUntilNotified();
}
private void processFromIds(BatchProcess batchProcess) throws Exception{
BatchProcessProgressionServices batchProcessProgressionServices = new InMemoryBatchProcessProgressionServices();
RecordFromIdListIterator iterator = new RecordFromIdListIterator(batchProcess.getRecords(), modelLayerFactory);
BatchBuilderIterator<Record> batchIterator = new BatchBuilderIterator<>(iterator, 100);
StoredBatchProcessPart previousPart = batchProcessProgressionServices.getLastBatchProcessPart(batchProcess);
if (previousPart != null) {
iterator.beginAfterId(previousPart.getLastId());
}
ForkJoinPool pool = newForkJoinPool();
TaskList taskList = new TaskList(pool);
List<String> recordsWithErrors = new ArrayList<>();
while (batchIterator.hasNext()) {
List<Record> records = batchIterator.next();
int index = previousPart == null ? 0 : previousPart.getIndex() + 1;
String firstId = records.get(0).getId();
String lastId = records.get(records.size() - 1).getId();
StoredBatchProcessPart storedBatchProcessPart = new StoredBatchProcessPart(batchProcess.getId(), index, firstId,
lastId, false, false);
//System.out.println("processing batch #" + index + " [" + firstId + "-" + lastId + "]");
batchProcessProgressionServices.markNewPartAsStarted(storedBatchProcessPart);
List<BatchProcessTask> tasks = newBatchProcessTasksFactory(taskList).createBatchProcessTasks(batchProcess,
records, recordsWithErrors, numberOfRecordsPerTask, schemasManager);
for (BatchProcessTask task : tasks) {
List<String> errors = pool.invoke(task);
recordsWithErrors.addAll(errors);
}
batchProcessProgressionServices.markPartAsFinished(storedBatchProcessPart);
previousPart = storedBatchProcessPart;
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.DAYS);
batchProcessesManager.markAsFinished(batchProcess, recordsWithErrors.size());
}
private void processFromQuery(BatchProcess batchProcess)
throws Exception {
BatchProcessProgressionServices batchProcessProgressionServices = new InMemoryBatchProcessProgressionServices();
ModifiableSolrParams params = SolrUtils.parseQueryString(batchProcess.getQuery());
params.set("sort", "principalPath_s asc, id asc");
RecordSearchResponseIterator iterator = new RecordSearchResponseIterator(modelLayerFactory, params, 100, true);
BatchBuilderIterator<Record> batchIterator = new BatchBuilderIterator<>(iterator, 100);
StoredBatchProcessPart previousPart = batchProcessProgressionServices.getLastBatchProcessPart(batchProcess);
if (previousPart != null) {
iterator.beginAfterId(previousPart.getLastId());
}
ForkJoinPool pool = newForkJoinPool();
TaskList taskList = new TaskList(pool);
List<String> recordsWithErrors = new ArrayList<>();
while (batchIterator.hasNext()) {
List<Record> records = batchIterator.next();
int index = previousPart == null ? 0 : previousPart.getIndex() + 1;
String firstId = records.get(0).getId();
String lastId = records.get(records.size() - 1).getId();
StoredBatchProcessPart storedBatchProcessPart = new StoredBatchProcessPart(batchProcess.getId(), index, firstId,
lastId, false, false);
//System.out.println("processing batch #" + index + " [" + firstId + "-" + lastId + "]");
batchProcessProgressionServices.markNewPartAsStarted(storedBatchProcessPart);
List<BatchProcessTask> tasks = newBatchProcessTasksFactory(taskList).createBatchProcessTasks(batchProcess,
records, recordsWithErrors, numberOfRecordsPerTask, schemasManager);
for (BatchProcessTask task : tasks) {
List<String> errors = pool.invoke(task);
recordsWithErrors.addAll(errors);
}
batchProcessProgressionServices.markPartAsFinished(storedBatchProcessPart);
previousPart = storedBatchProcessPart;
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.DAYS);
batchProcessesManager.markAsFinished(batchProcess, recordsWithErrors.size());
}
void waitUntilNotified()
throws InterruptedException {
newEventSemaphore.tryAcquire(500, TimeUnit.MILLISECONDS);
}
public boolean isStopRequested() {
return stopRequested;
}
public void stopRequested() {
this.stopRequested = true;
if (newEventSemaphore.availablePermits() == 0) {
newEventSemaphore.release();
}
}
public void notifyBatchProcessesListConfigUpdated() {
newEventSemaphore.release();
}
BatchProcessTasksFactory newBatchProcessTasksFactory(TaskList taskList) {
return new BatchProcessTasksFactory(recordServices, searchServices, taskList);
}
ForkJoinPool newForkJoinPool() {
return new ForkJoinPool(1);
}
}