pipeline interrupt [2]
This commit is contained in:
parent
69746d2b5e
commit
9794cc6244
6 changed files with 112 additions and 28 deletions
|
|
@ -1,16 +1,52 @@
|
||||||
package ru.nbch.credit_tracker.service.task;
|
package ru.nbch.credit_tracker.service.task;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineTerminator;
|
import ru.nbch.credit_tracker.service.task.setup.IPipeLineTerminator;
|
||||||
|
import ru.nbch.credit_tracker.utils.LogUtil;
|
||||||
|
|
||||||
public class Pipeline {
|
public class Pipeline {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||||
private Runnable starter;
|
private Runnable starter;
|
||||||
private IPipeLineTerminator<?,?> terminator;
|
private IPipeLineTerminator<?,?> terminator;
|
||||||
private List<Stage<?, ?>> stages;
|
private List<Stage<?, ?>> stages;
|
||||||
|
private Semaphore semaphore;
|
||||||
|
private Future<?> starterFuture;
|
||||||
|
private Future<?> terminatorFuture;
|
||||||
|
private final AtomicBoolean closed = new AtomicBoolean(false);
|
||||||
|
|
||||||
public Pipeline() {
|
public Pipeline() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void start(ExecutorService executorService) {
|
||||||
|
this.starterFuture = executorService.submit(LogUtil.wrap(LogUtil.extractMdcMap(), starter));
|
||||||
|
this.terminatorFuture = executorService.submit(LogUtil.wrap(LogUtil.extractMdcMap(), terminator));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (closed.compareAndSet(false, true)) {
|
||||||
|
log.info("closing the pipeline...");
|
||||||
|
try {
|
||||||
|
stages.forEach(Stage::close);
|
||||||
|
this.starterFuture.cancel(true);
|
||||||
|
this.terminatorFuture.cancel(true);
|
||||||
|
} finally {
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("pipeline already closed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSemaphore(Semaphore semaphore) {
|
||||||
|
this.semaphore = semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
public Stage<?, ?> getLast() {
|
public Stage<?, ?> getLast() {
|
||||||
return stages.getLast();
|
return stages.getLast();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ import ru.nbch.credit_tracker.service.signal.SignalRestService;
|
||||||
import ru.nbch.credit_tracker.service.task.impl.FidIdEnricher;
|
import ru.nbch.credit_tracker.service.task.impl.FidIdEnricher;
|
||||||
import ru.nbch.credit_tracker.service.task.impl.FlagsEnricherV2;
|
import ru.nbch.credit_tracker.service.task.impl.FlagsEnricherV2;
|
||||||
import ru.nbch.credit_tracker.service.task.impl.SaveSubjectHandler;
|
import ru.nbch.credit_tracker.service.task.impl.SaveSubjectHandler;
|
||||||
|
import ru.nbch.credit_tracker.service.task.impl.XmlMergeCoordinator;
|
||||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter;
|
import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter;
|
||||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineTerminator;
|
import ru.nbch.credit_tracker.service.task.setup.IPipeLineTerminator;
|
||||||
import ru.nbch.credit_tracker.service.task.setup.PipeLineBuilderV2;
|
import ru.nbch.credit_tracker.service.task.setup.PipeLineBuilderV2;
|
||||||
import ru.nbch.credit_tracker.utils.LogUtil;
|
|
||||||
import ru.nbch.credit_tracker.utils.error.ExceptionUtils;
|
import ru.nbch.credit_tracker.utils.error.ExceptionUtils;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
|
@ -84,17 +84,19 @@ public class PipelinePackageManager {
|
||||||
.andThen()
|
.andThen()
|
||||||
.fillInitialQueue(pipelineStarterFactory.apply(packageId, pkgPath))
|
.fillInitialQueue(pipelineStarterFactory.apply(packageId, pkgPath))
|
||||||
.pollFinishingQueue(pipelineTerminatorFactory.get())
|
.pollFinishingQueue(pipelineTerminatorFactory.get())
|
||||||
|
.semaphore(semaphore)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
executorService.submit(LogUtil.wrap(LogUtil.extractMdcMap(), pipeline.getStarter()));
|
pipeline.start(executorService);
|
||||||
executorService.submit(LogUtil.wrap(LogUtil.extractMdcMap(), pipeline.getTerminator()));
|
|
||||||
|
|
||||||
pipeline.getTerminator()
|
pipeline.getTerminator()
|
||||||
.doneFuture()
|
.doneFuture()
|
||||||
.orTimeout(creditTrackerSettings.getTimeout(), TimeUnit.MINUTES)
|
.orTimeout(creditTrackerSettings.getTimeout(), TimeUnit.MINUTES)
|
||||||
.whenComplete((v, e) -> {
|
.whenComplete((v, e) -> {
|
||||||
try {
|
try {
|
||||||
if (e != null) {
|
if (e instanceof XmlMergeCoordinator.PipelineTerminatorException) {
|
||||||
|
log.info("when complete: terminator was interrupted.");
|
||||||
|
return;
|
||||||
|
} else if (e != null) {
|
||||||
log.error(ExceptionUtils.getStackTrace(e));
|
log.error(ExceptionUtils.getStackTrace(e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -105,8 +107,8 @@ public class PipelinePackageManager {
|
||||||
log.error("pipeline finished with error{}", ExceptionUtils.getStackTrace(ex));
|
log.error("pipeline finished with error{}", ExceptionUtils.getStackTrace(ex));
|
||||||
} finally {
|
} finally {
|
||||||
log.info("packageId={}: pipeline finished", packageId);
|
log.info("packageId={}: pipeline finished", packageId);
|
||||||
pipeline.getStages().forEach(Stage::close);
|
pipeline.close();
|
||||||
semaphore.release();
|
log.info("semaphore amount: {}", semaphore.availablePermits());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
import org.mybatis.spring.MyBatisSystemException;
|
||||||
import ru.nbch.credit_tracker.model.StagePayload;
|
import ru.nbch.credit_tracker.model.StagePayload;
|
||||||
import ru.nbch.credit_tracker.utils.LogUtil;
|
import ru.nbch.credit_tracker.utils.LogUtil;
|
||||||
|
|
||||||
|
|
@ -24,6 +26,7 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
|
||||||
private final ExecutorService ioPool;
|
private final ExecutorService ioPool;
|
||||||
private Consumer<Z> consumer = null;
|
private Consumer<Z> consumer = null;
|
||||||
private final String dispatcherName;
|
private final String dispatcherName;
|
||||||
|
private Pipeline pipeline;
|
||||||
|
|
||||||
private static final ThreadPoolExecutor.CallerRunsPolicy callerRunsPolicy
|
private static final ThreadPoolExecutor.CallerRunsPolicy callerRunsPolicy
|
||||||
= new ThreadPoolExecutor.CallerRunsPolicy();
|
= new ThreadPoolExecutor.CallerRunsPolicy();
|
||||||
|
|
@ -54,6 +57,10 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
|
||||||
this.POISON = POISON;
|
this.POISON = POISON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPipeline(Pipeline pipeline) {
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
public void startRunning() {
|
public void startRunning() {
|
||||||
ioPool.submit(this);
|
ioPool.submit(this);
|
||||||
}
|
}
|
||||||
|
|
@ -61,6 +68,7 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
List<Future<Z>> inflight = new ArrayList<>();
|
List<Future<Z>> inflight = new ArrayList<>();
|
||||||
|
log.info("this is main thread for {}", dispatcherName);
|
||||||
try {
|
try {
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
@ -74,34 +82,47 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
|
||||||
// дождаться всех in-flight задач
|
// дождаться всех in-flight задач
|
||||||
for (Future<Z> f : inflight) {
|
for (Future<Z> f : inflight) {
|
||||||
if (wasInterrupted()) {
|
if (wasInterrupted()) {
|
||||||
log.info("stage {} was interrupted while waiting for tasks to finish",
|
log.info("stage {} was interrupted while waiting for tasks to finish", dispatcherName);
|
||||||
dispatcher.getClass().getSimpleName());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
f.get();
|
f.get();
|
||||||
}
|
}
|
||||||
putQuiet(out, POISON);
|
putQuiet(out, POISON);
|
||||||
log.info("stage {} finished.", dispatcher.getClass().getSimpleName());
|
log.info("stage {} finished.", dispatcherName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
counter++;
|
counter++;
|
||||||
int finalCounter = counter;
|
int finalCounter = counter;
|
||||||
|
AtomicInteger retryForSingleTask = new AtomicInteger(0);
|
||||||
Future<Z> f = ioPool.submit(() -> {
|
Future<Z> f = ioPool.submit(() -> {
|
||||||
|
while (retryForSingleTask.incrementAndGet() < 3) {
|
||||||
try {
|
try {
|
||||||
if (wasInterrupted()) {
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
log.info("stage {} task {} interrupted...", dispatcher.getClass().getSimpleName(), finalCounter);
|
log.info("stage {} was interrupted. skipping task {}...", dispatcherName, finalCounter);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
log.info("stage {} task {} started.", dispatcher.getClass().getSimpleName(), finalCounter);
|
log.info("stage {} task {} started.", dispatcher.getClass().getSimpleName(), finalCounter);
|
||||||
Z p = dispatcher.handle(b);
|
Z p = dispatcher.handle(b);
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
|
log.info("after {} task {} finished, worker has been interrupted. not putting result in queue.",
|
||||||
|
dispatcherName, finalCounter);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
putQuiet(out, p);
|
putQuiet(out, p);
|
||||||
log.info("stage {} task {} ended.", dispatcher.getClass().getSimpleName(), finalCounter);
|
log.info("stage {} task {} ended.", dispatcher.getClass().getSimpleName(), finalCounter);
|
||||||
return p;
|
return p;
|
||||||
|
} catch (MyBatisSystemException e) {
|
||||||
|
log.warn("{} RETRY AFTER ERROR: {}", dispatcherName, ExceptionUtils.getStackTrace(e));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("{} ERROR: {}", dispatcherName, ExceptionUtils.getStackTrace(e));
|
log.error("{} ERROR: {}", dispatcherName, ExceptionUtils.getStackTrace(e));
|
||||||
|
pipeline.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.error("tried for {} times, failed batch...", retryForSingleTask.get());
|
||||||
|
pipeline.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
inflight.add(f);
|
inflight.add(f);
|
||||||
if ((counter % 20) == 0) {
|
if ((counter % 20) == 0) {
|
||||||
|
|
@ -124,9 +145,9 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean wasInterrupted() {
|
private boolean wasInterrupted() {
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
if (Thread.interrupted()) {
|
||||||
log.warn("pipeline was interrupted...");
|
log.warn("pipeline was interrupted...");
|
||||||
putQuiet(out, POISON);
|
//putQuiet(out, POISON);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -153,7 +174,7 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
|
||||||
try {
|
try {
|
||||||
return q.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
return q.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
log.error("Error for {}: {}; {}", dispatcherName, info(), ExceptionUtils.getStackTrace(e));
|
log.error("Main thread for {}: {}; {}", dispatcherName, info(), ExceptionUtils.getStackTrace(e));
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||||
|
import ru.nbch.credit_tracker.exceptions.SignalClientException;
|
||||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter;
|
import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter;
|
||||||
|
|
@ -35,6 +36,12 @@ public class KickPipeLineImpl implements IPipeLineStarter<SubjectPayload> {
|
||||||
this.BATCH_SIZE = creditTrackerSettings.getBatchSize();
|
this.BATCH_SIZE = creditTrackerSettings.getBatchSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class PipelineStarterException extends SignalClientException {
|
||||||
|
public PipelineStarterException() {
|
||||||
|
super(null, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startFillingQueue(BlockingQueue<SubjectPayload> firstQueue) {
|
public void startFillingQueue(BlockingQueue<SubjectPayload> firstQueue) {
|
||||||
log.info("Start registering subjects. PackageId: {}", packageId);
|
log.info("Start registering subjects. PackageId: {}", packageId);
|
||||||
|
|
@ -55,8 +62,8 @@ public class KickPipeLineImpl implements IPipeLineStarter<SubjectPayload> {
|
||||||
try {
|
try {
|
||||||
firstQueue.put(new SubjectPayload(packageId, subjectProfileBatch));
|
firstQueue.put(new SubjectPayload(packageId, subjectProfileBatch));
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
log.error("{}", ExceptionUtils.getStackTrace(e));
|
log.error("Pipeline starter was interrupted!");
|
||||||
throw new RuntimeException(e);
|
throw new PipelineStarterException();
|
||||||
}
|
}
|
||||||
request.getSubjects().getSubject().clear();
|
request.getSubjects().getSubject().clear();
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +73,8 @@ public class KickPipeLineImpl implements IPipeLineStarter<SubjectPayload> {
|
||||||
BATCH_SIZE, "/MonitoringRequest/Subjects/Subject");
|
BATCH_SIZE, "/MonitoringRequest/Subjects/Subject");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("{}", ExceptionUtils.getStackTrace(e));
|
log.error("{}", ExceptionUtils.getStackTrace(e));
|
||||||
|
} catch (PipelineStarterException e) {
|
||||||
|
log.info("Pipeline starter shitting down...");
|
||||||
} finally {
|
} finally {
|
||||||
log.debug("Send poison pill");
|
log.debug("Send poison pill");
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,16 @@ public final class XmlMergeCoordinator implements IPipeLineTerminator<PartFilePa
|
||||||
}
|
}
|
||||||
mergeBatch(Collections.singletonList(part));
|
mergeBatch(Collections.singletonList(part));
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
Thread.currentThread().interrupt();
|
log.info("pipeline terminator was interrupted!");
|
||||||
|
done.completeExceptionally(new PipelineTerminatorException());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error: {}", ExceptionUtils.getStackTrace(e));
|
log.error("Error: {}", ExceptionUtils.getStackTrace(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
done.completeExceptionally(new PipelineTerminatorException());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PipelineTerminatorException extends RuntimeException {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mergeBatch(List<PartFilePayload> inputs) throws Exception {
|
private void mergeBatch(List<PartFilePayload> inputs) throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
import ru.nbch.credit_tracker.model.StagePayload;
|
import ru.nbch.credit_tracker.model.StagePayload;
|
||||||
import ru.nbch.credit_tracker.service.task.Pipeline;
|
import ru.nbch.credit_tracker.service.task.Pipeline;
|
||||||
import ru.nbch.credit_tracker.service.task.Stage;
|
import ru.nbch.credit_tracker.service.task.Stage;
|
||||||
|
|
@ -15,6 +16,7 @@ public class PipeLineBuilderV2<FIRST extends StagePayload, LAST extends StagePay
|
||||||
private BlockingQueue<LAST> lastQueue;
|
private BlockingQueue<LAST> lastQueue;
|
||||||
private IPipeLineStarter<FIRST> pipeLineStarter;
|
private IPipeLineStarter<FIRST> pipeLineStarter;
|
||||||
private IPipeLineTerminator<LAST, ?> pipeLineTerminator;
|
private IPipeLineTerminator<LAST, ?> pipeLineTerminator;
|
||||||
|
private Semaphore semaphore;
|
||||||
|
|
||||||
private PipeLineBuilderV2() {
|
private PipeLineBuilderV2() {
|
||||||
this.stages = new ArrayList<>();
|
this.stages = new ArrayList<>();
|
||||||
|
|
@ -53,10 +55,19 @@ public class PipeLineBuilderV2<FIRST extends StagePayload, LAST extends StagePay
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PipeLineBuilderV2<FIRST, LAST> semaphore(Semaphore semaphore) {
|
||||||
|
this.semaphore = semaphore;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Pipeline build() {
|
public Pipeline build() {
|
||||||
Pipeline p = new Pipeline();
|
Pipeline p = new Pipeline();
|
||||||
|
p.setSemaphore(semaphore);
|
||||||
p.setStarter(() -> {
|
p.setStarter(() -> {
|
||||||
stages.forEach(Stage::startRunning);
|
stages.forEach(stage -> {
|
||||||
|
stage.setPipeline(p);
|
||||||
|
stage.startRunning();
|
||||||
|
});
|
||||||
pipeLineStarter.startFillingQueue(firstStage.getIn());
|
pipeLineStarter.startFillingQueue(firstStage.getIn());
|
||||||
});
|
});
|
||||||
pipeLineTerminator.setPollingQueue(lastQueue);
|
pipeLineTerminator.setPollingQueue(lastQueue);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue