Commit b86e377bcf799a0b6aa34d28cc4b15876a92511e

Authored by Andrew Shvayka
Committed by GitHub
2 parents 795d54b7 af5e52dd

Merge pull request #78 from thingsboard/feature/dispatchers

Separate Dispatchers for system and tenant rules/plugins
@@ -32,10 +32,7 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -32,10 +32,7 @@ import com.google.common.util.concurrent.ListenableFuture;
32 import lombok.extern.slf4j.Slf4j; 32 import lombok.extern.slf4j.Slf4j;
33 import org.thingsboard.server.common.data.DataConstants; 33 import org.thingsboard.server.common.data.DataConstants;
34 import org.thingsboard.server.common.data.Device; 34 import org.thingsboard.server.common.data.Device;
35 -import org.thingsboard.server.common.data.id.CustomerId;  
36 -import org.thingsboard.server.common.data.id.DeviceId;  
37 -import org.thingsboard.server.common.data.id.PluginId;  
38 -import org.thingsboard.server.common.data.id.TenantId; 35 +import org.thingsboard.server.common.data.id.*;
39 import org.thingsboard.server.common.data.kv.AttributeKey; 36 import org.thingsboard.server.common.data.kv.AttributeKey;
40 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 37 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
41 import org.thingsboard.server.common.data.kv.TsKvEntry; 38 import org.thingsboard.server.common.data.kv.TsKvEntry;
@@ -74,6 +71,10 @@ public final class PluginProcessingContext implements PluginContext { @@ -74,6 +71,10 @@ public final class PluginProcessingContext implements PluginContext {
74 this.securityCtx = Optional.ofNullable(securityCtx); 71 this.securityCtx = Optional.ofNullable(securityCtx);
75 } 72 }
76 73
  74 + public void persistError(String method, Exception e) {
  75 + pluginCtx.persistError(method, e);
  76 + }
  77 +
