Commit 671a0b579ef2b6850311b3ba8c3cdfffb9a34747

Authored by Dima Landiak
1 parent d1c6a907

events debug mode rate limits added

@@ -36,6 +36,7 @@ import org.springframework.stereotype.Component; @@ -36,6 +36,7 @@ import org.springframework.stereotype.Component;
36 import org.thingsboard.rule.engine.api.MailService; 36 import org.thingsboard.rule.engine.api.MailService;
37 import org.thingsboard.rule.engine.api.RuleChainTransactionService; 37 import org.thingsboard.rule.engine.api.RuleChainTransactionService;
38 import org.thingsboard.server.actors.service.ActorService; 38 import org.thingsboard.server.actors.service.ActorService;
  39 +import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
39 import org.thingsboard.server.common.data.DataConstants; 40 import org.thingsboard.server.common.data.DataConstants;
40 import org.thingsboard.server.common.data.Event; 41 import org.thingsboard.server.common.data.Event;
41 import org.thingsboard.server.common.data.id.EntityId; 42 import org.thingsboard.server.common.data.id.EntityId;
@@ -43,6 +44,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -43,6 +44,7 @@ import org.thingsboard.server.common.data.id.TenantId;
43 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 44 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
44 import org.thingsboard.server.common.msg.TbMsg; 45 import org.thingsboard.server.common.msg.TbMsg;
45 import org.thingsboard.server.common.msg.cluster.ServerAddress; 46 import org.thingsboard.server.common.msg.cluster.ServerAddress;
  47 +import org.thingsboard.server.common.msg.tools.TbRateLimits;
46 import org.thingsboard.server.common.transport.auth.DeviceAuthService; 48 import org.thingsboard.server.common.transport.auth.DeviceAuthService;
47 import org.thingsboard.server.dao.alarm.AlarmService; 49 import org.thingsboard.server.dao.alarm.AlarmService;
48 import org.thingsboard.server.dao.asset.AssetService; 50 import org.thingsboard.server.dao.asset.AssetService;
@@ -84,6 +86,8 @@ import java.io.IOException; @@ -84,6 +86,8 @@ import java.io.IOException;
84 import java.io.PrintWriter; 86 import java.io.PrintWriter;
85 import java.io.StringWriter; 87 import java.io.StringWriter;
86 import java.util.Optional; 88 import java.util.Optional;
  89 +import java.util.concurrent.ConcurrentHashMap;
  90 +import java.util.concurrent.ConcurrentMap;
