Browse Source

Merge pull request #12224 from YevhenBondarenko/feature/tbel-stats

Added TBEL metrics, refactored JS stats
pull/12883/head
Viacheslav Klimov 1 year ago
committed by GitHub
parent
commit
beb54fa4a9
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 16
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  2. 21
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  3. 83
      application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java
  4. 2
      application/src/main/resources/thingsboard.yml
  5. 4
      application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java
  6. 5
      application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java
  7. 8
      application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java
  8. 44
      common/actor/src/main/java/org/thingsboard/server/actors/JsInvokeStats.java
  9. 54
      common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java
  10. 14
      common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java
  11. 5
      common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java
  12. 6
      common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java
  13. 4
      common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java
  14. 1
      common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java
  15. 98
      docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json
  16. 6
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
  17. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java
  18. 8
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java
  19. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java
  20. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java
  21. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java
  22. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java
  23. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java
  24. 20
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java

16
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.MailService;
@ -432,10 +431,6 @@ public class ActorSystemContext {
@Getter
private ClaimDevicesService claimDevicesService;
@Autowired
@Getter
private JsInvokeStats jsInvokeStats;
//TODO: separate context for TbCore and TbRuleEngine
@Autowired(required = false)
@Getter
@ -595,17 +590,6 @@ public class ActorSystemContext {
this.localCacheType = "caffeine".equals(cacheType);
}
@Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}")
public void printStats() {
if (statisticsEnabled) {
if (jsInvokeStats.getRequests() > 0 || jsInvokeStats.getResponses() > 0 || jsInvokeStats.getFailures() > 0) {
log.info("Rule Engine JS Invoke Stats: requests [{}] responses [{}] failures [{}]",
jsInvokeStats.getRequests(), jsInvokeStats.getResponses(), jsInvokeStats.getFailures());
jsInvokeStats.reset();
}
}
}
@Value("${actors.tenant.create_components_on_init:true}")
@Getter
private boolean tenantComponentsInitEnabled;

