diff --git a/application/src/main/data/json/system/scada_symbols/leak-sensor.svg b/application/src/main/data/json/system/scada_symbols/leak-sensor.svg
new file mode 100644
index 0000000000..506385d7fb
--- /dev/null
+++ b/application/src/main/data/json/system/scada_symbols/leak-sensor.svg
@@ -0,0 +1,158 @@
+
\ No newline at end of file
diff --git a/application/src/main/data/json/system/scada_symbols/stand-filter.svg b/application/src/main/data/json/system/scada_symbols/stand-filter.svg
index 87d578605d..ebd1e28edd 100644
--- a/application/src/main/data/json/system/scada_symbols/stand-filter.svg
+++ b/application/src/main/data/json/system/scada_symbols/stand-filter.svg
@@ -7,7 +7,7 @@
],
"widgetSizeX": 3,
"widgetSizeY": 5,
- "stateRenderFunction": "",
+ "stateRenderFunction": "var running = ctx.values.running;\nif (running) {\n ctx.api.enable(svg.children());\n} else {\n ctx.api.disable(svg.children());\n}",
"tags": [
{
"tag": "background",
@@ -25,7 +25,7 @@
},
{
"tag": "filterMode",
- "stateRenderFunction": "var defaultBorderColor = ctx.properties.defaultBorderColor;\nvar activeBorderColor = ctx.properties.activeBorderColor;\nvar defaultLabelColor = ctx.properties.defaultLabelColor;\nvar activeLabelColor = ctx.properties.activeLabelColor;\nvar boxBackground = ctx.properties.modeBoxBackground;\n\nvar running = ctx.values.running;\nvar filtrationMode = ctx.values.filtrationMode;\n\nvar filtrationMap = {};\nvar bottomShift = 104;\nvar rightShift = 226;\nvar middleShift = 121;\n\nvar filterModeSet = element.remember('filterModeSet');\n\nif (!filterModeSet) {\n element.remember('filterModeSet', true);\n var clone = element.children()[0];\n setFilterModeColors(clone);\n element.clear();\n \n var i = 0;\n if (ctx.properties.filtrationMode) {\n i++;\n filtrationMap[i] = 'filter';\n }\n if (ctx.properties.wasteMode) {\n i++;\n filtrationMap[i] = 'waste';\n }\n if (ctx.properties.backwashMode) {\n i++;\n filtrationMap[i] = 'backwash';\n }\n if (ctx.properties.recirculateMode) {\n i++;\n filtrationMap[i] = 'recirculate';\n }\n if (ctx.properties.rinseMode) {\n i++;\n filtrationMap[i] = 'rinse';\n }\n if (ctx.properties.closedMode) {\n i++;\n filtrationMap[i] = 'closed';\n }\n \n var filterMode = Object.values(filtrationMap);\n var lastToMiddle = filterMode.length % 2;\n \n filterMode.forEach((mode, index, arr) => {\n var template = clone.clone();\n var x = (index % 2) * rightShift;\n var y = Math.floor((index % filterMode.length) / 2) * bottomShift;\n if (index === filterMode.length-1 && lastToMiddle) {\n x = middleShift;\n }\n template.attr({'class': mode}).css('cursor', 'pointer').translate(x, y);\n ctx.api.text(template.findOne('text'), capitalizeFirstLetter(mode));\n template.click((event) => click(event, getFilterModeKey(mode)));\n element.add(template);\n })\n if (isFinite(filtrationMode)) {\n setFilterModeColorsByMap(filtrationMode, running);\n }\n}\n\nif (running) {\n ctx.api.enable(element.children());\n} else {\n ctx.api.disable(element.children());\n}\n\nfunction click(event, index) {\n var filtrationMode = ctx.values.filtrationMode;\n if (ctx.values.running && isFinite(filtrationMode)) {\n ctx.api.disable(element.children());\n var newValue = +index;\n if (newValue === filtrationMode) {\n newValue = 0;\n } else {\n setFilterModeColorsByMap(filtrationMode);\n }\n ctx.api.setValue('filtrationMode', newValue);\n ctx.api.callAction(event, 'filtrationModeUpdateState', newValue, {\n next: () => {\n setFilterModeColorsByMap(newValue ? newValue: filtrationMode, newValue);\n ctx.api.enable(element.children());\n },\n error: () => {\n setFilterModeColorsByMap(newValue);\n ctx.api.setValue('filtrationMode', filtrationMode);\n }\n });\n }\n}\n\nfunction getFilterModeKey(value) {\n return Object.keys(filtrationMap).find(key => filtrationMap[key] === value);\n}\n\nfunction setFilterModeColorsByMap(mode, active = false) {\n var filterBox = element.findOne('g.'+filtrationMap[mode])\n if (filterBox) {\n return setFilterModeColors(filterBox, active);\n }\n}\n\nfunction setFilterModeColors(filterBox, active = false) {\n if (filterBox) {\n var borderColor = active ? activeBorderColor : defaultBorderColor;\n var labelColor = active ? activeLabelColor : defaultLabelColor;\n filterBox.children()[0].fill(boxBackground);\n filterBox.children()[1].stroke(borderColor);\n filterBox.children()[2].fill(borderColor);\n ctx.api.font(filterBox.findOne('text'), null, labelColor);\n }\n}\n\nfunction capitalizeFirstLetter(string) {\n return string.charAt(0).toUpperCase() + string.slice(1);\n}",
+ "stateRenderFunction": "var defaultBorderColor = ctx.properties.defaultBorderColor;\nvar activeBorderColor = ctx.properties.activeBorderColor;\nvar defaultLabelColor = ctx.properties.defaultLabelColor;\nvar activeLabelColor = ctx.properties.activeLabelColor;\nvar boxBackground = ctx.properties.modeBoxBackground;\n\nvar running = ctx.values.running;\nvar filtrationMode = ctx.values.filtrationMode;\n\nvar filtrationMap = {};\nvar bottomShift = 104;\nvar rightShift = 226;\nvar middleShift = 121;\n\nvar filterModeSet = element.remember('filterModeSet');\n\nvar i = 0;\nif (ctx.properties.filtrationMode) {\n i++;\n filtrationMap[i] = 'filter';\n}\nif (ctx.properties.wasteMode) {\n i++;\n filtrationMap[i] = 'waste';\n}\nif (ctx.properties.backwashMode) {\n i++;\n filtrationMap[i] = 'backwash';\n}\nif (ctx.properties.recirculateMode) {\n i++;\n filtrationMap[i] = 'recirculate';\n}\nif (ctx.properties.rinseMode) {\n i++;\n filtrationMap[i] = 'rinse';\n}\nif (ctx.properties.closedMode) {\n i++;\n filtrationMap[i] = 'closed';\n}\n\nif (!filterModeSet) {\n element.remember('filterModeSet', true);\n var clone = element.children()[0];\n setFilterModeColors(clone);\n element.clear();\n \n var filterMode = Object.values(filtrationMap);\n var lastToMiddle = filterMode.length % 2;\n \n filterMode.forEach((mode, index, arr) => {\n var template = clone.clone();\n var x = (index % 2) * rightShift;\n var y = Math.floor((index % filterMode.length) / 2) * bottomShift;\n if (index === filterMode.length-1 && lastToMiddle) {\n x = middleShift;\n }\n template.attr({'class': mode}).css('cursor', 'pointer').translate(x, y);\n ctx.api.text(template.findOne('text'), capitalizeFirstLetter(mode));\n template.click((event) => click(event, getFilterModeKey(mode)));\n element.add(template);\n })\n}\n\nif (isFinite(filtrationMode)) {\n if (element.findOne('.active')) {\n setFilterModeColors(element.findOne('.active'));\n }\n setFilterModeColorsByMap(filtrationMode, running);\n}\n\nfunction click(event, index) {\n var filtrationMode = ctx.values.filtrationMode;\n if (ctx.values.running && isFinite(filtrationMode)) {\n ctx.api.disable(element.children());\n var newValue = +index;\n if (newValue === filtrationMode) {\n newValue = 0;\n } else {\n setFilterModeColorsByMap(filtrationMode);\n }\n ctx.api.setValue('filtrationMode', newValue);\n ctx.api.callAction(event, 'filtrationModeUpdateState', newValue, {\n next: () => {\n setFilterModeColorsByMap(newValue ? newValue: filtrationMode, newValue);\n ctx.api.enable(element.children());\n },\n error: () => {\n setFilterModeColorsByMap(newValue);\n ctx.api.setValue('filtrationMode', filtrationMode);\n ctx.api.enable(element.children());\n }\n });\n }\n}\n\nfunction getFilterModeKey(value) {\n return Object.keys(filtrationMap).find(key => filtrationMap[key] === value);\n}\n\nfunction setFilterModeColorsByMap(mode, active = false) {\n var filterBox = element.findOne('g.'+filtrationMap[mode])\n if (filterBox) {\n return setFilterModeColors(filterBox, active);\n }\n}\n\nfunction setFilterModeColors(filterBox, active = false) {\n if (filterBox) {\n if (active) {\n filterBox.addClass('active');\n } else {\n filterBox.removeClass('active');\n }\n var borderColor = active ? activeBorderColor : defaultBorderColor;\n var labelColor = active ? activeLabelColor : defaultLabelColor;\n filterBox.children()[0].fill(boxBackground);\n filterBox.children()[1].stroke(borderColor);\n filterBox.children()[2].fill(borderColor);\n ctx.api.font(filterBox.findOne('text'), null, labelColor);\n }\n}\n\nfunction capitalizeFirstLetter(string) {\n return string.charAt(0).toUpperCase() + string.slice(1);\n}",
"actions": null
}
],
@@ -159,7 +159,7 @@
"id": "filtrationMode",
"name": "{i18n:scada.symbol.filter-mode}",
"type": "switch",
- "default": false,
+ "default": true,
"required": null,
"subLabel": null,
"divider": null,
@@ -175,7 +175,7 @@
"id": "wasteMode",
"name": "{i18n:scada.symbol.waste-mode}",
"type": "switch",
- "default": false,
+ "default": true,
"required": null,
"subLabel": null,
"divider": null,
@@ -191,7 +191,7 @@
"id": "backwashMode",
"name": "{i18n:scada.symbol.backwash-mode}",
"type": "switch",
- "default": false,
+ "default": true,
"required": null,
"subLabel": null,
"divider": null,
@@ -207,7 +207,7 @@
"id": "recirculateMode",
"name": "{i18n:scada.symbol.recirculate-mode}",
"type": "switch",
- "default": false,
+ "default": true,
"required": null,
"subLabel": null,
"divider": null,
@@ -223,7 +223,7 @@
"id": "rinseMode",
"name": "{i18n:scada.symbol.rinse-mode}",
"type": "switch",
- "default": false,
+ "default": true,
"required": null,
"subLabel": null,
"divider": null,
@@ -239,7 +239,7 @@
"id": "closedMode",
"name": "{i18n:scada.symbol.closed-mode}",
"type": "switch",
- "default": false,
+ "default": true,
"required": null,
"subLabel": null,
"divider": null,
diff --git a/application/src/main/data/json/system/scada_symbols/waterstop.svg b/application/src/main/data/json/system/scada_symbols/waterstop.svg
new file mode 100644
index 0000000000..e3fb963d71
--- /dev/null
+++ b/application/src/main/data/json/system/scada_symbols/waterstop.svg
@@ -0,0 +1,276 @@
+
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json b/application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json
index 181a7480ba..22575e0697 100644
--- a/application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json
+++ b/application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json
@@ -36,6 +36,7 @@
"vertical_inline_flow_meter",
"left_analog_water_level_meter",
"right_analog_water_level_meter",
+ "leak_sensor",
"centrifugal_pump",
"small_right_motor_pump",
"small_left_motor_pump",
@@ -52,6 +53,7 @@
"vertical_wheel_valve",
"horizontal_ball_valve",
"vertical_ball_valve",
+ "water_stop",
"vertical_tank",
"stand_vertical_tank",
"cylindrical_tank",
diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json
index a62550ed95..8030d3e731 100644
--- a/application/src/main/data/json/tenant/dashboards/gateways.json
+++ b/application/src/main/data/json/tenant/dashboards/gateways.json
@@ -200,7 +200,7 @@
"useShowWidgetActionFunction": null,
"showWidgetActionFunction": "return true;",
"type": "customPretty",
- "customHtml": "\n gateway.gateway-configuration
\n \n \n\n\n\n
\n\n",
+ "customHtml": "\n\n\n",
"customCss": ".gateway-config {\n width: 800px !important;\n padding: 0 !important;\n min-height: 75vh;\n max-width: 100%;\n display: grid !important;\n}\n\n@media screen and (max-width: 599px) {\n .mat-mdc-dialog-content {\n max-height: calc(100% - 60px) !important;\n }\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\n}\n\nfunction AddEntityDialogController(instance) {\n let vm = instance;\n \n vm.device = additionalParams.entity.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n",
"customResources": [],
diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
index 93cf5bd8c7..622812b327 100644
--- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
@@ -37,12 +37,10 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
-import org.thingsboard.server.common.msg.edge.EdgeSessionMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.ServiceType;
-import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
@@ -110,11 +108,6 @@ public class AppActor extends ContextAwareActor {
case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
onToDeviceActorMsg((TenantAwareMsg) msg, true);
break;
- case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
- case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG:
- case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG:
- onToEdgeSessionMsg((EdgeSessionMsg) msg);
- break;
case SESSION_TIMEOUT_MSG:
ctx.broadcastToChildrenByType(msg, EntityType.TENANT);
break;
@@ -206,20 +199,6 @@ public class AppActor extends ContextAwareActor {
() -> true));
}
- private void onToEdgeSessionMsg(EdgeSessionMsg msg) {
- TbActorRef target = null;
- if (ModelConstants.SYSTEM_TENANT.equals(msg.getTenantId())) {
- log.warn("Message has system tenant id: {}", msg);
- } else {
- target = getOrCreateTenantActor(msg.getTenantId()).orElse(null);
- }
- if (target != null) {
- target.tellWithHighPriority(msg);
- } else {
- log.debug("[{}] Invalid edge session msg: {}", msg.getTenantId(), msg);
- }
- }
-
@Override
public ProcessFailureStrategy onProcessFailure(TbActorMsg msg, Throwable t) {
log.error("Failed to process msg: {}", msg, t);
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index b34ddca1af..3e8cd9592f 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.common.msg.edge.EdgeHighPriorityMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg;
@@ -214,7 +215,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
log.debug("[{}][{}] device is related to edge: [{}]. Saving RPC request: [{}][{}] to edge queue", tenantId, deviceId, edgeId.getId(), rpcId, requestId);
try {
if (systemContext.getEdgeService().isEdgeActiveAsync(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE).get()) {
- saveRpcRequestToEdgeQueue(request, requestId).get();
+ saveRpcRequestToEdgeQueue(request, requestId);
} else {
log.error("[{}][{}][{}] Failed to save RPC request to edge queue {}. The Edge is currently offline or unreachable", tenantId, deviceId, edgeId.getId(), request);
}
@@ -921,7 +922,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
systemContext.getTbCoreToTransportService().process(nodeId, msg);
}
- private ListenableFuture saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
+ private void saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
ObjectNode body = JacksonUtil.newObjectNode();
body.put("requestId", requestId);
body.put("requestUUID", msg.getId().toString());
@@ -935,7 +936,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.DEVICE, EdgeEventActionType.RPC_CALL, deviceId, body);
- return systemContext.getEdgeEventService().saveAsync(edgeEvent);
+ systemContext.getClusterService().onEdgeHighPriorityMsg(new EdgeHighPriorityMsg(tenantId, edgeEvent));
}
void restoreSessions() {
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
index d55e08143b..5edecba31a 100644
--- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
@@ -33,9 +33,7 @@ import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -48,14 +46,12 @@ import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
-import org.thingsboard.server.common.msg.edge.EdgeSessionMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.rule.engine.DeviceDeleteMsg;
-import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import java.util.HashSet;
@@ -163,11 +159,6 @@ public class TenantActor extends RuleChainManagerActor {
case RULE_CHAIN_TO_RULE_CHAIN_MSG:
onRuleChainMsg((RuleChainAwareMsg) msg);
break;
- case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
- case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG:
- case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG:
- onToEdgeSessionMsg((EdgeSessionMsg) msg);
- break;
default:
return false;
}
@@ -270,18 +261,6 @@ public class TenantActor extends RuleChainManagerActor {
initRuleChains();
}
}
- if (msg.getEntityId().getEntityType() == EntityType.EDGE && isCore) {
- EdgeId edgeId = new EdgeId(msg.getEntityId().getId());
- EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService();
- if (edgeRpcService != null) {
- if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
- edgeRpcService.deleteEdge(tenantId, edgeId);
- } else if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) {
- Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId);
- edgeRpcService.updateEdge(tenantId, edge);
- }
- }
- }
if (msg.getEntityId().getEntityType() == EntityType.DEVICE && ComponentLifecycleEvent.DELETED == msg.getEvent() && isMyPartition(msg.getEntityId())) {
DeviceId deviceId = (DeviceId) msg.getEntityId();
onToDeviceActorMsg(new DeviceDeleteMsg(tenantId, deviceId), true);
@@ -311,10 +290,6 @@ public class TenantActor extends RuleChainManagerActor {
() -> true);
}
- private void onToEdgeSessionMsg(EdgeSessionMsg msg) {
- systemContext.getEdgeRpcService().onToEdgeSessionMsg(tenantId, msg);
- }
-
private ApiUsageState getApiUsageState() {
if (apiUsageState == null) {
apiUsageState = new ApiUsageState(systemContext.getApiUsageStateService().getApiUsageState(tenantId));
diff --git a/application/src/main/java/org/thingsboard/server/config/RequestSizeFilter.java b/application/src/main/java/org/thingsboard/server/config/RequestSizeFilter.java
deleted file mode 100644
index c2be2ed027..0000000000
--- a/application/src/main/java/org/thingsboard/server/config/RequestSizeFilter.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Copyright © 2016-2024 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.config;
-
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import org.springframework.util.AntPathMatcher;
-import org.springframework.web.filter.OncePerRequestFilter;
-import org.thingsboard.server.common.msg.tools.MaxPayloadSizeExceededException;
-import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
-
-import java.io.IOException;
-import java.util.List;
-
-@Slf4j
-@Component
-@RequiredArgsConstructor
-public class RequestSizeFilter extends OncePerRequestFilter {
-
- private final List urls = List.of("/api/plugins/rpc/**", "/api/rpc/**");
- private final AntPathMatcher pathMatcher = new AntPathMatcher();
- private final ThingsboardErrorResponseHandler errorResponseHandler;
-
- @Value("${transport.http.max_payload_size:65536}")
- private int maxPayloadSize;
-
- @Override
- public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
- if (request.getContentLength() > maxPayloadSize) {
- if (log.isDebugEnabled()) {
- log.debug("Too large payload size. Url: {}, client ip: {}, content length: {}", request.getRequestURL(),
- request.getRemoteAddr(), request.getContentLength());
- }
- errorResponseHandler.handle(new MaxPayloadSizeExceededException(), response);
- return;
- }
- chain.doFilter(request, response);
- }
-
- @Override
- protected boolean shouldNotFilter(HttpServletRequest request) {
- for (String url : urls) {
- if (pathMatcher.match(url, request.getRequestURI())) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- protected boolean shouldNotFilterAsyncDispatch() {
- return false;
- }
-
- @Override
- protected boolean shouldNotFilterErrorDispatch() {
- return false;
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
index 77ad1a1b9e..55b89b05f4 100644
--- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
@@ -56,6 +57,7 @@ import org.thingsboard.server.service.security.auth.oauth2.HttpCookieOAuth2Autho
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider;
import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter;
import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter;
+import org.thingsboard.server.transport.http.config.PayloadSizeFilter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -82,6 +84,9 @@ public class ThingsboardSecurityConfiguration {
public static final String MAIL_OAUTH2_PROCESSING_ENTRY_POINT = "/api/admin/mail/oauth2/code";
public static final String DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT = "/api/device-connectivity/mqtts/certificate/download";
+ @Value("${server.http.max_payload_size:/api/image*/**=52428800;/api/resource/**=52428800;/api/**=16777216}")
+ private String maxPayloadSizeConfig;
+
@Autowired
private ThingsboardErrorResponseHandler restAccessDeniedHandler;
@@ -124,8 +129,10 @@ public class ThingsboardSecurityConfiguration {
@Autowired
private RateLimitProcessingFilter rateLimitProcessingFilter;
- @Autowired
- private RequestSizeFilter requestSizeFilter;
+ @Bean
+ protected PayloadSizeFilter payloadSizeFilter() {
+ return new PayloadSizeFilter(maxPayloadSizeConfig);
+ }
@Bean
protected FilterRegistrationBean buildEtagFilter() throws Exception {
@@ -214,7 +221,6 @@ public class ThingsboardSecurityConfiguration {
.authorizeHttpRequests(config -> config
.requestMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points (webjars included)
.requestMatchers(
- DEVICE_API_ENTRY_POINT, // Device HTTP Transport API
FORM_BASED_LOGIN_ENTRY_POINT, // Login end-point
PUBLIC_LOGIN_ENTRY_POINT, // Public login end-point
TOKEN_REFRESH_ENTRY_POINT, // Token refresh end-point
@@ -228,7 +234,7 @@ public class ThingsboardSecurityConfiguration {
.addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
- .addFilterBefore(requestSizeFilter, UsernamePasswordAuthenticationFilter.class)
+ .addFilterBefore(payloadSizeFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class);
if (oauth2Configuration != null) {
http.oauth2Login(login -> login
diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
index 26c30f85d1..2cb8eb7378 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
@@ -478,8 +478,7 @@ public class EdgeController extends BaseController {
edgeId = checkNotNull(edgeId);
SecurityUser user = getCurrentUser();
TenantId tenantId = user.getTenantId();
- ToEdgeSyncRequest request = new ToEdgeSyncRequest(UUID.randomUUID(), tenantId, edgeId);
- edgeRpcServiceOpt.get().processSyncRequest(request, fromEdgeSyncResponse -> reply(response, fromEdgeSyncResponse));
+ edgeRpcServiceOpt.get().processSyncRequest(tenantId, edgeId, fromEdgeSyncResponse -> reply(response, fromEdgeSyncResponse));
} else {
throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
index 261c4705a9..8a8055c4af 100644
--- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
+++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
@@ -75,7 +75,7 @@ public class OAuth2Controller extends BaseController {
@ApiOperation(value = "Get OAuth2 clients (getOAuth2Clients)", notes = "Get the list of OAuth2 clients " +
"to log in with, available for such domain scheme (HTTP or HTTPS) (if x-forwarded-proto request header is present - " +
"the scheme is known from it) and domain name and port (port may be known from x-forwarded-port header)")
- @PostMapping(value = "/noauth/oauth2/client")
+ @PostMapping(value = "/noauth/oauth2Clients")
public List getOAuth2Clients(HttpServletRequest request,
@Parameter(description = "Mobile application package name, to find OAuth2 clients " +
"where there is configured mobile application with such package name")
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
deleted file mode 100644
index 740b19e5d8..0000000000
--- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/**
- * Copyright © 2016-2024 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.edge;
-
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import jakarta.annotation.PostConstruct;
-import jakarta.annotation.PreDestroy;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.stereotype.Service;
-import org.thingsboard.common.util.JacksonUtil;
-import org.thingsboard.common.util.ThingsBoardExecutors;
-import org.thingsboard.server.common.data.audit.ActionType;
-import org.thingsboard.server.common.data.edge.Edge;
-import org.thingsboard.server.common.data.edge.EdgeEventType;
-import org.thingsboard.server.common.data.id.RuleChainId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.msg.queue.TbCallback;
-import org.thingsboard.server.dao.edge.EdgeService;
-import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
-import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.queue.util.TbCoreComponent;
-import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.asset.profile.AssetProfileEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.customer.CustomerEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.edge.EdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.notification.NotificationEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.oauth2.OAuth2EdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantProfileEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor;
-import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor;
-
-import java.util.UUID;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.TimeUnit;
-
-@Service
-@TbCoreComponent
-@Slf4j
-public class DefaultEdgeNotificationService implements EdgeNotificationService {
-
- public static final String EDGE_IS_ROOT_BODY_KEY = "isRoot";
-
- @Autowired
- private EdgeService edgeService;
-
- @Autowired
- private EdgeProcessor edgeProcessor;
-
- @Autowired
- private AssetEdgeProcessor assetProcessor;
-
- @Autowired
- private AssetProfileEdgeProcessor assetProfileEdgeProcessor;
-
- @Autowired
- private DeviceEdgeProcessor deviceProcessor;
-
- @Autowired
- private DeviceProfileEdgeProcessor deviceProfileEdgeProcessor;
-
- @Autowired
- private EntityViewEdgeProcessor entityViewProcessor;
-
- @Autowired
- private DashboardEdgeProcessor dashboardProcessor;
-
- @Autowired
- private RuleChainEdgeProcessor ruleChainProcessor;
-
- @Autowired
- private UserEdgeProcessor userProcessor;
-
- @Autowired
- private CustomerEdgeProcessor customerProcessor;
-
- @Autowired
- private OtaPackageEdgeProcessor otaPackageProcessor;
-
- @Autowired
- private WidgetBundleEdgeProcessor widgetBundleProcessor;
-
- @Autowired
- private WidgetTypeEdgeProcessor widgetTypeProcessor;
-
- @Autowired
- private QueueEdgeProcessor queueProcessor;
-
- @Autowired
- private TenantEdgeProcessor tenantEdgeProcessor;
-
- @Autowired
- private TenantProfileEdgeProcessor tenantProfileEdgeProcessor;
-
- @Autowired
- private AlarmEdgeProcessor alarmProcessor;
-
- @Autowired
- private RelationEdgeProcessor relationProcessor;
-
- @Autowired
- private ResourceEdgeProcessor resourceEdgeProcessor;
-
- @Autowired
- private NotificationEdgeProcessor notificationEdgeProcessor;
-
- @Autowired
- private OAuth2EdgeProcessor oAuth2EdgeProcessor;
-
- @Autowired
- protected ApplicationEventPublisher eventPublisher;
-
- @Value("${actors.system.edge_dispatcher_pool_size:4}")
- private int edgeDispatcherSize;
-
- private ExecutorService executor;
-
- @PostConstruct
- public void initExecutor() {
- executor = ThingsBoardExecutors.newWorkStealingPool(edgeDispatcherSize, "edge-notifications");
- }
-
- @PreDestroy
- public void shutdownExecutor() {
- if (executor != null) {
- executor.shutdownNow();
- }
- }
-
- @Override
- public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) {
- edge.setRootRuleChainId(ruleChainId);
- Edge savedEdge = edgeService.saveEdge(edge);
- ObjectNode isRootBody = JacksonUtil.newObjectNode();
- isRootBody.put(EDGE_IS_ROOT_BODY_KEY, Boolean.TRUE);
- eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).edgeId(edge.getId()).entityId(ruleChainId)
- .body(JacksonUtil.toString(isRootBody)).actionType(ActionType.UPDATED).build());
- return savedEdge;
- }
-
- @Override
- public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
- TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
- log.debug("[{}] Pushing notification to edge {}", tenantId, edgeNotificationMsg);
- final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(60);
- try {
- executor.submit(() -> {
- try {
- if (deadline < System.nanoTime()) {
- log.warn("[{}] Skipping notification message because deadline reached {}", tenantId, edgeNotificationMsg);
- return;
- }
- EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
- switch (type) {
- case EDGE -> edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg);
- case ASSET -> assetProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case ASSET_PROFILE -> assetProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case DEVICE -> deviceProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case DEVICE_PROFILE -> deviceProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case ENTITY_VIEW -> entityViewProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case DASHBOARD -> dashboardProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case RULE_CHAIN -> ruleChainProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case USER -> userProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case CUSTOMER -> customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg);
- case OTA_PACKAGE -> otaPackageProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case WIDGETS_BUNDLE -> widgetBundleProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case WIDGET_TYPE -> widgetTypeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case QUEUE -> queueProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case ALARM -> alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg);
- case ALARM_COMMENT -> alarmProcessor.processAlarmCommentNotification(tenantId, edgeNotificationMsg);
- case RELATION -> relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg);
- case TENANT -> tenantEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case TENANT_PROFILE -> tenantProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case NOTIFICATION_RULE, NOTIFICATION_TARGET, NOTIFICATION_TEMPLATE ->
- notificationEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case TB_RESOURCE -> resourceEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- case DOMAIN, OAUTH2_CLIENT -> oAuth2EdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
- default -> log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type);
- }
- } catch (Exception e) {
- callBackFailure(tenantId, edgeNotificationMsg, callback, e);
- }
- });
- callback.onSuccess();
- } catch (Exception e) {
- callBackFailure(tenantId, edgeNotificationMsg, callback, e);
- }
- }
-
- private void callBackFailure(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback, Throwable throwable) {
- log.error("[{}] Can't push to edge updates, edgeNotificationMsg [{}]", tenantId, edgeNotificationMsg, throwable);
- callback.onFailure(throwable);
- }
-
-}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
index 2d9fa01796..f0f41e3083 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
@@ -48,6 +48,7 @@ import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings;
+import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor;
import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorFactory;
@@ -97,6 +98,9 @@ public class EdgeContextComponent {
@Autowired
private EdgeService edgeService;
+ @Autowired(required = false)
+ private EdgeRpcService edgeRpcService;
+
@Autowired
private EdgeEventService edgeEventService;
@@ -224,19 +228,19 @@ public class EdgeContextComponent {
private AdminSettingsEdgeProcessor adminSettingsProcessor;
@Autowired
- private OtaPackageEdgeProcessor otaPackageEdgeProcessor;
+ private OtaPackageEdgeProcessor otaPackageProcessor;
@Autowired
- private QueueEdgeProcessor queueEdgeProcessor;
+ private QueueEdgeProcessor queueProcessor;
@Autowired
- private TenantEdgeProcessor tenantEdgeProcessor;
+ private TenantEdgeProcessor tenantProcessor;
@Autowired
- private TenantProfileEdgeProcessor tenantProfileEdgeProcessor;
+ private TenantProfileEdgeProcessor tenantProfileProcessor;
@Autowired
- private ResourceEdgeProcessor resourceEdgeProcessor;
+ private ResourceEdgeProcessor resourceProcessor;
@Autowired
private NotificationEdgeProcessor notificationEdgeProcessor;
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java
index 789320e352..840750a781 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java
@@ -130,8 +130,7 @@ public class EdgeEventSourcingListener {
@TransactionalEventListener(fallbackExecution = true)
public void handleEvent(ActionEntityEvent> event) {
- if (EntityType.DEVICE.equals(event.getEntityId().getEntityType())
- && ActionType.ASSIGNED_TO_TENANT.equals(event.getActionType())) {
+ if (EntityType.DEVICE.equals(event.getEntityId().getEntityType()) && ActionType.ASSIGNED_TO_TENANT.equals(event.getActionType())) {
return;
}
try {
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java
index 64f96aa4e4..7c8489067c 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java
@@ -94,4 +94,5 @@ public class DefaultEdgeInstallInstructionsService extends BaseEdgeInstallUpgrad
protected String getBaseDirName() {
return INSTALL_DIR;
}
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java
index 275de6bafe..2f783c98ab 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java
@@ -74,7 +74,8 @@ public class DefaultEdgeUpgradeInstructionsService extends BaseEdgeInstallUpgrad
Optional attributeKvEntryOpt = attributesService.find(tenantId, edgeId, AttributeScope.SERVER_SCOPE, DataConstants.EDGE_VERSION_ATTR_KEY).get();
if (attributeKvEntryOpt.isPresent()) {
String edgeVersionFormatted = convertEdgeVersionToDocsFormat(attributeKvEntryOpt.get().getValueAsString());
- return isVersionGreaterOrEqualsThan(edgeVersionFormatted, "3.6.0") && !isVersionGreaterOrEqualsThan(edgeVersionFormatted, appVersion);
+ String appVersionFormatted = appVersion.replace("-SNAPSHOT", "");
+ return isVersionGreaterOrEqualsThan(edgeVersionFormatted, "3.6.0") && !isVersionGreaterOrEqualsThan(edgeVersionFormatted, appVersionFormatted);
}
return false;
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java
index 2517b615a8..47bde24bc7 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java
@@ -31,4 +31,5 @@ public interface EdgeUpgradeInstructionsService {
void setAppVersion(String version);
boolean isUpgradeAvailable(TenantId tenantId, EdgeId edgeId) throws Exception;
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
index 46bf3f69a4..5d45e1c3d5 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
@@ -28,14 +28,17 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
+import org.thingsboard.server.cache.TbTransactionalCache;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@@ -47,12 +50,14 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
+import org.thingsboard.server.common.msg.edge.EdgeHighPriorityMsg;
import org.thingsboard.server.common.msg.edge.EdgeSessionMsg;
import org.thingsboard.server.common.msg.edge.FromEdgeSyncResponse;
import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest;
import org.thingsboard.server.gen.edge.v1.EdgeRpcServiceGrpc;
import org.thingsboard.server.gen.edge.v1.RequestMsg;
import org.thingsboard.server.gen.edge.v1.ResponseMsg;
+import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.service.state.DefaultDeviceStateService;
@@ -84,7 +89,6 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private final ConcurrentMap sessionNewEventsLocks = new ConcurrentHashMap<>();
private final Map sessionNewEvents = new HashMap<>();
private final ConcurrentMap> sessionEdgeEventChecks = new ConcurrentHashMap<>();
-
private final ConcurrentMap> localSyncEdgeRequests = new ConcurrentHashMap<>();
@Value("${edges.rpc.port}")
@@ -111,7 +115,11 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
@Value("${edges.send_scheduler_pool_size}")
private int sendSchedulerPoolSize;
+ @Value("${edges.max_high_priority_queue_size_per_session:10000}")
+ private int maxHighPriorityQueueSizePerSession;
+
@Autowired
+ @Lazy
private EdgeContextComponent ctx;
@Autowired
@@ -120,6 +128,12 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
@Autowired
private TbClusterService clusterService;
+ @Autowired
+ private TbServiceInfoProvider serviceInfoProvider;
+
+ @Autowired
+ private TbTransactionalCache edgeIdServiceIdCache;
+
private Server server;
private ScheduledExecutorService edgeEventProcessingExecutorService;
@@ -188,79 +202,100 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
@Override
public StreamObserver handleMsgs(StreamObserver outputStream) {
- return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, sendDownlinkExecutorService, this.maxInboundMessageSize).getInputStream();
+ return new EdgeGrpcSession(ctx,
+ outputStream,
+ this::onEdgeConnect,
+ this::onEdgeDisconnect,
+ sendDownlinkExecutorService,
+ this.maxInboundMessageSize,
+ this.maxHighPriorityQueueSizePerSession).getInputStream();
}
@Override
public void onToEdgeSessionMsg(TenantId tenantId, EdgeSessionMsg msg) {
- executorService.execute(() -> {
- switch (msg.getMsgType()) {
- case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
- EdgeEventUpdateMsg edgeEventUpdateMsg = (EdgeEventUpdateMsg) msg;
- log.trace("[{}] onToEdgeSessionMsg [{}]", tenantId, msg);
- onEdgeEvent(tenantId, edgeEventUpdateMsg.getEdgeId());
- break;
- case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG:
- ToEdgeSyncRequest toEdgeSyncRequest = (ToEdgeSyncRequest) msg;
- log.trace("[{}] toEdgeSyncRequest [{}]", tenantId, msg);
- startSyncProcess(tenantId, toEdgeSyncRequest.getEdgeId(), toEdgeSyncRequest.getId());
- break;
- case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG:
- FromEdgeSyncResponse fromEdgeSyncResponse = (FromEdgeSyncResponse) msg;
- log.trace("[{}] fromEdgeSyncResponse [{}]", tenantId, msg);
- processSyncResponse(fromEdgeSyncResponse);
- break;
+ switch (msg.getMsgType()) {
+ case EDGE_HIGH_PRIORITY_TO_EDGE_SESSION_MSG -> {
+ EdgeHighPriorityMsg edgeHighPriorityMsg = (EdgeHighPriorityMsg) msg;
+ log.trace("[{}] edgeEventMsg [{}]", tenantId, msg);
+ onEdgeHighPriorityEvent(edgeHighPriorityMsg);
}
- });
+ case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG -> {
+ EdgeEventUpdateMsg edgeEventUpdateMsg = (EdgeEventUpdateMsg) msg;
+ log.trace("[{}] onToEdgeEventUpdateMsg [{}]", tenantId, msg);
+ onEdgeEventUpdate(tenantId, edgeEventUpdateMsg.getEdgeId());
+ }
+ case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG -> {
+ ToEdgeSyncRequest toEdgeSyncRequest = (ToEdgeSyncRequest) msg;
+ log.trace("[{}] toEdgeSyncRequest [{}]", tenantId, msg);
+ startSyncProcess(tenantId, toEdgeSyncRequest.getEdgeId(), toEdgeSyncRequest.getId(), toEdgeSyncRequest.getServiceId());
+ }
+ case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG -> {
+ FromEdgeSyncResponse fromEdgeSyncResponse = (FromEdgeSyncResponse) msg;
+ log.trace("[{}] fromEdgeSyncResponse [{}]", tenantId, msg);
+ processSyncResponse(fromEdgeSyncResponse);
+ }
+ }
}
@Override
public void updateEdge(TenantId tenantId, Edge edge) {
- executorService.execute(() -> {
- EdgeGrpcSession session = sessions.get(edge.getId());
- if (session != null && session.isConnected()) {
- log.debug("[{}] Updating configuration for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
- session.onConfigurationUpdate(edge);
- } else {
- log.debug("[{}] Session doesn't exist for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
- }
- });
+ EdgeGrpcSession session = sessions.get(edge.getId());
+ if (session != null && session.isConnected()) {
+ log.debug("[{}] Updating configuration for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
+ session.onConfigurationUpdate(edge);
+ } else {
+ log.debug("[{}] Session doesn't exist for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
+ }
}
@Override
public void deleteEdge(TenantId tenantId, EdgeId edgeId) {
- executorService.execute(() -> {
- EdgeGrpcSession session = sessions.get(edgeId);
- if (session != null && session.isConnected()) {
- log.info("[{}] Closing and removing session for edge [{}]", tenantId, edgeId);
- session.close();
- sessions.remove(edgeId);
- final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
- newEventLock.lock();
- try {
- sessionNewEvents.remove(edgeId);
- } finally {
- newEventLock.unlock();
- }
- cancelScheduleEdgeEventsCheck(edgeId);
- }
- });
- }
-
- private void onEdgeEvent(TenantId tenantId, EdgeId edgeId) {
EdgeGrpcSession session = sessions.get(edgeId);
if (session != null && session.isConnected()) {
- log.trace("[{}] onEdgeEvent [{}]", tenantId, edgeId.getId());
+ log.info("[{}] Closing and removing session for edge [{}]", tenantId, edgeId);
+ session.close();
+ sessions.remove(edgeId);
final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
newEventLock.lock();
try {
- if (Boolean.FALSE.equals(sessionNewEvents.get(edgeId))) {
- log.trace("[{}] set session new events flag to true [{}]", tenantId, edgeId.getId());
- sessionNewEvents.put(edgeId, true);
- }
+ sessionNewEvents.remove(edgeId);
} finally {
newEventLock.unlock();
}
+ cancelScheduleEdgeEventsCheck(edgeId);
+ }
+ }
+
+ private void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId) {
+ EdgeGrpcSession session = sessions.get(edgeId);
+ if (session != null && session.isConnected()) {
+ log.trace("[{}] onEdgeEventUpdate [{}]", tenantId, edgeId.getId());
+ updateSessionEventsFlag(tenantId, edgeId);
+ }
+ }
+
+ private void onEdgeHighPriorityEvent(EdgeHighPriorityMsg msg) {
+ TenantId tenantId = msg.getTenantId();
+ EdgeEvent edgeEvent = msg.getEdgeEvent();
+ EdgeId edgeId = edgeEvent.getEdgeId();
+ EdgeGrpcSession session = sessions.get(edgeId);
+ if (session != null && session.isConnected()) {
+ log.trace("[{}] onEdgeEvent [{}]", tenantId, edgeId);
+ session.addEventToHighPriorityQueue(edgeEvent);
+ updateSessionEventsFlag(tenantId, edgeId);
+ }
+ }
+
+ private void updateSessionEventsFlag(TenantId tenantId, EdgeId edgeId) {
+ final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
+ newEventLock.lock();
+ try {
+ if (Boolean.FALSE.equals(sessionNewEvents.get(edgeId))) {
+ log.trace("[{}] set session new events flag to true [{}]", tenantId, edgeId.getId());
+ sessionNewEvents.put(edgeId, true);
+ }
+ } finally {
+ newEventLock.unlock();
}
}
@@ -279,37 +314,40 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
save(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true);
long lastConnectTs = System.currentTimeMillis();
save(tenantId, edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, lastConnectTs);
+ edgeIdServiceIdCache.put(edgeId, serviceInfoProvider.getServiceId());
pushRuleEngineMessage(tenantId, edge, lastConnectTs, TbMsgType.CONNECT_EVENT);
cancelScheduleEdgeEventsCheck(edgeId);
scheduleEdgeEventsCheck(edgeGrpcSession);
}
- private void startSyncProcess(TenantId tenantId, EdgeId edgeId, UUID requestId) {
+ private void startSyncProcess(TenantId tenantId, EdgeId edgeId, UUID requestId, String requestServiceId) {
EdgeGrpcSession session = sessions.get(edgeId);
if (session != null) {
if (!session.isSyncCompleted()) {
- clusterService.pushEdgeSyncResponseToCore(new FromEdgeSyncResponse(requestId, tenantId, edgeId, false, "Sync process is active at the moment"));
+ clusterService.pushEdgeSyncResponseToCore(new FromEdgeSyncResponse(requestId, tenantId, edgeId, false, "Sync process is active at the moment"), requestServiceId);
} else {
boolean success = false;
if (session.isConnected()) {
session.startSyncProcess(true);
success = true;
}
- clusterService.pushEdgeSyncResponseToCore(new FromEdgeSyncResponse(requestId, tenantId, edgeId, success, ""));
+ clusterService.pushEdgeSyncResponseToCore(new FromEdgeSyncResponse(requestId, tenantId, edgeId, success, ""), requestServiceId);
}
}
}
@Override
- public void processSyncRequest(ToEdgeSyncRequest request, Consumer responseConsumer) {
+ public void processSyncRequest(TenantId tenantId, EdgeId edgeId, Consumer responseConsumer) {
+ ToEdgeSyncRequest request = new ToEdgeSyncRequest(UUID.randomUUID(), tenantId, edgeId, serviceInfoProvider.getServiceId());
+
UUID requestId = request.getId();
EdgeGrpcSession session = sessions.get(request.getEdgeId());
if (session != null && !session.isSyncCompleted()) {
responseConsumer.accept(new FromEdgeSyncResponse(requestId, request.getTenantId(), request.getEdgeId(), false, "Sync process is active at the moment"));
} else {
- log.trace("[{}][{}] Processing sync edge request [{}]", request.getTenantId(), request.getId(), request.getEdgeId());
+ log.trace("[{}][{}] Processing sync edge request [{}], serviceId [{}]", request.getTenantId(), request.getId(), request.getEdgeId(), request.getServiceId());
localSyncEdgeRequests.put(requestId, responseConsumer);
- clusterService.pushEdgeSyncRequestToCore(request);
+ clusterService.pushEdgeSyncRequestToEdge(request);
scheduleSyncRequestTimeout(request, requestId);
}
}
@@ -415,6 +453,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
} else {
log.debug("[{}] edge session [{}] is not available anymore, nothing to remove. most probably this session is already outdated!", edgeId, sessionId);
}
+ edgeIdServiceIdCache.evict(edgeId);
}
private void save(TenantId tenantId, EdgeId edgeId, String key, long value) {
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
index c4e8f1ec7c..c397807c5f 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
@@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
@@ -96,6 +97,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@@ -106,6 +108,7 @@ import java.util.function.BiConsumer;
public final class EdgeGrpcSession implements Closeable {
private static final ReentrantLock downlinkMsgLock = new ReentrantLock();
+ private static final ConcurrentLinkedQueue highPriorityQueue = new ConcurrentLinkedQueue<>();
private static final int MAX_DOWNLINK_ATTEMPTS = 10; // max number of attemps to send downlink message if edge connected
@@ -125,7 +128,7 @@ public final class EdgeGrpcSession implements Closeable {
private StreamObserver inputStream;
private StreamObserver outputStream;
private boolean connected;
- private boolean syncCompleted;
+ private volatile boolean syncCompleted;
private Long newStartTs;
private Long previousStartTs;
@@ -137,11 +140,12 @@ public final class EdgeGrpcSession implements Closeable {
private int maxInboundMessageSize;
private int clientMaxInboundMessageSize;
+ private int maxHighPriorityQueueSizePerSession;
private ScheduledExecutorService sendDownlinkExecutorService;
EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver outputStream, BiConsumer sessionOpenListener,
- BiConsumer sessionCloseListener, ScheduledExecutorService sendDownlinkExecutorService, int maxInboundMessageSize) {
+ BiConsumer sessionCloseListener, ScheduledExecutorService sendDownlinkExecutorService, int maxInboundMessageSize, int maxHighPriorityQueueSizePerSession) {
this.sessionId = UUID.randomUUID();
this.ctx = ctx;
this.outputStream = outputStream;
@@ -149,6 +153,7 @@ public final class EdgeGrpcSession implements Closeable {
this.sessionCloseListener = sessionCloseListener;
this.sendDownlinkExecutorService = sendDownlinkExecutorService;
this.maxInboundMessageSize = maxInboundMessageSize;
+ this.maxHighPriorityQueueSizePerSession = maxHighPriorityQueueSizePerSession;
initInputStream();
}
@@ -223,7 +228,7 @@ public final class EdgeGrpcSession implements Closeable {
}
public void startSyncProcess(boolean fullSync) {
- log.trace("[{}][{}][{}] Staring edge sync process", this.tenantId, edge.getId(), this.sessionId);
+ log.info("[{}][{}][{}] Staring edge sync process", this.tenantId, edge.getId(), this.sessionId);
syncCompleted = false;
interruptGeneralProcessingOnSync();
doSync(new EdgeSyncCursor(ctx, edge, fullSync));
@@ -255,18 +260,23 @@ public final class EdgeGrpcSession implements Closeable {
Futures.addCallback(sendDownlinkMsgsPack(Collections.singletonList(syncCompleteDownlinkMsg)), new FutureCallback<>() {
@Override
public void onSuccess(Boolean isInterrupted) {
- syncCompleted = true;
- ctx.getClusterService().onEdgeEventUpdate(edge.getTenantId(), edge.getId());
+ markSyncCompletedSendEdgeEventUpdate();
}
@Override
public void onFailure(Throwable t) {
log.error("[{}][{}] Exception during sending sync complete", tenantId, edge.getId(), t);
+ markSyncCompletedSendEdgeEventUpdate();
}
}, ctx.getGrpcCallbackExecutorService());
}
}
+ private void markSyncCompletedSendEdgeEventUpdate() {
+ syncCompleted = true;
+ ctx.getClusterService().onEdgeEventUpdate(new EdgeEventUpdateMsg(edge.getTenantId(), edge.getId()));
+ }
+
private void onUplinkMsg(UplinkMsg uplinkMsg) {
if (isRateLimitViolated(uplinkMsg)) {
return;
@@ -429,6 +439,9 @@ public final class EdgeGrpcSession implements Closeable {
private void processEdgeEvents(EdgeEventFetcher fetcher, PageLink pageLink, SettableFuture> result) {
try {
+ if (!highPriorityQueue.isEmpty()) {
+ processHighPriorityEvents();
+ }
PageData pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink);
if (isConnected() && !pageData.getData().isEmpty()) {
log.trace("[{}][{}][{}] event(s) are going to be processed.", this.tenantId, this.sessionId, pageData.getData().size());
@@ -472,6 +485,20 @@ public final class EdgeGrpcSession implements Closeable {
}
}
+ private void processHighPriorityEvents() {
+ try {
+ List highPriorityEvents = new ArrayList<>();
+ EdgeEvent event;
+ while ((event = highPriorityQueue.poll()) != null) {
+ highPriorityEvents.add(event);
+ }
+ List downlinkMsgsPack = convertToDownlinkMsgsPack(highPriorityEvents);
+ sendDownlinkMsgsPack(downlinkMsgsPack).get();
+ } catch (Exception e) {
+ log.error("[{}] Failed to process high priority events", this.sessionId, e);
+ }
+ }
+
private ListenableFuture sendDownlinkMsgsPack(List downlinkMsgsPack) {
interruptPreviousSendDownlinkMsgsTask();
@@ -556,7 +583,9 @@ public final class EdgeGrpcSession implements Closeable {
DownlinkMsg downlinkMsg = null;
try {
switch (edgeEvent.getAction()) {
- case UPDATED, ADDED, DELETED, ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE, ALARM_ACK, ALARM_CLEAR, ALARM_DELETE, CREDENTIALS_UPDATED, RELATION_ADD_OR_UPDATE, RELATION_DELETED, RPC_CALL, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, ADDED_COMMENT, UPDATED_COMMENT, DELETED_COMMENT -> {
+ case UPDATED, ADDED, DELETED, ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE, ALARM_ACK, ALARM_CLEAR, ALARM_DELETE,
+ CREDENTIALS_UPDATED, RELATION_ADD_OR_UPDATE, RELATION_DELETED, RPC_CALL,
+ ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, ADDED_COMMENT, UPDATED_COMMENT, DELETED_COMMENT -> {
downlinkMsg = convertEntityEventToDownlink(edgeEvent);
if (downlinkMsg != null && downlinkMsg.getWidgetTypeUpdateMsgCount() > 0) {
log.trace("[{}][{}] widgetTypeUpdateMsg message processed, downlinkMsgId = {}", this.tenantId, this.sessionId, downlinkMsg.getDownlinkMsgId());
@@ -683,15 +712,15 @@ public final class EdgeGrpcSession implements Closeable {
case ADMIN_SETTINGS:
return ctx.getAdminSettingsProcessor().convertAdminSettingsEventToDownlink(edgeEvent, this.edgeVersion);
case OTA_PACKAGE:
- return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent, this.edgeVersion);
+ return ctx.getOtaPackageProcessor().convertOtaPackageEventToDownlink(edgeEvent, this.edgeVersion);
case TB_RESOURCE:
- return ctx.getResourceEdgeProcessor().convertResourceEventToDownlink(edgeEvent, this.edgeVersion);
+ return ctx.getResourceProcessor().convertResourceEventToDownlink(edgeEvent, this.edgeVersion);
case QUEUE:
- return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent, this.edgeVersion);
+ return ctx.getQueueProcessor().convertQueueEventToDownlink(edgeEvent, this.edgeVersion);
case TENANT:
- return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent, this.edgeVersion);
+ return ctx.getTenantProcessor().convertTenantEventToDownlink(edgeEvent, this.edgeVersion);
case TENANT_PROFILE:
- return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent, this.edgeVersion);
+ return ctx.getTenantProfileProcessor().convertTenantProfileEventToDownlink(edgeEvent, this.edgeVersion);
case NOTIFICATION_RULE:
return ctx.getNotificationEdgeProcessor().convertNotificationRuleToDownlink(edgeEvent);
case NOTIFICATION_TARGET:
@@ -914,4 +943,15 @@ public final class EdgeGrpcSession implements Closeable {
}
}
+ public void addEventToHighPriorityQueue(EdgeEvent edgeEvent) {
+ while (highPriorityQueue.size() > maxHighPriorityQueueSizePerSession) {
+ EdgeEvent oldestHighPriority = highPriorityQueue.poll();
+ if (oldestHighPriority != null) {
+ log.warn("[{}][{}][{}] High priority queue is full. Removing oldest high priority event from queue {}",
+ this.tenantId, edge.getId(), this.sessionId, oldestHighPriority);
+ }
+ }
+ highPriorityQueue.add(edgeEvent);
+ }
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java
index 12203db0c2..7c832ca760 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java
@@ -32,5 +32,6 @@ public interface EdgeRpcService {
void deleteEdge(TenantId tenantId, EdgeId edgeId);
- void processSyncRequest(ToEdgeSyncRequest request, Consumer responseConsumer);
+ void processSyncRequest(TenantId tenantId, EdgeId edgeId, Consumer responseConsumer);
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSessionState.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSessionState.java
index 6218b1f9a6..3f925c3787 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSessionState.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSessionState.java
@@ -30,4 +30,5 @@ public class EdgeSessionState {
private final Map pendingMsgsMap = Collections.synchronizedMap(new LinkedHashMap<>());
private SettableFuture sendDownlinkMsgsFuture;
private ScheduledFuture> scheduledSendDownlinkTask;
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BasePageableEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BasePageableEdgeEventFetcher.java
index 747dc5722f..40fc3559ed 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BasePageableEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BasePageableEdgeEventFetcher.java
@@ -35,7 +35,7 @@ public abstract class BasePageableEdgeEventFetcher implements EdgeEventFetche
@Override
public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
- log.trace("[{}] start fetching edge events [{}]", tenantId, edge.getId());
+ log.trace("[{}][{}] start fetching edge events [{}], pageLink {}", getClass().getSimpleName(), tenantId, edge.getId(), pageLink);
PageData entities = fetchEntities(tenantId, edge, pageLink);
List result = new ArrayList<>();
if (!entities.getData().isEmpty()) {
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java
index 5569c30018..328072aeea 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java
@@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.rule.RuleChainService;
-import static org.thingsboard.server.service.edge.DefaultEdgeNotificationService.EDGE_IS_ROOT_BODY_KEY;
+import static org.thingsboard.server.dao.edge.EdgeServiceImpl.EDGE_IS_ROOT_BODY_KEY;
@Slf4j
@AllArgsConstructor
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
index 8dee74aac1..3c18e02324 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
@@ -374,23 +374,20 @@ public abstract class BaseEdgeProcessor {
}, dbCallbackExecutorService);
}
- private boolean doSaveIfEdgeIsOffline(EdgeEventType type,
- EdgeEventActionType action) {
+ private boolean doSaveIfEdgeIsOffline(EdgeEventType type, EdgeEventActionType action) {
return switch (action) {
case TIMESERIES_UPDATED, ALARM_ACK, ALARM_CLEAR, ALARM_ASSIGNED, ALARM_UNASSIGNED, ADDED_COMMENT, UPDATED_COMMENT ->
true;
default -> switch (type) {
case ALARM, ALARM_COMMENT, RULE_CHAIN, RULE_CHAIN_METADATA, USER, CUSTOMER, TENANT, TENANT_PROFILE, WIDGETS_BUNDLE, WIDGET_TYPE,
- ADMIN_SETTINGS, OTA_PACKAGE, QUEUE, RELATION, NOTIFICATION_TEMPLATE, NOTIFICATION_TARGET, NOTIFICATION_RULE ->
- true;
+ ADMIN_SETTINGS, OTA_PACKAGE, QUEUE, RELATION, NOTIFICATION_TEMPLATE, NOTIFICATION_TARGET, NOTIFICATION_RULE -> true;
default -> false;
};
};
}
private ListenableFuture doSaveEdgeEvent(TenantId tenantId, EdgeId edgeId, EdgeEventType type, EdgeEventActionType action, EntityId entityId, JsonNode body) {
- log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " +
- "action [{}], entityId [{}], body [{}]",
+ log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], action [{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java
index b71493fa39..310d8d9b9b 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java
@@ -32,7 +32,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructor;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
-import static org.thingsboard.server.service.edge.DefaultEdgeNotificationService.EDGE_IS_ROOT_BODY_KEY;
+import static org.thingsboard.server.dao.edge.EdgeServiceImpl.EDGE_IS_ROOT_BODY_KEY;
@Slf4j
@Component
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
index 6e4117869b..46f1d7ef57 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
@@ -113,10 +113,8 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() == 0 || ruleChainMetadataRequestMsg.getRuleChainIdLSB() == 0) {
return Futures.immediateFuture(null);
}
- RuleChainId ruleChainId =
- new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
- return saveEdgeEvent(tenantId, edge.getId(),
- EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null);
+ RuleChainId ruleChainId = new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
+ return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null);
}
@Override
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java
index 08b1e57651..863be23e42 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java
@@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -48,6 +47,8 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
@@ -107,7 +108,7 @@ public class EntityStateSourcingListener {
onDeviceProfileUpdate(deviceProfile, event.getOldEntity(), isCreated);
}
case EDGE -> {
- handleEdgeEvent(tenantId, entityId, event.getEntity(), lifecycleEvent);
+ onEdgeEvent(tenantId, entityId, event.getEntity(), lifecycleEvent);
}
case TB_RESOURCE -> {
TbResource tbResource = (TbResource) event.getEntity();
@@ -221,10 +222,7 @@ public class EntityStateSourcingListener {
}
private DeviceProfile getOldDeviceProfile(Object oldEntity) {
- if (oldEntity instanceof DeviceProfile) {
- return (DeviceProfile) oldEntity;
- }
- return null;
+ return oldEntity instanceof DeviceProfile ? (DeviceProfile) oldEntity : null;
}
private void onDeviceProfileDelete(TenantId tenantId, EntityId entityId, DeviceProfile deviceProfile) {
@@ -241,11 +239,11 @@ public class EntityStateSourcingListener {
tbClusterService.onDeviceUpdated(device, oldDevice);
}
- private void handleEdgeEvent(TenantId tenantId, EntityId entityId, Object entity, ComponentLifecycleEvent lifecycleEvent) {
+ private void onEdgeEvent(TenantId tenantId, EntityId entityId, Object entity, ComponentLifecycleEvent lifecycleEvent) {
if (entity instanceof Edge) {
- tbClusterService.broadcastEntityStateChangeEvent(tenantId, entityId, lifecycleEvent);
- } else if (entity instanceof EdgeEvent) {
- tbClusterService.onEdgeEventUpdate(tenantId, (EdgeId) entityId);
+ tbClusterService.onEdgeStateChangeEvent(new ComponentLifecycleMsg(tenantId, entityId, lifecycleEvent));
+ } else if (entity instanceof EdgeEvent edgeEvent) {
+ tbClusterService.onEdgeEventUpdate(new EdgeEventUpdateMsg(tenantId, edgeEvent.getEdgeId()));
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java
index 9fcd8e2187..8771b48c35 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java
@@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.queue.util.TbCoreComponent;
-import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
@AllArgsConstructor
@@ -40,7 +39,6 @@ import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
@Slf4j
public class DefaultTbEdgeService extends AbstractTbEntityService implements TbEdgeService {
- private final EdgeNotificationService edgeNotificationService;
private final RuleChainService ruleChainService;
@Override
@@ -56,7 +54,7 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE
if (ActionType.ADDED.equals(actionType)) {
ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), edgeId);
- edgeNotificationService.setEdgeRootRuleChain(tenantId, savedEdge, edgeTemplateRootRuleChain.getId());
+ savedEdge = edgeService.setEdgeRootRuleChain(tenantId, savedEdge, edgeTemplateRootRuleChain.getId());
edgeService.assignDefaultRuleChainsToEdge(tenantId, edgeId);
}
@@ -143,7 +141,7 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE
TenantId tenantId = edge.getTenantId();
EdgeId edgeId = edge.getId();
try {
- Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(tenantId, edge, ruleChainId);
+ Edge updatedEdge = edgeService.setEdgeRootRuleChain(tenantId, edge, ruleChainId);
logEntityActionService.logEntityAction(tenantId, edgeId, edge, null, ActionType.UPDATED, user);
return updatedEdge;
} catch (Exception e) {
@@ -152,4 +150,5 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE
throw e;
}
}
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java
index 8dc447b731..3afd32c3fd 100644
--- a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java
@@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain;
public interface TbEdgeService {
+
Edge save(Edge edge, RuleChain edgeTemplateRootRuleChain, User user) throws Exception;
void delete(Edge edge, User user);
@@ -36,4 +37,5 @@ public interface TbEdgeService {
Edge assignEdgeToPublicCustomer(TenantId tenantId, EdgeId edgeId, User user) throws ThingsboardException;
Edge setEdgeRootRuleChain(Edge edge, RuleChainId ruleChainId, User user) throws Exception;
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java b/application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java
index 676dbc1e3d..fc5ce5214c 100644
--- a/application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java
+++ b/application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java
@@ -90,13 +90,13 @@ public abstract class AbstractPartitionBasedService extends
@Override
protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
log.debug("onTbApplicationEvent, processing event: {}", partitionChangeEvent);
- subscribeQueue.add(partitionChangeEvent.getPartitions());
+ subscribeQueue.add(partitionChangeEvent.getCorePartitions());
scheduledExecutor.submit(this::pollInitStateFromDB);
}
@Override
protected boolean filterTbApplicationEvent(PartitionChangeEvent event) {
- return getServiceType().equals(event.getServiceType());
+ return event.getServiceType() == getServiceType();
}
protected void pollInitStateFromDB() {
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
index 7699a4f7c2..301f8e8838 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
@@ -23,6 +23,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
+import org.thingsboard.server.cache.TbTransactionalCache;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.DataConstants;
@@ -55,6 +56,7 @@ import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
+import org.thingsboard.server.common.msg.edge.EdgeHighPriorityMsg;
import org.thingsboard.server.common.msg.edge.FromEdgeSyncResponse;
import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
@@ -66,14 +68,23 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.common.util.ProtoUtils;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.ComponentLifecycleMsgProto;
+import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
+import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto;
+import org.thingsboard.server.gen.transport.TransportProtos.EntityDeleteMsg;
import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
import org.thingsboard.server.gen.transport.TransportProtos.QueueDeleteMsg;
import org.thingsboard.server.gen.transport.TransportProtos.QueueUpdateMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ResourceDeleteMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ResourceUpdateMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper;
@@ -112,6 +123,8 @@ public class DefaultTbClusterService implements TbClusterService {
private final AtomicInteger toRuleEngineMsgs = new AtomicInteger(0);
private final AtomicInteger toRuleEngineNfs = new AtomicInteger(0);
private final AtomicInteger toTransportNfs = new AtomicInteger(0);
+ private final AtomicInteger toEdgeMsgs = new AtomicInteger(0);
+ private final AtomicInteger toEdgeNfs = new AtomicInteger(0);
@Autowired
@Lazy
@@ -133,6 +146,7 @@ public class DefaultTbClusterService implements TbClusterService {
private final TbAssetProfileCache assetProfileCache;
private final GatewayNotificationsService gatewayNotificationsService;
private final EdgeService edgeService;
+ private final TbTransactionalCache edgeIdServiceIdCache;
@Override
public void pushMsgToCore(TenantId tenantId, EntityId entityId, ToCoreMsg msg, TbQueueCallback callback) {
@@ -169,7 +183,7 @@ public class DefaultTbClusterService implements TbClusterService {
}
@Override
- public void pushMsgToVersionControl(TenantId tenantId, TransportProtos.ToVersionControlServiceMsg msg, TbQueueCallback callback) {
+ public void pushMsgToVersionControl(TenantId tenantId, ToVersionControlServiceMsg msg, TbQueueCallback callback) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_VC_EXECUTOR, TenantId.SYS_TENANT_ID, tenantId);
log.trace("PUSHING msg: {} to:{}", msg, tpi);
producerProvider.getTbVersionControlMsgProducer().send(tpi, new TbProtoQueueMsg<>(tenantId.getId(), msg), callback);
@@ -386,7 +400,7 @@ public class DefaultTbClusterService implements TbClusterService {
if (resource.getResourceType() == ResourceType.LWM2M_MODEL) {
TenantId tenantId = resource.getTenantId();
log.trace("[{}][{}][{}] Processing change resource", tenantId, resource.getResourceType(), resource.getResourceKey());
- TransportProtos.ResourceUpdateMsg resourceUpdateMsg = TransportProtos.ResourceUpdateMsg.newBuilder()
+ ResourceUpdateMsg resourceUpdateMsg = ResourceUpdateMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setResourceType(resource.getResourceType().name())
@@ -401,7 +415,7 @@ public class DefaultTbClusterService implements TbClusterService {
public void onResourceDeleted(TbResourceInfo resource, TbQueueCallback callback) {
if (resource.getResourceType() == ResourceType.LWM2M_MODEL) {
log.trace("[{}][{}][{}] Processing delete resource", resource.getTenantId(), resource.getResourceType(), resource.getResourceKey());
- TransportProtos.ResourceDeleteMsg resourceDeleteMsg = TransportProtos.ResourceDeleteMsg.newBuilder()
+ ResourceDeleteMsg resourceDeleteMsg = ResourceDeleteMsg.newBuilder()
.setTenantIdMSB(resource.getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(resource.getTenantId().getId().getLeastSignificantBits())
.setResourceType(resource.getResourceType().name())
@@ -421,7 +435,7 @@ public class DefaultTbClusterService implements TbClusterService {
private void broadcastEntityDeleteToTransport(TenantId tenantId, EntityId entityId, String name, TbQueueCallback callback) {
log.trace("[{}][{}][{}] Processing [{}] delete event", tenantId, entityId.getEntityType(), entityId.getId(), name);
- TransportProtos.EntityDeleteMsg entityDeleteMsg = TransportProtos.EntityDeleteMsg.newBuilder()
+ EntityDeleteMsg entityDeleteMsg = EntityDeleteMsg.newBuilder()
.setEntityType(entityId.getEntityType().name())
.setEntityIdMSB(entityId.getId().getMostSignificantBits())
.setEntityIdLSB(entityId.getId().getLeastSignificantBits())
@@ -453,39 +467,76 @@ public class DefaultTbClusterService implements TbClusterService {
}
@Override
- public void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId) {
- log.trace("[{}] Processing edge {} event update ", tenantId, edgeId);
- EdgeEventUpdateMsg msg = new EdgeEventUpdateMsg(tenantId, edgeId);
- ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setEdgeEventUpdate(toProto(msg)).build();
- pushEdgeSyncMsgToCore(edgeId, toCoreMsg);
+ public void pushMsgToEdge(TenantId tenantId, EntityId entityId, ToEdgeMsg msg, TbQueueCallback callback) {
+ TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, DataConstants.EDGE_QUEUE_NAME, tenantId, entityId);
+ TbQueueProducer> toEdgeProducer = producerProvider.getTbEdgeMsgProducer();
+ toEdgeProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), callback);
+ toEdgeMsgs.incrementAndGet();
}
@Override
- public void pushEdgeSyncRequestToCore(ToEdgeSyncRequest toEdgeSyncRequest) {
- log.trace("[{}] Processing edge sync request {} ", toEdgeSyncRequest.getTenantId(), toEdgeSyncRequest);
- ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setToEdgeSyncRequest(toProto(toEdgeSyncRequest)).build();
- pushEdgeSyncMsgToCore(toEdgeSyncRequest.getEdgeId(), toCoreMsg);
+ public void onEdgeHighPriorityMsg(EdgeHighPriorityMsg msg) {
+ log.trace("[{}] Processing edge event for edgeId: {}", msg.getTenantId(), msg.getEdgeEvent().getEdgeId());
+ ToEdgeNotificationMsg toEdgeNotificationMsg = ToEdgeNotificationMsg.newBuilder().setEdgeHighPriority(toProto(msg)).build();
+ processEdgeNotification(msg.getEdgeEvent().getEdgeId(), toEdgeNotificationMsg);
}
@Override
- public void pushEdgeSyncResponseToCore(FromEdgeSyncResponse fromEdgeSyncResponse) {
- log.trace("[{}] Processing edge sync response {}", fromEdgeSyncResponse.getTenantId(), fromEdgeSyncResponse);
- ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setFromEdgeSyncResponse(toProto(fromEdgeSyncResponse)).build();
- pushEdgeSyncMsgToCore(fromEdgeSyncResponse.getEdgeId(), toCoreMsg);
+ public void onEdgeEventUpdate(EdgeEventUpdateMsg msg) {
+ log.trace("[{}] Processing edge event update for edgeId: {}", msg.getTenantId(), msg.getEdgeId());
+ ToEdgeNotificationMsg toEdgeNotificationMsg = ToEdgeNotificationMsg.newBuilder().setEdgeEventUpdate(toProto(msg)).build();
+ processEdgeNotification(msg.getEdgeId(), toEdgeNotificationMsg);
}
- private void pushEdgeSyncMsgToCore(EdgeId edgeId, ToCoreNotificationMsg toCoreMsg) {
- TbQueueProducer> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer();
- Set tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE);
- for (String serviceId : tbCoreServices) {
- TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId);
- toCoreNfProducer.send(tpi, new TbProtoQueueMsg<>(edgeId.getId(), toCoreMsg), null);
- toCoreNfs.incrementAndGet();
+ @Override
+ public void onEdgeStateChangeEvent(ComponentLifecycleMsg msg) {
+ log.trace("[{}] Processing {} state change event: {}", msg.getTenantId(), EntityType.EDGE, msg.getEvent());
+ ComponentLifecycleMsgProto componentLifecycleMsgProto = toProto(msg);
+ ToEdgeNotificationMsg toEdgeNotificationMsg = ToEdgeNotificationMsg.newBuilder().setComponentLifecycle(componentLifecycleMsgProto).build();
+ processEdgeNotification((EdgeId) msg.getEntityId(), toEdgeNotificationMsg);
+ }
+
+ @Override
+ public void pushEdgeSyncRequestToEdge(ToEdgeSyncRequest request) {
+ log.trace("[{}] Processing edge sync request for edgeId: {}", request.getTenantId(), request.getEdgeId());
+ ToEdgeNotificationMsg toEdgeNotificationMsg = ToEdgeNotificationMsg.newBuilder().setToEdgeSyncRequest(toProto(request)).build();
+ processEdgeNotification(request.getEdgeId(), toEdgeNotificationMsg);
+ }
+
+ @Override
+ public void pushEdgeSyncResponseToCore(FromEdgeSyncResponse response, String requestServiceId) {
+ log.trace("[{}] Processing edge sync response for edgeId: {}", response.getTenantId(), response.getEdgeId());
+ ToEdgeNotificationMsg toEdgeNotificationMsg = ToEdgeNotificationMsg.newBuilder().setFromEdgeSyncResponse(toProto(response)).build();
+ pushMsgToEdgeNotification(toEdgeNotificationMsg, requestServiceId);
+ }
+
+ private void processEdgeNotification(EdgeId edgeId, ToEdgeNotificationMsg toEdgeNotificationMsg) {
+ var serviceIdOpt = Optional.ofNullable(edgeIdServiceIdCache.get(edgeId));
+ serviceIdOpt.ifPresentOrElse(
+ serviceId -> pushMsgToEdgeNotification(toEdgeNotificationMsg, serviceId.get()),
+ () -> broadcastEdgeNotification(edgeId, toEdgeNotificationMsg)
+ );
+ }
+
+ private void pushMsgToEdgeNotification(ToEdgeNotificationMsg toEdgeNotificationMsg, String serviceId) {
+ TopicPartitionInfo tpi = topicService.getEdgeNotificationsTopic(serviceId);
+ TbQueueProducer> toEdgeNotificationProducer = producerProvider.getTbEdgeNotificationsMsgProducer();
+ toEdgeNotificationProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), toEdgeNotificationMsg), null);
+ toEdgeNfs.incrementAndGet();
+ }
+
+ private void broadcastEdgeNotification(EdgeId edgeId, ToEdgeNotificationMsg toEdgeNotificationMsg) {
+ TbQueueProducer> toEdgeNotificationProducer = producerProvider.getTbEdgeNotificationsMsgProducer();
+ Set serviceIds = partitionService.getAllServiceIds(ServiceType.TB_CORE);
+ for (String serviceId : serviceIds) {
+ TopicPartitionInfo tpi = topicService.getEdgeNotificationsTopic(serviceId);
+ toEdgeNotificationProducer.send(tpi, new TbProtoQueueMsg<>(edgeId.getId(), toEdgeNotificationMsg), null);
+ toEdgeNfs.incrementAndGet();
}
}
private void broadcast(ComponentLifecycleMsg msg) {
- TransportProtos.ComponentLifecycleMsgProto componentLifecycleMsgProto = toProto(msg);
+ ComponentLifecycleMsgProto componentLifecycleMsgProto = toProto(msg);
TbQueueProducer> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer();
Set tbRuleEngineServices = partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE);
EntityType entityType = msg.getEntityId().getEntityType();
@@ -497,7 +548,6 @@ public class DefaultTbClusterService implements TbClusterService {
|| entityType.equals(EntityType.API_USAGE_STATE)
|| (entityType.equals(EntityType.DEVICE) && msg.getEvent() == ComponentLifecycleEvent.UPDATED)
|| entityType.equals(EntityType.ENTITY_VIEW)
- || entityType.equals(EntityType.EDGE)
|| entityType.equals(EntityType.NOTIFICATION_RULE)) {
TbQueueProducer> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer();
Set tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE);
@@ -526,15 +576,17 @@ public class DefaultTbClusterService implements TbClusterService {
int toRuleEngineMsgsCnt = toRuleEngineMsgs.getAndSet(0);
int toRuleEngineNfsCnt = toRuleEngineNfs.getAndSet(0);
int toTransportNfsCnt = toTransportNfs.getAndSet(0);
- if (toCoreMsgCnt > 0 || toCoreNfsCnt > 0 || toRuleEngineMsgsCnt > 0 || toRuleEngineNfsCnt > 0 || toTransportNfsCnt > 0) {
- log.info("To TbCore: [{}] messages [{}] notifications; To TbRuleEngine: [{}] messages [{}] notifications; To Transport: [{}] notifications",
- toCoreMsgCnt, toCoreNfsCnt, toRuleEngineMsgsCnt, toRuleEngineNfsCnt, toTransportNfsCnt);
+ int toEdgeMsgCnt = toEdgeMsgs.getAndSet(0);
+ int toEdgeNfsCnt = toEdgeNfs.getAndSet(0);
+ if (toCoreMsgCnt > 0 || toCoreNfsCnt > 0 || toRuleEngineMsgsCnt > 0 || toRuleEngineNfsCnt > 0 || toTransportNfsCnt > 0 || toEdgeMsgCnt > 0 || toEdgeNfsCnt > 0) {
+ log.info("To TbCore: [{}] messages [{}] notifications; To TbRuleEngine: [{}] messages [{}] notifications; To Transport: [{}] notifications;" +
+ "To Edge: [{}] messages [{}] notifications", toCoreMsgCnt, toCoreNfsCnt, toRuleEngineMsgsCnt, toRuleEngineNfsCnt, toTransportNfsCnt, toEdgeMsgCnt, toEdgeNfsCnt);
}
}
}
private void sendDeviceStateServiceEvent(TenantId tenantId, DeviceId deviceId, boolean added, boolean updated, boolean deleted) {
- TransportProtos.DeviceStateServiceMsgProto.Builder builder = TransportProtos.DeviceStateServiceMsgProto.newBuilder();
+ DeviceStateServiceMsgProto.Builder builder = DeviceStateServiceMsgProto.newBuilder();
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
builder.setDeviceIdMSB(deviceId.getId().getMostSignificantBits());
@@ -542,8 +594,8 @@ public class DefaultTbClusterService implements TbClusterService {
builder.setAdded(added);
builder.setUpdated(updated);
builder.setDeleted(deleted);
- TransportProtos.DeviceStateServiceMsgProto msg = builder.build();
- pushMsgToCore(tenantId, deviceId, TransportProtos.ToCoreMsg.newBuilder().setDeviceStateServiceMsg(msg).build(), null);
+ DeviceStateServiceMsgProto msg = builder.build();
+ pushMsgToCore(tenantId, deviceId, ToCoreMsg.newBuilder().setDeviceStateServiceMsg(msg).build(), null);
}
@Override
@@ -581,7 +633,7 @@ public class DefaultTbClusterService implements TbClusterService {
return;
}
}
- TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder();
+ EdgeNotificationMsgProto.Builder builder = EdgeNotificationMsgProto.newBuilder();
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
builder.setType(type.name());
@@ -602,9 +654,9 @@ public class DefaultTbClusterService implements TbClusterService {
builder.setOriginatorEdgeIdMSB(originatorEdgeId.getId().getMostSignificantBits());
builder.setOriginatorEdgeIdLSB(originatorEdgeId.getId().getLeastSignificantBits());
}
- TransportProtos.EdgeNotificationMsgProto msg = builder.build();
+ EdgeNotificationMsgProto msg = builder.build();
log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg);
- pushMsgToCore(tenantId, entityId != null ? entityId : tenantId, TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
+ pushMsgToEdge(tenantId, entityId != null ? entityId : tenantId, ToEdgeMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
if (entityId != null && EntityType.DEVICE.equals(entityId.getEntityType())) {
pushDeviceUpdateMessage(tenantId, edgeId, entityId, action);
@@ -690,4 +742,5 @@ public class DefaultTbClusterService implements TbClusterService {
toTransportNfs.incrementAndGet();
}
}
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
index 4462183baa..2a64dd3388 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
@@ -59,7 +59,6 @@ import org.thingsboard.server.dao.resource.ImageCacheKey;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
-import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.ErrorEventProto;
import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
import org.thingsboard.server.gen.transport.TransportProtos.LifecycleEventProto;
@@ -86,7 +85,6 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
-import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.notification.NotificationSchedulerService;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbAssetProfileCache;
@@ -142,7 +140,6 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService tpi.newByTopic(usageStatsConsumer.getConsumer().getTopic()))
.collect(Collectors.toSet()));
}
private void processMsgs(List> msgs, TbQueueConsumer> consumer, CoreQueueConfig config) throws Exception {
- List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).collect(Collectors.toList());
+ List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).toList();
ConcurrentMap> pendingMap = orderedMsgList.stream().collect(
Collectors.toConcurrentMap(IdMsgPair::getUuid, IdMsgPair::getMsg));
CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
@@ -282,9 +277,6 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService 0) {
partitionService.updateQueues(toCoreNotification.getQueueUpdateMsgsList());
callback.onSuccess();
@@ -686,13 +669,6 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService implements TbEdgeConsumerService {
+
+ @Value("${queue.edge.pool-interval:25}")
+ private int pollInterval;
+ @Value("${queue.edge.pack-processing-timeout:10000}")
+ private int packProcessingTimeout;
+ @Value("${queue.edge.consumer-per-partition:false}")
+ private boolean consumerPerPartition;
+ @Value("${queue.edge.pack-processing-retries:3}")
+ private int packProcessingRetries;
+ @Value("${queue.edge.stats.enabled:false}")
+ private boolean statsEnabled;
+
+ private final TbCoreQueueFactory queueFactory;
+ private final EdgeContextComponent edgeCtx;
+ private final EdgeConsumerStats stats;
+
+ private MainQueueConsumerManager, EdgeQueueConfig> mainConsumer;
+
+ public DefaultTbEdgeConsumerService(TbCoreQueueFactory tbCoreQueueFactory, ActorSystemContext actorContext,
+ StatsFactory statsFactory, EdgeContextComponent edgeCtx) {
+ super(actorContext, null, null, null, null, null,
+ null, null);
+ this.edgeCtx = edgeCtx;
+ this.stats = new EdgeConsumerStats(statsFactory);
+ this.queueFactory = tbCoreQueueFactory;
+ }
+
+ @PostConstruct
+ public void init() {
+ super.init("tb-edge");
+
+ this.mainConsumer = MainQueueConsumerManager., EdgeQueueConfig>builder()
+ .queueKey(new QueueKey(ServiceType.TB_CORE).withQueueName(DataConstants.EDGE_QUEUE_NAME))
+ .config(EdgeQueueConfig.of(consumerPerPartition, (int) pollInterval))
+ .msgPackProcessor(this::processMsgs)
+ .consumerCreator((config, partitionId) -> queueFactory.createEdgeMsgConsumer())
+ .consumerExecutor(consumersExecutor)
+ .scheduler(scheduler)
+ .taskExecutor(mgmtExecutor)
+ .build();
+ }
+
+ @PreDestroy
+ public void destroy() {
+ super.destroy();
+ }
+
+ @Override
+ protected void startConsumers() {
+ super.startConsumers();
+ }
+
+ @Override
+ protected void onTbApplicationEvent(PartitionChangeEvent event) {
+ var partitions = event.getEdgePartitions();
+ log.debug("Subscribing to partitions: {}", partitions);
+ mainConsumer.update(partitions);
+ }
+
+ private void processMsgs(List> msgs, TbQueueConsumer> consumer, EdgeQueueConfig edgeQueueConfig) throws InterruptedException {
+ List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).toList();
+ ConcurrentMap> pendingMap = orderedMsgList.stream().collect(
+ Collectors.toConcurrentMap(IdMsgPair::getUuid, IdMsgPair::getMsg));
+ CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
+ TbPackProcessingContext> ctx = new TbPackProcessingContext<>(
+ processingTimeoutLatch, pendingMap, new ConcurrentHashMap<>());
+ PendingMsgHolder pendingMsgHolder = new PendingMsgHolder();
+ Future> submitFuture = consumersExecutor.submit(() -> {
+ orderedMsgList.forEach((element) -> {
+ UUID id = element.getUuid();
+ TbProtoQueueMsg msg = element.getMsg();
+ TbCallback callback = new TbPackCallback<>(id, ctx);
+ try {
+ ToEdgeMsg toEdgeMsg = msg.getValue();
+ pendingMsgHolder.setToEdgeMsg(toEdgeMsg);
+ if (toEdgeMsg.hasEdgeNotificationMsg()) {
+ pushNotificationToEdge(toEdgeMsg.getEdgeNotificationMsg(), 0, packProcessingRetries, callback);
+ }
+ if (statsEnabled) {
+ stats.log(toEdgeMsg);
+ }
+ } catch (Throwable e) {
+ log.warn("[{}] Failed to process message: {}", id, msg, e);
+ callback.onFailure(e);
+ }
+ });
+ });
+ if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) {
+ if (!submitFuture.isDone()) {
+ submitFuture.cancel(true);
+ ToEdgeMsg lastSubmitMsg = pendingMsgHolder.getToEdgeMsg();
+ log.info("Timeout to process message: {}", lastSubmitMsg);
+ }
+ ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue()));
+ }
+ consumer.commit();
+ }
+
+ private static class PendingMsgHolder {
+ @Getter
+ @Setter
+ private volatile ToEdgeMsg toEdgeMsg;
+ }
+
+ @Override
+ protected ServiceType getServiceType() {
+ return ServiceType.TB_CORE;
+ }
+
+ @Override
+ protected long getNotificationPollDuration() {
+ return pollInterval;
+ }
+
+ @Override
+ protected long getNotificationPackProcessingTimeout() {
+ return packProcessingTimeout;
+ }
+
+ @Override
+ protected int getMgmtThreadPoolSize() {
+ return Math.max(Runtime.getRuntime().availableProcessors(), 4);
+ }
+
+ @Override
+ protected TbQueueConsumer> createNotificationsConsumer() {
+ return queueFactory.createToEdgeNotificationsMsgConsumer();
+ }
+
+ @Override
+ protected void handleNotification(UUID id, TbProtoQueueMsg msg, TbCallback callback) {
+ ToEdgeNotificationMsg toEdgeNotificationMsg = msg.getValue();
+ try {
+ if (toEdgeNotificationMsg.hasEdgeHighPriority()) {
+ EdgeSessionMsg edgeSessionMsg = ProtoUtils.fromProto(toEdgeNotificationMsg.getEdgeHighPriority());
+ edgeCtx.getEdgeRpcService().onToEdgeSessionMsg(edgeSessionMsg.getTenantId(), edgeSessionMsg);
+ callback.onSuccess();
+ } else if (toEdgeNotificationMsg.hasEdgeEventUpdate()) {
+ EdgeSessionMsg edgeSessionMsg = ProtoUtils.fromProto(toEdgeNotificationMsg.getEdgeEventUpdate());
+ edgeCtx.getEdgeRpcService().onToEdgeSessionMsg(edgeSessionMsg.getTenantId(), edgeSessionMsg);
+ callback.onSuccess();
+ } else if (toEdgeNotificationMsg.hasToEdgeSyncRequest()) {
+ EdgeSessionMsg edgeSessionMsg = ProtoUtils.fromProto(toEdgeNotificationMsg.getToEdgeSyncRequest());
+ edgeCtx.getEdgeRpcService().onToEdgeSessionMsg(edgeSessionMsg.getTenantId(), edgeSessionMsg);
+ callback.onSuccess();
+ } else if (toEdgeNotificationMsg.hasFromEdgeSyncResponse()) {
+ EdgeSessionMsg edgeSessionMsg = ProtoUtils.fromProto(toEdgeNotificationMsg.getFromEdgeSyncResponse());
+ edgeCtx.getEdgeRpcService().onToEdgeSessionMsg(edgeSessionMsg.getTenantId(), edgeSessionMsg);
+ callback.onSuccess();
+ } else if (toEdgeNotificationMsg.hasComponentLifecycle()) {
+ ComponentLifecycleMsg componentLifecycle = ProtoUtils.fromProto(toEdgeNotificationMsg.getComponentLifecycle());
+ TenantId tenantId = componentLifecycle.getTenantId();
+ EdgeId edgeId = new EdgeId(componentLifecycle.getEntityId().getId());
+ if (ComponentLifecycleEvent.DELETED.equals(componentLifecycle.getEvent())) {
+ edgeCtx.getEdgeRpcService().deleteEdge(tenantId, edgeId);
+ } else if (ComponentLifecycleEvent.UPDATED.equals(componentLifecycle.getEvent())) {
+ Edge edge = edgeCtx.getEdgeService().findEdgeById(tenantId, edgeId);
+ edgeCtx.getEdgeRpcService().updateEdge(tenantId, edge);
+ }
+ callback.onSuccess();
+ }
+ } catch (Exception e) {
+ log.error("Error processing edge notification message", e);
+ callback.onFailure(e);
+ }
+
+ if (statsEnabled) {
+ stats.log(msg.getValue());
+ }
+ }
+
+ private void pushNotificationToEdge(EdgeNotificationMsgProto edgeNotificationMsg, int retryCount, int retryLimit, TbCallback callback) {
+ TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
+ log.debug("[{}] Pushing notification to edge {}", tenantId, edgeNotificationMsg);
+ try {
+ EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
+ ListenableFuture future;
+ switch (type) {
+ case EDGE -> future = edgeCtx.getEdgeProcessor().processEdgeNotification(tenantId, edgeNotificationMsg);
+ case ASSET -> future = edgeCtx.getAssetProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case ASSET_PROFILE -> future = edgeCtx.getAssetProfileProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case DEVICE -> future = edgeCtx.getDeviceProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case DEVICE_PROFILE -> future = edgeCtx.getDeviceProfileProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case ENTITY_VIEW -> future = edgeCtx.getEntityViewProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case DASHBOARD -> future = edgeCtx.getDashboardProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case RULE_CHAIN -> future = edgeCtx.getRuleChainProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case USER -> future = edgeCtx.getUserProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case CUSTOMER -> future = edgeCtx.getCustomerProcessor().processCustomerNotification(tenantId, edgeNotificationMsg);
+ case OTA_PACKAGE -> future = edgeCtx.getOtaPackageProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case WIDGETS_BUNDLE -> future = edgeCtx.getWidgetBundleProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case WIDGET_TYPE -> future = edgeCtx.getWidgetTypeProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case QUEUE -> future = edgeCtx.getQueueProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case ALARM -> future = edgeCtx.getAlarmProcessor().processAlarmNotification(tenantId, edgeNotificationMsg);
+ case ALARM_COMMENT -> future = edgeCtx.getAlarmProcessor().processAlarmCommentNotification(tenantId, edgeNotificationMsg);
+ case RELATION -> future = edgeCtx.getRelationProcessor().processRelationNotification(tenantId, edgeNotificationMsg);
+ case TENANT -> future = edgeCtx.getTenantProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case TENANT_PROFILE -> future = edgeCtx.getTenantProfileProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case NOTIFICATION_RULE, NOTIFICATION_TARGET, NOTIFICATION_TEMPLATE ->
+ future = edgeCtx.getNotificationEdgeProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case TB_RESOURCE -> future = edgeCtx.getResourceProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ case DOMAIN, OAUTH2_CLIENT -> future = edgeCtx.getOAuth2EdgeProcessor().processEntityNotification(tenantId, edgeNotificationMsg);
+ default -> {
+ future = Futures.immediateFuture(null);
+ log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type);
+ }
+ }
+ Futures.addCallback(future, new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable Void unused) {
+ callback.onSuccess();
+ }
+
+ @Override
+ public void onFailure(@NotNull Throwable throwable) {
+ if (retryCount < retryLimit) {
+ log.warn("[{}] Retry {} for message due to failure: {}", tenantId, retryCount + 1, throwable.getMessage());
+ pushNotificationToEdge(edgeNotificationMsg, retryCount + 1, retryLimit, callback);
+ } else {
+ callBackFailure(tenantId, edgeNotificationMsg, callback, throwable);
+ }
+ }
+ }, MoreExecutors.directExecutor());
+ } catch (Exception e) {
+ if (retryCount < retryLimit) {
+ log.warn("[{}] Retry {} for message due to exception: {}", tenantId, retryCount + 1, e.getMessage());
+ pushNotificationToEdge(edgeNotificationMsg, retryCount + 1, retryLimit, callback);
+ } else {
+ callBackFailure(tenantId, edgeNotificationMsg, callback, e);
+ }
+ }
+ }
+
+ private void callBackFailure(TenantId tenantId, EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback, Throwable throwable) {
+ log.error("[{}] Can't push to edge updates, edgeNotificationMsg [{}]", tenantId, edgeNotificationMsg, throwable);
+ callback.onFailure(throwable);
+ }
+
+ @Scheduled(fixedDelayString = "${queue.edge.stats.print-interval-ms}")
+ public void printStats() {
+ if (statsEnabled) {
+ stats.printStats();
+ stats.reset();
+ }
+ }
+
+ @Override
+ protected void stopConsumers() {
+ super.stopConsumers();
+ mainConsumer.stop();
+ mainConsumer.awaitStop();
+ }
+
+ @Data(staticConstructor = "of")
+ public static class EdgeQueueConfig implements QueueConfig {
+ private final boolean consumerPerPartition;
+ private final int pollInterval;
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
index d36d44e276..c9a6dee588 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
@@ -165,7 +165,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
}
@Override
- protected void handleNotification(UUID id, TbProtoQueueMsg msg, TbCallback callback) throws Exception {
+ protected void handleNotification(UUID id, TbProtoQueueMsg msg, TbCallback callback) {
ToRuleEngineNotificationMsg nfMsg = msg.getValue();
if (nfMsg.hasComponentLifecycle()) {
handleComponentLifecycleMsg(id, ProtoUtils.fromProto(nfMsg.getComponentLifecycle()));
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/EdgeConsumerStats.java b/application/src/main/java/org/thingsboard/server/service/queue/EdgeConsumerStats.java
new file mode 100644
index 0000000000..be2d060f03
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/queue/EdgeConsumerStats.java
@@ -0,0 +1,99 @@
+/**
+ * Copyright © 2016-2024 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.queue;
+
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.stats.StatsCounter;
+import org.thingsboard.server.common.stats.StatsFactory;
+import org.thingsboard.server.common.stats.StatsType;
+import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg;
+import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+public class EdgeConsumerStats {
+
+ public static final String TOTAL_MSGS = "totalMsgs";
+ public static final String EDGE_NOTIFICATIONS = "edgeNfs";
+ public static final String TO_CORE_NF_EDGE_EVENT = "coreNfEdgeHPUpd";
+ public static final String TO_CORE_NF_EDGE_EVENT_UPDATE = "coreNfEdgeUpd";
+ public static final String TO_CORE_NF_EDGE_SYNC_REQUEST = "coreNfEdgeSyncReq";
+ public static final String TO_CORE_NF_EDGE_SYNC_RESPONSE = "coreNfEdgeSyncResp";
+ public static final String TO_CORE_NF_EDGE_COMPONENT_LIFECYCLE = "coreNfEdgeCompLfcl";
+
+ private final StatsCounter totalCounter;
+ private final StatsCounter edgeNotificationsCounter;
+ private final StatsCounter edgeHighPriorityCounter;
+ private final StatsCounter edgeEventUpdateCounter;
+ private final StatsCounter edgeSyncRequestCounter;
+ private final StatsCounter edgeSyncResponseCounter;
+ private final StatsCounter edgeComponentLifecycle;
+
+ private final List counters = new ArrayList<>(7);
+
+ public EdgeConsumerStats(StatsFactory statsFactory) {
+ String statsKey = StatsType.EDGE.getName();
+
+ this.totalCounter = register(statsFactory.createStatsCounter(statsKey, TOTAL_MSGS));
+ this.edgeNotificationsCounter = register(statsFactory.createStatsCounter(statsKey, EDGE_NOTIFICATIONS));
+ this.edgeHighPriorityCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_EVENT));
+ this.edgeEventUpdateCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_EVENT_UPDATE));
+ this.edgeSyncRequestCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_SYNC_REQUEST));
+ this.edgeSyncResponseCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_SYNC_RESPONSE));
+ this.edgeComponentLifecycle = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_COMPONENT_LIFECYCLE));
+ }
+
+ private StatsCounter register(StatsCounter counter) {
+ counters.add(counter);
+ return counter;
+ }
+
+ public void log(ToEdgeNotificationMsg msg) {
+ totalCounter.increment();
+ if (msg.hasEdgeHighPriority()) {
+ edgeHighPriorityCounter.increment();
+ } else if (msg.hasEdgeEventUpdate()) {
+ edgeEventUpdateCounter.increment();
+ } else if (msg.hasToEdgeSyncRequest()) {
+ edgeSyncRequestCounter.increment();
+ } else if (msg.hasFromEdgeSyncResponse()) {
+ edgeSyncResponseCounter.increment();
+ } else if (msg.hasComponentLifecycle()) {
+ edgeComponentLifecycle.increment();
+ }
+ }
+
+ public void log(ToEdgeMsg msg) {
+ totalCounter.increment();
+ edgeNotificationsCounter.increment();
+ }
+
+ public void printStats() {
+ int total = totalCounter.get();
+ if (total > 0) {
+ StringBuilder stats = new StringBuilder();
+ counters.forEach(counter -> stats.append(counter.getName()).append(" = [").append(counter.get()).append("] "));
+ log.info("Edge Stats: {}", stats);
+ }
+ }
+
+ public void reset() {
+ counters.forEach(StatsCounter::clear);
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java
index 1bd1cb2d5e..aede1263eb 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java
@@ -36,7 +36,6 @@ public class TbCoreConsumerStats {
public static final String DEVICE_CLAIMS = "claimDevice";
public static final String DEVICE_STATES = "deviceState";
public static final String SUBSCRIPTION_MSGS = "subMsgs";
- public static final String EDGE_NOTIFICATIONS = "edgeNfs";
public static final String DEVICE_CONNECTS = "deviceConnect";
public static final String DEVICE_ACTIVITIES = "deviceActivity";
public static final String DEVICE_DISCONNECTS = "deviceDisconnect";
@@ -45,9 +44,6 @@ public class TbCoreConsumerStats {
public static final String TO_CORE_NF_OTHER = "coreNfOther"; // normally, there is no messages when codebase is fine
public static final String TO_CORE_NF_COMPONENT_LIFECYCLE = "coreNfCompLfcl";
public static final String TO_CORE_NF_DEVICE_RPC_RESPONSE = "coreNfDevRpcRsp";
- public static final String TO_CORE_NF_EDGE_EVENT_UPDATE = "coreNfEdgeUpd";
- public static final String TO_CORE_NF_EDGE_SYNC_REQUEST = "coreNfEdgeSyncReq";
- public static final String TO_CORE_NF_EDGE_SYNC_RESPONSE = "coreNfEdgeSyncResp";
public static final String TO_CORE_NF_NOTIFICATION_RULE_PROCESSOR = "coreNfNfRlProc";
public static final String TO_CORE_NF_QUEUE_UPDATE = "coreNfQueueUpd";
public static final String TO_CORE_NF_QUEUE_DELETE = "coreNfQueueDel";
@@ -65,7 +61,6 @@ public class TbCoreConsumerStats {
private final StatsCounter claimDeviceCounter;
private final StatsCounter deviceStateCounter;
private final StatsCounter subscriptionMsgCounter;
- private final StatsCounter edgeNotificationsCounter;
private final StatsCounter deviceConnectsCounter;
private final StatsCounter deviceActivitiesCounter;
private final StatsCounter deviceDisconnectsCounter;
@@ -74,9 +69,6 @@ public class TbCoreConsumerStats {
private final StatsCounter toCoreNfOtherCounter;
private final StatsCounter toCoreNfComponentLifecycleCounter;
private final StatsCounter toCoreNfDeviceRpcResponseCounter;
- private final StatsCounter toCoreNfEdgeEventUpdateCounter;
- private final StatsCounter toCoreNfEdgeSyncRequestCounter;
- private final StatsCounter toCoreNfEdgeSyncResponseCounter;
private final StatsCounter toCoreNfNotificationRuleProcessorCounter;
private final StatsCounter toCoreNfQueueUpdateCounter;
private final StatsCounter toCoreNfQueueDeleteCounter;
@@ -84,7 +76,7 @@ public class TbCoreConsumerStats {
private final StatsCounter toCoreNfSubscriptionManagerCounter;
private final StatsCounter toCoreNfVersionControlResponseCounter;
- private final List counters = new ArrayList<>(24);
+ private final List counters = new ArrayList<>(23);
public TbCoreConsumerStats(StatsFactory statsFactory) {
String statsKey = StatsType.CORE.getName();
@@ -99,7 +91,6 @@ public class TbCoreConsumerStats {
this.claimDeviceCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_CLAIMS));
this.deviceStateCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_STATES));
this.subscriptionMsgCounter = register(statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_MSGS));
- this.edgeNotificationsCounter = register(statsFactory.createStatsCounter(statsKey, EDGE_NOTIFICATIONS));
this.deviceConnectsCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_CONNECTS));
this.deviceActivitiesCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_ACTIVITIES));
this.deviceDisconnectsCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_DISCONNECTS));
@@ -109,9 +100,6 @@ public class TbCoreConsumerStats {
this.toCoreNfOtherCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_OTHER));
this.toCoreNfComponentLifecycleCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_COMPONENT_LIFECYCLE));
this.toCoreNfDeviceRpcResponseCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_DEVICE_RPC_RESPONSE));
- this.toCoreNfEdgeEventUpdateCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_EVENT_UPDATE));
- this.toCoreNfEdgeSyncRequestCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_SYNC_REQUEST));
- this.toCoreNfEdgeSyncResponseCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_SYNC_RESPONSE));
this.toCoreNfNotificationRuleProcessorCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_NOTIFICATION_RULE_PROCESSOR));
this.toCoreNfQueueUpdateCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_QUEUE_UPDATE));
this.toCoreNfQueueDeleteCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_QUEUE_DELETE));
@@ -155,11 +143,6 @@ public class TbCoreConsumerStats {
deviceStateCounter.increment();
}
- public void log(TransportProtos.EdgeNotificationMsgProto msg) {
- totalCounter.increment();
- edgeNotificationsCounter.increment();
- }
-
public void log(TransportProtos.DeviceConnectProto msg) {
totalCounter.increment();
deviceConnectsCounter.increment();
@@ -193,12 +176,6 @@ public class TbCoreConsumerStats {
toCoreNfDeviceRpcResponseCounter.increment();
} else if (msg.hasComponentLifecycle()) {
toCoreNfComponentLifecycleCounter.increment();
- } else if (msg.hasEdgeEventUpdate()) {
- toCoreNfEdgeEventUpdateCounter.increment();
- } else if (msg.hasToEdgeSyncRequest()) {
- toCoreNfEdgeSyncRequestCounter.increment();
- } else if (msg.hasFromEdgeSyncResponse()) {
- toCoreNfEdgeSyncResponseCounter.increment();
} else if (msg.getQueueUpdateMsgsCount() > 0) {
toCoreNfQueueUpdateCounter.increment();
} else if (msg.getQueueDeleteMsgsCount() > 0) {
@@ -218,9 +195,8 @@ public class TbCoreConsumerStats {
int total = totalCounter.get();
if (total > 0) {
StringBuilder stats = new StringBuilder();
- counters.forEach(counter -> {
- stats.append(counter.getName()).append(" = [").append(counter.get()).append("] ");
- });
+ counters.forEach(counter ->
+ stats.append(counter.getName()).append(" = [").append(counter.get()).append("] "));
log.info("Core Stats: {}", stats);
}
}
@@ -228,4 +204,5 @@ public class TbCoreConsumerStats {
public void reset() {
counters.forEach(StatsCounter::clear);
}
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbEdgeConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/TbEdgeConsumerService.java
new file mode 100644
index 0000000000..086a4b2a63
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/queue/TbEdgeConsumerService.java
@@ -0,0 +1,19 @@
+/**
+ * Copyright © 2016-2024 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.queue;
+
+public interface TbEdgeConsumerService {
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
index a264f912cb..1b7e5d53a3 100644
--- a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
@@ -124,7 +124,7 @@ public abstract class AbstractConsumerService> createNotificationsConsumer();
protected void processNotifications(List> msgs, TbQueueConsumer> consumer) throws Exception {
- List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).collect(Collectors.toList());
+ List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).toList();
ConcurrentMap> pendingMap = orderedMsgList.stream().collect(
Collectors.toConcurrentMap(IdMsgPair::getUuid, IdMsgPair::getMsg));
CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
@@ -211,4 +211,5 @@ public abstract class AbstractConsumerService() {
@Override
- public void onSuccess(@Nullable DeviceStateData state) {
+ public void onSuccess(DeviceStateData state) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, device.getId());
- if (addDeviceUsingState(tpi, state)) {
- save(deviceId, ACTIVITY_STATE, false);
+ Set deviceIds = partitionedEntities.get(tpi);
+ boolean isMyPartition = deviceIds != null;
+ if (isMyPartition) {
+ deviceIds.add(state.getDeviceId());
+ initializeActivityState(deviceId, state);
callback.onSuccess();
} else {
- log.debug("[{}][{}] Device belongs to external partition. Probably rebalancing is in progress. Topic: {}"
- , tenantId, deviceId, tpi.getFullTopicName());
+ log.debug("[{}][{}] Device belongs to external partition. Probably rebalancing is in progress. Topic: {}", tenantId, deviceId, tpi.getFullTopicName());
callback.onFailure(new RuntimeException("Device belongs to external partition " + tpi.getFullTopicName() + "!"));
}
}
@@ -400,6 +403,21 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService deviceIdSet = partitionedEntities.get(tpi);
+ if (deviceIdSet != null) {
+ deviceIdSet.remove(deviceId);
+ }
+ }
+
+ private void initializeActivityState(DeviceId deviceId, DeviceStateData fetchedState) {
+ DeviceStateData cachedState = deviceStates.putIfAbsent(fetchedState.getDeviceId(), fetchedState);
+ boolean activityState = Objects.requireNonNullElse(cachedState, fetchedState).getState().isActive();
+ save(deviceId, ACTIVITY_STATE, activityState);
+ }
+
@Override
protected Map>> onAddedPartitions(Set addedPartitions) {
var result = new HashMap>>();
@@ -436,10 +454,16 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService deviceIds = partitionedEntities.get(tpi);
+ boolean isMyPartition = deviceIds != null;
+ if (isMyPartition) {
+ deviceIds.add(state.getDeviceId());
+ deviceStates.putIfAbsent(state.getDeviceId(), state);
+ checkAndUpdateState(state.getDeviceId(), state);
+ } else {
+ log.debug("[{}] Device belongs to external partition {}", state.getDeviceId(), tpi.getFullTopicName());
}
- checkAndUpdateState(state.getDeviceId(), state);
}
log.info("[{}] Initialized {} out of {} device states", entry.getKey().getPartition().orElse(0), counter.addAndGet(states.size()), entry.getValue().size());
}
@@ -475,18 +499,6 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService deviceIds = partitionedEntities.get(tpi);
- if (deviceIds != null) {
- deviceIds.add(state.getDeviceId());
- deviceStates.putIfAbsent(state.getDeviceId(), state);
- return true;
- } else {
- log.debug("[{}] Device belongs to external partition {}", state.getDeviceId(), tpi.getFullTopicName());
- return false;
- }
- }
-
void checkStates() {
try {
final long ts = getCurrentTimeMillis();
@@ -619,15 +631,6 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService deviceIdSet = partitionedEntities.get(tpi);
- if (deviceIdSet != null) {
- deviceIdSet.remove(deviceId);
- }
- }
-
@Override
protected void cleanupEntityOnPartitionRemoval(DeviceId deviceId) {
cleanupEntity(deviceId);
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
index b18cf7992c..37427c2f99 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
@@ -117,7 +117,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
}
callback.onSuccess();
if (event.hasTsOrAttrSub()) {
- sendSubEventCallback(serviceId, entityId, event.getSeqNumber());
+ sendSubEventCallback(tenantId, serviceId, entityId, event.getSeqNumber());
}
} else {
log.warn("[{}][{}][{}] Event belongs to external partition. Probably re-balancing is in progress. Topic: {}"
@@ -141,12 +141,12 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
}
}
- private void sendSubEventCallback(String targetId, EntityId entityId, int seqNumber) {
+ private void sendSubEventCallback(TenantId tenantId, String targetId, EntityId entityId, int seqNumber) {
var update = getEntityUpdatesInfo(entityId);
if (serviceId.equals(targetId)) {
- localSubscriptionService.onSubEventCallback(entityId, seqNumber, update, TbCallback.EMPTY);
+ localSubscriptionService.onSubEventCallback(tenantId, entityId, seqNumber, update, TbCallback.EMPTY);
} else {
- sendCoreNotification(targetId, entityId, TbSubscriptionUtils.toProto(entityId.getId(), seqNumber, update));
+ sendCoreNotification(targetId, entityId, TbSubscriptionUtils.toProto(tenantId, entityId.getId(), seqNumber, update));
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
index 8fdb18cd1d..bbfdd2d44f 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
+import org.springframework.util.ConcurrentReferenceHashMap;
import org.thingsboard.common.util.DeduplicationUtil;
import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.common.util.ThingsBoardExecutors;
@@ -60,6 +61,7 @@ import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -121,12 +123,12 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
private String serviceId;
private ExecutorService subscriptionUpdateExecutor;
- private final Lock subsLock = new ReentrantLock();
+ private final ConcurrentReferenceHashMap locks = new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.SOFT);
@PostConstruct
public void initExecutor() {
subscriptionUpdateExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass());
- tsCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("ts-sub-callback"));
+ tsCallBackExecutor = Executors.newFixedThreadPool(8, ThingsBoardThreadFactory.forName("ts-sub-callback")); //since we are using locks by TenantId
serviceId = serviceInfoProvider.getServiceId();
staleSessionCleanupExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("stale-session-cleanup"));
staleSessionCleanupExecutor.scheduleWithFixedDelay(this::cleanupStaleSessions, 60, 60, TimeUnit.SECONDS);
@@ -157,28 +159,29 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
* Even if we cache locally the list of active subscriptions by entity id, it is still time-consuming operation to get them from cache
* Since number of subscriptions is usually much less than number of devices that are pushing data.
*/
- Set staleSubs = new HashSet<>();
+ Map> staleSubs = new HashMap<>();
subscriptionsByEntityId.forEach((id, sub) -> {
try {
pushSubEventToManagerService(sub.getTenantId(), sub.getEntityId(), sub.toEvent(ComponentLifecycleEvent.UPDATED));
} catch (TenantNotFoundException e) {
- staleSubs.add(id);
+ staleSubs.computeIfAbsent(sub.getTenantId(), key -> new HashSet<>()).add(id);
log.warn("Cleaning up stale subscription {} for tenant {} due to TenantNotFoundException", id, sub.getTenantId());
} catch (Exception e) {
log.error("Failed to push subscription {} to manager service", sub, e);
}
});
- if (!staleSubs.isEmpty()) {
+ staleSubs.forEach((tenantId, subs) -> {
+ var subsLock = getSubsLock(tenantId);
subsLock.lock();
try {
- staleSubs.forEach(entityId -> {
+ subs.forEach(entityId -> {
subscriptionsByEntityId.remove(entityId);
entityUpdates.remove(entityId);
});
} finally {
subsLock.unlock();
}
- }
+ });
}
}
@@ -198,6 +201,10 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
});
}
+ Lock getSubsLock(TenantId tenantId) {
+ return locks.computeIfAbsent(tenantId, x -> new ReentrantLock());
+ }
+
@Override
public void addSubscription(TbSubscription> subscription, WebSocketSessionRef sessionRef) {
TenantId tenantId = subscription.getTenantId();
@@ -213,6 +220,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
log.debug("[{}][{}] Register subscription: {}", tenantId, entityId, subscription);
SubscriptionModificationResult result;
+ final Lock subsLock = getSubsLock(tenantId);
subsLock.lock();
try {
Map> sessionSubscriptions = subscriptionsBySessionId.computeIfAbsent(subscription.getSessionId(), k -> new ConcurrentHashMap<>());
@@ -228,19 +236,26 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
@Override
public void onSubEventCallback(TransportProtos.TbEntitySubEventCallbackProto subEventCallback, TbCallback callback) {
+ TenantId tenantId;
+ if (subEventCallback.getTenantIdMSB() == 0 && subEventCallback.getTenantIdLSB() == 0) {
+ tenantId = TenantId.SYS_TENANT_ID; //TODO: remove after release
+ } else {
+ tenantId = TenantId.fromUUID(new UUID(subEventCallback.getTenantIdMSB(), subEventCallback.getTenantIdLSB()));
+ }
UUID entityId = new UUID(subEventCallback.getEntityIdMSB(), subEventCallback.getEntityIdLSB());
- onSubEventCallback(entityId, subEventCallback.getSeqNumber(), new TbEntityUpdatesInfo(subEventCallback.getAttributesUpdateTs(), subEventCallback.getTimeSeriesUpdateTs()), callback);
+ onSubEventCallback(tenantId, entityId, subEventCallback.getSeqNumber(), new TbEntityUpdatesInfo(subEventCallback.getAttributesUpdateTs(), subEventCallback.getTimeSeriesUpdateTs()), callback);
}
@Override
- public void onSubEventCallback(EntityId entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback callback) {
- onSubEventCallback(entityId.getId(), seqNumber, entityUpdatesInfo, callback);
+ public void onSubEventCallback(TenantId tenantId, EntityId entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback callback) {
+ onSubEventCallback(tenantId, entityId.getId(), seqNumber, entityUpdatesInfo, callback);
}
- public void onSubEventCallback(UUID entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback callback) {
- log.debug("[{}][{}] Processing sub event callback: {}.", entityId, seqNumber, entityUpdatesInfo);
+ private void onSubEventCallback(TenantId tenantId, UUID entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback callback) {
+ log.debug("[{}][{}][{}] Processing sub event callback: {}.", tenantId, entityId, seqNumber, entityUpdatesInfo);
entityUpdates.put(entityId, entityUpdatesInfo);
Set> pendingSubs = null;
+ Lock subsLock = getSubsLock(tenantId);
subsLock.lock();
try {
TbEntityLocalSubsInfo entitySubs = subscriptionsByEntityId.get(entityId);
@@ -257,24 +272,26 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
}
@Override
- public void cancelSubscription(String sessionId, int subscriptionId) {
- log.debug("[{}][{}] Going to remove subscription.", sessionId, subscriptionId);
+ public void cancelSubscription(TenantId tenantId, String sessionId, int subscriptionId) {
+ log.debug("[{}][{}][{}] Going to remove subscription.", tenantId, sessionId, subscriptionId);
SubscriptionModificationResult result = null;
+ Lock subsLock = getSubsLock(tenantId);
subsLock.lock();
try {
Map> sessionSubscriptions = subscriptionsBySessionId.get(sessionId);
if (sessionSubscriptions != null) {
TbSubscription> subscription = sessionSubscriptions.remove(subscriptionId);
if (subscription != null) {
+
if (sessionSubscriptions.isEmpty()) {
subscriptionsBySessionId.remove(sessionId);
}
result = modifySubscription(subscription.getTenantId(), subscription.getEntityId(), subscription, false);
} else {
- log.debug("[{}][{}] Subscription not found!", sessionId, subscriptionId);
+ log.debug("[{}][{}][{}] Subscription not found!", tenantId, sessionId, subscriptionId);
}
} else {
- log.debug("[{}] No session subscriptions found!", sessionId);
+ log.debug("[{}][{}] No session subscriptions found!", tenantId, sessionId);
}
} finally {
subsLock.unlock();
@@ -285,18 +302,19 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
}
@Override
- public void cancelAllSessionSubscriptions(String sessionId) {
- log.debug("[{}] Going to remove session subscriptions.", sessionId);
+ public void cancelAllSessionSubscriptions(TenantId tenantId, String sessionId) {
+ log.debug("[{}][{}] Going to remove session subscriptions.", tenantId, sessionId);
List results = new ArrayList<>();
+ Lock subsLock = getSubsLock(tenantId);
subsLock.lock();
try {
Map> sessionSubscriptions = subscriptionsBySessionId.remove(sessionId);
if (sessionSubscriptions != null) {
for (TbSubscription> subscription : sessionSubscriptions.values()) {
- results.add(modifySubscription(subscription.getTenantId(), subscription.getEntityId(), subscription, false));
+ results.add(modifySubscription(tenantId, subscription.getEntityId(), subscription, false));
}
} else {
- log.debug("[{}] No session subscriptions found!", sessionId);
+ log.debug("[{}][{}] No session subscriptions found!", tenantId, sessionId);
}
} finally {
subsLock.unlock();
@@ -604,7 +622,12 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
}
private void cleanupStaleSessions() {
- subscriptionsBySessionId.keySet().forEach(webSocketService::cleanupIfStale);
+ subscriptionsBySessionId.forEach((sessionId, subscriptions) ->
+ subscriptions.values()
+ .stream()
+ .findAny()
+ .ifPresent(subscription -> webSocketService.cleanupIfStale(subscription.getTenantId(), sessionId))
+ );
}
private void handleRateLimitError(TbSubscription> subscription, WebSocketSessionRef sessionRef, String message) {
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java
index 77ef960d72..1ec47beeab 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java
@@ -113,7 +113,7 @@ public abstract class TbAbstractDataSubCtx {
protected void clearDynamicValueSubscriptions() {
if (subToDynamicValueKeySet != null) {
for (Integer subId : subToDynamicValueKeySet) {
- localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), subId);
+ localSubscriptionService.cancelSubscription(getTenantId(), sessionRef.getSessionId(), subId);
}
subToDynamicValueKeySet.clear();
}
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java
index 828da388fb..93c5f4323d 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java
@@ -341,7 +341,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx {
long startTs = System.currentTimeMillis() - query.getPageLink().getTimeWindow();
newSubsList.forEach(entity -> createAlarmSubscriptionForEntity(query.getPageLink(), startTs, entity));
}
- subIdsToCancel.forEach(subId -> localSubscriptionService.cancelSubscription(getSessionId(), subId));
+ subIdsToCancel.forEach(subId -> localSubscriptionService.cancelSubscription(getTenantId(), getSessionId(), subId));
subsToAdd.forEach(subscription -> localSubscriptionService.addSubscription(subscription, sessionRef));
}
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java
index 48458ca6fd..704e4f5c80 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java
@@ -225,7 +225,7 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx {
}
}
}
- subIdsToCancel.forEach(subId -> localSubscriptionService.cancelSubscription(getSessionId(), subId));
+ subIdsToCancel.forEach(subId -> localSubscriptionService.cancelSubscription(getTenantId(), getSessionId(), subId));
subsToAdd.forEach(subscription -> localSubscriptionService.addSubscription(subscription, sessionRef));
sendWsMsg(new EntityDataUpdate(cmdId, data, null, maxEntitiesPerDataSubscription));
}
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java
index ade8b8236f..f5a5639ec0 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java
@@ -27,8 +27,6 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
/**
* Information about the local websocket subscriptions.
@@ -42,8 +40,6 @@ public class TbEntityLocalSubsInfo {
@Getter
private final EntityId entityId;
@Getter
- private final Lock lock = new ReentrantLock();
- @Getter
private final Set> subs = ConcurrentHashMap.newKeySet();
private volatile TbSubscriptionsInfo state = new TbSubscriptionsInfo();
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
index 59e7ad532d..6d742e88a6 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
@@ -34,11 +34,11 @@ public interface TbLocalSubscriptionService {
void onSubEventCallback(TransportProtos.TbEntitySubEventCallbackProto subEventCallback, TbCallback callback);
- void onSubEventCallback(EntityId entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback empty);
+ void onSubEventCallback(TenantId tenantId, EntityId entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback empty);
- void cancelSubscription(String sessionId, int subscriptionId);
+ void cancelSubscription(TenantId tenantId, String sessionId, int subscriptionId);
- void cancelAllSessionSubscriptions(String sessionId);
+ void cancelAllSessionSubscriptions(TenantId tenantId, String sessionId);
void onTimeSeriesUpdate(TransportProtos.TbSubUpdateProto tsUpdate, TbCallback callback);
diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java
index 2d8440fa20..f5572a9a5c 100644
--- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java
+++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java
@@ -82,10 +82,12 @@ public class TbSubscriptionUtils {
return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder).build();
}
- public static ToCoreNotificationMsg toProto(UUID id, int seqNumber, TbEntityUpdatesInfo update) {
+ public static ToCoreNotificationMsg toProto(TenantId tenantId, UUID id, int seqNumber, TbEntityUpdatesInfo update) {
TransportProtos.TbEntitySubEventCallbackProto.Builder updateProto = TransportProtos.TbEntitySubEventCallbackProto.newBuilder()
.setEntityIdMSB(id.getMostSignificantBits())
.setEntityIdLSB(id.getLeastSignificantBits())
+ .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
+ .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setSeqNumber(seqNumber)
.setAttributesUpdateTs(update.attributesUpdateTs)
.setTimeSeriesUpdateTs(update.timeSeriesUpdateTs);
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
index 846c32b7fb..e26ede2bae 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
@@ -78,7 +78,7 @@ public abstract class AbstractSubscriptionService extends TbApplicationEventList
protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) {
if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) {
currentPartitions.clear();
- currentPartitions.addAll(partitionChangeEvent.getPartitions());
+ currentPartitions.addAll(partitionChangeEvent.getCorePartitions());
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java
index 33b011cc0a..8d96d14b49 100644
--- a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java
+++ b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java
@@ -193,17 +193,18 @@ public class DefaultWebSocketService implements WebSocketService {
@Override
public void handleSessionEvent(WebSocketSessionRef sessionRef, SessionEvent event) {
String sessionId = sessionRef.getSessionId();
+ TenantId tenantId = sessionRef.getSecurityCtx().getTenantId();
log.debug(PROCESSING_MSG, sessionId, event);
switch (event.getEventType()) {
case ESTABLISHED:
wsSessionsMap.put(sessionId, new WsSessionMetaData(sessionRef));
break;
case ERROR:
- log.debug("[{}] Unknown websocket session error: ", sessionId,
+ log.debug("[{}][{}] Unknown websocket session error: ", tenantId, sessionId,
event.getError().orElse(new RuntimeException("No error specified")));
break;
case CLOSED:
- cleanupSessionById(sessionId);
+ cleanupSessionById(tenantId, sessionId);
processSessionClose(sessionRef);
break;
}
@@ -297,10 +298,10 @@ public class DefaultWebSocketService implements WebSocketService {
}
@Override
- public void cleanupIfStale(String sessionId) {
+ public void cleanupIfStale(TenantId tenantId, String sessionId) {
if (!msgEndpoint.isOpen(sessionId)) {
log.info("[{}] Cleaning up stale session ", sessionId);
- cleanupSessionById(sessionId);
+ cleanupSessionById(tenantId, sessionId);
}
}
@@ -754,22 +755,23 @@ public class DefaultWebSocketService implements WebSocketService {
}
private void unsubscribe(WebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) {
+ TenantId tenantId = sessionRef.getSecurityCtx().getTenantId();
if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) {
- log.warn("[{}][{}] Cleanup session due to empty entity id.", sessionId, cmd.getCmdId());
- cleanupSessionById(sessionId);
+ log.warn("[{}][{}][{}] Cleanup session due to empty entity id.", tenantId, sessionId, cmd.getCmdId());
+ cleanupSessionById(tenantId, sessionId);
} else {
Integer subId = sessionCmdMap.getOrDefault(sessionId, Collections.emptyMap()).remove(cmd.getCmdId());
if (subId == null) {
- log.trace("[{}][{}] Failed to lookup subscription id mapping", sessionId, cmd.getCmdId());
+ log.trace("[{}][{}][{}] Failed to lookup subscription id mapping", tenantId, sessionId, cmd.getCmdId());
subId = cmd.getCmdId();
}
- oldSubService.cancelSubscription(sessionId, subId);
+ oldSubService.cancelSubscription(tenantId, sessionId, subId);
}
}
- private void cleanupSessionById(String sessionId) {
+ private void cleanupSessionById(TenantId tenantId, String sessionId) {
wsSessionsMap.remove(sessionId);
- oldSubService.cancelAllSessionSubscriptions(sessionId);
+ oldSubService.cancelAllSessionSubscriptions(tenantId, sessionId);
sessionCmdMap.remove(sessionId);
entityDataSubService.cancelAllSessionSubscriptions(sessionId);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java
index 284431ff84..00fd19780d 100644
--- a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java
+++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.service.ws;
import org.springframework.web.socket.CloseStatus;
+import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.subscription.SubscriptionErrorCode;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate;
import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate;
@@ -37,6 +38,6 @@ public interface WebSocketService {
void close(String sessionId, CloseStatus status);
- void cleanupIfStale(String sessionId);
+ void cleanupIfStale(TenantId tenantId, String sessionId);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java
index 44919b138c..7ce7bd5735 100644
--- a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java
+++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.ws;
import lombok.Builder;
import lombok.Data;
+import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.net.InetSocketAddress;
@@ -39,6 +40,10 @@ public class WebSocketSessionRef {
private final WebSocketSessionType sessionType;
private final AtomicInteger sessionSubIdSeq = new AtomicInteger();
+ public TenantId getTenantId() {
+ return securityCtx != null ? securityCtx.getTenantId() : TenantId.SYS_TENANT_ID;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java
index 285c58a0f5..1a9ff62924 100644
--- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java
+++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java
@@ -249,7 +249,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH
@Override
public void handleUnsubCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd) {
- localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), cmd.getCmdId());
+ localSubscriptionService.cancelSubscription(sessionRef.getTenantId(), sessionRef.getSessionId(), cmd.getCmdId());
}
private void sendUpdate(String sessionId, CmdUpdate update) {
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index c3a5998b60..785f124583 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -50,6 +50,10 @@ server:
key_alias: "${SSL_KEY_ALIAS:tomcat}"
# Password used to access the key
key_password: "${SSL_KEY_PASSWORD:thingsboard}"
+ # HTTP settings
+ http:
+ # Semi-colon-separated list of urlPattern=maxPayloadSize pairs that define max http request size for specified url pattern. After first match all other will be skipped
+ max_payload_size: "${HTTP_MAX_PAYLOAD_SIZE_LIMIT_CONFIGURATION:/api/image*/**=52428800;/api/resource/**=52428800;/api/**=16777216}"
# HTTP/2 support (takes effect only if server SSL is enabled)
http2:
# Enable/disable HTTP/2 support
@@ -572,6 +576,9 @@ cache:
edges:
timeToLiveInMinutes: "${CACHE_SPECS_EDGES_TTL:1440}" # Edges cache TTL
maxSize: "${CACHE_SPECS_EDGES_MAX_SIZE:10000}" # 0 means the cache is disabled
+ edgeSessions:
+ timeToLiveInMinutes: "${CACHE_SPECS_EDGE_SESSIONS_TTL:0}" # Edge Sessions cache TTL; no expiration time if set to '0'
+ maxSize: "${CACHE_SPECS_EDGE_SESSIONS_MAX_SIZE:10000}" # 0 means the cache is disabled
relatedEdges:
timeToLiveInMinutes: "${CACHE_SPECS_RELATED_EDGES_TTL:1440}" # Related Edges cache TTL
maxSize: "${CACHE_SPECS_RELATED_EDGES_MAX_SIZE:10000}" # 0 means the cache is disabled
@@ -995,8 +1002,8 @@ transport:
request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}"
# HTTP maximum request processing timeout in milliseconds
max_request_timeout: "${HTTP_MAX_REQUEST_TIMEOUT:300000}"
- # Maximum request size
- max_payload_size: "${HTTP_MAX_PAYLOAD_SIZE:65536}" # max payload size in bytes
+ # Semi-colon-separated list of urlPattern=maxPayloadSize pairs that define max http request size for specified url pattern. After first match all other will be skipped
+ max_payload_size: "${HTTP_TRANSPORT_MAX_PAYLOAD_SIZE_LIMIT_CONFIGURATION:/api/v1/*/rpc/**=65536;/api/v1/**=52428800}"
# Local MQTT transport parameters
mqtt:
# Enable/disable mqtt transport protocol.
@@ -1380,6 +1387,8 @@ edges:
no_read_records_sleep: "${EDGES_NO_READ_RECORDS_SLEEP:1000}"
# Number of milliseconds to wait before resending failed batch of edge events to edge
sleep_between_batches: "${EDGES_SLEEP_BETWEEN_BATCHES:60000}"
+ # Max number of high priority edge events per edge session. No persistence - stored in memory
+ max_high_priority_queue_size_per_session: "${EDGES_MAX_HIGH_PRIORITY_QUEUE_SIZE_PER_SESSION:10000}"
# Number of threads that are used to check DB for edge events
scheduler_pool_size: "${EDGES_SCHEDULER_POOL_SIZE:1}"
# Number of threads that are used to send downlink messages to edge over gRPC
@@ -1503,9 +1512,9 @@ queue:
- key: max.poll.interval.ms
# Example of specific consumer properties value per topic for VC
value: "${TB_QUEUE_KAFKA_VC_MAX_POLL_INTERVAL_MS:600000}"
- # tb_rule_engine.sq:
- # - key: max.poll.records
- # value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}"
+ # tb_rule_engine.sq:
+ # - key: max.poll.records
+ # value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}"
tb_housekeeper:
# Consumer properties for Housekeeper tasks topic
- key: max.poll.records
@@ -1541,6 +1550,8 @@ queue:
housekeeper: "${TB_QUEUE_KAFKA_HOUSEKEEPER_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
# Kafka properties for Housekeeper reprocessing topic; retention.ms is set to 90 days; partitions is set to 1 since only one reprocessing service is running at a time
housekeeper-reprocessing: "${TB_QUEUE_KAFKA_HOUSEKEEPER_REPROCESSING_TOPIC_PROPERTIES:retention.ms:7776000000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
+ # Kafka properties for Edge topic
+ edge: "${TB_QUEUE_KAFKA_EDGE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
consumer-stats:
# Prints lag between consumer group offset and last messages offset in Kafka topics
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
@@ -1576,6 +1587,8 @@ queue:
ota-updates: "${TB_QUEUE_AWS_SQS_OTA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
+ # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
+ edge: "${TB_QUEUE_AWS_SQS_EDGE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
pubsub:
# Project ID from Google Cloud
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@@ -1600,6 +1613,8 @@ queue:
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
# Pub/Sub properties for Transport Api subscribers, messages which will commit after ackDeadlineInSec period can be consumed again
version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
+ # Pub/Sub properties for Edge subscribers, messages which will commit after ackDeadlineInSec period can be consumed again
+ edge: "${TB_QUEUE_PUBSUB_EDGE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus:
# Azure namespace
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
@@ -1622,6 +1637,8 @@ queue:
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
# Azure Service Bus properties for Version Control queues
version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
+ # Azure Service Bus properties for Edge queues
+ edge: "${TB_QUEUE_SERVICE_BUS_EDGE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq:
# By default empty
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
@@ -1654,6 +1671,8 @@ queue:
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
# RabbitMQ properties for Version Control queues
version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
+ # RabbitMQ properties for Edge queues
+ edge: "${TB_QUEUE_RABBIT_MQ_EDGE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256
transport_api:
@@ -1772,6 +1791,24 @@ queue:
notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}"
# Interval in milliseconds to poll messages
poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}"
+ edge:
+ # Default topic name for Kafka, RabbitMQ, etc.
+ topic: "${TB_QUEUE_EDGE_TOPIC:tb_edge}"
+ # Amount of partitions used by Edge services
+ partitions: "${TB_QUEUE_EDGE_PARTITIONS:10}"
+ # Poll interval for topics related to Edge services
+ poll-interval: "${TB_QUEUE_EDGE_POLL_INTERVAL_MS:25}"
+ # Timeout for processing a message pack by Edge services
+ pack-processing-timeout: "${TB_QUEUE_EDGE_PACK_PROCESSING_TIMEOUT_MS:10000}"
+ # Retries for processing a failure message pack by Edge services
+ pack-processing-retries: "${TB_QUEUE_EDGE_MESSAGE_PROCESSING_RETRIES:3}"
+ # Enable/disable a separate consumer per partition for Edge queue
+ consumer-per-partition: "${TB_QUEUE_EDGE_CONSUMER_PER_PARTITION:false}"
+ stats:
+ # Enable/disable statistics for Edge services
+ enabled: "${TB_QUEUE_EDGE_STATS_ENABLED:true}"
+ # Statistics printing interval for Edge services
+ print-interval-ms: "${TB_QUEUE_EDGE_STATS_PRINT_INTERVAL_MS:60000}"
# Event configuration parameters
event:
diff --git a/application/src/test/java/org/thingsboard/server/cache/CaffeineCacheDefaultConfigurationTest.java b/application/src/test/java/org/thingsboard/server/cache/CaffeineCacheDefaultConfigurationTest.java
index d92ccfeacb..a173b4889a 100644
--- a/application/src/test/java/org/thingsboard/server/cache/CaffeineCacheDefaultConfigurationTest.java
+++ b/application/src/test/java/org/thingsboard/server/cache/CaffeineCacheDefaultConfigurationTest.java
@@ -46,7 +46,11 @@ public class CaffeineCacheDefaultConfigurationTest {
SoftAssertions softly = new SoftAssertions();
cacheSpecsMap.getSpecs().forEach((name, cacheSpecs) -> {
softly.assertThat(name).as("cache name").isNotEmpty();
- softly.assertThat(cacheSpecs.getTimeToLiveInMinutes()).as("cache %s time to live", name).isGreaterThan(0);
+ if (name.equals("edgeSessions")) {
+ softly.assertThat(cacheSpecs.getTimeToLiveInMinutes()).as("cache %s time to live", name).isEqualTo(0);
+ } else {
+ softly.assertThat(cacheSpecs.getTimeToLiveInMinutes()).as("cache %s time to live", name).isGreaterThan(0);
+ }
softly.assertThat(cacheSpecs.getMaxSize()).as("cache %s max size", name).isGreaterThan(0);
});
softly.assertAll();
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java
index d549407c44..238203cdb0 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java
@@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
+import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.model.ModelConstants;
@@ -230,6 +231,19 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.reset(tbClusterService, auditLogService);
}
+ protected void testNotifyEdgeStateChangeEventManyTimeMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
+ TenantId tenantId, CustomerId customerId, UserId userId, String userName,
+ ActionType actionType, int cntTimeBroadcast, Object... additionalInfo) {
+ int cntTime = 1;
+ testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
+ testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
+ ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId);
+ testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
+ testBroadcastEdgeStateChangeEventTime(tenantId, cntTimeBroadcast);
+ Mockito.reset(tbClusterService, auditLogService);
+ }
+
+
protected void testNotifyEntityBroadcastEntityStateChangeEventMany(HasName entity, HasName originator,
TenantId tenantId, CustomerId customerId,
UserId userId, String userName, ActionType actionType,
@@ -351,11 +365,17 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
protected void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) {
ArgumentMatcher matcherTenantIdId = cntTime > 1 || tenantId == null ? argument -> argument.getClass().equals(TenantId.class) :
- argument -> argument.equals(tenantId) ;
+ argument -> argument.equals(tenantId);
Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.argThat(matcherTenantIdId),
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
}
+ protected void testBroadcastEdgeStateChangeEventTime(TenantId tenantId, int cntTime) {
+ ArgumentMatcher matcherTenantIdId = cntTime > 1 || tenantId == null ? argument -> argument.getClass().equals(TenantId.class) :
+ argument -> argument.equals(tenantId);
+ Mockito.verify(tbClusterService, times(cntTime)).onEdgeStateChangeEvent(Mockito.any(ComponentLifecycleMsg.class));
+ }
+
private void testPushMsgToCoreTime(int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).pushMsgToCore(Mockito.any(ToDeviceActorNotificationMsg.class), Mockito.isNull());
}
@@ -608,8 +628,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
}
private String entityClassToEntityTypeName(HasName entity) {
- String entityType = entityClassToString(entity);
- return "SAVE_OTA_PACKAGE_INFO_REQUEST".equals(entityType) || "OTA_PACKAGE_INFO".equals(entityType)?
+ String entityType = entityClassToString(entity);
+ return "SAVE_OTA_PACKAGE_INFO_REQUEST".equals(entityType) || "OTA_PACKAGE_INFO".equals(entityType) ?
EntityType.OTA_PACKAGE.name().toUpperCase(Locale.ENGLISH) : entityType;
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
index cf46d0f7fd..c036f70e4a 100644
--- a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
@@ -1503,11 +1503,12 @@ public class DeviceControllerTest extends AbstractControllerTest {
Assert.assertTrue(deviceBulkImportResult.getErrorsList().isEmpty());
Device updatedDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
+ savedDevice.setVersion(updatedDevice.getVersion());
Assert.assertEquals(savedDevice, updatedDevice);
DeviceCredentials updatedCredentials =
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
-
+ savedCredentials.setVersion(updatedCredentials.getVersion());
Assert.assertEquals(savedCredentials, updatedCredentials);
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
index b636849774..4d38934dcb 100644
--- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
@@ -40,8 +40,8 @@ import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.AdminSettings;
+import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.Customer;
-import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUpgradeInfo;
@@ -62,6 +62,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@@ -166,7 +167,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertEquals(NULL_UUID, savedEdge.getCustomerId().getId());
Assert.assertEquals(edge.getName(), savedEdge.getName());
- testNotifyEntityBroadcastEntityStateChangeEventManyTimeMsgToEdgeServiceNever(savedEdge, savedEdge.getId(), savedEdge.getId(),
+ testNotifyEdgeStateChangeEventManyTimeMsgToEdgeServiceNever(savedEdge, savedEdge.getId(), savedEdge.getId(),
tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(),
ActionType.ADDED, 2);
@@ -176,7 +177,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
Edge foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class);
Assert.assertEquals(foundEdge.getName(), savedEdge.getName());
- testNotifyEntityBroadcastEntityStateChangeEventManyTimeMsgToEdgeServiceNever(foundEdge, foundEdge.getId(), foundEdge.getId(),
+ testNotifyEdgeStateChangeEventManyTimeMsgToEdgeServiceNever(foundEdge, foundEdge.getId(), foundEdge.getId(),
tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(),
ActionType.UPDATED, 1);
}
@@ -876,7 +877,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
Asset asset = new Asset();
asset.setName("Test Sync Edge Asset 1");
- asset.setType("test");
+ asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Device device = new Device();
@@ -896,7 +897,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class);
edgeImitator.ignoreType(OAuth2DomainUpdateMsg.class);
- edgeImitator.expectMessageAmount(27);
+ edgeImitator.expectMessageAmount(26);
edgeImitator.connect();
waitForMessages(edgeImitator);
@@ -905,16 +906,16 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId()));
- Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test"));
+ Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
- Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty());
+ printQueueMsgsIfNotEmpty(edgeImitator);
- edgeImitator.expectMessageAmount(22);
- doPost("/api/edge/sync/" + edge.getId());
+ edgeImitator.expectMessageAmount(21);
+ doPost("/api/edge/sync/" + edge.getId()).andExpect(status().isOk());
waitForMessages(edgeImitator);
verifyFetchersMsgs(edgeImitator, savedDevice);
- Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty());
+ printQueueMsgsIfNotEmpty(edgeImitator);
edgeImitator.allowIgnoredTypes();
try {
@@ -930,6 +931,15 @@ public class EdgeControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
+ private static void printQueueMsgsIfNotEmpty(EdgeImitator edgeImitator) {
+ if (!edgeImitator.getDownlinkMsgs().isEmpty()) {
+ for (AbstractMessage downlinkMsg : edgeImitator.getDownlinkMsgs()) {
+ log.warn("Unexpected message in the queue: {}", downlinkMsg);
+ }
+ }
+ Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty());
+ }
+
private RuleChainId getEdgeRootRuleChainId(EdgeImitator edgeImitator) {
try {
EdgeId edgeId = new EdgeId(new UUID(edgeImitator.getConfiguration().getEdgeIdMSB(), edgeImitator.getConfiguration().getEdgeIdLSB()));
@@ -948,11 +958,18 @@ public class EdgeControllerTest extends AbstractControllerTest {
}
private void simulateEdgeActivation(Edge edge) throws Exception {
+ Awaitility.await()
+ .atMost(TIMEOUT, TimeUnit.SECONDS)
+ .until(() -> {
+ List ruleChains = getEdgeRuleChains(edge.getId());
+ return ruleChains.size() == 1 && ruleChains.get(0).getId().equals(edge.getRootRuleChainId());
+ });
+
ObjectNode attributes = JacksonUtil.newObjectNode();
attributes.put("active", true);
- doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes);
+ doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + AttributeScope.SERVER_SCOPE, attributes);
Awaitility.await()
- .atMost(30, TimeUnit.SECONDS)
+ .atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> {
List