77 @Override 78 @Override
78 public void sendPluginRpcMsg(RpcMsg msg) { 79 public void sendPluginRpcMsg(RpcMsg msg) {
79 this.pluginCtx.rpcService.tell(new PluginRpcMsg(pluginCtx.tenantId, pluginCtx.pluginId, msg)); 80 this.pluginCtx.rpcService.tell(new PluginRpcMsg(pluginCtx.tenantId, pluginCtx.pluginId, msg));
@@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
20 import org.thingsboard.server.actors.ActorSystemContext; 20 import org.thingsboard.server.actors.ActorSystemContext;
21 import org.thingsboard.server.common.data.Device; 21 import org.thingsboard.server.common.data.Device;
22 import org.thingsboard.server.common.data.id.DeviceId; 22 import org.thingsboard.server.common.data.id.DeviceId;
  23 +import org.thingsboard.server.common.data.id.EntityId;
23 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.msg.cluster.ServerAddress; 25 import org.thingsboard.server.common.msg.cluster.ServerAddress;
25 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; 26 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
@@ -73,6 +74,10 @@ public final class SharedPluginProcessingContext { @@ -73,6 +74,10 @@ public final class SharedPluginProcessingContext {
73 return pluginId; 74 return pluginId;
74 } 75 }
75 76
  77 + public TenantId getPluginTenantId() {
  78 + return tenantId;
  79 + }
  80 +
76 public void toDeviceActor(DeviceAttributesEventNotificationMsg msg) { 81 public void toDeviceActor(DeviceAttributesEventNotificationMsg msg) {
77 forward(msg.getDeviceId(), msg, rpcService::tell); 82 forward(msg.getDeviceId(), msg, rpcService::tell);
78 } 83 }
@@ -105,6 +110,10 @@ public final class SharedPluginProcessingContext { @@ -105,6 +110,10 @@ public final class SharedPluginProcessingContext {
105 110
106 } 111 }
107 112
  113 + public void persistError(String method, Exception e) {
  114 + systemContext.persistError(tenantId, pluginId, method, e);
  115 + }
  116 +
108 public ActorRef self() { 117 public ActorRef self() {
109 return currentActor; 118 return currentActor;
110 } 119 }
@@ -69,8 +69,10 @@ public class DefaultActorService implements ActorService { @@ -69,8 +69,10 @@ public class DefaultActorService implements ActorService {
69 69
70 public static final String APP_DISPATCHER_NAME = "app-dispatcher"; 70 public static final String APP_DISPATCHER_NAME = "app-dispatcher";
71 public static final String CORE_DISPATCHER_NAME = "core-dispatcher"; 71 public static final String CORE_DISPATCHER_NAME = "core-dispatcher";
72 - public static final String RULE_DISPATCHER_NAME = "rule-dispatcher";  
73 - public static final String PLUGIN_DISPATCHER_NAME = "plugin-dispatcher"; 72 + public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher";
  73 + public static final String SYSTEM_PLUGIN_DISPATCHER_NAME = "system-plugin-dispatcher";
  74 + public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher";
  75 + public static final String TENANT_PLUGIN_DISPATCHER_NAME = "plugin-dispatcher";
74 public static final String SESSION_DISPATCHER_NAME = "session-dispatcher"; 76 public static final String SESSION_DISPATCHER_NAME = "session-dispatcher";
75 public static final String RPC_DISPATCHER_NAME = "rpc-dispatcher"; 77 public static final String RPC_DISPATCHER_NAME = "rpc-dispatcher";
76 78
@@ -22,7 +22,6 @@ import lombok.extern.slf4j.Slf4j; @@ -22,7 +22,6 @@ import lombok.extern.slf4j.Slf4j;
22 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
23 import org.thingsboard.server.actors.plugin.PluginActor; 23 import org.thingsboard.server.actors.plugin.PluginActor;
24 import org.thingsboard.server.actors.service.ContextAwareActor; 24 import org.thingsboard.server.actors.service.ContextAwareActor;
25 -import org.thingsboard.server.actors.service.DefaultActorService;  
26 import org.thingsboard.server.common.data.id.PluginId; 25 import org.thingsboard.server.common.data.id.PluginId;
27 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
28 import org.thingsboard.server.common.data.page.PageDataIterable; 27 import org.thingsboard.server.common.data.page.PageDataIterable;
@@ -60,10 +59,12 @@ public abstract class PluginManager { @@ -60,10 +59,12 @@ public abstract class PluginManager {
60 59
61 abstract TenantId getTenantId(); 60 abstract TenantId getTenantId();
62 61
  62 + abstract String getDispatcherName();
  63 +
63 public ActorRef getOrCreatePluginActor(ActorContext context, PluginId pluginId) { 64 public ActorRef getOrCreatePluginActor(ActorContext context, PluginId pluginId) {
64 return pluginActors.computeIfAbsent(pluginId, pId -> 65 return pluginActors.computeIfAbsent(pluginId, pId ->
65 context.actorOf(Props.create(new PluginActor.ActorCreator(systemContext, getTenantId(), pId)) 66 context.actorOf(Props.create(new PluginActor.ActorCreator(systemContext, getTenantId(), pId))
66 - .withDispatcher(DefaultActorService.PLUGIN_DISPATCHER_NAME), pId.toString())); 67 + .withDispatcher(getDispatcherName()), pId.toString()));
67 } 68 }
68 69
69 public void broadcast(Object msg) { 70 public void broadcast(Object msg) {
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.actors.shared.plugin; 16 package org.thingsboard.server.actors.shared.plugin;
17 17
18 import org.thingsboard.server.actors.ActorSystemContext; 18 import org.thingsboard.server.actors.ActorSystemContext;
  19 +import org.thingsboard.server.actors.service.DefaultActorService;
19 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; 21 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
21 import org.thingsboard.server.common.data.plugin.PluginMetaData; 22 import org.thingsboard.server.common.data.plugin.PluginMetaData;
@@ -37,4 +38,8 @@ public class SystemPluginManager extends PluginManager { @@ -37,4 +38,8 @@ public class SystemPluginManager extends PluginManager {
37 return BasePluginService.SYSTEM_TENANT; 38 return BasePluginService.SYSTEM_TENANT;
38 } 39 }
39 40
  41 + @Override
  42 + protected String getDispatcherName() {
  43 + return DefaultActorService.SYSTEM_PLUGIN_DISPATCHER_NAME;
  44 + }
40 } 45 }
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.actors.shared.plugin; 16 package org.thingsboard.server.actors.shared.plugin;
17 17
18 import org.thingsboard.server.actors.ActorSystemContext; 18 import org.thingsboard.server.actors.ActorSystemContext;
  19 +import org.thingsboard.server.actors.service.DefaultActorService;
19 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; 21 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
21 import org.thingsboard.server.common.data.plugin.PluginMetaData; 22 import org.thingsboard.server.common.data.plugin.PluginMetaData;
@@ -38,4 +39,10 @@ public class TenantPluginManager extends PluginManager { @@ -38,4 +39,10 @@ public class TenantPluginManager extends PluginManager {
38 TenantId getTenantId() { 39 TenantId getTenantId() {
39 return tenantId; 40 return tenantId;
40 } 41 }
  42 +
  43 + @Override
  44 + protected String getDispatcherName() {
  45 + return DefaultActorService.TENANT_PLUGIN_DISPATCHER_NAME;
  46 + }
  47 +
41 } 48 }
@@ -100,10 +100,12 @@ public abstract class RuleManager { @@ -100,10 +100,12 @@ public abstract class RuleManager {
100 100
101 abstract FetchFunction<RuleMetaData> getFetchRulesFunction(); 101 abstract FetchFunction<RuleMetaData> getFetchRulesFunction();
102 102
  103 + abstract String getDispatcherName();
  104 +
103 public ActorRef getOrCreateRuleActor(ActorContext context, RuleId ruleId) { 105 public ActorRef getOrCreateRuleActor(ActorContext context, RuleId ruleId) {
104 return ruleActors.computeIfAbsent(ruleId, rId -> 106 return ruleActors.computeIfAbsent(ruleId, rId ->
105 context.actorOf(Props.create(new RuleActor.ActorCreator(systemContext, tenantId, rId)) 107 context.actorOf(Props.create(new RuleActor.ActorCreator(systemContext, tenantId, rId))
106 - .withDispatcher(DefaultActorService.RULE_DISPATCHER_NAME), rId.toString())); 108 + .withDispatcher(getDispatcherName()), rId.toString()));
107 } 109 }
108 110
109 public RuleActorChain getRuleChain() { 111 public RuleActorChain getRuleChain() {
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.actors.shared.rule; 16 package org.thingsboard.server.actors.shared.rule;
17 17
18 import org.thingsboard.server.actors.ActorSystemContext; 18 import org.thingsboard.server.actors.ActorSystemContext;
  19 +import org.thingsboard.server.actors.service.DefaultActorService;
19 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; 21 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
21 import org.thingsboard.server.common.data.rule.RuleMetaData; 22 import org.thingsboard.server.common.data.rule.RuleMetaData;
@@ -32,4 +33,8 @@ public class SystemRuleManager extends RuleManager { @@ -32,4 +33,8 @@ public class SystemRuleManager extends RuleManager {
32 return ruleService::findSystemRules; 33 return ruleService::findSystemRules;
33 } 34 }
34 35
  36 + @Override
  37 + String getDispatcherName() {
  38 + return DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME;
  39 + }
35 } 40 }
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.actors.shared.rule; 16 package org.thingsboard.server.actors.shared.rule;
17 17
18 import org.thingsboard.server.actors.ActorSystemContext; 18 import org.thingsboard.server.actors.ActorSystemContext;
  19 +import org.thingsboard.server.actors.service.DefaultActorService;
19 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; 21 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
21 import org.thingsboard.server.common.data.rule.RuleMetaData; 22 import org.thingsboard.server.common.data.rule.RuleMetaData;
@@ -31,4 +32,9 @@ public class TenantRuleManager extends RuleManager { @@ -31,4 +32,9 @@ public class TenantRuleManager extends RuleManager {
31 return link -> ruleService.findTenantRules(tenantId, link); 32 return link -> ruleService.findTenantRules(tenantId, link);
32 } 33 }
33 34
  35 + @Override
  36 + String getDispatcherName() {
  37 + return DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
  38 + }
  39 +
34 } 40 }
@@ -92,7 +92,53 @@ core-dispatcher { @@ -92,7 +92,53 @@ core-dispatcher {
92 throughput = 5 92 throughput = 5
93 } 93 }
94 94
95 -# This dispatcher is used for rule actors 95 +# This dispatcher is used for system rule actors
  96 +system-rule-dispatcher {
  97 + type = Dispatcher
  98 + executor = "fork-join-executor"
  99 + fork-join-executor {
  100 + # Min number of threads to cap factor-based parallelism number to
  101 + parallelism-min = 2
  102 + # Max number of threads to cap factor-based parallelism number to
  103 + parallelism-max = 12
  104 +
  105 + # The parallelism factor is used to determine thread pool size using the
  106 + # following formula: ceil(available processors * factor). Resulting size
  107 + # is then bounded by the parallelism-min and parallelism-max values.
  108 + parallelism-factor = 0.25
  109 + }
  110 + # How long time the dispatcher will wait for new actors until it shuts down
  111 + shutdown-timeout = 1s
  112 +
  113 + # Throughput defines the number of messages that are processed in a batch
  114 + # before the thread is returned to the pool. Set to 1 for as fair as possible.
  115 + throughput = 5
  116 +}
  117 +
  118 +# This dispatcher is used for system plugin actors
  119 +system-plugin-dispatcher {
  120 + type = Dispatcher
  121 + executor = "fork-join-executor"
  122 + fork-join-executor {
  123 + # Min number of threads to cap factor-based parallelism number to
  124 + parallelism-min = 2
  125 + # Max number of threads to cap factor-based parallelism number to
  126 + parallelism-max = 12
  127 +
  128 + # The parallelism factor is used to determine thread pool size using the
  129 + # following formula: ceil(available processors * factor). Resulting size
  130 + # is then bounded by the parallelism-min and parallelism-max values.
  131 + parallelism-factor = 0.25
  132 + }
  133 + # How long time the dispatcher will wait for new actors until it shuts down
  134 + shutdown-timeout = 1s
  135 +
  136 + # Throughput defines the number of messages that are processed in a batch
  137 + # before the thread is returned to the pool. Set to 1 for as fair as possible.
  138 + throughput = 5
  139 +}
  140 +
  141 +# This dispatcher is used for tenant rule actors
96 rule-dispatcher { 142 rule-dispatcher {
97 type = Dispatcher 143 type = Dispatcher
98 executor = "fork-join-executor" 144 executor = "fork-join-executor"
@@ -115,7 +161,7 @@ rule-dispatcher { @@ -115,7 +161,7 @@ rule-dispatcher {
115 throughput = 5 161 throughput = 5
116 } 162 }
117 163
118 -# This dispatcher is used for rule actors 164 +# This dispatcher is used for tenant plugin actors
119 plugin-dispatcher { 165 plugin-dispatcher {
120 type = Dispatcher 166 type = Dispatcher
121 executor = "fork-join-executor" 167 executor = "fork-join-executor"
@@ -16,10 +16,7 @@ @@ -16,10 +16,7 @@
16 package org.thingsboard.server.extensions.api.plugins; 16 package org.thingsboard.server.extensions.api.plugins;
17 17
18 import org.thingsboard.server.common.data.Device; 18 import org.thingsboard.server.common.data.Device;
19 -import org.thingsboard.server.common.data.id.CustomerId;  
20 -import org.thingsboard.server.common.data.id.DeviceId;  
21 -import org.thingsboard.server.common.data.id.PluginId;  
22 -import org.thingsboard.server.common.data.id.TenantId; 19 +import org.thingsboard.server.common.data.id.*;
23 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 20 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
24 import org.thingsboard.server.common.data.kv.TsKvEntry; 21 import org.thingsboard.server.common.data.kv.TsKvEntry;
25 import org.thingsboard.server.common.data.kv.TsKvQuery; 22 import org.thingsboard.server.common.data.kv.TsKvQuery;
@@ -46,6 +43,8 @@ public interface PluginContext { @@ -46,6 +43,8 @@ public interface PluginContext {
46 43
47 Optional<PluginApiCallSecurityContext> getSecurityCtx(); 44 Optional<PluginApiCallSecurityContext> getSecurityCtx();
48 45
  46 + void persistError(String method, Exception e);
  47 +
49 /* 48 /*
50 Device RPC API 49 Device RPC API
51 */ 50 */
@@ -33,6 +33,9 @@ import org.thingsboard.server.extensions.core.action.mail.SendMailActionMsg; @@ -33,6 +33,9 @@ import org.thingsboard.server.extensions.core.action.mail.SendMailActionMsg;
33 import javax.mail.MessagingException; 33 import javax.mail.MessagingException;
34 import javax.mail.internet.MimeMessage; 34 import javax.mail.internet.MimeMessage;
35 import java.util.Properties; 35 import java.util.Properties;
  36 +import java.util.concurrent.Executor;
  37 +import java.util.concurrent.ExecutorService;
  38 +import java.util.concurrent.Executors;
36 39
37 /** 40 /**
38 * @author Andrew Shvayka 41 * @author Andrew Shvayka
@@ -41,6 +44,9 @@ import java.util.Properties; @@ -41,6 +44,9 @@ import java.util.Properties;
41 @Slf4j 44 @Slf4j
42 public class MailPlugin extends AbstractPlugin<MailPluginConfiguration> implements RuleMsgHandler { 45 public class MailPlugin extends AbstractPlugin<MailPluginConfiguration> implements RuleMsgHandler {
43 46
  47 + //TODO: Add logic to close this executor on shutdown.
  48 + private static final ExecutorService executor = Executors.newSingleThreadExecutor();
  49 +
44 private MailPluginConfiguration configuration; 50 private MailPluginConfiguration configuration;
45 private JavaMailSenderImpl mailSender; 51 private JavaMailSenderImpl mailSender;
46 52
@@ -84,12 +90,14 @@ public class MailPlugin extends AbstractPlugin<MailPluginConfiguration> implemen @@ -84,12 +90,14 @@ public class MailPlugin extends AbstractPlugin<MailPluginConfiguration> implemen
84 @Override 90 @Override
85 public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException { 91 public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException {
86 if (msg.getPayload() instanceof SendMailActionMsg) { 92 if (msg.getPayload() instanceof SendMailActionMsg) {
87 - try {  
88 - sendMail((SendMailActionMsg) msg.getPayload());  
89 - } catch (Exception e) {  
90 - log.warn("Failed to send email", e);  
91 - throw new RuleException("Failed to send email", e);  
92 - } 93 + executor.submit(() -> {
  94 + try {
  95 + sendMail((SendMailActionMsg) msg.getPayload());
  96 + } catch (Exception e) {
  97 + log.warn("[{}] Failed to send email", ctx.getPluginId(), e);
  98 + ctx.persistError("Failed to send email", e);
  99 + }
  100 + });
93 } else { 101 } else {
94 throw new RuntimeException("Not supported msg type: " + msg.getPayload().getClass() + "!"); 102 throw new RuntimeException("Not supported msg type: " + msg.getPayload().getClass() + "!");
95 } 103 }