State storage improvements
This commit is contained in:
parent
be12e5b985
commit
618d350791
@ -107,16 +107,20 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
|
|||||||
|
|
||||||
public void process(EntityInitCalculatedFieldMsg msg) throws CalculatedFieldException {
|
public void process(EntityInitCalculatedFieldMsg msg) throws CalculatedFieldException {
|
||||||
log.info("[{}] Processing entity init CF msg.", msg.getCtx().getCfId());
|
log.info("[{}] Processing entity init CF msg.", msg.getCtx().getCfId());
|
||||||
var cfCtx = msg.getCtx();
|
var ctx = msg.getCtx();
|
||||||
if (msg.isForceReinit()) {
|
if (msg.isForceReinit()) {
|
||||||
log.info("Force reinitialization of CF: [{}].", cfCtx.getCfId());
|
log.info("Force reinitialization of CF: [{}].", ctx.getCfId());
|
||||||
states.remove(cfCtx.getCfId());
|
states.remove(ctx.getCfId());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var cfState = getOrInitState(cfCtx);
|
var state = getOrInitState(ctx);
|
||||||
processStateIfReady(cfCtx, Collections.singletonList(cfCtx.getCfId()), cfState, null, null, msg.getCallback());
|
if (!state.isSizeExceedsLimit()) {
|
||||||
|
processStateIfReady(ctx, Collections.singletonList(ctx.getCfId()), state, null, null, msg.getCallback());
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(ctx.getSizeExceedsLimitMessage());
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(entityId).cause(e).build();
|
throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).cause(e).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +218,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
|
|||||||
state = getOrInitState(ctx);
|
state = getOrInitState(ctx);
|
||||||
justRestored = true;
|
justRestored = true;
|
||||||
}
|
}
|
||||||
|
if (state.isSizeOk()) {
|
||||||
if (state.updateState(newArgValues) || justRestored) {
|
if (state.updateState(newArgValues) || justRestored) {
|
||||||
cfIdList = new ArrayList<>(cfIdList);
|
cfIdList = new ArrayList<>(cfIdList);
|
||||||
cfIdList.add(ctx.getCfId());
|
cfIdList.add(ctx.getCfId());
|
||||||
@ -221,6 +226,9 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
|
|||||||
} else {
|
} else {
|
||||||
callback.onSuccess(CALLBACKS_PER_CF);
|
callback.onSuccess(CALLBACKS_PER_CF);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@ -235,7 +243,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
|
|||||||
// Alternatively, we can fetch the state outside the actor system and push separate command to create this actor,
|
// Alternatively, we can fetch the state outside the actor system and push separate command to create this actor,
|
||||||
// but this will significantly complicate the code.
|
// but this will significantly complicate the code.
|
||||||
state = stateFuture.get(1, TimeUnit.MINUTES);
|
state = stateFuture.get(1, TimeUnit.MINUTES);
|
||||||
state.checkStateSize(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId), ctx.getMaxStateSizeInKBytes());
|
state.checkStateSize(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId), ctx.getMaxStateSize());
|
||||||
states.put(ctx.getCfId(), state);
|
states.put(ctx.getCfId(), state);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
@ -244,18 +252,29 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private void processStateIfReady(CalculatedFieldCtx ctx, List<CalculatedFieldId> cfIdList, CalculatedFieldState state, UUID tbMsgId, TbMsgType tbMsgType, TbCallback callback) {
|
private void processStateIfReady(CalculatedFieldCtx ctx, List<CalculatedFieldId> cfIdList, CalculatedFieldState state, UUID tbMsgId, TbMsgType tbMsgType, TbCallback callback) {
|
||||||
CalculatedFieldEntityCtxId ctxId = new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId);
|
CalculatedFieldEntityCtxId ctxId = new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId);
|
||||||
if (state.isReady() && ctx.isInitialized()) {
|
boolean stateSizeOk;
|
||||||
|
if (ctx.isInitialized() && state.isReady()) {
|
||||||
CalculatedFieldResult calculationResult = state.performCalculation(ctx).get(5, TimeUnit.SECONDS);
|
CalculatedFieldResult calculationResult = state.performCalculation(ctx).get(5, TimeUnit.SECONDS);
|
||||||
state.checkStateSize(ctxId, ctx.getMaxStateSizeInKBytes());
|
state.checkStateSize(ctxId, ctx.getMaxStateSize());
|
||||||
|
stateSizeOk = state.isSizeOk();
|
||||||
|
if (stateSizeOk) {
|
||||||
cfService.pushMsgToRuleEngine(tenantId, entityId, calculationResult, cfIdList, callback);
|
cfService.pushMsgToRuleEngine(tenantId, entityId, calculationResult, cfIdList, callback);
|
||||||
if (DebugModeUtil.isDebugAllAvailable(ctx.getCalculatedField())) {
|
if (DebugModeUtil.isDebugAllAvailable(ctx.getCalculatedField())) {
|
||||||
systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArguments(), tbMsgId, tbMsgType, JacksonUtil.writeValueAsString(calculationResult.getResult()), null);
|
systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArguments(), tbMsgId, tbMsgType, JacksonUtil.writeValueAsString(calculationResult.getResult()), null);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
state.checkStateSize(ctxId, ctx.getMaxStateSizeInKBytes());
|
state.checkStateSize(ctxId, ctx.getMaxStateSize());
|
||||||
|
stateSizeOk = state.isSizeOk();
|
||||||
|
if (stateSizeOk) {
|
||||||
callback.onSuccess(); // State was updated but no calculation performed;
|
callback.onSuccess(); // State was updated but no calculation performed;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (stateSizeOk) {
|
||||||
cfStateService.persistState(ctxId, state, callback);
|
cfStateService.persistState(ctxId, state, callback);
|
||||||
|
} else {
|
||||||
|
throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, List<TsKvProto> data) {
|
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, List<TsKvProto> data) {
|
||||||
|
|||||||
@ -94,17 +94,14 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
|
|||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFieldInitMsg(CalculatedFieldInitMsg msg) {
|
public void onFieldInitMsg(CalculatedFieldInitMsg msg) throws CalculatedFieldException {
|
||||||
log.info("[{}] Processing CF init message.", msg.getCf().getId());
|
log.info("[{}] Processing CF init message.", msg.getCf().getId());
|
||||||
var cf = msg.getCf();
|
var cf = msg.getCf();
|
||||||
var cfCtx = new CalculatedFieldCtx(cf, systemContext.getTbelInvokeService(), systemContext.getApiLimitService());
|
var cfCtx = new CalculatedFieldCtx(cf, systemContext.getTbelInvokeService(), systemContext.getApiLimitService());
|
||||||
try {
|
try {
|
||||||
cfCtx.init();
|
cfCtx.init();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.debug("[{}] Failed to initialize CF context.", cf.getId(), e);
|
throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
|
||||||
if (DebugModeUtil.isDebugAllAvailable(cf)) {
|
|
||||||
systemContext.persistCalculatedFieldDebugEvent(cf.getTenantId(), cf.getId(), cf.getEntityId(), null, null, null, null, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
calculatedFields.put(cf.getId(), cfCtx);
|
calculatedFields.put(cf.getId(), cfCtx);
|
||||||
// We use copy on write lists to safely pass the reference to another actor for the iteration.
|
// We use copy on write lists to safely pass the reference to another actor for the iteration.
|
||||||
@ -135,7 +132,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEntityLifecycleMsg(CalculatedFieldEntityLifecycleMsg msg) {
|
public void onEntityLifecycleMsg(CalculatedFieldEntityLifecycleMsg msg) throws CalculatedFieldException {
|
||||||
log.info("Processing entity lifecycle event: [{}] for entity: [{}]", msg.getData().getEvent(), msg.getData().getEntityId());
|
log.info("Processing entity lifecycle event: [{}] for entity: [{}]", msg.getData().getEvent(), msg.getData().getEntityId());
|
||||||
var entityType = msg.getData().getEntityId().getEntityType();
|
var entityType = msg.getData().getEntityId().getEntityType();
|
||||||
var event = msg.getData().getEvent();
|
var event = msg.getData().getEvent();
|
||||||
@ -220,7 +217,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
|
|||||||
getOrCreateActor(msg.getEntityId()).tell(new CalculatedFieldEntityDeleteMsg(tenantId, msg.getEntityId(), callback));
|
getOrCreateActor(msg.getEntityId()).tell(new CalculatedFieldEntityDeleteMsg(tenantId, msg.getEntityId(), callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCfCreated(ComponentLifecycleMsg msg, TbCallback callback) {
|
private void onCfCreated(ComponentLifecycleMsg msg, TbCallback callback) throws CalculatedFieldException {
|
||||||
var cfId = new CalculatedFieldId(msg.getEntityId().getId());
|
var cfId = new CalculatedFieldId(msg.getEntityId().getId());
|
||||||
if (calculatedFields.containsKey(cfId)) {
|
if (calculatedFields.containsKey(cfId)) {
|
||||||
log.warn("[{}] CF was already initialized [{}]", tenantId, cfId);
|
log.warn("[{}] CF was already initialized [{}]", tenantId, cfId);
|
||||||
@ -235,10 +232,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
|
|||||||
try {
|
try {
|
||||||
cfCtx.init();
|
cfCtx.init();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.debug("[{}] Failed to initialize CF context.", cf.getId(), e);
|
throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
|
||||||
if (DebugModeUtil.isDebugAllAvailable(cf)) {
|
|
||||||
systemContext.persistCalculatedFieldDebugEvent(cf.getTenantId(), cf.getId(), cf.getEntityId(), null, null, null, null, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
calculatedFields.put(cf.getId(), cfCtx);
|
calculatedFields.put(cf.getId(), cfCtx);
|
||||||
// We use copy on write lists to safely pass the reference to another actor for the iteration.
|
// We use copy on write lists to safely pass the reference to another actor for the iteration.
|
||||||
@ -250,7 +244,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCfUpdated(ComponentLifecycleMsg msg, TbCallback callback) {
|
private void onCfUpdated(ComponentLifecycleMsg msg, TbCallback callback) throws CalculatedFieldException {
|
||||||
var cfId = new CalculatedFieldId(msg.getEntityId().getId());
|
var cfId = new CalculatedFieldId(msg.getEntityId().getId());
|
||||||
var oldCfCtx = calculatedFields.get(cfId);
|
var oldCfCtx = calculatedFields.get(cfId);
|
||||||
if (oldCfCtx == null) {
|
if (oldCfCtx == null) {
|
||||||
@ -265,9 +259,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
|
|||||||
try {
|
try {
|
||||||
newCfCtx.init();
|
newCfCtx.init();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (DebugModeUtil.isDebugAllAvailable(newCf)) {
|
throw CalculatedFieldException.builder().ctx(newCfCtx).eventEntity(newCfCtx.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
|
||||||
systemContext.persistCalculatedFieldDebugEvent(newCf.getTenantId(), newCf.getId(), newCf.getEntityId(), null, null, null, null, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
calculatedFields.put(newCf.getId(), newCfCtx);
|
calculatedFields.put(newCf.getId(), newCfCtx);
|
||||||
List<CalculatedFieldCtx> oldCfList = entityIdCalculatedFields.get(newCf.getEntityId());
|
List<CalculatedFieldCtx> oldCfList = entityIdCalculatedFields.get(newCf.getEntityId());
|
||||||
|
|||||||
@ -34,7 +34,7 @@ public abstract class AbstractCalculatedFieldStateService implements CalculatedF
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void persistState(CalculatedFieldEntityCtxId stateId, CalculatedFieldState state, TbCallback callback) {
|
public final void persistState(CalculatedFieldEntityCtxId stateId, CalculatedFieldState state, TbCallback callback) {
|
||||||
if (state.isStateTooLarge()) {
|
if (state.isSizeExceedsLimit()) {
|
||||||
throw new CalculatedFieldStateException("State size exceeds the maximum allowed limit. The state will not be persisted to RocksDB.");
|
throw new CalculatedFieldStateException("State size exceeds the maximum allowed limit. The state will not be persisted to RocksDB.");
|
||||||
}
|
}
|
||||||
doPersist(stateId, toProto(stateId, state), callback);
|
doPersist(stateId, toProto(stateId, state), callback);
|
||||||
|
|||||||
@ -31,7 +31,7 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState {
|
|||||||
|
|
||||||
protected List<String> requiredArguments;
|
protected List<String> requiredArguments;
|
||||||
protected Map<String, ArgumentEntry> arguments;
|
protected Map<String, ArgumentEntry> arguments;
|
||||||
protected boolean stateTooLarge;
|
protected boolean sizeExceedsLimit;
|
||||||
|
|
||||||
public BaseCalculatedFieldState(List<String> requiredArguments) {
|
public BaseCalculatedFieldState(List<String> requiredArguments) {
|
||||||
this.requiredArguments = requiredArguments;
|
this.requiredArguments = requiredArguments;
|
||||||
@ -75,9 +75,9 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void checkStateSize(CalculatedFieldEntityCtxId ctxId, long maxStateSize) {
|
public void checkStateSize(CalculatedFieldEntityCtxId ctxId, long maxStateSize) {
|
||||||
if (!stateTooLarge && maxStateSize > 0 && CalculatedFieldUtils.toProto(ctxId, this).getSerializedSize() > maxStateSize) {
|
if (!sizeExceedsLimit && maxStateSize > 0 && CalculatedFieldUtils.toProto(ctxId, this).getSerializedSize() > maxStateSize) {
|
||||||
arguments.clear();
|
arguments.clear();
|
||||||
setStateTooLarge(true);
|
sizeExceedsLimit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,7 +70,7 @@ public class CalculatedFieldCtx {
|
|||||||
private boolean initialized;
|
private boolean initialized;
|
||||||
|
|
||||||
private long maxDataPointsPerRollingArg;
|
private long maxDataPointsPerRollingArg;
|
||||||
private long maxStateSizeInKBytes;
|
private long maxStateSize;
|
||||||
|
|
||||||
public CalculatedFieldCtx(CalculatedField calculatedField, TbelInvokeService tbelInvokeService, ApiLimitService apiLimitService) {
|
public CalculatedFieldCtx(CalculatedField calculatedField, TbelInvokeService tbelInvokeService, ApiLimitService apiLimitService) {
|
||||||
this.calculatedField = calculatedField;
|
this.calculatedField = calculatedField;
|
||||||
@ -103,7 +103,7 @@ public class CalculatedFieldCtx {
|
|||||||
this.tbelInvokeService = tbelInvokeService;
|
this.tbelInvokeService = tbelInvokeService;
|
||||||
|
|
||||||
this.maxDataPointsPerRollingArg = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg);
|
this.maxDataPointsPerRollingArg = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg);
|
||||||
this.maxStateSizeInKBytes = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxStateSizeInKBytes);
|
this.maxStateSize = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxStateSizeInKBytes) * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
@ -224,4 +224,8 @@ public class CalculatedFieldCtx {
|
|||||||
return typeChanged || argumentsChanged;
|
return typeChanged || argumentsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSizeExceedsLimitMessage() {
|
||||||
|
return "Failed to init CF state. State size exceeds limit of " + (maxStateSize / 1024) + "Kb!";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,12 @@ public interface CalculatedFieldState {
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
boolean isReady();
|
boolean isReady();
|
||||||
|
|
||||||
boolean isStateTooLarge();
|
boolean isSizeExceedsLimit();
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
default boolean isSizeOk() {
|
||||||
|
return !isSizeExceedsLimit();
|
||||||
|
}
|
||||||
|
|
||||||
void checkStateSize(CalculatedFieldEntityCtxId ctxId, long maxStateSize);
|
void checkStateSize(CalculatedFieldEntityCtxId ctxId, long maxStateSize);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user