21
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -654,27 +654,6 @@ public class DefaultTbContext implements TbContext {
}
}
@Override
public void logJsEvalRequest() {
if (mainCtx.isStatisticsEnabled()) {
mainCtx.getJsInvokeStats().incrementRequests();
}
}
@Override
public void logJsEvalResponse() {
if (mainCtx.isStatisticsEnabled()) {
mainCtx.getJsInvokeStats().incrementResponses();
}
}
@Override
public void logJsEvalFailure() {
if (mainCtx.isStatisticsEnabled()) {
mainCtx.getJsInvokeStats().incrementFailures();
}
}
@Override
public String getServiceId() {
return mainCtx.getServiceInfoProvider().getServiceId();

83
application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java

@ -1,83 +0,0 @@
/**
* Copyright © 2016-2025 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.stats;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.actors.JsInvokeStats;
import org.thingsboard.server.common.stats.StatsCounter;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.common.stats.StatsType;
@Service
public class DefaultJsInvokeStats implements JsInvokeStats {
private static final String REQUESTS = "requests";
private static final String RESPONSES = "responses";
private static final String FAILURES = "failures";
private StatsCounter requestsCounter;
private StatsCounter responsesCounter;
private StatsCounter failuresCounter;
@Autowired
private StatsFactory statsFactory;
@PostConstruct
public void init() {
String key = StatsType.JS_INVOKE.getName();
this.requestsCounter = statsFactory.createStatsCounter(key, REQUESTS);
this.responsesCounter = statsFactory.createStatsCounter(key, RESPONSES);
this.failuresCounter = statsFactory.createStatsCounter(key, FAILURES);
}
@Override
public void incrementRequests(int amount) {
requestsCounter.add(amount);
}
@Override
public void incrementResponses(int amount) {
responsesCounter.add(amount);
}
@Override
public void incrementFailures(int amount) {
failuresCounter.add(amount);
}
@Override
public int getRequests() {
return requestsCounter.get();
}
@Override
public int getResponses() {
return responsesCounter.get();
}
@Override
public int getFailures() {
return failuresCounter.get();
}
@Override
public void reset() {
requestsCounter.clear();
responsesCounter.clear();
failuresCounter.clear();
}
}

2
application/src/main/resources/thingsboard.yml

@ -502,8 +502,6 @@ actors:
statistics:
# Enable/disable actor statistics
enabled: "${ACTORS_STATISTICS_ENABLED:true}"
# Frequency of printing the JS executor statistics
js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}"
# Actors statistic persistence frequency in milliseconds
persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}"
calculated_fields:

4
application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.cf.ctx.state;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.stats.DefaultStatsFactory;
import org.thingsboard.server.dao.usagerecord.ApiLimitService;
import org.thingsboard.server.service.cf.CalculatedFieldResult;
@ -51,7 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@SpringBootTest(classes = DefaultTbelInvokeService.class)
@SpringBootTest(classes = {SimpleMeterRegistry.class, DefaultStatsFactory.class, DefaultTbelInvokeService.class})
public class ScriptCalculatedFieldStateTest {
private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("5b18e321-3327-4290-b996-d72a65e90382"));

5
application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.script;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.thingsboard.common.util.JacksonUtil;
@ -22,7 +23,7 @@ import org.thingsboard.script.api.ScriptType;
import org.thingsboard.script.api.tbel.DefaultTbelInvokeService;
import org.thingsboard.script.api.tbel.TbelInvokeService;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.common.stats.DefaultStatsFactory;
import java.util.Map;
import java.util.UUID;
@ -30,7 +31,7 @@ import java.util.concurrent.ExecutionException;
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST;
@SpringBootTest(classes = DefaultTbelInvokeService.class)
@SpringBootTest(classes = {SimpleMeterRegistry.class, DefaultStatsFactory.class, DefaultTbelInvokeService.class })
public abstract class AbstractTbelInvokeTest {
@Autowired

8
application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java

@ -21,9 +21,13 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.test.util.ReflectionTestUtils;
import org.thingsboard.script.api.ScriptType;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.stats.DefaultStatsFactory;
import org.thingsboard.server.common.stats.StatsCounter;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.common.stats.TbApiUsageReportClient;
import org.thingsboard.server.common.stats.TbApiUsageStateClient;
import org.thingsboard.server.gen.js.JsInvokeProtos;
@ -68,6 +72,10 @@ class RemoteJsInvokeServiceTest {
remoteJsInvokeService = new RemoteJsInvokeService(Optional.of(apiUsageStateClient), Optional.of(apiUsageReportClient));
jsRequestTemplate = mock(TbQueueRequestTemplate.class);
remoteJsInvokeService.requestTemplate = jsRequestTemplate;
StatsFactory statsFactory = mock(StatsFactory.class);
when(statsFactory.createStatsCounter(any(), any())).thenReturn(mock(StatsCounter.class));
ReflectionTestUtils.setField(remoteJsInvokeService, "statsFactory",statsFactory);
remoteJsInvokeService.init();
}
@AfterEach

44
common/actor/src/main/java/org/thingsboard/server/actors/JsInvokeStats.java

@ -1,44 +0,0 @@
/**
* Copyright © 2016-2025 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.actors;
public interface JsInvokeStats {
default void incrementRequests() {
incrementRequests(1);
}
void incrementRequests(int amount);
default void incrementResponses() {
incrementResponses(1);
}
void incrementResponses(int amount);
default void incrementFailures() {
incrementFailures(1);
}
void incrementFailures(int amount);
int getRequests();
int getResponses();
int getFailures();
void reset();
}

54
common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java

@ -20,12 +20,16 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.script.api.tbel.TbelCfArg;
import org.thingsboard.script.api.tbel.TbelCfObject;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.stats.StatsCounter;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.common.stats.StatsType;
import java.util.Map;
import java.util.UUID;
@ -34,22 +38,31 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.String.format;
@Slf4j
public abstract class AbstractScriptInvokeService implements ScriptInvokeService {
private static final String REQUESTS = "requests";
private static final String INVOKE_RESPONSES = "invoke_responses";
private static final String EVAL_RESPONSES = "eval_responses";
private static final String FAILURES = "failures";
private static final String TIMEOUTS = "timeouts";
protected final Map<UUID, BlockedScriptInfo> disabledScripts = new ConcurrentHashMap<>();
private final AtomicInteger pushedMsgs = new AtomicInteger(0);
private final AtomicInteger invokeMsgs = new AtomicInteger(0);
private final AtomicInteger evalMsgs = new AtomicInteger(0);
protected final AtomicInteger failedMsgs = new AtomicInteger(0);
protected final AtomicInteger timeoutMsgs = new AtomicInteger(0);
private final FutureCallback<UUID> evalCallback = new ScriptStatCallback<>(evalMsgs, timeoutMsgs, failedMsgs);
private final FutureCallback<Object> invokeCallback = new ScriptStatCallback<>(invokeMsgs, timeoutMsgs, failedMsgs);
private StatsCounter requestsCounter;
private StatsCounter invokeResponsesCounter;
private StatsCounter evalResponsesCounter;
private StatsCounter failuresCounter;
private StatsCounter timeoutsCounter;
private FutureCallback<UUID> evalCallback;
private FutureCallback<Object> invokeCallback;
@Autowired
private StatsFactory statsFactory;
protected ScheduledExecutorService timeoutExecutorService;
@ -78,6 +91,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService
protected abstract boolean isScriptPresent(UUID scriptId);
protected abstract boolean isExecEnabled(TenantId tenantId);
protected abstract void reportExecution(TenantId tenantId, CustomerId customerId);
protected abstract ListenableFuture<UUID> doEvalScript(TenantId tenantId, ScriptType scriptType, String scriptBody, UUID scriptId, String[] argNames);
@ -87,6 +101,14 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService
protected abstract void doRelease(UUID scriptId) throws Exception;
public void init() {
String key = getStatsType().getName();
this.requestsCounter = statsFactory.createStatsCounter(key, REQUESTS);
this.invokeResponsesCounter = statsFactory.createStatsCounter(key, INVOKE_RESPONSES);
this.evalResponsesCounter = statsFactory.createStatsCounter(key, EVAL_RESPONSES);
this.failuresCounter = statsFactory.createStatsCounter(key, FAILURES);
this.timeoutsCounter = statsFactory.createStatsCounter(key, TIMEOUTS);
this.evalCallback = new ScriptStatCallback<>(evalResponsesCounter, timeoutsCounter, failuresCounter);
this.invokeCallback = new ScriptStatCallback<>(invokeResponsesCounter, timeoutsCounter, failuresCounter);
if (getMaxEvalRequestsTimeout() > 0 || getMaxInvokeRequestsTimeout() > 0) {
timeoutExecutorService = ThingsBoardExecutors.newSingleThreadScheduledExecutor("script-timeout");
}
@ -100,11 +122,11 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService
public void printStats() {
if (isStatsEnabled()) {
int pushed = pushedMsgs.getAndSet(0);
int invoked = invokeMsgs.getAndSet(0);
int evaluated = evalMsgs.getAndSet(0);
int failed = failedMsgs.getAndSet(0);
int timedOut = timeoutMsgs.getAndSet(0);
int pushed = requestsCounter.getAndClear();
int invoked = invokeResponsesCounter.getAndClear();
int evaluated = evalResponsesCounter.getAndClear();
int failed = failuresCounter.getAndClear();
int timedOut = timeoutsCounter.getAndClear();
if (pushed > 0 || invoked > 0 || evaluated > 0 || failed > 0 || timedOut > 0) {
log.info("{}: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]",
getStatsName(), pushed, invoked + evaluated, invoked, evaluated, failed, timedOut);
@ -119,7 +141,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService
return error(format("Script body exceeds maximum allowed size of %s symbols", getMaxScriptBodySize()));
}
UUID scriptId = UUID.randomUUID();
pushedMsgs.incrementAndGet();
requestsCounter.increment();
return withTimeoutAndStatsCallback(scriptId, null,
doEvalScript(tenantId, scriptType, scriptBody, scriptId, argNames), evalCallback, getMaxEvalRequestsTimeout());
} else {
@ -141,7 +163,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService
return Futures.immediateFailedFuture(handleScriptException(scriptId, null, t));
}
reportExecution(tenantId, customerId);
pushedMsgs.incrementAndGet();
requestsCounter.increment();
log.trace("[{}] InvokeScript uuid {} with timeout {}ms", tenantId, scriptId, getMaxInvokeRequestsTimeout());
var task = doInvokeFunction(scriptId, args);
@ -278,4 +300,6 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService
private <T> ListenableFuture<T> error(String message) {
return Futures.immediateFailedFuture(new RuntimeException(message));
}
protected abstract StatsType getStatsType();
}

14
common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java

@ -19,29 +19,29 @@ import com.google.common.util.concurrent.FutureCallback;
import jakarta.annotation.Nullable;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.stats.StatsCounter;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@AllArgsConstructor
public class ScriptStatCallback<T> implements FutureCallback<T> {
private final AtomicInteger successMsgs;
private final AtomicInteger timeoutMsgs;
private final AtomicInteger failedMsgs;
private final StatsCounter successMsgs;
private final StatsCounter timeoutMsgs;
private final StatsCounter failedMsgs;
@Override
public void onSuccess(@Nullable T result) {
successMsgs.incrementAndGet();
successMsgs.increment();
}
@Override
public void onFailure(Throwable t) {
if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
timeoutMsgs.incrementAndGet();
timeoutMsgs.increment();
} else {
failedMsgs.incrementAndGet();
failedMsgs.increment();
}
}
}

5
common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java

@ -26,6 +26,7 @@ import org.thingsboard.script.api.ScriptType;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.stats.StatsType;
import org.thingsboard.server.common.stats.TbApiUsageReportClient;
import org.thingsboard.server.common.stats.TbApiUsageStateClient;
@ -117,4 +118,8 @@ public abstract class AbstractJsInvokeService extends AbstractScriptInvokeServic
.hash().toString();
}
@Override
protected StatsType getStatsType() {
return StatsType.JS_INVOKE;
}
}

6
common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java

@ -44,6 +44,7 @@ import org.thingsboard.script.api.TbScriptException;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.stats.StatsType;
import org.thingsboard.server.common.stats.TbApiUsageReportClient;
import org.thingsboard.server.common.stats.TbApiUsageStateClient;
@ -263,4 +264,9 @@ public class DefaultTbelInvokeService extends AbstractScriptInvokeService implem
protected long getMaxEvalRequestsTimeout() {
return maxInvokeRequestsTimeout * 2;
}
@Override
protected StatsType getStatsType() {
return StatsType.TBEL_INVOKE;
}
}

4
common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java

@ -41,6 +41,10 @@ public class DefaultCounter {
return aiCounter.get();
}
public int getAndClear() {
return aiCounter.getAndSet(0);
}
public void add(int delta){
aiCounter.addAndGet(delta);
micrometerCounter.increment(delta);

1
common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java

@ -20,6 +20,7 @@ public enum StatsType {
CORE("core"),
TRANSPORT("transport"),
JS_INVOKE("jsInvoke"),
TBEL_INVOKE("tbelInvoke"),
RATE_EXECUTOR("rateExecutor"),
HOUSEKEEPER("housekeeper"),
EDGE("edge"),

98
docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json

@ -223,8 +223,8 @@
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 12,
"w": 24,
"h": 10,
"w": 12,
"x": 0,
"y": 10
},
@ -303,6 +303,100 @@
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fieldConfig": {
"defaults": {},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 10,
"w": 12,
"x": 12,
"y": 10
},
"hiddenSeries": false,
"id": 19,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.5.4",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"exemplar": true,
"expr": "sum by(statsName) (increase(tbelInvoke_total[1m]))",
"interval": "",
"legendFormat": "{{statsName}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "TbelInvoke Stats",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"schemaVersion": 27,

6
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java

@ -375,12 +375,6 @@ public interface TbContext {
ScriptEngine createScriptEngine(ScriptLanguage scriptLang, String script, String... argNames);
void logJsEvalRequest();
void logJsEvalResponse();
void logJsEvalFailure();
String getServiceId();
EventLoopGroup getSharedEventLoop();

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java

@ -69,10 +69,8 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
}
private ListenableFuture<TbAlarmResult> clearAlarm(TbContext ctx, TbMsg msg, Alarm alarm) {
ctx.logJsEvalRequest();
ListenableFuture<JsonNode> asyncDetails = buildAlarmDetails(msg, alarm.getDetails());
return Futures.transform(asyncDetails, details -> {
ctx.logJsEvalResponse();
AlarmApiCallResult result = ctx.getAlarmService().clearAlarm(ctx.getTenantId(), alarm.getId(), System.currentTimeMillis(), details);
if (result.isSuccessful()) {
return new TbAlarmResult(false, false, result.isCleared(), result.getAlarm());

8
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java

@ -118,15 +118,11 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
ListenableFuture<JsonNode> asyncDetails;
boolean buildDetails = !config.isUseMessageAlarmData() || config.isOverwriteAlarmDetails();
if (buildDetails) {
ctx.logJsEvalRequest();
asyncDetails = buildAlarmDetails(msg, null);
} else {
asyncDetails = Futures.immediateFuture(null);
}
ListenableFuture<Alarm> asyncAlarm = Futures.transform(asyncDetails, details -> {
if (buildDetails) {
ctx.logJsEvalResponse();
}
Alarm newAlarm;
if (msgAlarm != null) {
newAlarm = msgAlarm;
@ -147,15 +143,11 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
ListenableFuture<JsonNode> asyncDetails;
boolean buildDetails = !config.isUseMessageAlarmData() || config.isOverwriteAlarmDetails();
if (buildDetails) {
ctx.logJsEvalRequest();
asyncDetails = buildAlarmDetails(msg, existingAlarm.getDetails());
} else {
asyncDetails = Futures.immediateFuture(null);
}
ListenableFuture<AlarmApiCallResult> asyncUpdated = Futures.transform(asyncDetails, details -> {
if (buildDetails) {
ctx.logJsEvalResponse();
}
if (msgAlarm != null) {
existingAlarm.setSeverity(msgAlarm.getSeverity());
existingAlarm.setPropagate(msgAlarm.isPropagate());

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java

@ -75,18 +75,15 @@ public class TbLogNode implements TbNode {
return;
}
ctx.logJsEvalRequest();
Futures.addCallback(scriptEngine.executeToStringAsync(msg), new FutureCallback<String>() {
@Override
public void onSuccess(@Nullable String result) {
ctx.logJsEvalResponse();
log.info(result);
ctx.tellSuccess(msg);
}
@Override
public void onFailure(Throwable t) {
ctx.logJsEvalResponse();
ctx.tellFailure(msg, t);
}
}, MoreExecutors.directExecutor()); //usually js responses runs on js callback executor

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java

@ -160,10 +160,8 @@ public class TbMsgGeneratorNode implements TbNode {
prevMsg = ctx.newMsg(queueName, TbMsg.EMPTY_STRING, originatorId, msg.getCustomerId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT);
}
if (initialized.get()) {
ctx.logJsEvalRequest();
return Futures.transformAsync(scriptEngine.executeGenerateAsync(prevMsg), generated -> {
log.trace("generate process response, generated {}, config {}", generated, config);
ctx.logJsEvalResponse();
prevMsg = ctx.newMsg(queueName, generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData());
return Futures.immediateFuture(prevMsg);
}, MoreExecutors.directExecutor()); //usually it runs on js-executor-remote-callback thread pool

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java

@ -60,15 +60,12 @@ public class TbJsFilterNode implements TbNode {
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
ctx.logJsEvalRequest();
withCallback(scriptEngine.executeFilterAsync(msg),
filterResult -> {
ctx.logJsEvalResponse();
ctx.tellNext(msg, filterResult ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE);
},
t -> {
ctx.tellFailure(msg, t);
ctx.logJsEvalFailure();
}, ctx.getDbCallbackExecutor());
}

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java

@ -61,17 +61,14 @@ public class TbJsSwitchNode implements TbNode {
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
ctx.logJsEvalRequest();
Futures.addCallback(scriptEngine.executeSwitchAsync(msg), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Set<String> result) {
ctx.logJsEvalResponse();
processSwitch(ctx, msg, result);
}
@Override
public void onFailure(Throwable t) {
ctx.logJsEvalFailure();
ctx.tellFailure(msg, t);
}
}, MoreExecutors.directExecutor()); //usually runs in a callbackExecutor

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java

@ -57,13 +57,11 @@ public class TbTransformMsgNode extends TbAbstractTransformNode<TbTransformMsgNo
@Override
protected ListenableFuture<List<TbMsg>> transform(TbContext ctx, TbMsg msg) {
ctx.logJsEvalRequest();
return scriptEngine.executeUpdateAsync(msg);
}
@Override
protected void transformFailure(TbContext ctx, TbMsg msg, Throwable t) {
ctx.logJsEvalFailure();
super.transformFailure(ctx, msg, t);
}

20
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java

@ -119,8 +119,8 @@ class TbCreateAlarmNodeTest {
delete metadata.prevAlarmDetails;
//now metadata is the same as it comes IN this rule node
}
return details;""");
assertThat(config.getAlarmDetailsBuildTbel()).isEqualTo("""
\
@ -131,8 +131,8 @@ class TbCreateAlarmNodeTest {
metadata.remove('prevAlarmDetails');
//now metadata is the same as it comes IN this rule node
}
return details;""");
assertThat(config.getSeverity()).isEqualTo(AlarmSeverity.CRITICAL.name());
assertThat(config.isPropagate()).isFalse();
@ -247,9 +247,7 @@ class TbCreateAlarmNodeTest {
// THEN
// verify alarm details script evaluation
then(ctxMock).should().logJsEvalRequest();
then(alarmDetailsScriptMock).should().executeJsonAsync(incomingMsg);
then(ctxMock).should().logJsEvalResponse();
// verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest
then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest);
@ -421,9 +419,7 @@ class TbCreateAlarmNodeTest {
// THEN
// verify alarm details script evaluation
then(ctxMock).should().logJsEvalRequest();
then(alarmDetailsScriptMock).should().executeJsonAsync(incomingMsg);
then(ctxMock).should().logJsEvalResponse();
// verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest
then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest);
@ -616,14 +612,12 @@ class TbCreateAlarmNodeTest {
// THEN
// verify alarm details script evaluation
then(ctxMock).should().logJsEvalRequest();
var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture());
TbMsg actualDummyMsg = dummyMsgCaptor.getValue();
assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType());
assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData());
assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(oldAlarmDetails));
then(ctxMock).should().logJsEvalResponse();
// verify we called updateAlarm() with correct AlarmUpdateRequest
then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest);
@ -793,9 +787,7 @@ class TbCreateAlarmNodeTest {
// THEN
// verify alarm details script was not evaluated
then(ctxMock).should(never()).logJsEvalRequest();
then(alarmDetailsScriptMock).should(never()).executeJsonAsync(any());
then(ctxMock).should(never()).logJsEvalResponse();
// verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest
then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest);
@ -985,14 +977,12 @@ class TbCreateAlarmNodeTest {
// THEN
// verify alarm details script evaluation
then(ctxMock).should().logJsEvalRequest();
var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture());
TbMsg actualDummyMsg = dummyMsgCaptor.getValue();
assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType());
assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData());
assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(oldAlarmDetails));
then(ctxMock).should().logJsEvalResponse();
// verify we called updateAlarm() with correct AlarmUpdateRequest
then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest);
@ -1171,14 +1161,12 @@ class TbCreateAlarmNodeTest {
// THEN
// verify alarm details script evaluation
then(ctxMock).should().logJsEvalRequest();
var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture());
TbMsg actualDummyMsg = dummyMsgCaptor.getValue();
assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType());
assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData());
assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(alarmDetails));
then(ctxMock).should().logJsEvalResponse();
// verify we called updateAlarm() with correct AlarmUpdateRequest
then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest);

Loading…
Cancel
Save