Browse Source

fix: resolve REINIT crash when switching alarm duration from static to dynamic

Move fetchArguments() and state.update() before state.init() in initState() so that arguments are available when init() triggers reeval for DURATION conditions with active tracking (firstEventTs > 0).
pull/15439/head
Oleksandra Matviienko 6 months ago
parent
commit
f2e1a74cbc
  1. 2
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java
  2. 48
      application/src/test/java/org/thingsboard/server/cf/AlarmRulesTest.java

2
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java

@ -485,7 +485,6 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
private void initState(CalculatedFieldState state, CalculatedFieldCtx ctx) {
state.setCtx(ctx, actorCtx);
state.init(false);
if (ctx.getCfType() == CalculatedFieldType.GEOFENCING && ctx.isCfHasRelationPathQuerySource()) {
GeofencingCalculatedFieldState geofencingState = (GeofencingCalculatedFieldState) state;
@ -494,6 +493,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
Map<String, ArgumentEntry> arguments = fetchArguments(ctx);
state.update(arguments, ctx);
state.init(false);
state.checkStateSize(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId), ctx.getMaxStateSize());
states.put(ctx.getCfId(), state);

48
application/src/test/java/org/thingsboard/server/cf/AlarmRulesTest.java

@ -402,6 +402,54 @@ public class AlarmRulesTest extends AbstractControllerTest {
});
}
@Test
public void testChangeDurationConditionFromStaticToDynamic() throws Exception {
Argument temperatureArgument = new Argument();
temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null));
temperatureArgument.setDefaultValue("0");
Map<String, Argument> arguments = new HashMap<>(Map.of(
"temperature", temperatureArgument
));
long staticDurationMs = 5000L;
Map<AlarmSeverity, Condition> createRules = Map.of(
AlarmSeverity.CRITICAL, new Condition("return temperature >= 50;", null, staticDurationMs)
);
CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm",
arguments, createRules, null);
// post telemetry to trigger condition, so that firstEventTs > 0 in AlarmRuleState
postTelemetry(deviceId, "{\"temperature\":50}");
Thread.sleep(1000);
// update CF: add attribute argument and switch duration from static to dynamic
AlarmCalculatedFieldConfiguration configuration =
(AlarmCalculatedFieldConfiguration) calculatedField.getConfiguration();
Argument durationArgument = new Argument();
durationArgument.setRefEntityKey(new ReferencedEntityKey("durationThreshold",
ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE));
durationArgument.setDefaultValue("-1");
configuration.getArguments().put("durationThreshold", durationArgument);
DurationAlarmCondition durationCondition = (DurationAlarmCondition)
configuration.getCreateRules().get(AlarmSeverity.CRITICAL).getCondition();
durationCondition.setValue(new AlarmConditionValue<>(null, "durationThreshold"));
calculatedField = saveCalculatedField(calculatedField);
long dynamicDurationMs = 3000L;
postAttributes(deviceId, AttributeScope.SERVER_SCOPE,
"{\"durationThreshold\":" + dynamicDurationMs + "}");
checkAlarmResult(calculatedField, alarmResult -> {
assertThat(alarmResult.isCreated()).isTrue();
assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL);
assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK);
});
}
@Test
public void testCreateAlarm_currentOwnerArgument() throws Exception {
Argument temperatureArgument = new Argument();

Loading…
Cancel
Save