pipeline context example

This commit is contained in:
ialbert 2025-12-03 17:12:25 +03:00
parent e331ff403f
commit 51e6bb05c9
11 changed files with 97 additions and 3 deletions

View file

@ -0,0 +1,21 @@
package ru.nbch.credit_tracker.model;
import java.util.EnumMap;
public class PipelineContext<T extends Enum<T>> {
private final EnumMap<T, Object> storage;
public PipelineContext(Class<T> keyClass) {
this.storage = new EnumMap<>(keyClass);
}
public void store(T key, Object value) {
storage.put(key, value);
}
@SuppressWarnings("unchecked")
public <S> S get(T key) {
Object o = storage.get(key);
return (S) o;
}
}

View file

@ -2,4 +2,6 @@ package ru.nbch.credit_tracker.model;
public interface StagePayload { public interface StagePayload {
boolean isPoison(); boolean isPoison();
default void setContext(PipelineContext<?> context) {}
default PipelineContext<?> getContext() {return null;}
} }

View file

@ -0,0 +1,5 @@
package ru.nbch.credit_tracker.model.impl;
public enum PackageVariable {
packageId,
}

View file

@ -2,6 +2,7 @@ package ru.nbch.credit_tracker.model.impl;
import java.util.List; import java.util.List;
import ru.nbch.credit_tracker.enums.PoisonType; import ru.nbch.credit_tracker.enums.PoisonType;
import ru.nbch.credit_tracker.model.PipelineContext;
import ru.nbch.credit_tracker.model.StagePayload; import ru.nbch.credit_tracker.model.StagePayload;
import ru.nbch.credit_tracker.model.SubjectProfile; import ru.nbch.credit_tracker.model.SubjectProfile;
@ -11,6 +12,7 @@ public class SubjectPayload implements StagePayload {
private final String memberCode; private final String memberCode;
private final String packageName; private final String packageName;
private final List<SubjectProfile> subjects; private final List<SubjectProfile> subjects;
private PipelineContext<PackageVariable> ctx;
public static final SubjectPayload POISON = new SubjectPayload(null, PoisonType.NORMALLY); public static final SubjectPayload POISON = new SubjectPayload(null, PoisonType.NORMALLY);
private final PoisonType poison; private final PoisonType poison;
@ -30,6 +32,16 @@ public class SubjectPayload implements StagePayload {
this.poison = PoisonType.NONE; this.poison = PoisonType.NONE;
} }
@Override
public void setContext(PipelineContext<?> context) {
this.ctx = (PipelineContext<PackageVariable>) context;
}
@Override
public PipelineContext<?> getContext() {
return this.ctx;
}
public Long getPackageId() { public Long getPackageId() {
return packageId; return packageId;
} }

View file