87 91
88 @Slf4j 92 @Slf4j
89 @Component 93 @Component
@@ -92,6 +96,12 @@ public class ActorSystemContext { @@ -92,6 +96,12 @@ public class ActorSystemContext {
92 96
93 protected final ObjectMapper mapper = new ObjectMapper(); 97 protected final ObjectMapper mapper = new ObjectMapper();
94 98
  99 + private final ConcurrentMap<TenantId, DebugTbRateLimits> debugPerTenantLimits = new ConcurrentHashMap<>();
  100 +
  101 + public ConcurrentMap<TenantId, DebugTbRateLimits> getDebugPerTenantLimits() {
  102 + return debugPerTenantLimits;
  103 + }
  104 +
95 @Getter 105 @Getter
96 @Setter 106 @Setter
97 private ActorService actorService; 107 private ActorService actorService;
@@ -291,6 +301,14 @@ public class ActorSystemContext { @@ -291,6 +301,14 @@ public class ActorSystemContext {
291 @Getter 301 @Getter
292 private long sessionReportTimeout; 302 private long sessionReportTimeout;
293 303
  304 + @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled}")
  305 + @Getter
  306 + private boolean debugPerTenantEnabled;
  307 +
  308 + @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.configuration}")
  309 + @Getter
  310 + private String debugPerTenantLimitsConfiguration;
  311 +
294 @Getter 312 @Getter
295 @Setter 313 @Setter
296 private ActorSystem actorSystem; 314 private ActorSystem actorSystem;
@@ -318,8 +336,6 @@ public class ActorSystemContext { @@ -318,8 +336,6 @@ public class ActorSystemContext {
318 @Getter 336 @Getter
319 private CassandraBufferedRateExecutor cassandraBufferedRateExecutor; 337 private CassandraBufferedRateExecutor cassandraBufferedRateExecutor;
320 338
321 -  
322 -  
323 public ActorSystemContext() { 339 public ActorSystemContext() {
324 config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); 340 config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load());
325 } 341 }
@@ -392,46 +408,97 @@ public class ActorSystemContext { @@ -392,46 +408,97 @@ public class ActorSystemContext {
392 } 408 }
393 409
394 private void persistDebugAsync(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, String relationType, Throwable error) { 410 private void persistDebugAsync(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, String relationType, Throwable error) {
395 - try {  
396 - Event event = new Event();  
397 - event.setTenantId(tenantId);  
398 - event.setEntityId(entityId);  
399 - event.setType(DataConstants.DEBUG_RULE_NODE);  
400 -  
401 - String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData());  
402 -  
403 - ObjectNode node = mapper.createObjectNode()  
404 - .put("type", type)  
405 - .put("server", getServerAddress())  
406 - .put("entityId", tbMsg.getOriginator().getId().toString())  
407 - .put("entityName", tbMsg.getOriginator().getEntityType().name())  
408 - .put("msgId", tbMsg.getId().toString())  
409 - .put("msgType", tbMsg.getType())  
410 - .put("dataType", tbMsg.getDataType().name())  
411 - .put("relationType", relationType)  
412 - .put("data", tbMsg.getData())  
413 - .put("metadata", metadata);  
414 -  
415 - if (error != null) {  
416 - node = node.put("error", toString(error)); 411 + if (checkLimits(tenantId, tbMsg, error)) {
  412 + try {
  413 + Event event = new Event();
  414 + event.setTenantId(tenantId);
  415 + event.setEntityId(entityId);
  416 + event.setType(DataConstants.DEBUG_RULE_NODE);
  417 +
  418 + String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData());
  419 +
  420 + ObjectNode node = mapper.createObjectNode()
  421 + .put("type", type)
  422 + .put("server", getServerAddress())
  423 + .put("entityId", tbMsg.getOriginator().getId().toString())
  424 + .put("entityName", tbMsg.getOriginator().getEntityType().name())
  425 + .put("msgId", tbMsg.getId().toString())
  426 + .put("msgType", tbMsg.getType())
  427 + .put("dataType", tbMsg.getDataType().name())
  428 + .put("relationType", relationType)
  429 + .put("data", tbMsg.getData())
  430 + .put("metadata", metadata);
  431 +
  432 + if (error != null) {
  433 + node = node.put("error", toString(error));
  434 + }
  435 +
  436 + event.setBody(node);
  437 + ListenableFuture<Event> future = eventService.saveAsync(event);
  438 + Futures.addCallback(future, new FutureCallback<Event>() {
  439 + @Override
  440 + public void onSuccess(@Nullable Event event) {
  441 +
  442 + }
  443 +
  444 + @Override
  445 + public void onFailure(Throwable th) {
  446 + log.error("Could not save debug Event for Node", th);
  447 + }
  448 + });
  449 + } catch (IOException ex) {
  450 + log.warn("Failed to persist rule node debug message", ex);
417 } 451 }
  452 + }
  453 + }
418 454
419 - event.setBody(node);  
420 - ListenableFuture<Event> future = eventService.saveAsync(event);  
421 - Futures.addCallback(future, new FutureCallback<Event>() {  
422 - @Override  
423 - public void onSuccess(@Nullable Event event) { 455 + private boolean checkLimits(TenantId tenantId, TbMsg tbMsg, Throwable error) {
  456 + if (debugPerTenantEnabled) {
  457 + DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.computeIfAbsent(tenantId, id ->
  458 + new DebugTbRateLimits(new TbRateLimits(debugPerTenantLimitsConfiguration), false));
424 459
  460 + if (!debugTbRateLimits.getTbRateLimits().tryConsume()) {
  461 + if (!debugTbRateLimits.isRuleChainEventSaved()) {
  462 + persistRuleChainDebugModeEvent(tenantId, tbMsg.getRuleChainId(), error);
  463 + debugTbRateLimits.setRuleChainEventSaved(true);
425 } 464 }
426 -  
427 - @Override  
428 - public void onFailure(Throwable th) {  
429 - log.error("Could not save debug Event for Node", th); 465 + if (log.isTraceEnabled()) {
  466 + log.trace("[{}] Tenant level debug mode rate limit detected: {}", tenantId, tbMsg);
430 } 467 }
431 - });  
432 - } catch (IOException ex) {  
433 - log.warn("Failed to persist rule node debug message", ex); 468 + return false;
  469 + }
  470 + }
  471 + return true;
  472 + }
  473 +
  474 + private void persistRuleChainDebugModeEvent(TenantId tenantId, EntityId entityId, Throwable error) {
  475 + Event event = new Event();
  476 + event.setTenantId(tenantId);
  477 + event.setEntityId(entityId);
  478 + event.setType(DataConstants.DEBUG_RULE_CHAIN);
  479 +
  480 + ObjectNode node = mapper.createObjectNode()
  481 + //todo: what fields are needed here?
  482 + .put("server", getServerAddress())
  483 + .put("message", "Reached debug mode rate limit!");
  484 +
  485 + if (error != null) {
  486 + node = node.put("error", toString(error));
434 } 487 }
  488 +
  489 + event.setBody(node);
  490 + ListenableFuture<Event> future = eventService.saveAsync(event);
  491 + Futures.addCallback(future, new FutureCallback<Event>() {
  492 + @Override
  493 + public void onSuccess(@Nullable Event event) {
  494 +
  495 + }
  496 +
  497 + @Override
  498 + public void onFailure(Throwable th) {
  499 + log.error("Could not save debug Event for Rule Chain", th);
  500 + }
  501 + });
435 } 502 }
436 503
437 public static Exception toException(Throwable error) { 504 public static Exception toException(Throwable error) {
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.tenant;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.msg.tools.TbRateLimits;
  21 +
  22 +@Data
  23 +@AllArgsConstructor
  24 +public class DebugTbRateLimits {
  25 +
  26 + private TbRateLimits tbRateLimits;
  27 + private boolean ruleChainEventSaved;
  28 +
  29 +}
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
22 import com.fasterxml.jackson.databind.node.ObjectNode; 22 import com.fasterxml.jackson.databind.node.ObjectNode;
23 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
24 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.beans.factory.annotation.Value;
25 import org.springframework.http.HttpStatus; 26 import org.springframework.http.HttpStatus;
26 import org.springframework.security.access.prepost.PreAuthorize; 27 import org.springframework.security.access.prepost.PreAuthorize;
27 import org.springframework.util.StringUtils; 28 import org.springframework.util.StringUtils;
@@ -34,6 +35,8 @@ import org.springframework.web.bind.annotation.ResponseBody; @@ -34,6 +35,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
34 import org.springframework.web.bind.annotation.ResponseStatus; 35 import org.springframework.web.bind.annotation.ResponseStatus;
35 import org.springframework.web.bind.annotation.RestController; 36 import org.springframework.web.bind.annotation.RestController;
36 import org.thingsboard.rule.engine.api.ScriptEngine; 37 import org.thingsboard.rule.engine.api.ScriptEngine;
  38 +import org.thingsboard.server.actors.ActorSystemContext;
  39 +import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
37 import org.thingsboard.server.common.data.DataConstants; 40 import org.thingsboard.server.common.data.DataConstants;
38 import org.thingsboard.server.common.data.EntityType; 41 import org.thingsboard.server.common.data.EntityType;
39 import org.thingsboard.server.common.data.Event; 42 import org.thingsboard.server.common.data.Event;
@@ -56,10 +59,10 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; @@ -56,10 +59,10 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
56 import org.thingsboard.server.service.security.permission.Operation; 59 import org.thingsboard.server.service.security.permission.Operation;
57 import org.thingsboard.server.service.security.permission.Resource; 60 import org.thingsboard.server.service.security.permission.Resource;
58 61
59 -import java.util.HashSet;  
60 import java.util.List; 62 import java.util.List;
61 import java.util.Map; 63 import java.util.Map;
62 import java.util.Set; 64 import java.util.Set;
  65 +import java.util.concurrent.ConcurrentMap;
63 import java.util.stream.Collectors; 66 import java.util.stream.Collectors;
64 67
65 @Slf4j 68 @Slf4j
@@ -78,6 +81,12 @@ public class RuleChainController extends BaseController { @@ -78,6 +81,12 @@ public class RuleChainController extends BaseController {
78 @Autowired 81 @Autowired
79 private JsInvokeService jsInvokeService; 82 private JsInvokeService jsInvokeService;
80 83
  84 + @Autowired(required = false)
  85 + private ActorSystemContext actorContext;
  86 +
  87 + @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled}")
  88 + private boolean debugPerTenantEnabled;
  89 +
81 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 90 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
82 @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET) 91 @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET)
83 @ResponseBody 92 @ResponseBody
@@ -182,8 +191,17 @@ public class RuleChainController extends BaseController { @@ -182,8 +191,17 @@ public class RuleChainController extends BaseController {
182 @ResponseBody 191 @ResponseBody
183 public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException { 192 public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException {
184 try { 193 try {
  194 + TenantId tenantId = getTenantId();
  195 + if (debugPerTenantEnabled) {
  196 + ConcurrentMap<TenantId, DebugTbRateLimits> debugPerTenantLimits = actorContext.getDebugPerTenantLimits();
  197 + DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.getOrDefault(tenantId, null);
  198 + if (debugTbRateLimits != null) {
  199 + debugPerTenantLimits.remove(tenantId, debugTbRateLimits);
  200 + }
  201 + }
  202 +
185 RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); 203 RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
186 - RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(getTenantId(), ruleChainMetaData)); 204 + RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData));
187 205
188 actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); 206 actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
189 207
@@ -236,7 +254,7 @@ public class RuleChainController extends BaseController { @@ -236,7 +254,7 @@ public class RuleChainController extends BaseController {
236 referencingRuleChainIds.remove(ruleChain.getId()); 254 referencingRuleChainIds.remove(ruleChain.getId());
237 255
238 referencingRuleChainIds.forEach(referencingRuleChainId -> 256 referencingRuleChainIds.forEach(referencingRuleChainId ->
239 - actorService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED)); 257 + actorService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
240 258
241 actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); 259 actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
242 260
@@ -291,7 +309,8 @@ public class RuleChainController extends BaseController { @@ -291,7 +309,8 @@ public class RuleChainController extends BaseController {
291 309
292 String data = inputParams.get("msg").asText(); 310 String data = inputParams.get("msg").asText();
293 JsonNode metadataJson = inputParams.get("metadata"); 311 JsonNode metadataJson = inputParams.get("metadata");
294 - Map<String, String> metadata = objectMapper.convertValue(metadataJson, new TypeReference<Map<String, String>>() {}); 312 + Map<String, String> metadata = objectMapper.convertValue(metadataJson, new TypeReference<Map<String, String>>() {
  313 + });
295 String msgType = inputParams.get("msgType").asText(); 314 String msgType = inputParams.get("msgType").asText();
296 String output = ""; 315 String output = "";
297 String errorText = ""; 316 String errorText = "";
@@ -210,6 +210,9 @@ actors: @@ -210,6 +210,9 @@ actors:
210 chain: 210 chain:
211 # Errors for particular actor are persisted once per specified amount of milliseconds 211 # Errors for particular actor are persisted once per specified amount of milliseconds
212 error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}" 212 error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}"
  213 + debug_mode_rate_limits_per_tenant:
  214 + enabled: "${ACTORS_RULE_CHAIN_DEBUG_MODE_RATE_LIMITS_PER_TENANT_ENABLED:true}"
  215 + configuration: "${ACTORS_RULE_CHAIN_DEBUG_MODE_RATE_LIMITS_PER_TENANT_CONFIGURATION:1000:3600}"
213 node: 216 node:
214 # Errors for particular actor are persisted once per specified amount of milliseconds 217 # Errors for particular actor are persisted once per specified amount of milliseconds
215 error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}" 218 error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}"
@@ -38,6 +38,7 @@ public class DataConstants { @@ -38,6 +38,7 @@ public class DataConstants {
38 public static final String LC_EVENT = "LC_EVENT"; 38 public static final String LC_EVENT = "LC_EVENT";
39 public static final String STATS = "STATS"; 39 public static final String STATS = "STATS";
40 public static final String DEBUG_RULE_NODE = "DEBUG_RULE_NODE"; 40 public static final String DEBUG_RULE_NODE = "DEBUG_RULE_NODE";
  41 + public static final String DEBUG_RULE_CHAIN = "DEBUG_RULE_CHAIN";
41 42
42 public static final String ONEWAY = "ONEWAY"; 43 public static final String ONEWAY = "ONEWAY";
43 public static final String TWOWAY = "TWOWAY"; 44 public static final String TWOWAY = "TWOWAY";