Commit 6c1074a8b04767c652940aba3b3298f075727b89
1 parent
411c9dab
Fix for race condition in the partition change events
Showing
14 changed files
with
158 additions
and
45 deletions
@@ -34,6 +34,7 @@ import org.thingsboard.server.actors.app.AppInitMsg; | @@ -34,6 +34,7 @@ import org.thingsboard.server.actors.app.AppInitMsg; | ||
34 | import org.thingsboard.server.actors.stats.StatsActor; | 34 | import org.thingsboard.server.actors.stats.StatsActor; |
35 | import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; | 35 | import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; |
36 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | 36 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
37 | +import org.thingsboard.server.queue.discovery.TbApplicationEventListener; | ||
37 | 38 | ||
38 | import javax.annotation.PostConstruct; | 39 | import javax.annotation.PostConstruct; |
39 | import javax.annotation.PreDestroy; | 40 | import javax.annotation.PreDestroy; |
@@ -43,7 +44,7 @@ import java.util.concurrent.ScheduledExecutorService; | @@ -43,7 +44,7 @@ import java.util.concurrent.ScheduledExecutorService; | ||
43 | 44 | ||
44 | @Service | 45 | @Service |
45 | @Slf4j | 46 | @Slf4j |
46 | -public class DefaultActorService implements ActorService { | 47 | +public class DefaultActorService extends TbApplicationEventListener<PartitionChangeEvent> implements ActorService { |
47 | 48 | ||
48 | public static final String APP_DISPATCHER_NAME = "app-dispatcher"; | 49 | public static final String APP_DISPATCHER_NAME = "app-dispatcher"; |
49 | public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher"; | 50 | public static final String TENANT_DISPATCHER_NAME = "tenant-dispatcher"; |
@@ -120,10 +121,10 @@ public class DefaultActorService implements ActorService { | @@ -120,10 +121,10 @@ public class DefaultActorService implements ActorService { | ||
120 | appActor.tellWithHighPriority(new AppInitMsg()); | 121 | appActor.tellWithHighPriority(new AppInitMsg()); |
121 | } | 122 | } |
122 | 123 | ||
123 | - @EventListener(PartitionChangeEvent.class) | ||
124 | - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | 124 | + @Override |
125 | + protected void onTbApplicationEvent(PartitionChangeEvent event) { | ||
125 | log.info("Received partition change event."); | 126 | log.info("Received partition change event."); |
126 | - this.appActor.tellWithHighPriority(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions())); | 127 | + this.appActor.tellWithHighPriority(new PartitionChangeMsg(event.getServiceQueueKey(), event.getPartitions())); |
127 | } | 128 | } |
128 | 129 | ||
129 | @PreDestroy | 130 | @PreDestroy |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
@@ -54,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; | @@ -54,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; | ||
54 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 54 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
55 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | 55 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
56 | import org.thingsboard.server.queue.discovery.PartitionService; | 56 | import org.thingsboard.server.queue.discovery.PartitionService; |
57 | +import org.thingsboard.server.queue.discovery.TbApplicationEventListener; | ||
57 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; | 58 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; |
58 | import org.thingsboard.server.service.queue.TbClusterService; | 59 | import org.thingsboard.server.service.queue.TbClusterService; |
59 | import org.thingsboard.server.service.telemetry.InternalTelemetryService; | 60 | import org.thingsboard.server.service.telemetry.InternalTelemetryService; |
@@ -78,7 +79,7 @@ import java.util.stream.Collectors; | @@ -78,7 +79,7 @@ import java.util.stream.Collectors; | ||
78 | 79 | ||
79 | @Slf4j | 80 | @Slf4j |
80 | @Service | 81 | @Service |
81 | -public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | 82 | +public class DefaultTbApiUsageStateService extends TbApplicationEventListener<PartitionChangeEvent> implements TbApiUsageStateService { |
82 | 83 | ||
83 | public static final String HOURLY = "Hourly"; | 84 | public static final String HOURLY = "Hourly"; |
84 | public static final FutureCallback<Integer> VOID_CALLBACK = new FutureCallback<Integer>() { | 85 | public static final FutureCallback<Integer> VOID_CALLBACK = new FutureCallback<Integer>() { |
@@ -188,7 +189,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -188,7 +189,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
188 | } | 189 | } |
189 | 190 | ||
190 | @Override | 191 | @Override |
191 | - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | 192 | + protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { |
192 | if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { | 193 | if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { |
193 | myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); | 194 | myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); |
194 | otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); | 195 | otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); |
@@ -151,12 +151,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore | @@ -151,12 +151,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore | ||
151 | } | 151 | } |
152 | 152 | ||
153 | @Override | 153 | @Override |
154 | - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | ||
155 | - if (partitionChangeEvent.getServiceType().equals(getServiceType())) { | ||
156 | - log.info("Subscribing to partitions: {}", partitionChangeEvent.getPartitions()); | ||
157 | - this.mainConsumer.subscribe(partitionChangeEvent.getPartitions()); | 154 | + protected void onTbApplicationEvent(PartitionChangeEvent event) { |
155 | + if (event.getServiceType().equals(getServiceType())) { | ||
156 | + log.info("Subscribing to partitions: {}", event.getPartitions()); | ||
157 | + this.mainConsumer.subscribe(event.getPartitions()); | ||
158 | this.usageStatsConsumer.subscribe( | 158 | this.usageStatsConsumer.subscribe( |
159 | - partitionChangeEvent | 159 | + event |
160 | .getPartitions() | 160 | .getPartitions() |
161 | .stream() | 161 | .stream() |
162 | .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic())) | 162 | .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic())) |
@@ -140,11 +140,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< | @@ -140,11 +140,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< | ||
140 | } | 140 | } |
141 | 141 | ||
142 | @Override | 142 | @Override |
143 | - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | ||
144 | - if (partitionChangeEvent.getServiceType().equals(getServiceType())) { | ||
145 | - ServiceQueue serviceQueue = partitionChangeEvent.getServiceQueueKey().getServiceQueue(); | ||
146 | - log.info("[{}] Subscribing to partitions: {}", serviceQueue.getQueue(), partitionChangeEvent.getPartitions()); | ||
147 | - consumers.get(serviceQueue.getQueue()).subscribe(partitionChangeEvent.getPartitions()); | 143 | + protected void onTbApplicationEvent(PartitionChangeEvent event) { |
144 | + if (event.getServiceType().equals(getServiceType())) { | ||
145 | + ServiceQueue serviceQueue = event.getServiceQueueKey().getServiceQueue(); | ||
146 | + log.info("[{}] Subscribing to partitions: {}", serviceQueue.getQueue(), event.getPartitions()); | ||
147 | + consumers.get(serviceQueue.getQueue()).subscribe(event.getPartitions()); | ||
148 | } | 148 | } |
149 | } | 149 | } |
150 | 150 |
@@ -36,6 +36,7 @@ import org.thingsboard.server.queue.TbQueueConsumer; | @@ -36,6 +36,7 @@ import org.thingsboard.server.queue.TbQueueConsumer; | ||
36 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 36 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
37 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | 37 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
38 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | 38 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
39 | +import org.thingsboard.server.queue.discovery.TbApplicationEventListener; | ||
39 | import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | 40 | import org.thingsboard.server.service.apiusage.TbApiUsageStateService; |
40 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; | 41 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
41 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | 42 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
@@ -56,7 +57,7 @@ import java.util.function.Function; | @@ -56,7 +57,7 @@ import java.util.function.Function; | ||
56 | import java.util.stream.Collectors; | 57 | import java.util.stream.Collectors; |
57 | 58 | ||
58 | @Slf4j | 59 | @Slf4j |
59 | -public abstract class AbstractConsumerService<N extends com.google.protobuf.GeneratedMessageV3> implements ApplicationListener<PartitionChangeEvent> { | 60 | +public abstract class AbstractConsumerService<N extends com.google.protobuf.GeneratedMessageV3> extends TbApplicationEventListener<PartitionChangeEvent> { |
60 | 61 | ||
61 | protected volatile ExecutorService consumersExecutor; | 62 | protected volatile ExecutorService consumersExecutor; |
62 | protected volatile ExecutorService notificationsConsumerExecutor; | 63 | protected volatile ExecutorService notificationsConsumerExecutor; |
@@ -56,6 +56,7 @@ import org.thingsboard.server.dao.util.mapping.JacksonUtil; | @@ -56,6 +56,7 @@ import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
56 | import org.thingsboard.server.gen.transport.TransportProtos; | 56 | import org.thingsboard.server.gen.transport.TransportProtos; |
57 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | 57 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
58 | import org.thingsboard.server.queue.discovery.PartitionService; | 58 | import org.thingsboard.server.queue.discovery.PartitionService; |
59 | +import org.thingsboard.server.queue.discovery.TbApplicationEventListener; | ||
59 | import org.thingsboard.server.queue.util.TbCoreComponent; | 60 | import org.thingsboard.server.queue.util.TbCoreComponent; |
60 | import org.thingsboard.server.service.queue.TbClusterService; | 61 | import org.thingsboard.server.service.queue.TbClusterService; |
61 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | 62 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
@@ -90,7 +91,7 @@ import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; | @@ -90,7 +91,7 @@ import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; | ||
90 | @Service | 91 | @Service |
91 | @TbCoreComponent | 92 | @TbCoreComponent |
92 | @Slf4j | 93 | @Slf4j |
93 | -public class DefaultDeviceStateService implements DeviceStateService { | 94 | +public class DefaultDeviceStateService extends TbApplicationEventListener<PartitionChangeEvent> implements DeviceStateService { |
94 | 95 | ||
95 | public static final String ACTIVITY_STATE = "active"; | 96 | public static final String ACTIVITY_STATE = "active"; |
96 | public static final String LAST_CONNECT_TIME = "lastConnectTime"; | 97 | public static final String LAST_CONNECT_TIME = "lastConnectTime"; |
@@ -294,7 +295,7 @@ public class DefaultDeviceStateService implements DeviceStateService { | @@ -294,7 +295,7 @@ public class DefaultDeviceStateService implements DeviceStateService { | ||
294 | } | 295 | } |
295 | 296 | ||
296 | @Override | 297 | @Override |
297 | - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | 298 | + protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { |
298 | if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { | 299 | if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { |
299 | deduplicationExecutor.submit(partitionChangeEvent.getPartitions()); | 300 | deduplicationExecutor.submit(partitionChangeEvent.getPartitions()); |
300 | } | 301 | } |
@@ -48,6 +48,7 @@ import org.thingsboard.server.queue.TbQueueProducer; | @@ -48,6 +48,7 @@ import org.thingsboard.server.queue.TbQueueProducer; | ||
48 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 48 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
49 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | 49 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
50 | import org.thingsboard.server.queue.discovery.PartitionService; | 50 | import org.thingsboard.server.queue.discovery.PartitionService; |
51 | +import org.thingsboard.server.queue.discovery.TbApplicationEventListener; | ||
51 | import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; | 52 | import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
52 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; | 53 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
53 | import org.thingsboard.server.queue.util.TbCoreComponent; | 54 | import org.thingsboard.server.queue.util.TbCoreComponent; |
@@ -76,7 +77,7 @@ import java.util.function.Predicate; | @@ -76,7 +77,7 @@ import java.util.function.Predicate; | ||
76 | @Slf4j | 77 | @Slf4j |
77 | @TbCoreComponent | 78 | @TbCoreComponent |
78 | @Service | 79 | @Service |
79 | -public class DefaultSubscriptionManagerService implements SubscriptionManagerService { | 80 | +public class DefaultSubscriptionManagerService extends TbApplicationEventListener<PartitionChangeEvent> implements SubscriptionManagerService { |
80 | 81 | ||
81 | @Autowired | 82 | @Autowired |
82 | private AttributesService attrService; | 83 | private AttributesService attrService; |
@@ -178,7 +179,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer | @@ -178,7 +179,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer | ||
178 | } | 179 | } |
179 | 180 | ||
180 | @Override | 181 | @Override |
181 | - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | 182 | + protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { |
182 | if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { | 183 | if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { |
183 | Set<TopicPartitionInfo> removedPartitions = new HashSet<>(currentPartitions); | 184 | Set<TopicPartitionInfo> removedPartitions = new HashSet<>(currentPartitions); |
184 | removedPartitions.removeAll(partitionChangeEvent.getPartitions()); | 185 | removedPartitions.removeAll(partitionChangeEvent.getPartitions()); |
@@ -28,6 +28,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; | @@ -28,6 +28,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; | ||
28 | import org.thingsboard.server.common.msg.queue.ServiceType; | 28 | import org.thingsboard.server.common.msg.queue.ServiceType; |
29 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | 29 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
30 | import org.thingsboard.server.common.msg.queue.TbCallback; | 30 | import org.thingsboard.server.common.msg.queue.TbCallback; |
31 | +import org.thingsboard.server.queue.discovery.TbApplicationEventListener; | ||
31 | import org.thingsboard.server.queue.util.TbCoreComponent; | 32 | import org.thingsboard.server.queue.util.TbCoreComponent; |
32 | import org.thingsboard.server.service.queue.TbClusterService; | 33 | import org.thingsboard.server.service.queue.TbClusterService; |
33 | import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; | 34 | import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; |
@@ -62,6 +63,34 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer | @@ -62,6 +63,34 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer | ||
62 | private SubscriptionManagerService subscriptionManagerService; | 63 | private SubscriptionManagerService subscriptionManagerService; |
63 | 64 | ||
64 | private ExecutorService subscriptionUpdateExecutor; | 65 | private ExecutorService subscriptionUpdateExecutor; |
66 | + | ||
67 | + private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() { | ||
68 | + @Override | ||
69 | + protected void onTbApplicationEvent(PartitionChangeEvent event) { | ||
70 | + if (ServiceType.TB_CORE.equals(event.getServiceType())) { | ||
71 | + currentPartitions.clear(); | ||
72 | + currentPartitions.addAll(event.getPartitions()); | ||
73 | + } | ||
74 | + } | ||
75 | + }; | ||
76 | + | ||
77 | + private TbApplicationEventListener<ClusterTopologyChangeEvent> clusterTopologyChangeListener = new TbApplicationEventListener<>() { | ||
78 | + @Override | ||
79 | + protected void onTbApplicationEvent(ClusterTopologyChangeEvent event) { | ||
80 | + if (event.getServiceQueueKeys().stream().anyMatch(key -> ServiceType.TB_CORE.equals(key.getServiceType()))) { | ||
81 | + /* | ||
82 | + * If the cluster topology has changed, we need to push all current subscriptions to SubscriptionManagerService again. | ||
83 | + * Otherwise, the SubscriptionManagerService may "forget" those subscriptions in case of restart. | ||
84 | + * Although this is resource consuming operation, it is cheaper than sending ping/pong commands periodically | ||
85 | + * It is also cheaper then caching the subscriptions by entity id and then lookup of those caches every time we have new telemetry in SubscriptionManagerService. | ||
86 | + * Even if we cache locally the list of active subscriptions by entity id, it is still time consuming operation to get them from cache | ||
87 | + * Since number of subscriptions is usually much less then number of devices that are pushing data. | ||
88 | + */ | ||
89 | + subscriptionsBySessionId.values().forEach(map -> map.values() | ||
90 | + .forEach(sub -> pushSubscriptionToManagerService(sub, true))); | ||
91 | + } | ||
92 | + } | ||
93 | + }; | ||
65 | 94 | ||
66 | @PostConstruct | 95 | @PostConstruct |
67 | public void initExecutor() { | 96 | public void initExecutor() { |
@@ -77,28 +106,14 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer | @@ -77,28 +106,14 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer | ||
77 | 106 | ||
78 | @Override | 107 | @Override |
79 | @EventListener(PartitionChangeEvent.class) | 108 | @EventListener(PartitionChangeEvent.class) |
80 | - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | ||
81 | - if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { | ||
82 | - currentPartitions.clear(); | ||
83 | - currentPartitions.addAll(partitionChangeEvent.getPartitions()); | ||
84 | - } | 109 | + public void onApplicationEvent(PartitionChangeEvent event) { |
110 | + partitionChangeListener.onApplicationEvent(event); | ||
85 | } | 111 | } |
86 | 112 | ||
87 | @Override | 113 | @Override |
88 | @EventListener(ClusterTopologyChangeEvent.class) | 114 | @EventListener(ClusterTopologyChangeEvent.class) |
89 | public void onApplicationEvent(ClusterTopologyChangeEvent event) { | 115 | public void onApplicationEvent(ClusterTopologyChangeEvent event) { |
90 | - if (event.getServiceQueueKeys().stream().anyMatch(key -> ServiceType.TB_CORE.equals(key.getServiceType()))) { | ||
91 | - /* | ||
92 | - * If the cluster topology has changed, we need to push all current subscriptions to SubscriptionManagerService again. | ||
93 | - * Otherwise, the SubscriptionManagerService may "forget" those subscriptions in case of restart. | ||
94 | - * Although this is resource consuming operation, it is cheaper than sending ping/pong commands periodically | ||
95 | - * It is also cheaper then caching the subscriptions by entity id and then lookup of those caches every time we have new telemetry in SubscriptionManagerService. | ||
96 | - * Even if we cache locally the list of active subscriptions by entity id, it is still time consuming operation to get them from cache | ||
97 | - * Since number of subscriptions is usually much less then number of devices that are pushing data. | ||
98 | - */ | ||
99 | - subscriptionsBySessionId.values().forEach(map -> map.values() | ||
100 | - .forEach(sub -> pushSubscriptionToManagerService(sub, true))); | ||
101 | - } | 116 | + clusterTopologyChangeListener.onApplicationEvent(event); |
102 | } | 117 | } |
103 | 118 | ||
104 | //TODO 3.1: replace null callbacks with callbacks from websocket service. | 119 | //TODO 3.1: replace null callbacks with callbacks from websocket service. |
@@ -41,6 +41,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; | @@ -41,6 +41,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; | ||
41 | import org.thingsboard.server.gen.transport.TransportProtos; | 41 | import org.thingsboard.server.gen.transport.TransportProtos; |
42 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | 42 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
43 | import org.thingsboard.server.queue.discovery.PartitionService; | 43 | import org.thingsboard.server.queue.discovery.PartitionService; |
44 | +import org.thingsboard.server.queue.discovery.TbApplicationEventListener; | ||
44 | import org.thingsboard.server.service.queue.TbClusterService; | 45 | import org.thingsboard.server.service.queue.TbClusterService; |
45 | import org.thingsboard.server.service.subscription.SubscriptionManagerService; | 46 | import org.thingsboard.server.service.subscription.SubscriptionManagerService; |
46 | import org.thingsboard.server.service.subscription.TbSubscriptionUtils; | 47 | import org.thingsboard.server.service.subscription.TbSubscriptionUtils; |
@@ -61,7 +62,7 @@ import java.util.function.Consumer; | @@ -61,7 +62,7 @@ import java.util.function.Consumer; | ||
61 | * Created by ashvayka on 27.03.18. | 62 | * Created by ashvayka on 27.03.18. |
62 | */ | 63 | */ |
63 | @Slf4j | 64 | @Slf4j |
64 | -public abstract class AbstractSubscriptionService implements ApplicationListener<PartitionChangeEvent> { | 65 | +public abstract class AbstractSubscriptionService extends TbApplicationEventListener<PartitionChangeEvent>{ |
65 | 66 | ||
66 | protected final Set<TopicPartitionInfo> currentPartitions = ConcurrentHashMap.newKeySet(); | 67 | protected final Set<TopicPartitionInfo> currentPartitions = ConcurrentHashMap.newKeySet(); |
67 | 68 | ||
@@ -97,8 +98,7 @@ public abstract class AbstractSubscriptionService implements ApplicationListener | @@ -97,8 +98,7 @@ public abstract class AbstractSubscriptionService implements ApplicationListener | ||
97 | } | 98 | } |
98 | 99 | ||
99 | @Override | 100 | @Override |
100 | - @EventListener(PartitionChangeEvent.class) | ||
101 | - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | 101 | + protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { |
102 | if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { | 102 | if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { |
103 | currentPartitions.clear(); | 103 | currentPartitions.clear(); |
104 | currentPartitions.addAll(partitionChangeEvent.getPartitions()); | 104 | currentPartitions.addAll(partitionChangeEvent.getPartitions()); |
@@ -22,7 +22,9 @@ import org.thingsboard.server.common.msg.queue.ServiceQueueKey; | @@ -22,7 +22,9 @@ import org.thingsboard.server.common.msg.queue.ServiceQueueKey; | ||
22 | import java.util.Set; | 22 | import java.util.Set; |
23 | 23 | ||
24 | 24 | ||
25 | -public class ClusterTopologyChangeEvent extends ApplicationEvent { | 25 | +public class ClusterTopologyChangeEvent extends TbApplicationEvent { |
26 | + | ||
27 | + private static final long serialVersionUID = -2441739930040282254L; | ||
26 | 28 | ||
27 | @Getter | 29 | @Getter |
28 | private final Set<ServiceQueueKey> serviceQueueKeys; | 30 | private final Set<ServiceQueueKey> serviceQueueKeys; |
@@ -126,7 +126,7 @@ public class HashPartitionService implements PartitionService { | @@ -126,7 +126,7 @@ public class HashPartitionService implements PartitionService { | ||
126 | } | 126 | } |
127 | 127 | ||
128 | @Override | 128 | @Override |
129 | - public void recalculatePartitions(ServiceInfo currentService, List<ServiceInfo> otherServices) { | 129 | + public synchronized void recalculatePartitions(ServiceInfo currentService, List<ServiceInfo> otherServices) { |
130 | logServiceInfo(currentService); | 130 | logServiceInfo(currentService); |
131 | otherServices.forEach(this::logServiceInfo); | 131 | otherServices.forEach(this::logServiceInfo); |
132 | Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>(); | 132 | Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>(); |
@@ -134,7 +134,7 @@ public class HashPartitionService implements PartitionService { | @@ -134,7 +134,7 @@ public class HashPartitionService implements PartitionService { | ||
134 | for (ServiceInfo other : otherServices) { | 134 | for (ServiceInfo other : otherServices) { |
135 | addNode(queueServicesMap, other); | 135 | addNode(queueServicesMap, other); |
136 | } | 136 | } |
137 | - queueServicesMap.values().forEach(list -> list.sort((a, b) -> a.getServiceId().compareTo(b.getServiceId()))); | 137 | + queueServicesMap.values().forEach(list -> list.sort(Comparator.comparing(ServiceInfo::getServiceId))); |
138 | 138 | ||
139 | ConcurrentMap<ServiceQueueKey, List<Integer>> oldPartitions = myPartitions; | 139 | ConcurrentMap<ServiceQueueKey, List<Integer>> oldPartitions = myPartitions; |
140 | TenantId myIsolatedOrSystemTenantId = getSystemOrIsolatedTenantId(currentService); | 140 | TenantId myIsolatedOrSystemTenantId = getSystemOrIsolatedTenantId(currentService); |
@@ -24,7 +24,9 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | @@ -24,7 +24,9 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | ||
24 | import java.util.Set; | 24 | import java.util.Set; |
25 | 25 | ||
26 | 26 | ||
27 | -public class PartitionChangeEvent extends ApplicationEvent { | 27 | +public class PartitionChangeEvent extends TbApplicationEvent { |
28 | + | ||
29 | + private static final long serialVersionUID = -8731788167026510559L; | ||
28 | 30 | ||
29 | @Getter | 31 | @Getter |
30 | private final ServiceQueueKey serviceQueueKey; | 32 | private final ServiceQueueKey serviceQueueKey; |
common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEvent.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2021 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.queue.discovery; | ||
17 | + | ||
18 | +import lombok.Getter; | ||
19 | +import org.springframework.context.ApplicationEvent; | ||
20 | + | ||
21 | +import java.util.concurrent.atomic.AtomicInteger; | ||
22 | + | ||
23 | +public class TbApplicationEvent extends ApplicationEvent { | ||
24 | + | ||
25 | + private static final long serialVersionUID = 3884264064887765146L; | ||
26 | + | ||
27 | + private static final AtomicInteger sequence = new AtomicInteger(); | ||
28 | + | ||
29 | + @Getter | ||
30 | + private final int sequenceNumber; | ||
31 | + | ||
32 | + public TbApplicationEvent(Object source) { | ||
33 | + super(source); | ||
34 | + sequenceNumber = sequence.incrementAndGet(); | ||
35 | + } | ||
36 | + | ||
37 | +} |
common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2021 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.queue.discovery; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.springframework.context.ApplicationListener; | ||
20 | + | ||
21 | +import java.util.concurrent.locks.Lock; | ||
22 | +import java.util.concurrent.locks.ReentrantLock; | ||
23 | + | ||
24 | +@Slf4j | ||
25 | +public abstract class TbApplicationEventListener<T extends TbApplicationEvent> implements ApplicationListener<T> { | ||
26 | + | ||
27 | + private int lastProcessedSequenceNumber = Integer.MIN_VALUE; | ||
28 | + private final Lock seqNumberLock = new ReentrantLock(); | ||
29 | + | ||
30 | + @Override | ||
31 | + public void onApplicationEvent(T event) { | ||
32 | + boolean validUpdate = false; | ||
33 | + seqNumberLock.lock(); | ||
34 | + try { | ||
35 | + if (event.getSequenceNumber() > lastProcessedSequenceNumber) { | ||
36 | + validUpdate = true; | ||
37 | + lastProcessedSequenceNumber = event.getSequenceNumber(); | ||
38 | + } | ||
39 | + } finally { | ||
40 | + seqNumberLock.unlock(); | ||
41 | + } | ||
42 | + if (validUpdate) { | ||
43 | + onTbApplicationEvent(event); | ||
44 | + } else { | ||
45 | + log.info("Application event ignored due to invalid sequence number ({} > {}). Event: {}", lastProcessedSequenceNumber, event.getSequenceNumber(), event); | ||
46 | + } | ||
47 | + } | ||
48 | + | ||
49 | + protected abstract void onTbApplicationEvent(T event); | ||
50 | + | ||
51 | + | ||
52 | +} |