@ -1,7 +1,17 @@
package ru.nbch.credit_tracker.service.task; package ru.nbch.credit_tracker.service.task;
public interface Handler<X, Z> { import ru.nbch.credit_tracker.model.PipelineContext;
import ru.nbch.credit_tracker.model.StagePayload;
public interface Handler<X extends StagePayload, Z extends StagePayload> {
Z handle(X taskPayload); Z handle(X taskPayload);
@SuppressWarnings("unchecked")
default <T extends Enum<T>> PipelineContext<T> obtainContext(Class<T> keyClass, X input) {
PipelineContext<?> ctx = input.getContext();
return (PipelineContext<T>) ctx;
}
default void handleOnPoison(X poison){ default void handleOnPoison(X poison){
//do nothing //do nothing
} }

View file

@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.config.task.StageQueueFactory; import ru.nbch.credit_tracker.config.task.StageQueueFactory;
import ru.nbch.credit_tracker.model.impl.PackageVariable;
import ru.nbch.credit_tracker.model.impl.PartFilePayload; import ru.nbch.credit_tracker.model.impl.PartFilePayload;
import ru.nbch.credit_tracker.model.impl.SignalXmlFilePayload; import ru.nbch.credit_tracker.model.impl.SignalXmlFilePayload;
import ru.nbch.credit_tracker.model.impl.SubjectPayload; import ru.nbch.credit_tracker.model.impl.SubjectPayload;
@ -64,6 +65,7 @@ public class PipelinePackageFabric implements InitializingBean {
.fillInitialQueue(pipelineStarterFactory.apply(setupParam)) .fillInitialQueue(pipelineStarterFactory.apply(setupParam))
.pollFinishingQueue(pipelineTerminatorFactory.get()) .pollFinishingQueue(pipelineTerminatorFactory.get())
.backoff(pipelineBackoff) .backoff(pipelineBackoff)
.context(PackageVariable.class)
.build(); .build();
} }

View file

@ -12,6 +12,7 @@ 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 ru.nbch.credit_tracker.model.PipelineContext;
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;
import ru.nbch.credit_tracker.utils.TaskTimer; import ru.nbch.credit_tracker.utils.TaskTimer;
@ -30,6 +31,7 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
private static final ThreadPoolExecutor.CallerRunsPolicy callerRunsPolicy private static final ThreadPoolExecutor.CallerRunsPolicy callerRunsPolicy
= new ThreadPoolExecutor.CallerRunsPolicy(); = new ThreadPoolExecutor.CallerRunsPolicy();
private PipelineContext<?> context;
public Stage(Handler<X, Z> dispatcher, public Stage(Handler<X, Z> dispatcher,
BlockingQueue<X> in, BlockingQueue<X> in,
@ -106,7 +108,11 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
log.info("stage {} task {} started.", dispatcher.getClass().getSimpleName(), finalCounter); log.info("stage {} task {} started.", dispatcher.getClass().getSimpleName(), finalCounter);
Z p; Z p;
try (var m = stopWatch.start(dispatcherName + "-handler")) { try (var m = stopWatch.start(dispatcherName + "-handler")) {
if (b.getContext() == null) {
b.setContext(context);
}
p = dispatcher.handle(b); p = dispatcher.handle(b);
p.setContext(b.getContext()); // нужно т.к. в конце придет в терминатор
} }
if (Thread.currentThread().isInterrupted()) { if (Thread.currentThread().isInterrupted()) {
log.info("after {} task {} finished, worker has been interrupted. not putting result in queue.", log.info("after {} task {} finished, worker has been interrupted. not putting result in queue.",
@ -210,4 +216,12 @@ public class Stage<X extends StagePayload, Z extends StagePayload> implements Ru
public void setBackoff(PipelineBackoff pipelineBackoff) { public void setBackoff(PipelineBackoff pipelineBackoff) {
this.pipelineBackoff = pipelineBackoff; this.pipelineBackoff = pipelineBackoff;
} }
public void setContext(PipelineContext<?> context) {
this.context = context;
}
public PipelineContext<?> getContext() {
return context;
}
} }

View file

@ -10,6 +10,7 @@ import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
import ru.nbch.credit_tracker.entities.indic.Name; import ru.nbch.credit_tracker.entities.indic.Name;
import ru.nbch.credit_tracker.model.NameData; import ru.nbch.credit_tracker.model.NameData;
import ru.nbch.credit_tracker.model.SubjectProfile; import ru.nbch.credit_tracker.model.SubjectProfile;
import ru.nbch.credit_tracker.model.impl.PackageVariable;
import ru.nbch.credit_tracker.model.impl.SubjectPayload; import ru.nbch.credit_tracker.model.impl.SubjectPayload;
import ru.nbch.credit_tracker.service.indic.PhoneService2; import ru.nbch.credit_tracker.service.indic.PhoneService2;
import ru.nbch.credit_tracker.service.task.Handler; import ru.nbch.credit_tracker.service.task.Handler;
@ -35,7 +36,9 @@ public class FidNameEnricher implements Handler<SubjectPayload, SubjectPayload>
@Override @Override
public SubjectPayload handle(SubjectPayload taskPayload) { public SubjectPayload handle(SubjectPayload taskPayload) {
List<SubjectProfile> subjects = taskPayload.getSubjects(); List<SubjectProfile> subjects = taskPayload.getSubjects();
log.debug("NameEnricherStage handle by packageId: {}, size of bucket for save: {}", taskPayload.getPackageId(), subjects.size()); var ctx = obtainContext(PackageVariable.class, taskPayload);
Long packageId = ctx.get(PackageVariable.packageId);
log.debug("NameEnricherStage handle by packageId: {}, size of bucket for save: {}", packageId, subjects.size());
if (subjects.getFirst().getFid() == null) { if (subjects.getFirst().getFid() == null) {
processByNumber(subjects); processByNumber(subjects);

View file

@ -12,7 +12,9 @@ import ru.nbch.credit_tracker.enums.PoisonType;
import ru.nbch.credit_tracker.exceptions.SignalClientException; import ru.nbch.credit_tracker.exceptions.SignalClientException;
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper; import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
import ru.nbch.credit_tracker.mapper.signal.UserPackageMapper; import ru.nbch.credit_tracker.mapper.signal.UserPackageMapper;
import ru.nbch.credit_tracker.model.PipelineContext;
import ru.nbch.credit_tracker.model.SubjectProfile; import ru.nbch.credit_tracker.model.SubjectProfile;
import ru.nbch.credit_tracker.model.impl.PackageVariable;
import ru.nbch.credit_tracker.model.impl.SubjectPayload; import ru.nbch.credit_tracker.model.impl.SubjectPayload;
import ru.nbch.credit_tracker.service.task.SetupParam; import ru.nbch.credit_tracker.service.task.SetupParam;
import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter; import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter;
@ -30,6 +32,7 @@ public abstract class KickPipeLine implements IPipeLineStarter<SubjectPayload> {
private final int BATCH_SIZE; private final int BATCH_SIZE;
private boolean isInterrupted = false; private boolean isInterrupted = false;
protected int offset = 0; protected int offset = 0;
private PipelineContext<PackageVariable> context;
public KickPipeLine(StaxXmlProcessor xmlProcessor, public KickPipeLine(StaxXmlProcessor xmlProcessor,
@ -52,10 +55,17 @@ public abstract class KickPipeLine implements IPipeLineStarter<SubjectPayload> {
} }
} }
@SuppressWarnings("unchecked")
@Override
public void setContext(PipelineContext<?> context) {
this.context = (PipelineContext<PackageVariable>) context;
}
@Override @Override
public void startFillingQueue(BlockingQueue<SubjectPayload> firstQueue) { public void startFillingQueue(BlockingQueue<SubjectPayload> firstQueue) {
try { try {
log.info("Start kicker: mode:{}, packageId: {};", mode(), packageId); log.info("Start kicker: mode:{}, packageId: {};", mode(), packageId);
context.store(PackageVariable.packageId, packageId);
int limit = BATCH_SIZE; int limit = BATCH_SIZE;
UserPackage userPackage = userPackageMapper.findPackage(packageId); UserPackage userPackage = userPackageMapper.findPackage(packageId);
List<PhoneFidMap> page = nextPage(limit); List<PhoneFidMap> page = nextPage(limit);
@ -69,7 +79,9 @@ public abstract class KickPipeLine implements IPipeLineStarter<SubjectPayload> {
.phone(phoneFidMap.getPhone()) .phone(phoneFidMap.getPhone())
.build()).toList(); .build()).toList();
try { try {
firstQueue.put(new SubjectPayload(packageId, userPackage.getUserId(), userPackage.getPackageName(), subjectProfiles)); SubjectPayload payload = new SubjectPayload(packageId, userPackage.getUserId(), userPackage.getPackageName(), subjectProfiles);
payload.setContext(context);
firstQueue.put(payload);
} catch (InterruptedException e) { } catch (InterruptedException e) {
isInterrupted = true; isInterrupted = true;
log.error("Pipeline starter was interrupted!"); log.error("Pipeline starter was interrupted!");

View file

@ -1,8 +1,11 @@
package ru.nbch.credit_tracker.service.task.setup; package ru.nbch.credit_tracker.service.task.setup;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import ru.nbch.credit_tracker.model.PipelineContext;
import ru.nbch.credit_tracker.model.StagePayload; import ru.nbch.credit_tracker.model.StagePayload;
public interface IPipeLineStarter<Q extends StagePayload> { public interface IPipeLineStarter<Q extends StagePayload> {
void startFillingQueue(BlockingQueue<Q> queue); void startFillingQueue(BlockingQueue<Q> queue);
default void setContext(PipelineContext<?> context) {}
} }

View file

@ -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 ru.nbch.credit_tracker.model.PipelineContext;
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.PipelineBackoff; import ru.nbch.credit_tracker.service.task.PipelineBackoff;
@ -17,6 +18,7 @@ public class PipeLineBuilderV2<FIRST extends StagePayload, LAST extends StagePay
private IPipeLineStarter<FIRST> pipeLineStarter; private IPipeLineStarter<FIRST> pipeLineStarter;
private IPipeLineTerminator<LAST, ?> pipeLineTerminator; private IPipeLineTerminator<LAST, ?> pipeLineTerminator;
private PipelineBackoff backoff; private PipelineBackoff backoff;
private PipelineContext<?> context;
private PipeLineBuilderV2() { private PipeLineBuilderV2() {
this.stages = new ArrayList<>(); this.stages = new ArrayList<>();
@ -60,14 +62,22 @@ public class PipeLineBuilderV2<FIRST extends StagePayload, LAST extends StagePay
return this; return this;
} }
public <T extends Enum<T>>
PipeLineBuilderV2<FIRST, LAST> context(Class<T> contextKeyClass) {
this.context = new PipelineContext<>(contextKeyClass);
return this;
}
public Pipeline build() { public Pipeline build() {
Pipeline p = new Pipeline(); Pipeline p = new Pipeline();
p.setStarter(() -> { p.setStarter(() -> {
stages.forEach(stage -> { stages.forEach(stage -> {
stage.setPipeline(p); stage.setPipeline(p);
stage.setBackoff(backoff); stage.setBackoff(backoff);
stage.setContext(context);
stage.startRunning(); stage.startRunning();
}); });
pipeLineStarter.setContext(context);
pipeLineStarter.startFillingQueue(firstStage.getIn()); pipeLineStarter.startFillingQueue(firstStage.getIn());
}); });
pipeLineTerminator.setPollingQueue(lastQueue); pipeLineTerminator.setPollingQueue(lastQueue);