Showing
96 changed files
with
1922 additions
and
1408 deletions
Too many changes to show.
To preserve performance only 96 of 191 files are displayed.
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | "resources": [], | 15 | "resources": [], |
16 | "templateHtml": "", | 16 | "templateHtml": "", |
17 | "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", | 17 | "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", |
18 | - "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"<div id='\" + datasourceId +\n \"' class='tbDatasource-container'></div>\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"<div class='tbDatasource-title'>\" +\n tbDatasource.name + \"</div>\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"<table id='\" + tableId +\n \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n \"</td><td id='\" + cellId +\n \"'></td></tr>\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n console.log(self.ctx); //del\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasoirceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasoirceTitleFontSize = self.ctx.width/12;\n }\n datasoirceTitleFontSize = Math.min(datasoirceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasoirceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n", | 18 | + "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"<div id='\" + datasourceId +\n \"' class='tbDatasource-container'></div>\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"<div class='tbDatasource-title'>\" +\n tbDatasource.name + \"</div>\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"<table id='\" + tableId +\n \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n \"</td><td id='\" + cellId +\n \"'></td></tr>\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasoirceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasoirceTitleFontSize = self.ctx.width/12;\n }\n datasoirceTitleFontSize = Math.min(datasoirceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasoirceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n", |
19 | "settingsSchema": "{}", | 19 | "settingsSchema": "{}", |
20 | "dataKeySettingsSchema": "{}\n", | 20 | "dataKeySettingsSchema": "{}\n", |
21 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\"}" | 21 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\"}" |
@@ -27,7 +27,7 @@ | @@ -27,7 +27,7 @@ | ||
27 | "descriptor": { | 27 | "descriptor": { |
28 | "type": "latest", | 28 | "type": "latest", |
29 | "sizeX": 7.5, | 29 | "sizeX": 7.5, |
30 | - "sizeY": 4.5, | 30 | + "sizeY": 6.5, |
31 | "resources": [], | 31 | "resources": [], |
32 | "templateHtml": "<tb-entities-table-widget \n table-id=\"tableId\"\n ctx=\"ctx\">\n</tb-entities-table-widget>", | 32 | "templateHtml": "<tb-entities-table-widget \n table-id=\"tableId\"\n ctx=\"ctx\">\n</tb-entities-table-widget>", |
33 | "templateCss": "", | 33 | "templateCss": "", |
1 | +-- | ||
2 | +-- Copyright © 2016-2018 The Thingsboard Authors | ||
3 | +-- | ||
4 | +-- Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +-- you may not use this file except in compliance with the License. | ||
6 | +-- You may obtain a copy of the License at | ||
7 | +-- | ||
8 | +-- http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +-- | ||
10 | +-- Unless required by applicable law or agreed to in writing, software | ||
11 | +-- distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +-- See the License for the specific language governing permissions and | ||
14 | +-- limitations under the License. | ||
15 | +-- | ||
16 | + | ||
17 | +ALTER TABLE component_descriptor ADD UNIQUE (clazz); |
@@ -67,6 +67,7 @@ import org.thingsboard.server.service.mail.MailExecutorService; | @@ -67,6 +67,7 @@ import org.thingsboard.server.service.mail.MailExecutorService; | ||
67 | import org.thingsboard.server.service.rpc.DeviceRpcService; | 67 | import org.thingsboard.server.service.rpc.DeviceRpcService; |
68 | import org.thingsboard.server.service.script.JsExecutorService; | 68 | import org.thingsboard.server.service.script.JsExecutorService; |
69 | import org.thingsboard.server.service.script.JsInvokeService; | 69 | import org.thingsboard.server.service.script.JsInvokeService; |
70 | +import org.thingsboard.server.service.session.DeviceSessionCacheService; | ||
70 | import org.thingsboard.server.service.state.DeviceStateService; | 71 | import org.thingsboard.server.service.state.DeviceStateService; |
71 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | 72 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
72 | import org.thingsboard.server.service.transport.RuleEngineTransportService; | 73 | import org.thingsboard.server.service.transport.RuleEngineTransportService; |
@@ -201,6 +202,10 @@ public class ActorSystemContext { | @@ -201,6 +202,10 @@ public class ActorSystemContext { | ||
201 | @Getter | 202 | @Getter |
202 | private DeviceStateService deviceStateService; | 203 | private DeviceStateService deviceStateService; |
203 | 204 | ||
205 | + @Autowired | ||
206 | + @Getter | ||
207 | + private DeviceSessionCacheService deviceSessionCacheService; | ||
208 | + | ||
204 | @Lazy | 209 | @Lazy |
205 | @Autowired | 210 | @Autowired |
206 | @Getter | 211 | @Getter |
@@ -254,6 +259,14 @@ public class ActorSystemContext { | @@ -254,6 +259,14 @@ public class ActorSystemContext { | ||
254 | @Getter | 259 | @Getter |
255 | private boolean allowSystemMailService; | 260 | private boolean allowSystemMailService; |
256 | 261 | ||
262 | + @Value("${transport.sessions.inactivity_timeout}") | ||
263 | + @Getter | ||
264 | + private long sessionInactivityTimeout; | ||
265 | + | ||
266 | + @Value("${transport.sessions.report_timeout}") | ||
267 | + @Getter | ||
268 | + private long sessionReportTimeout; | ||
269 | + | ||
257 | @Getter | 270 | @Getter |
258 | @Setter | 271 | @Setter |
259 | private ActorSystem actorSystem; | 272 | private ActorSystem actorSystem; |
@@ -44,11 +44,19 @@ public class DeviceActor extends ContextAwareActor { | @@ -44,11 +44,19 @@ public class DeviceActor extends ContextAwareActor { | ||
44 | } | 44 | } |
45 | 45 | ||
46 | @Override | 46 | @Override |
47 | + public void preStart() { | ||
48 | + logger.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId); | ||
49 | + try { | ||
50 | + processor.initSessionTimeout(context()); | ||
51 | + logger.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId); | ||
52 | + } catch (Exception e) { | ||
53 | + logger.error(e, "[{}][{}] Unknown failure", processor.tenantId, processor.deviceId); | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + @Override | ||
47 | protected boolean process(TbActorMsg msg) { | 58 | protected boolean process(TbActorMsg msg) { |
48 | switch (msg.getMsgType()) { | 59 | switch (msg.getMsgType()) { |
49 | - case CLUSTER_EVENT_MSG: | ||
50 | - processor.processClusterEventMsg((ClusterEventMsg) msg); | ||
51 | - break; | ||
52 | case TRANSPORT_TO_DEVICE_ACTOR_MSG: | 60 | case TRANSPORT_TO_DEVICE_ACTOR_MSG: |
53 | processor.process(context(), (TransportToDeviceActorMsgWrapper) msg); | 61 | processor.process(context(), (TransportToDeviceActorMsgWrapper) msg); |
54 | break; | 62 | break; |
@@ -73,6 +81,9 @@ public class DeviceActor extends ContextAwareActor { | @@ -73,6 +81,9 @@ public class DeviceActor extends ContextAwareActor { | ||
73 | case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG: | 81 | case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG: |
74 | processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg); | 82 | processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg); |
75 | break; | 83 | break; |
84 | + case SESSION_TIMEOUT_MSG: | ||
85 | + processor.checkSessionsTimeout(); | ||
86 | + break; | ||
76 | default: | 87 | default: |
77 | return false; | 88 | return false; |
78 | } | 89 | } |
@@ -41,7 +41,6 @@ import org.thingsboard.server.common.msg.TbMsg; | @@ -41,7 +41,6 @@ import org.thingsboard.server.common.msg.TbMsg; | ||
41 | import org.thingsboard.server.common.msg.TbMsgDataType; | 41 | import org.thingsboard.server.common.msg.TbMsgDataType; |
42 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 42 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
43 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; | 43 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
44 | -import org.thingsboard.server.common.msg.cluster.ServerAddress; | ||
45 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | 44 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; |
46 | import org.thingsboard.server.common.msg.session.SessionMsgType; | 45 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
47 | import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; | 46 | import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; |
@@ -89,11 +88,11 @@ import java.util.stream.Collectors; | @@ -89,11 +88,11 @@ import java.util.stream.Collectors; | ||
89 | /** | 88 | /** |
90 | * @author Andrew Shvayka | 89 | * @author Andrew Shvayka |
91 | */ | 90 | */ |
92 | -public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | 91 | +class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
93 | 92 | ||
94 | - private final TenantId tenantId; | ||
95 | - private final DeviceId deviceId; | ||
96 | - private final Map<UUID, SessionInfo> sessions; | 93 | + final TenantId tenantId; |
94 | + final DeviceId deviceId; | ||
95 | + private final Map<UUID, SessionInfoMetaData> sessions; | ||
97 | private final Map<UUID, SessionInfo> attributeSubscriptions; | 96 | private final Map<UUID, SessionInfo> attributeSubscriptions; |
98 | private final Map<UUID, SessionInfo> rpcSubscriptions; | 97 | private final Map<UUID, SessionInfo> rpcSubscriptions; |
99 | private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; | 98 | private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; |
@@ -117,6 +116,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -117,6 +116,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
117 | this.toDeviceRpcPendingMap = new HashMap<>(); | 116 | this.toDeviceRpcPendingMap = new HashMap<>(); |
118 | this.toServerRpcPendingMap = new HashMap<>(); | 117 | this.toServerRpcPendingMap = new HashMap<>(); |
119 | initAttributes(); | 118 | initAttributes(); |
119 | + restoreSessions(); | ||
120 | } | 120 | } |
121 | 121 | ||
122 | private void initAttributes() { | 122 | private void initAttributes() { |
@@ -152,7 +152,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -152,7 +152,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
152 | 152 | ||
153 | if (request.isOneway() && sent) { | 153 | if (request.isOneway() && sent) { |
154 | logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); | 154 | logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); |
155 | - systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(msg.getMsg().getId(), msg.getServerAddress(), null, null)); | 155 | + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); |
156 | } else { | 156 | } else { |
157 | registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); | 157 | registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); |
158 | } | 158 | } |
@@ -161,7 +161,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -161,7 +161,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
161 | } else { | 161 | } else { |
162 | logger.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId()); | 162 | logger.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId()); |
163 | } | 163 | } |
164 | - | ||
165 | } | 164 | } |
166 | 165 | ||
167 | private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { | 166 | private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { |
@@ -174,8 +173,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -174,8 +173,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
174 | ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); | 173 | ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); |
175 | if (requestMd != null) { | 174 | if (requestMd != null) { |
176 | logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); | 175 | logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); |
177 | - systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), | ||
178 | - requestMd.getMsg().getServerAddress(), null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); | 176 | + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), |
177 | + null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); | ||
179 | } | 178 | } |
180 | } | 179 | } |
181 | 180 | ||
@@ -202,12 +201,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -202,12 +201,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
202 | 201 | ||
203 | private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) { | 202 | private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) { |
204 | return entry -> { | 203 | return entry -> { |
205 | - ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg(); | ||
206 | ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg(); | 204 | ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg(); |
207 | ToDeviceRpcRequestBody body = request.getBody(); | 205 | ToDeviceRpcRequestBody body = request.getBody(); |
208 | if (request.isOneway()) { | 206 | if (request.isOneway()) { |
209 | sentOneWayIds.add(entry.getKey()); | 207 | sentOneWayIds.add(entry.getKey()); |
210 | - systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(request.getId(), requestActorMsg.getServerAddress(), null, null)); | 208 | + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(request.getId(), null, null)); |
211 | } | 209 | } |
212 | ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId( | 210 | ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId( |
213 | entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build(); | 211 | entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build(); |
@@ -228,11 +226,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -228,11 +226,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
228 | } | 226 | } |
229 | if (msg.hasPostAttributes()) { | 227 | if (msg.hasPostAttributes()) { |
230 | handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes()); | 228 | handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes()); |
231 | - reportActivity(); | 229 | + reportLogicalDeviceActivity(); |
232 | } | 230 | } |
233 | if (msg.hasPostTelemetry()) { | 231 | if (msg.hasPostTelemetry()) { |
234 | handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry()); | 232 | handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry()); |
235 | - reportActivity(); | 233 | + reportLogicalDeviceActivity(); |
236 | } | 234 | } |
237 | if (msg.hasGetAttributes()) { | 235 | if (msg.hasGetAttributes()) { |
238 | handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); | 236 | handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); |
@@ -242,11 +240,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -242,11 +240,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
242 | } | 240 | } |
243 | if (msg.hasToServerRPCCallRequest()) { | 241 | if (msg.hasToServerRPCCallRequest()) { |
244 | handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest()); | 242 | handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest()); |
245 | - reportActivity(); | 243 | + reportLogicalDeviceActivity(); |
244 | + } | ||
245 | + if (msg.hasSubscriptionInfo()) { | ||
246 | + handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); | ||
246 | } | 247 | } |
247 | } | 248 | } |
248 | 249 | ||
249 | - private void reportActivity() { | 250 | + private void reportLogicalDeviceActivity() { |
250 | systemContext.getDeviceStateService().onDeviceActivity(deviceId); | 251 | systemContext.getDeviceStateService().onDeviceActivity(deviceId); |
251 | } | 252 | } |
252 | 253 | ||
@@ -400,35 +401,27 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -400,35 +401,27 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
400 | ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); | 401 | ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); |
401 | boolean success = requestMd != null; | 402 | boolean success = requestMd != null; |
402 | if (success) { | 403 | if (success) { |
403 | - systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), | ||
404 | - requestMd.getMsg().getServerAddress(), responseMsg.getPayload(), null)); | 404 | + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), |
405 | + responseMsg.getPayload(), null)); | ||
405 | } else { | 406 | } else { |
406 | logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); | 407 | logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); |
407 | } | 408 | } |
408 | } | 409 | } |
409 | 410 | ||
410 | - void processClusterEventMsg(ClusterEventMsg msg) { | ||
411 | -// if (!msg.isAdded()) { | ||
412 | -// logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress()); | ||
413 | -// Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer() | ||
414 | -// .map(serverAddress -> serverAddress.equals(msg.getServerAddress())).orElse(false); | ||
415 | -// attributeSubscriptions.entrySet().removeIf(filter); | ||
416 | -// rpcSubscriptions.entrySet().removeIf(filter); | ||
417 | -// } | ||
418 | - } | ||
419 | - | ||
420 | private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { | 411 | private void processSubscriptionCommands(ActorContext context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { |
421 | UUID sessionId = getSessionId(sessionInfo); | 412 | UUID sessionId = getSessionId(sessionInfo); |
422 | if (subscribeCmd.getUnsubscribe()) { | 413 | if (subscribeCmd.getUnsubscribe()) { |
423 | logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); | 414 | logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); |
424 | attributeSubscriptions.remove(sessionId); | 415 | attributeSubscriptions.remove(sessionId); |
425 | } else { | 416 | } else { |
426 | - SessionInfo session = sessions.get(sessionId); | ||
427 | - if (session == null) { | ||
428 | - session = new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()); | 417 | + SessionInfoMetaData sessionMD = sessions.get(sessionId); |
418 | + if (sessionMD == null) { | ||
419 | + sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId())); | ||
429 | } | 420 | } |
421 | + sessionMD.setSubscribedToAttributes(true); | ||
430 | logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); | 422 | logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); |
431 | - attributeSubscriptions.put(sessionId, session); | 423 | + attributeSubscriptions.put(sessionId, sessionMD.getSessionInfo()); |
424 | + dumpSessions(); | ||
432 | } | 425 | } |
433 | } | 426 | } |
434 | 427 | ||
@@ -442,20 +435,22 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -442,20 +435,22 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
442 | logger.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId); | 435 | logger.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId); |
443 | rpcSubscriptions.remove(sessionId); | 436 | rpcSubscriptions.remove(sessionId); |
444 | } else { | 437 | } else { |
445 | - SessionInfo session = sessions.get(sessionId); | ||
446 | - if (session == null) { | ||
447 | - session = new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()); | 438 | + SessionInfoMetaData sessionMD = sessions.get(sessionId); |
439 | + if (sessionMD == null) { | ||
440 | + sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId())); | ||
448 | } | 441 | } |
442 | + sessionMD.setSubscribedToRPC(true); | ||
449 | logger.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); | 443 | logger.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); |
450 | - rpcSubscriptions.put(sessionId, session); | 444 | + rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo()); |
451 | sendPendingRequests(context, sessionId, sessionInfo); | 445 | sendPendingRequests(context, sessionId, sessionInfo); |
446 | + dumpSessions(); | ||
452 | } | 447 | } |
453 | } | 448 | } |
454 | 449 | ||
455 | private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) { | 450 | private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) { |
456 | UUID sessionId = getSessionId(sessionInfo); | 451 | UUID sessionId = getSessionId(sessionInfo); |
457 | if (msg.getEvent() == SessionEvent.OPEN) { | 452 | if (msg.getEvent() == SessionEvent.OPEN) { |
458 | - if(sessions.containsKey(sessionId)){ | 453 | + if (sessions.containsKey(sessionId)) { |
459 | logger.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId); | 454 | logger.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId); |
460 | return; | 455 | return; |
461 | } | 456 | } |
@@ -463,13 +458,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -463,13 +458,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
463 | if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) { | 458 | if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) { |
464 | UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null); | 459 | UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null); |
465 | if (sessionIdToRemove != null) { | 460 | if (sessionIdToRemove != null) { |
466 | - closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove)); | 461 | + notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove)); |
467 | } | 462 | } |
468 | } | 463 | } |
469 | - sessions.put(sessionId, new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId())); | 464 | + sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId()))); |
470 | if (sessions.size() == 1) { | 465 | if (sessions.size() == 1) { |
471 | reportSessionOpen(); | 466 | reportSessionOpen(); |
472 | } | 467 | } |
468 | + dumpSessions(); | ||
473 | } else if (msg.getEvent() == SessionEvent.CLOSED) { | 469 | } else if (msg.getEvent() == SessionEvent.CLOSED) { |
474 | logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); | 470 | logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); |
475 | sessions.remove(sessionId); | 471 | sessions.remove(sessionId); |
@@ -478,21 +474,40 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -478,21 +474,40 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
478 | if (sessions.isEmpty()) { | 474 | if (sessions.isEmpty()) { |
479 | reportSessionClose(); | 475 | reportSessionClose(); |
480 | } | 476 | } |
477 | + dumpSessions(); | ||
478 | + } | ||
479 | + } | ||
480 | + | ||
481 | + private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto subscriptionInfo) { | ||
482 | + UUID sessionId = getSessionId(sessionInfo); | ||
483 | + SessionInfoMetaData sessionMD = sessions.get(sessionId); | ||
484 | + if (sessionMD != null) { | ||
485 | + sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime()); | ||
486 | + sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription()); | ||
487 | + sessionMD.setSubscribedToRPC(subscriptionInfo.getRpcSubscription()); | ||
488 | + if (subscriptionInfo.getAttributeSubscription()) { | ||
489 | + attributeSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo()); | ||
490 | + } | ||
491 | + if (subscriptionInfo.getRpcSubscription()) { | ||
492 | + rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo()); | ||
493 | + } | ||
481 | } | 494 | } |
495 | + dumpSessions(); | ||
482 | } | 496 | } |
483 | 497 | ||
484 | void processCredentialsUpdate() { | 498 | void processCredentialsUpdate() { |
485 | - sessions.forEach(this::closeSession); | 499 | + sessions.forEach(this::notifyTransportAboutClosedSession); |
486 | attributeSubscriptions.clear(); | 500 | attributeSubscriptions.clear(); |
487 | rpcSubscriptions.clear(); | 501 | rpcSubscriptions.clear(); |
502 | + dumpSessions(); | ||
488 | } | 503 | } |
489 | 504 | ||
490 | - private void closeSession(UUID sessionId, SessionInfo sessionInfo) { | 505 | + private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) { |
491 | DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() | 506 | DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() |
492 | .setSessionIdMSB(sessionId.getMostSignificantBits()) | 507 | .setSessionIdMSB(sessionId.getMostSignificantBits()) |
493 | .setSessionIdLSB(sessionId.getLeastSignificantBits()) | 508 | .setSessionIdLSB(sessionId.getLeastSignificantBits()) |
494 | .setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build(); | 509 | .setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build(); |
495 | - systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg); | 510 | + systemContext.getRuleEngineTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg); |
496 | } | 511 | } |
497 | 512 | ||
498 | void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { | 513 | void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { |
@@ -606,4 +621,75 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -606,4 +621,75 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
606 | } | 621 | } |
607 | return builder.build(); | 622 | return builder.build(); |
608 | } | 623 | } |
624 | + | ||
625 | + private void restoreSessions() { | ||
626 | + logger.debug("[{}] Restoring sessions from cache", deviceId); | ||
627 | + TransportProtos.DeviceSessionsCacheEntry sessionsDump = systemContext.getDeviceSessionCacheService().get(deviceId); | ||
628 | + if (sessionsDump.getSerializedSize() == 0) { | ||
629 | + logger.debug("[{}] No session information found", deviceId); | ||
630 | + return; | ||
631 | + } | ||
632 | + for (TransportProtos.SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) { | ||
633 | + SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo(); | ||
634 | + UUID sessionId = getSessionId(sessionInfoProto); | ||
635 | + SessionInfo sessionInfo = new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfoProto.getNodeId()); | ||
636 | + TransportProtos.SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo(); | ||
637 | + SessionInfoMetaData sessionMD = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime()); | ||
638 | + sessions.put(sessionId, sessionMD); | ||
639 | + if (subInfo.getAttributeSubscription()) { | ||
640 | + attributeSubscriptions.put(sessionId, sessionInfo); | ||
641 | + sessionMD.setSubscribedToAttributes(true); | ||
642 | + } | ||
643 | + if (subInfo.getRpcSubscription()) { | ||
644 | + rpcSubscriptions.put(sessionId, sessionInfo); | ||
645 | + sessionMD.setSubscribedToRPC(true); | ||
646 | + } | ||
647 | + logger.debug("[{}] Restored session: {}", deviceId, sessionMD); | ||
648 | + } | ||
649 | + logger.debug("[{}] Restored sessions: {}, rpc subscriptions: {}, attribute subscriptions: {}", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size()); | ||
650 | + } | ||
651 | + | ||
652 | + private void dumpSessions() { | ||
653 | + logger.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size()); | ||
654 | + List<TransportProtos.SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size()); | ||
655 | + sessions.forEach((uuid, sessionMD) -> { | ||
656 | + if (sessionMD.getSessionInfo().getType() == TransportProtos.SessionType.SYNC) { | ||
657 | + return; | ||
658 | + } | ||
659 | + SessionInfo sessionInfo = sessionMD.getSessionInfo(); | ||
660 | + TransportProtos.SubscriptionInfoProto subscriptionInfoProto = TransportProtos.SubscriptionInfoProto.newBuilder() | ||
661 | + .setLastActivityTime(sessionMD.getLastActivityTime()) | ||
662 | + .setAttributeSubscription(sessionMD.isSubscribedToAttributes()) | ||
663 | + .setRpcSubscription(sessionMD.isSubscribedToRPC()).build(); | ||
664 | + TransportProtos.SessionInfoProto sessionInfoProto = TransportProtos.SessionInfoProto.newBuilder() | ||
665 | + .setSessionIdMSB(uuid.getMostSignificantBits()) | ||
666 | + .setSessionIdLSB(uuid.getLeastSignificantBits()) | ||
667 | + .setNodeId(sessionInfo.getNodeId()).build(); | ||
668 | + sessionsList.add(TransportProtos.SessionSubscriptionInfoProto.newBuilder() | ||
669 | + .setSessionInfo(sessionInfoProto) | ||
670 | + .setSubscriptionInfo(subscriptionInfoProto).build()); | ||
671 | + logger.debug("[{}] Dumping session: {}", deviceId, sessionMD); | ||
672 | + }); | ||
673 | + systemContext.getDeviceSessionCacheService() | ||
674 | + .put(deviceId, TransportProtos.DeviceSessionsCacheEntry.newBuilder() | ||
675 | + .addAllSessions(sessionsList).build()); | ||
676 | + } | ||
677 | + | ||
678 | + void initSessionTimeout(ActorContext context) { | ||
679 | + schedulePeriodicMsgWithDelay(context, SessionTimeoutCheckMsg.instance(), systemContext.getSessionInactivityTimeout(), systemContext.getSessionInactivityTimeout()); | ||
680 | + } | ||
681 | + | ||
682 | + void checkSessionsTimeout() { | ||
683 | + long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout(); | ||
684 | + Map<UUID, SessionInfoMetaData> sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | ||
685 | + sessionsToRemove.forEach((sessionId, sessionMD) -> { | ||
686 | + sessions.remove(sessionId); | ||
687 | + rpcSubscriptions.remove(sessionId); | ||
688 | + attributeSubscriptions.remove(sessionId); | ||
689 | + notifyTransportAboutClosedSession(sessionId, sessionMD); | ||
690 | + }); | ||
691 | + if (!sessionsToRemove.isEmpty()) { | ||
692 | + dumpSessions(); | ||
693 | + } | ||
694 | + } | ||
609 | } | 695 | } |
@@ -25,4 +25,5 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType; | @@ -25,4 +25,5 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType; | ||
25 | public class SessionInfo { | 25 | public class SessionInfo { |
26 | private final SessionType type; | 26 | private final SessionType type; |
27 | private final String nodeId; | 27 | private final String nodeId; |
28 | + private long lastActivityTime; | ||
28 | } | 29 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.actors.device; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionType; | ||
20 | + | ||
21 | +/** | ||
22 | + * @author Andrew Shvayka | ||
23 | + */ | ||
24 | +@Data | ||
25 | +class SessionInfoMetaData { | ||
26 | + private final SessionInfo sessionInfo; | ||
27 | + private long lastActivityTime; | ||
28 | + private boolean subscribedToAttributes; | ||
29 | + private boolean subscribedToRPC; | ||
30 | + | ||
31 | + SessionInfoMetaData(SessionInfo sessionInfo) { | ||
32 | + this(sessionInfo, System.currentTimeMillis()); | ||
33 | + } | ||
34 | + | ||
35 | + SessionInfoMetaData(SessionInfo sessionInfo, long lastActivityTime) { | ||
36 | + this.sessionInfo = sessionInfo; | ||
37 | + this.lastActivityTime = lastActivityTime; | ||
38 | + } | ||
39 | +} |
application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.actors.device; | ||
17 | + | ||
18 | +import org.thingsboard.server.common.msg.MsgType; | ||
19 | +import org.thingsboard.server.common.msg.TbActorMsg; | ||
20 | + | ||
21 | +/** | ||
22 | + * Created by ashvayka on 29.10.18. | ||
23 | + */ | ||
24 | +public class SessionTimeoutCheckMsg implements TbActorMsg { | ||
25 | + | ||
26 | + private static final SessionTimeoutCheckMsg INSTANCE = new SessionTimeoutCheckMsg(); | ||
27 | + | ||
28 | + private SessionTimeoutCheckMsg() { | ||
29 | + } | ||
30 | + | ||
31 | + public static SessionTimeoutCheckMsg instance() { | ||
32 | + return INSTANCE; | ||
33 | + } | ||
34 | + | ||
35 | + @Override | ||
36 | + public MsgType getMsgType() { | ||
37 | + return MsgType.SESSION_TIMEOUT_MSG; | ||
38 | + } | ||
39 | +} |
@@ -32,7 +32,7 @@ public class BasicRpcSessionListener implements GrpcSessionListener { | @@ -32,7 +32,7 @@ public class BasicRpcSessionListener implements GrpcSessionListener { | ||
32 | private final ActorRef manager; | 32 | private final ActorRef manager; |
33 | private final ActorRef self; | 33 | private final ActorRef self; |
34 | 34 | ||
35 | - public BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) { | 35 | + BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) { |
36 | this.service = service; | 36 | this.service = service; |
37 | this.manager = manager; | 37 | this.manager = manager; |
38 | this.self = self; | 38 | this.self = self; |
@@ -45,7 +45,7 @@ public class RpcManagerActor extends ContextAwareActor { | @@ -45,7 +45,7 @@ public class RpcManagerActor extends ContextAwareActor { | ||
45 | 45 | ||
46 | private final ServerAddress instance; | 46 | private final ServerAddress instance; |
47 | 47 | ||
48 | - public RpcManagerActor(ActorSystemContext systemContext) { | 48 | + RpcManagerActor(ActorSystemContext systemContext) { |
49 | super(systemContext); | 49 | super(systemContext); |
50 | this.sessionActors = new HashMap<>(); | 50 | this.sessionActors = new HashMap<>(); |
51 | this.pendingMsgs = new HashMap<>(); | 51 | this.pendingMsgs = new HashMap<>(); |
@@ -55,7 +55,6 @@ public class RpcManagerActor extends ContextAwareActor { | @@ -55,7 +55,6 @@ public class RpcManagerActor extends ContextAwareActor { | ||
55 | .filter(otherServer -> otherServer.getServerAddress().compareTo(instance) > 0) | 55 | .filter(otherServer -> otherServer.getServerAddress().compareTo(instance) > 0) |
56 | .forEach(otherServer -> onCreateSessionRequest( | 56 | .forEach(otherServer -> onCreateSessionRequest( |
57 | new RpcSessionCreateRequestMsg(UUID.randomUUID(), otherServer.getServerAddress(), null))); | 57 | new RpcSessionCreateRequestMsg(UUID.randomUUID(), otherServer.getServerAddress(), null))); |
58 | - | ||
59 | } | 58 | } |
60 | 59 | ||
61 | @Override | 60 | @Override |
@@ -104,10 +103,10 @@ public class RpcManagerActor extends ContextAwareActor { | @@ -104,10 +103,10 @@ public class RpcManagerActor extends ContextAwareActor { | ||
104 | ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(), msg.getServerAddress().getPort(), ServerType.CORE); | 103 | ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(), msg.getServerAddress().getPort(), ServerType.CORE); |
105 | SessionActorInfo session = sessionActors.get(address); | 104 | SessionActorInfo session = sessionActors.get(address); |
106 | if (session != null) { | 105 | if (session != null) { |
107 | - log.debug("{} Forwarding msg to session actor", address); | 106 | + log.debug("{} Forwarding msg to session actor: {}", address, msg); |
108 | session.getActor().tell(msg, ActorRef.noSender()); | 107 | session.getActor().tell(msg, ActorRef.noSender()); |
109 | } else { | 108 | } else { |
110 | - log.debug("{} Storing msg to pending queue", address); | 109 | + log.debug("{} Storing msg to pending queue: {}", address, msg); |
111 | Queue<ClusterAPIProtos.ClusterMessage> queue = pendingMsgs.get(address); | 110 | Queue<ClusterAPIProtos.ClusterMessage> queue = pendingMsgs.get(address); |
112 | if (queue == null) { | 111 | if (queue == null) { |
113 | queue = new LinkedList<>(); | 112 | queue = new LinkedList<>(); |
@@ -117,7 +116,7 @@ public class RpcManagerActor extends ContextAwareActor { | @@ -117,7 +116,7 @@ public class RpcManagerActor extends ContextAwareActor { | ||
117 | queue.add(msg); | 116 | queue.add(msg); |
118 | } | 117 | } |
119 | } else { | 118 | } else { |
120 | - logger.warning("Cluster msg doesn't have set Server Address [{}]", msg); | 119 | + logger.warning("Cluster msg doesn't have server address [{}]", msg); |
121 | } | 120 | } |
122 | } | 121 | } |
123 | 122 | ||
@@ -162,9 +161,9 @@ public class RpcManagerActor extends ContextAwareActor { | @@ -162,9 +161,9 @@ public class RpcManagerActor extends ContextAwareActor { | ||
162 | } | 161 | } |
163 | 162 | ||
164 | private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) { | 163 | private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) { |
165 | - log.debug("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect); | 164 | + log.info("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect); |
166 | SessionActorInfo sessionRef = sessionActors.get(remoteAddress); | 165 | SessionActorInfo sessionRef = sessionActors.get(remoteAddress); |
167 | - if (context().sender() != null && context().sender().equals(sessionRef.actor)) { | 166 | + if (sessionRef != null && context().sender() != null && context().sender().equals(sessionRef.actor)) { |
168 | sessionActors.remove(remoteAddress); | 167 | sessionActors.remove(remoteAddress); |
169 | pendingMsgs.remove(remoteAddress); | 168 | pendingMsgs.remove(remoteAddress); |
170 | if (reconnect) { | 169 | if (reconnect) { |
@@ -182,18 +181,18 @@ public class RpcManagerActor extends ContextAwareActor { | @@ -182,18 +181,18 @@ public class RpcManagerActor extends ContextAwareActor { | ||
182 | 181 | ||
183 | private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) { | 182 | private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) { |
184 | sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender)); | 183 | sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender)); |
185 | - log.debug("[{}][{}] Registering session actor.", remoteAddress, uuid); | 184 | + log.info("[{}][{}] Registering session actor.", remoteAddress, uuid); |
186 | Queue<ClusterAPIProtos.ClusterMessage> data = pendingMsgs.remove(remoteAddress); | 185 | Queue<ClusterAPIProtos.ClusterMessage> data = pendingMsgs.remove(remoteAddress); |
187 | if (data != null) { | 186 | if (data != null) { |
188 | - log.debug("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size()); | 187 | + log.info("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size()); |
189 | data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender())); | 188 | data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender())); |
190 | } else { | 189 | } else { |
191 | - log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid); | 190 | + log.info("[{}][{}] No pending messages to forward.", remoteAddress, uuid); |
192 | } | 191 | } |
193 | } | 192 | } |
194 | 193 | ||
195 | private ActorRef createSessionActor(RpcSessionCreateRequestMsg msg) { | 194 | private ActorRef createSessionActor(RpcSessionCreateRequestMsg msg) { |
196 | - log.debug("[{}] Creating session actor.", msg.getMsgUid()); | 195 | + log.info("[{}] Creating session actor.", msg.getMsgUid()); |
197 | ActorRef actor = context().actorOf( | 196 | ActorRef actor = context().actorOf( |
198 | Props.create(new RpcSessionActor.ActorCreator(systemContext, msg.getMsgUid())).withDispatcher(DefaultActorService.RPC_DISPATCHER_NAME)); | 197 | Props.create(new RpcSessionActor.ActorCreator(systemContext, msg.getMsgUid())).withDispatcher(DefaultActorService.RPC_DISPATCHER_NAME)); |
199 | actor.tell(msg, context().self()); | 198 | actor.tell(msg, context().self()); |
@@ -18,6 +18,7 @@ package org.thingsboard.server.actors.rpc; | @@ -18,6 +18,7 @@ package org.thingsboard.server.actors.rpc; | ||
18 | import akka.event.Logging; | 18 | import akka.event.Logging; |
19 | import akka.event.LoggingAdapter; | 19 | import akka.event.LoggingAdapter; |
20 | import io.grpc.Channel; | 20 | import io.grpc.Channel; |
21 | +import io.grpc.ManagedChannel; | ||
21 | import io.grpc.ManagedChannelBuilder; | 22 | import io.grpc.ManagedChannelBuilder; |
22 | import io.grpc.stub.StreamObserver; | 23 | import io.grpc.stub.StreamObserver; |
23 | import org.thingsboard.server.actors.ActorSystemContext; | 24 | import org.thingsboard.server.actors.ActorSystemContext; |
@@ -88,8 +89,8 @@ public class RpcSessionActor extends ContextAwareActor { | @@ -88,8 +89,8 @@ public class RpcSessionActor extends ContextAwareActor { | ||
88 | systemContext.getRpcService().onSessionCreated(msg.getMsgUid(), session.getInputStream()); | 89 | systemContext.getRpcService().onSessionCreated(msg.getMsgUid(), session.getInputStream()); |
89 | } else { | 90 | } else { |
90 | // Client session | 91 | // Client session |
91 | - Channel channel = ManagedChannelBuilder.forAddress(remoteServer.getHost(), remoteServer.getPort()).usePlaintext(true).build(); | ||
92 | - session = new GrpcSession(remoteServer, listener); | 92 | + ManagedChannel channel = ManagedChannelBuilder.forAddress(remoteServer.getHost(), remoteServer.getPort()).usePlaintext().build(); |
93 | + session = new GrpcSession(remoteServer, listener, channel); | ||
93 | session.initInputStream(); | 94 | session.initInputStream(); |
94 | 95 | ||
95 | ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel); | 96 | ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel); |
@@ -17,6 +17,7 @@ package org.thingsboard.server.actors.ruleChain; | @@ -17,6 +17,7 @@ package org.thingsboard.server.actors.ruleChain; | ||
17 | 17 | ||
18 | import akka.actor.ActorRef; | 18 | import akka.actor.ActorRef; |
19 | import com.datastax.driver.core.utils.UUIDs; | 19 | import com.datastax.driver.core.utils.UUIDs; |
20 | +import org.springframework.util.StringUtils; | ||
20 | import org.thingsboard.rule.engine.api.ListeningExecutor; | 21 | import org.thingsboard.rule.engine.api.ListeningExecutor; |
21 | import org.thingsboard.rule.engine.api.MailService; | 22 | import org.thingsboard.rule.engine.api.MailService; |
22 | import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; | 23 | import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; |
@@ -35,6 +36,8 @@ import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | @@ -35,6 +36,8 @@ import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | ||
35 | import org.thingsboard.server.common.data.rule.RuleNode; | 36 | import org.thingsboard.server.common.data.rule.RuleNode; |
36 | import org.thingsboard.server.common.msg.TbMsg; | 37 | import org.thingsboard.server.common.msg.TbMsg; |
37 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 38 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
39 | +import org.thingsboard.server.common.msg.cluster.ServerAddress; | ||
40 | +import org.thingsboard.server.common.msg.cluster.ServerType; | ||
38 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | 41 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; |
39 | import org.thingsboard.server.dao.alarm.AlarmService; | 42 | import org.thingsboard.server.dao.alarm.AlarmService; |
40 | import org.thingsboard.server.dao.asset.AssetService; | 43 | import org.thingsboard.server.dao.asset.AssetService; |
@@ -232,16 +235,22 @@ class DefaultTbContext implements TbContext { | @@ -232,16 +235,22 @@ class DefaultTbContext implements TbContext { | ||
232 | return new RuleEngineRpcService() { | 235 | return new RuleEngineRpcService() { |
233 | @Override | 236 | @Override |
234 | public void sendRpcReply(DeviceId deviceId, int requestId, String body) { | 237 | public void sendRpcReply(DeviceId deviceId, int requestId, String body) { |
235 | - mainCtx.getDeviceRpcService().sendRpcReplyToDevice(nodeCtx.getTenantId(), deviceId, requestId, body); | 238 | + mainCtx.getDeviceRpcService().sendReplyToRpcCallFromDevice(nodeCtx.getTenantId(), deviceId, requestId, body); |
236 | } | 239 | } |
237 | 240 | ||
238 | @Override | 241 | @Override |
239 | public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) { | 242 | public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) { |
240 | ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), nodeCtx.getTenantId(), src.getDeviceId(), | 243 | ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), nodeCtx.getTenantId(), src.getDeviceId(), |
241 | src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody())); | 244 | src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody())); |
242 | - mainCtx.getDeviceRpcService().processRpcRequestToDevice(request, response -> { | 245 | + mainCtx.getDeviceRpcService().forwardServerSideRPCRequestToDeviceActor(request, response -> { |
243 | if (src.isRestApiCall()) { | 246 | if (src.isRestApiCall()) { |
244 | - mainCtx.getDeviceRpcService().processRestAPIRpcResponseFromRuleEngine(response); | 247 | + ServerAddress requestOriginAddress; |
248 | + if (!StringUtils.isEmpty(src.getOriginHost())) { | ||
249 | + requestOriginAddress = new ServerAddress(src.getOriginHost(), src.getOriginPort(), ServerType.CORE); | ||
250 | + } else { | ||
251 | + requestOriginAddress = mainCtx.getRoutingService().getCurrentServer(); | ||
252 | + } | ||
253 | + mainCtx.getDeviceRpcService().processResponseToServerSideRPCRequestFromRuleEngine(requestOriginAddress, response); | ||
245 | } | 254 | } |
246 | consumer.accept(RuleEngineDeviceRpcResponse.builder() | 255 | consumer.accept(RuleEngineDeviceRpcResponse.builder() |
247 | .deviceId(src.getDeviceId()) | 256 | .deviceId(src.getDeviceId()) |
@@ -35,5 +35,4 @@ public interface ActorService extends SessionMsgProcessor, RpcMsgListener, Disco | @@ -35,5 +35,4 @@ public interface ActorService extends SessionMsgProcessor, RpcMsgListener, Disco | ||
35 | 35 | ||
36 | void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType); | 36 | void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType); |
37 | 37 | ||
38 | - void onMsg(ServiceToRuleEngineMsg serviceToRuleEngineMsg); | ||
39 | } | 38 | } |
@@ -42,7 +42,6 @@ import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | @@ -42,7 +42,6 @@ import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | ||
42 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 42 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
43 | import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; | 43 | import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; |
44 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; | 44 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
45 | -import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | ||
46 | import org.thingsboard.server.gen.cluster.ClusterAPIProtos; | 45 | import org.thingsboard.server.gen.cluster.ClusterAPIProtos; |
47 | import org.thingsboard.server.service.cluster.discovery.DiscoveryService; | 46 | import org.thingsboard.server.service.cluster.discovery.DiscoveryService; |
48 | import org.thingsboard.server.service.cluster.discovery.ServerInstance; | 47 | import org.thingsboard.server.service.cluster.discovery.ServerInstance; |
@@ -66,10 +65,7 @@ public class DefaultActorService implements ActorService { | @@ -66,10 +65,7 @@ public class DefaultActorService implements ActorService { | ||
66 | public static final String APP_DISPATCHER_NAME = "app-dispatcher"; | 65 | public static final String APP_DISPATCHER_NAME = "app-dispatcher"; |
67 | public static final String CORE_DISPATCHER_NAME = "core-dispatcher"; | 66 | public static final String CORE_DISPATCHER_NAME = "core-dispatcher"; |
68 | public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher"; | 67 | public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher"; |
69 | - public static final String SYSTEM_PLUGIN_DISPATCHER_NAME = "system-plugin-dispatcher"; | ||
70 | public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher"; | 68 | public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher"; |
71 | - public static final String TENANT_PLUGIN_DISPATCHER_NAME = "plugin-dispatcher"; | ||
72 | - public static final String SESSION_DISPATCHER_NAME = "session-dispatcher"; | ||
73 | public static final String RPC_DISPATCHER_NAME = "rpc-dispatcher"; | 69 | public static final String RPC_DISPATCHER_NAME = "rpc-dispatcher"; |
74 | 70 | ||
75 | @Autowired | 71 | @Autowired |
@@ -162,11 +158,6 @@ public class DefaultActorService implements ActorService { | @@ -162,11 +158,6 @@ public class DefaultActorService implements ActorService { | ||
162 | appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender()); | 158 | appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender()); |
163 | } | 159 | } |
164 | 160 | ||
165 | - @Override | ||
166 | - public void onMsg(ServiceToRuleEngineMsg msg) { | ||
167 | - appActor.tell(msg, ActorRef.noSender()); | ||
168 | - } | ||
169 | - | ||
170 | public void broadcast(ToAllNodesMsg msg) { | 161 | public void broadcast(ToAllNodesMsg msg) { |
171 | actorContext.getEncodingService().encode(msg); | 162 | actorContext.getEncodingService().encode(msg); |
172 | rpcService.broadcast(new RpcBroadcastMsg(ClusterAPIProtos.ClusterMessage | 163 | rpcService.broadcast(new RpcBroadcastMsg(ClusterAPIProtos.ClusterMessage |
@@ -186,9 +177,9 @@ public class DefaultActorService implements ActorService { | @@ -186,9 +177,9 @@ public class DefaultActorService implements ActorService { | ||
186 | @Override | 177 | @Override |
187 | public void onReceivedMsg(ServerAddress source, ClusterAPIProtos.ClusterMessage msg) { | 178 | public void onReceivedMsg(ServerAddress source, ClusterAPIProtos.ClusterMessage msg) { |
188 | ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort(), source.getServerType()); | 179 | ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort(), source.getServerType()); |
189 | - log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress); | ||
190 | if (log.isDebugEnabled()) { | 180 | if (log.isDebugEnabled()) { |
191 | - log.info("MSG: ", msg); | 181 | + log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress); |
182 | + log.info("MSG: {}", msg); | ||
192 | } | 183 | } |
193 | switch (msg.getMessageType()) { | 184 | switch (msg.getMessageType()) { |
194 | case CLUSTER_ACTOR_MESSAGE: | 185 | case CLUSTER_ACTOR_MESSAGE: |
@@ -222,7 +213,7 @@ public class DefaultActorService implements ActorService { | @@ -222,7 +213,7 @@ public class DefaultActorService implements ActorService { | ||
222 | actorContext.getTsSubService().onRemoteTsUpdate(serverAddress, msg.getPayload().toByteArray()); | 213 | actorContext.getTsSubService().onRemoteTsUpdate(serverAddress, msg.getPayload().toByteArray()); |
223 | break; | 214 | break; |
224 | case CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE: | 215 | case CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE: |
225 | - actorContext.getDeviceRpcService().processRemoteResponseFromDevice(serverAddress, msg.getPayload().toByteArray()); | 216 | + actorContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromRemoteServer(serverAddress, msg.getPayload().toByteArray()); |
226 | break; | 217 | break; |
227 | case CLUSTER_DEVICE_STATE_SERVICE_MESSAGE: | 218 | case CLUSTER_DEVICE_STATE_SERVICE_MESSAGE: |
228 | actorContext.getDeviceStateService().onRemoteMsg(serverAddress, msg.getPayload().toByteArray()); | 219 | actorContext.getDeviceStateService().onRemoteMsg(serverAddress, msg.getPayload().toByteArray()); |
application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java
@@ -40,43 +40,31 @@ public abstract class AbstractContextAwareMsgProcessor { | @@ -40,43 +40,31 @@ public abstract class AbstractContextAwareMsgProcessor { | ||
40 | this.logger = logger; | 40 | this.logger = logger; |
41 | } | 41 | } |
42 | 42 | ||
43 | - protected ActorRef getAppActor() { | ||
44 | - return systemContext.getAppActor(); | ||
45 | - } | ||
46 | - | ||
47 | - protected Scheduler getScheduler() { | 43 | + private Scheduler getScheduler() { |
48 | return systemContext.getScheduler(); | 44 | return systemContext.getScheduler(); |
49 | } | 45 | } |
50 | 46 | ||
51 | - protected ExecutionContextExecutor getSystemDispatcher() { | 47 | + private ExecutionContextExecutor getSystemDispatcher() { |
52 | return systemContext.getActorSystem().dispatcher(); | 48 | return systemContext.getActorSystem().dispatcher(); |
53 | } | 49 | } |
54 | 50 | ||
55 | protected void schedulePeriodicMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, long periodInMs) { | 51 | protected void schedulePeriodicMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, long periodInMs) { |
56 | - schedulePeriodicMsgWithDelay(ctx, msg, delayInMs, periodInMs, ctx.self()); | 52 | + schedulePeriodicMsgWithDelay(msg, delayInMs, periodInMs, ctx.self()); |
57 | } | 53 | } |
58 | 54 | ||
59 | - protected void schedulePeriodicMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, long periodInMs, ActorRef target) { | 55 | + private void schedulePeriodicMsgWithDelay(Object msg, long delayInMs, long periodInMs, ActorRef target) { |
60 | logger.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs); | 56 | logger.debug("Scheduling periodic msg {} every {} ms with delay {} ms", msg, periodInMs, delayInMs); |
61 | getScheduler().schedule(Duration.create(delayInMs, TimeUnit.MILLISECONDS), Duration.create(periodInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); | 57 | getScheduler().schedule(Duration.create(delayInMs, TimeUnit.MILLISECONDS), Duration.create(periodInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); |
62 | } | 58 | } |
63 | 59 | ||
64 | - | ||
65 | protected void scheduleMsgWithDelay(ActorContext ctx, Object msg, long delayInMs) { | 60 | protected void scheduleMsgWithDelay(ActorContext ctx, Object msg, long delayInMs) { |
66 | - scheduleMsgWithDelay(ctx, msg, delayInMs, ctx.self()); | 61 | + scheduleMsgWithDelay(msg, delayInMs, ctx.self()); |
67 | } | 62 | } |
68 | 63 | ||
69 | - protected void scheduleMsgWithDelay(ActorContext ctx, Object msg, long delayInMs, ActorRef target) { | 64 | + private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) { |
70 | logger.debug("Scheduling msg {} with delay {} ms", msg, delayInMs); | 65 | logger.debug("Scheduling msg {} with delay {} ms", msg, delayInMs); |
71 | getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); | 66 | getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); |
72 | } | 67 | } |
73 | 68 | ||
74 | - @Data | ||
75 | - @AllArgsConstructor | ||
76 | - private static class ComponentConfiguration { | ||
77 | - private final String clazz; | ||
78 | - private final String name; | ||
79 | - private final String configuration; | ||
80 | - } | ||
81 | 69 | ||
82 | } | 70 | } |
@@ -108,7 +108,7 @@ public class TenantActor extends RuleChainManagerActor { | @@ -108,7 +108,7 @@ public class TenantActor extends RuleChainManagerActor { | ||
108 | @Override | 108 | @Override |
109 | protected void broadcast(Object msg) { | 109 | protected void broadcast(Object msg) { |
110 | super.broadcast(msg); | 110 | super.broadcast(msg); |
111 | - deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); | 111 | +// deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); |
112 | } | 112 | } |
113 | 113 | ||
114 | private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { | 114 | private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { |
@@ -127,7 +127,6 @@ public class TenantActor extends RuleChainManagerActor { | @@ -127,7 +127,6 @@ public class TenantActor extends RuleChainManagerActor { | ||
127 | ruleChainManager.getOrCreateActor(context(), msg.getRuleChainId()).tell(msg, self()); | 127 | ruleChainManager.getOrCreateActor(context(), msg.getRuleChainId()).tell(msg, self()); |
128 | } | 128 | } |
129 | 129 | ||
130 | - | ||
131 | private void onToDeviceActorMsg(DeviceAwareMsg msg) { | 130 | private void onToDeviceActorMsg(DeviceAwareMsg msg) { |
132 | getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); | 131 | getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); |
133 | } | 132 | } |
@@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle; | @@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle; | ||
48 | import org.thingsboard.server.common.msg.TbMsg; | 48 | import org.thingsboard.server.common.msg.TbMsg; |
49 | import org.thingsboard.server.common.msg.TbMsgDataType; | 49 | import org.thingsboard.server.common.msg.TbMsgDataType; |
50 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 50 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
51 | +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | ||
51 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | 52 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
52 | import org.thingsboard.server.dao.alarm.AlarmService; | 53 | import org.thingsboard.server.dao.alarm.AlarmService; |
53 | import org.thingsboard.server.dao.asset.AssetService; | 54 | import org.thingsboard.server.dao.asset.AssetService; |
@@ -673,7 +674,7 @@ public abstract class BaseController { | @@ -673,7 +674,7 @@ public abstract class BaseController { | ||
673 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, entityId, metaData, TbMsgDataType.JSON | 674 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, entityId, metaData, TbMsgDataType.JSON |
674 | , json.writeValueAsString(entityNode) | 675 | , json.writeValueAsString(entityNode) |
675 | , null, null, 0L); | 676 | , null, null, 0L); |
676 | - actorService.onMsg(new ServiceToRuleEngineMsg(user.getTenantId(), tbMsg)); | 677 | + actorService.onMsg(new SendToClusterMsg(entityId, new ServiceToRuleEngineMsg(user.getTenantId(), tbMsg))); |
677 | } catch (Exception e) { | 678 | } catch (Exception e) { |
678 | log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); | 679 | log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); |
679 | } | 680 | } |
@@ -29,7 +29,6 @@ import org.springframework.web.bind.annotation.RequestParam; | @@ -29,7 +29,6 @@ import org.springframework.web.bind.annotation.RequestParam; | ||
29 | import org.springframework.web.bind.annotation.ResponseBody; | 29 | import org.springframework.web.bind.annotation.ResponseBody; |
30 | import org.springframework.web.bind.annotation.ResponseStatus; | 30 | import org.springframework.web.bind.annotation.ResponseStatus; |
31 | import org.springframework.web.bind.annotation.RestController; | 31 | import org.springframework.web.bind.annotation.RestController; |
32 | -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; | ||
33 | import org.thingsboard.server.common.data.Customer; | 32 | import org.thingsboard.server.common.data.Customer; |
34 | import org.thingsboard.server.common.data.DataConstants; | 33 | import org.thingsboard.server.common.data.DataConstants; |
35 | import org.thingsboard.server.common.data.EntitySubtype; | 34 | import org.thingsboard.server.common.data.EntitySubtype; |
@@ -39,7 +38,6 @@ import org.thingsboard.server.common.data.audit.ActionType; | @@ -39,7 +38,6 @@ import org.thingsboard.server.common.data.audit.ActionType; | ||
39 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; | 38 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
40 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 39 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
41 | import org.thingsboard.server.common.data.id.CustomerId; | 40 | import org.thingsboard.server.common.data.id.CustomerId; |
42 | -import org.thingsboard.server.common.data.id.DeviceId; | ||
43 | import org.thingsboard.server.common.data.id.EntityId; | 41 | import org.thingsboard.server.common.data.id.EntityId; |
44 | import org.thingsboard.server.common.data.id.EntityViewId; | 42 | import org.thingsboard.server.common.data.id.EntityViewId; |
45 | import org.thingsboard.server.common.data.id.TenantId; | 43 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -47,7 +45,6 @@ import org.thingsboard.server.common.data.id.UUIDBased; | @@ -47,7 +45,6 @@ import org.thingsboard.server.common.data.id.UUIDBased; | ||
47 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 45 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
48 | import org.thingsboard.server.common.data.page.TextPageData; | 46 | import org.thingsboard.server.common.data.page.TextPageData; |
49 | import org.thingsboard.server.common.data.page.TextPageLink; | 47 | import org.thingsboard.server.common.data.page.TextPageLink; |
50 | -import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | ||
51 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 48 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
52 | import org.thingsboard.server.dao.model.ModelConstants; | 49 | import org.thingsboard.server.dao.model.ModelConstants; |
53 | import org.thingsboard.server.service.security.model.SecurityUser; | 50 | import org.thingsboard.server.service.security.model.SecurityUser; |
@@ -174,7 +171,7 @@ public class EntityViewController extends BaseController { | @@ -174,7 +171,7 @@ public class EntityViewController extends BaseController { | ||
174 | EntityView entityView = checkEntityViewId(entityViewId); | 171 | EntityView entityView = checkEntityViewId(entityViewId); |
175 | entityViewService.deleteEntityView(entityViewId); | 172 | entityViewService.deleteEntityView(entityViewId); |
176 | logEntityAction(entityViewId, entityView, entityView.getCustomerId(), | 173 | logEntityAction(entityViewId, entityView, entityView.getCustomerId(), |
177 | - ActionType.DELETED,null, strEntityViewId); | 174 | + ActionType.DELETED, null, strEntityViewId); |
178 | } catch (Exception e) { | 175 | } catch (Exception e) { |
179 | logEntityAction(emptyId(EntityType.ENTITY_VIEW), | 176 | logEntityAction(emptyId(EntityType.ENTITY_VIEW), |
180 | null, | 177 | null, |
@@ -185,10 +182,23 @@ public class EntityViewController extends BaseController { | @@ -185,10 +182,23 @@ public class EntityViewController extends BaseController { | ||
185 | } | 182 | } |
186 | 183 | ||
187 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") | 184 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
185 | + @RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET) | ||
186 | + @ResponseBody | ||
187 | + public EntityView getTenantEntityView( | ||
188 | + @RequestParam String entityViewName) throws ThingsboardException { | ||
189 | + try { | ||
190 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
191 | + return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName)); | ||
192 | + } catch (Exception e) { | ||
193 | + throw handleException(e); | ||
194 | + } | ||
195 | + } | ||
196 | + | ||
197 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
188 | @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST) | 198 | @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST) |
189 | @ResponseBody | 199 | @ResponseBody |
190 | public EntityView assignEntityViewToCustomer(@PathVariable(CUSTOMER_ID) String strCustomerId, | 200 | public EntityView assignEntityViewToCustomer(@PathVariable(CUSTOMER_ID) String strCustomerId, |
191 | - @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { | 201 | + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { |
192 | checkParameter(CUSTOMER_ID, strCustomerId); | 202 | checkParameter(CUSTOMER_ID, strCustomerId); |
193 | checkParameter(ENTITY_VIEW_ID, strEntityViewId); | 203 | checkParameter(ENTITY_VIEW_ID, strEntityViewId); |
194 | try { | 204 | try { |
@@ -49,9 +49,11 @@ import org.thingsboard.server.common.data.kv.Aggregation; | @@ -49,9 +49,11 @@ import org.thingsboard.server.common.data.kv.Aggregation; | ||
49 | import org.thingsboard.server.common.data.kv.AttributeKey; | 49 | import org.thingsboard.server.common.data.kv.AttributeKey; |
50 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 50 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
51 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | 51 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
52 | +import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery; | ||
52 | import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; | 53 | import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
53 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 54 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
54 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 55 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
56 | +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | ||
55 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 57 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
56 | import org.thingsboard.server.common.data.kv.KvEntry; | 58 | import org.thingsboard.server.common.data.kv.KvEntry; |
57 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 59 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
@@ -60,12 +62,10 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | @@ -60,12 +62,10 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
60 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 62 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
61 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | 63 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
62 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 64 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
63 | -import org.thingsboard.server.dao.attributes.AttributesService; | ||
64 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 65 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
65 | import org.thingsboard.server.service.security.AccessValidator; | 66 | import org.thingsboard.server.service.security.AccessValidator; |
66 | import org.thingsboard.server.service.security.model.SecurityUser; | 67 | import org.thingsboard.server.service.security.model.SecurityUser; |
67 | import org.thingsboard.server.service.telemetry.AttributeData; | 68 | import org.thingsboard.server.service.telemetry.AttributeData; |
68 | -import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | ||
69 | import org.thingsboard.server.service.telemetry.TsData; | 69 | import org.thingsboard.server.service.telemetry.TsData; |
70 | import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; | 70 | import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; |
71 | import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; | 71 | import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; |
@@ -250,6 +250,60 @@ public class TelemetryController extends BaseController { | @@ -250,6 +250,60 @@ public class TelemetryController extends BaseController { | ||
250 | } | 250 | } |
251 | 251 | ||
252 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | 252 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
253 | + @RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE) | ||
254 | + @ResponseBody | ||
255 | + public DeferredResult<ResponseEntity> deleteEntityTimeseries(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, | ||
256 | + @RequestParam(name = "keys") String keysStr, | ||
257 | + @RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys, | ||
258 | + @RequestParam(name = "startTs", required = false) Long startTs, | ||
259 | + @RequestParam(name = "endTs", required = false) Long endTs, | ||
260 | + @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { | ||
261 | + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | ||
262 | + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); | ||
263 | + } | ||
264 | + | ||
265 | + private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, | ||
266 | + Long startTs, Long endTs, boolean rewriteLatestIfDeleted) throws ThingsboardException { | ||
267 | + List<String> keys = toKeysList(keysStr); | ||
268 | + if (keys.isEmpty()) { | ||
269 | + return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST); | ||
270 | + } | ||
271 | + SecurityUser user = getCurrentUser(); | ||
272 | + | ||
273 | + long deleteFromTs; | ||
274 | + long deleteToTs; | ||
275 | + if (deleteAllDataForKeys) { | ||
276 | + deleteFromTs = 0L; | ||
277 | + deleteToTs = System.currentTimeMillis(); | ||
278 | + } else { | ||
279 | + deleteFromTs = startTs; | ||
280 | + deleteToTs = endTs; | ||
281 | + } | ||
282 | + | ||
283 | + return accessValidator.validateEntityAndCallback(user, entityIdStr, (result, entityId) -> { | ||
284 | + List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>(); | ||
285 | + for (String key : keys) { | ||
286 | + deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted)); | ||
287 | + } | ||
288 | + | ||
289 | + ListenableFuture<List<Void>> future = tsService.remove(entityId, deleteTsKvQueries); | ||
290 | + Futures.addCallback(future, new FutureCallback<List<Void>>() { | ||
291 | + @Override | ||
292 | + public void onSuccess(@Nullable List<Void> tmp) { | ||
293 | + logTimeseriesDeleted(user, entityId, keys, null); | ||
294 | + result.setResult(new ResponseEntity<>(HttpStatus.OK)); | ||
295 | + } | ||
296 | + | ||
297 | + @Override | ||
298 | + public void onFailure(Throwable t) { | ||
299 | + logTimeseriesDeleted(user, entityId, keys, t); | ||
300 | + result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); | ||
301 | + } | ||
302 | + }, executor); | ||
303 | + }); | ||
304 | + } | ||
305 | + | ||
306 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
253 | @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) | 307 | @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) |
254 | @ResponseBody | 308 | @ResponseBody |
255 | public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, | 309 | public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, |
@@ -506,6 +560,15 @@ public class TelemetryController extends BaseController { | @@ -506,6 +560,15 @@ public class TelemetryController extends BaseController { | ||
506 | }; | 560 | }; |
507 | } | 561 | } |
508 | 562 | ||
563 | + private void logTimeseriesDeleted(SecurityUser user, EntityId entityId, List<String> keys, Throwable e) { | ||
564 | + try { | ||
565 | + logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.TIMESERIES_DELETED, toException(e), | ||
566 | + keys); | ||
567 | + } catch (ThingsboardException te) { | ||
568 | + log.warn("Failed to log timeseries delete", te); | ||
569 | + } | ||
570 | + } | ||
571 | + | ||
509 | private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) { | 572 | private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) { |
510 | try { | 573 | try { |
511 | logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e), | 574 | logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e), |
@@ -101,6 +101,10 @@ public class ThingsboardInstallService { | @@ -101,6 +101,10 @@ public class ThingsboardInstallService { | ||
101 | log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ..."); | 101 | log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ..."); |
102 | 102 | ||
103 | databaseUpgradeService.upgradeDatabase("2.1.1"); | 103 | databaseUpgradeService.upgradeDatabase("2.1.1"); |
104 | + case "2.1.3": | ||
105 | + log.info("Upgrading ThingsBoard from version 2.1.3 to 2.2.0 ..."); | ||
106 | + | ||
107 | + databaseUpgradeService.upgradeDatabase("2.1.3"); | ||
104 | 108 | ||
105 | log.info("Updating system data..."); | 109 | log.info("Updating system data..."); |
106 | 110 |
@@ -15,6 +15,8 @@ | @@ -15,6 +15,8 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.cluster.rpc; | 16 | package org.thingsboard.server.service.cluster.rpc; |
17 | 17 | ||
18 | +import io.grpc.Channel; | ||
19 | +import io.grpc.ManagedChannel; | ||
18 | import io.grpc.stub.StreamObserver; | 20 | import io.grpc.stub.StreamObserver; |
19 | import lombok.Data; | 21 | import lombok.Data; |
20 | import lombok.extern.slf4j.Slf4j; | 22 | import lombok.extern.slf4j.Slf4j; |
@@ -34,6 +36,7 @@ public final class GrpcSession implements Closeable { | @@ -34,6 +36,7 @@ public final class GrpcSession implements Closeable { | ||
34 | private final UUID sessionId; | 36 | private final UUID sessionId; |
35 | private final boolean client; | 37 | private final boolean client; |
36 | private final GrpcSessionListener listener; | 38 | private final GrpcSessionListener listener; |
39 | + private final ManagedChannel channel; | ||
37 | private StreamObserver<ClusterAPIProtos.ClusterMessage> inputStream; | 40 | private StreamObserver<ClusterAPIProtos.ClusterMessage> inputStream; |
38 | private StreamObserver<ClusterAPIProtos.ClusterMessage> outputStream; | 41 | private StreamObserver<ClusterAPIProtos.ClusterMessage> outputStream; |
39 | 42 | ||
@@ -41,10 +44,10 @@ public final class GrpcSession implements Closeable { | @@ -41,10 +44,10 @@ public final class GrpcSession implements Closeable { | ||
41 | private ServerAddress remoteServer; | 44 | private ServerAddress remoteServer; |
42 | 45 | ||
43 | public GrpcSession(GrpcSessionListener listener) { | 46 | public GrpcSession(GrpcSessionListener listener) { |
44 | - this(null, listener); | 47 | + this(null, listener, null); |
45 | } | 48 | } |
46 | 49 | ||
47 | - public GrpcSession(ServerAddress remoteServer, GrpcSessionListener listener) { | 50 | + public GrpcSession(ServerAddress remoteServer, GrpcSessionListener listener, ManagedChannel channel) { |
48 | this.sessionId = UUID.randomUUID(); | 51 | this.sessionId = UUID.randomUUID(); |
49 | this.listener = listener; | 52 | this.listener = listener; |
50 | if (remoteServer != null) { | 53 | if (remoteServer != null) { |
@@ -54,6 +57,7 @@ public final class GrpcSession implements Closeable { | @@ -54,6 +57,7 @@ public final class GrpcSession implements Closeable { | ||
54 | } else { | 57 | } else { |
55 | this.client = false; | 58 | this.client = false; |
56 | } | 59 | } |
60 | + this.channel = channel; | ||
57 | } | 61 | } |
58 | 62 | ||
59 | public void initInputStream() { | 63 | public void initInputStream() { |
@@ -105,5 +109,8 @@ public final class GrpcSession implements Closeable { | @@ -105,5 +109,8 @@ public final class GrpcSession implements Closeable { | ||
105 | } catch (IllegalStateException e) { | 109 | } catch (IllegalStateException e) { |
106 | log.debug("[{}] Failed to close output stream: {}", sessionId, e.getMessage()); | 110 | log.debug("[{}] Failed to close output stream: {}", sessionId, e.getMessage()); |
107 | } | 111 | } |
112 | + if (channel != null) { | ||
113 | + channel.shutdownNow(); | ||
114 | + } | ||
108 | } | 115 | } |
109 | } | 116 | } |
@@ -30,7 +30,6 @@ import org.thingsboard.rule.engine.api.NodeConfiguration; | @@ -30,7 +30,6 @@ import org.thingsboard.rule.engine.api.NodeConfiguration; | ||
30 | import org.thingsboard.rule.engine.api.NodeDefinition; | 30 | import org.thingsboard.rule.engine.api.NodeDefinition; |
31 | import org.thingsboard.rule.engine.api.RuleNode; | 31 | import org.thingsboard.rule.engine.api.RuleNode; |
32 | import org.thingsboard.rule.engine.api.TbRelationTypes; | 32 | import org.thingsboard.rule.engine.api.TbRelationTypes; |
33 | -import org.thingsboard.server.common.data.DataConstants; | ||
34 | import org.thingsboard.server.common.data.plugin.ComponentDescriptor; | 33 | import org.thingsboard.server.common.data.plugin.ComponentDescriptor; |
35 | import org.thingsboard.server.common.data.plugin.ComponentType; | 34 | import org.thingsboard.server.common.data.plugin.ComponentType; |
36 | import org.thingsboard.server.dao.component.ComponentDescriptorService; | 35 | import org.thingsboard.server.dao.component.ComponentDescriptorService; |
@@ -52,6 +51,7 @@ import java.util.Set; | @@ -52,6 +51,7 @@ import java.util.Set; | ||
52 | @Slf4j | 51 | @Slf4j |
53 | public class AnnotationComponentDiscoveryService implements ComponentDiscoveryService { | 52 | public class AnnotationComponentDiscoveryService implements ComponentDiscoveryService { |
54 | 53 | ||
54 | + public static final int MAX_OPTIMISITC_RETRIES = 3; | ||
55 | @Value("${plugins.scan_packages}") | 55 | @Value("${plugins.scan_packages}") |
56 | private String[] scanPackages; | 56 | private String[] scanPackages; |
57 | 57 | ||
@@ -81,17 +81,32 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe | @@ -81,17 +81,32 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe | ||
81 | private void registerRuleNodeComponents() { | 81 | private void registerRuleNodeComponents() { |
82 | Set<BeanDefinition> ruleNodeBeanDefinitions = getBeanDefinitions(RuleNode.class); | 82 | Set<BeanDefinition> ruleNodeBeanDefinitions = getBeanDefinitions(RuleNode.class); |
83 | for (BeanDefinition def : ruleNodeBeanDefinitions) { | 83 | for (BeanDefinition def : ruleNodeBeanDefinitions) { |
84 | - try { | ||
85 | - String clazzName = def.getBeanClassName(); | ||
86 | - Class<?> clazz = Class.forName(clazzName); | ||
87 | - RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class); | ||
88 | - ComponentType type = ruleNodeAnnotation.type(); | ||
89 | - ComponentDescriptor component = scanAndPersistComponent(def, type); | ||
90 | - components.put(component.getClazz(), component); | ||
91 | - componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); | ||
92 | - } catch (Exception e) { | ||
93 | - log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e); | ||
94 | - throw new RuntimeException(e); | 84 | + int retryCount = 0; |
85 | + Exception cause = null; | ||
86 | + while (retryCount < MAX_OPTIMISITC_RETRIES) { | ||
87 | + try { | ||
88 | + String clazzName = def.getBeanClassName(); | ||
89 | + Class<?> clazz = Class.forName(clazzName); | ||
90 | + RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class); | ||
91 | + ComponentType type = ruleNodeAnnotation.type(); | ||
92 | + ComponentDescriptor component = scanAndPersistComponent(def, type); | ||
93 | + components.put(component.getClazz(), component); | ||
94 | + componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); | ||
95 | + break; | ||
96 | + } catch (Exception e) { | ||
97 | + log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e); | ||
98 | + cause = e; | ||
99 | + retryCount++; | ||
100 | + try { | ||
101 | + Thread.sleep(1000); | ||
102 | + } catch (InterruptedException e1) { | ||
103 | + throw new RuntimeException(e1); | ||
104 | + } | ||
105 | + } | ||
106 | + } | ||
107 | + if (cause != null && retryCount == MAX_OPTIMISITC_RETRIES) { | ||
108 | + log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), cause.getMessage(), cause); | ||
109 | + throw new RuntimeException(cause); | ||
95 | } | 110 | } |
96 | } | 111 | } |
97 | } | 112 | } |
@@ -251,7 +251,8 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -251,7 +251,8 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | ||
251 | log.info("Entity views restored."); | 251 | log.info("Entity views restored."); |
252 | 252 | ||
253 | break; | 253 | break; |
254 | - | 254 | + case "2.1.3": |
255 | + break; | ||
255 | default: | 256 | default: |
256 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); | 257 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); |
257 | } | 258 | } |
@@ -149,7 +149,14 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -149,7 +149,14 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | ||
149 | log.info("Entity views restored."); | 149 | log.info("Entity views restored."); |
150 | } | 150 | } |
151 | break; | 151 | break; |
152 | - | 152 | + case "2.1.3": |
153 | + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | ||
154 | + log.info("Updating schema ..."); | ||
155 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.2.0", SCHEMA_UPDATE_SQL); | ||
156 | + loadSql(schemaUpdateFile, conn); | ||
157 | + log.info("Schema updated."); | ||
158 | + } | ||
159 | + break; | ||
153 | default: | 160 | default: |
154 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); | 161 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); |
155 | } | 162 | } |
@@ -90,7 +90,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -90,7 +90,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
90 | 90 | ||
91 | @Override | 91 | @Override |
92 | public void processRestAPIRpcRequestToRuleEngine(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { | 92 | public void processRestAPIRpcRequestToRuleEngine(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { |
93 | - log.trace("[{}] Processing local rpc call to rule engine [{}]", request.getTenantId(), request.getDeviceId()); | 93 | + log.trace("[{}][{}] Processing REST API call to rule engine [{}]", request.getTenantId(), request.getId(), request.getDeviceId()); |
94 | UUID requestId = request.getId(); | 94 | UUID requestId = request.getId(); |
95 | localToRuleEngineRpcRequests.put(requestId, responseConsumer); | 95 | localToRuleEngineRpcRequests.put(requestId, responseConsumer); |
96 | sendRpcRequestToRuleEngine(request); | 96 | sendRpcRequestToRuleEngine(request); |
@@ -98,31 +98,11 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -98,31 +98,11 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
98 | } | 98 | } |
99 | 99 | ||
100 | @Override | 100 | @Override |
101 | - public void processRestAPIRpcResponseFromRuleEngine(FromDeviceRpcResponse response) { | ||
102 | - UUID requestId = response.getId(); | ||
103 | - Consumer<FromDeviceRpcResponse> consumer = localToRuleEngineRpcRequests.remove(requestId); | ||
104 | - if (consumer != null) { | ||
105 | - consumer.accept(response); | ||
106 | - } else { | ||
107 | - log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response); | ||
108 | - } | ||
109 | - } | ||
110 | - | ||
111 | - @Override | ||
112 | - public void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { | ||
113 | - log.trace("[{}] Processing local rpc call to device [{}]", request.getTenantId(), request.getDeviceId()); | ||
114 | - UUID requestId = request.getId(); | ||
115 | - localToDeviceRpcRequests.put(requestId, responseConsumer); | ||
116 | - sendRpcRequestToDevice(request); | ||
117 | - scheduleTimeout(request, requestId, localToDeviceRpcRequests); | ||
118 | - } | ||
119 | - | ||
120 | - @Override | ||
121 | - public void processRpcResponseFromDevice(FromDeviceRpcResponse response) { | ||
122 | - log.trace("[{}] response to request: [{}]", this.hashCode(), response.getId()); | ||
123 | - if (routingService.getCurrentServer().equals(response.getServerAddress())) { | 101 | + public void processResponseToServerSideRPCRequestFromRuleEngine(ServerAddress requestOriginAddress, FromDeviceRpcResponse response) { |
102 | + log.trace("[{}] Received response to server-side RPC request from rule engine: [{}]", response.getId(), requestOriginAddress); | ||
103 | + if (routingService.getCurrentServer().equals(requestOriginAddress)) { | ||
124 | UUID requestId = response.getId(); | 104 | UUID requestId = response.getId(); |
125 | - Consumer<FromDeviceRpcResponse> consumer = localToDeviceRpcRequests.remove(requestId); | 105 | + Consumer<FromDeviceRpcResponse> consumer = localToRuleEngineRpcRequests.remove(requestId); |
126 | if (consumer != null) { | 106 | if (consumer != null) { |
127 | consumer.accept(response); | 107 | consumer.accept(response); |
128 | } else { | 108 | } else { |
@@ -138,12 +118,33 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -138,12 +118,33 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
138 | } else { | 118 | } else { |
139 | builder.setError(-1); | 119 | builder.setError(-1); |
140 | } | 120 | } |
141 | - rpcService.tell(response.getServerAddress(), ClusterAPIProtos.MessageType.CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE, builder.build().toByteArray()); | 121 | + rpcService.tell(requestOriginAddress, ClusterAPIProtos.MessageType.CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE, builder.build().toByteArray()); |
122 | + } | ||
123 | + } | ||
124 | + | ||
125 | + @Override | ||
126 | + public void forwardServerSideRPCRequestToDeviceActor(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { | ||
127 | + log.trace("[{}][{}] Processing local rpc call to device actor [{}]", request.getTenantId(), request.getId(), request.getDeviceId()); | ||
128 | + UUID requestId = request.getId(); | ||
129 | + localToDeviceRpcRequests.put(requestId, responseConsumer); | ||
130 | + sendRpcRequestToDevice(request); | ||
131 | + scheduleTimeout(request, requestId, localToDeviceRpcRequests); | ||
132 | + } | ||
133 | + | ||
134 | + @Override | ||
135 | + public void processResponseToServerSideRPCRequestFromDeviceActor(FromDeviceRpcResponse response) { | ||
136 | + log.trace("[{}] Received response to server-side RPC request from device actor.", response.getId()); | ||
137 | + UUID requestId = response.getId(); | ||
138 | + Consumer<FromDeviceRpcResponse> consumer = localToDeviceRpcRequests.remove(requestId); | ||
139 | + if (consumer != null) { | ||
140 | + consumer.accept(response); | ||
141 | + } else { | ||
142 | + log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response); | ||
142 | } | 143 | } |
143 | } | 144 | } |
144 | 145 | ||
145 | @Override | 146 | @Override |
146 | - public void processRemoteResponseFromDevice(ServerAddress serverAddress, byte[] data) { | 147 | + public void processResponseToServerSideRPCRequestFromRemoteServer(ServerAddress serverAddress, byte[] data) { |
147 | ClusterAPIProtos.FromDeviceRPCResponseProto proto; | 148 | ClusterAPIProtos.FromDeviceRPCResponseProto proto; |
148 | try { | 149 | try { |
149 | proto = ClusterAPIProtos.FromDeviceRPCResponseProto.parseFrom(data); | 150 | proto = ClusterAPIProtos.FromDeviceRPCResponseProto.parseFrom(data); |
@@ -151,13 +152,12 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -151,13 +152,12 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
151 | throw new RuntimeException(e); | 152 | throw new RuntimeException(e); |
152 | } | 153 | } |
153 | RpcError error = proto.getError() > 0 ? RpcError.values()[proto.getError()] : null; | 154 | RpcError error = proto.getError() > 0 ? RpcError.values()[proto.getError()] : null; |
154 | - FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()), routingService.getCurrentServer(), | ||
155 | - proto.getResponse(), error); | ||
156 | - processRpcResponseFromDevice(response); | 155 | + FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()), proto.getResponse(), error); |
156 | + processResponseToServerSideRPCRequestFromRuleEngine(routingService.getCurrentServer(), response); | ||
157 | } | 157 | } |
158 | 158 | ||
159 | @Override | 159 | @Override |
160 | - public void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) { | 160 | + public void sendReplyToRpcCallFromDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) { |
161 | ToServerRpcResponseActorMsg rpcMsg = new ToServerRpcResponseActorMsg(tenantId, deviceId, new ToServerRpcResponseMsg(requestId, body)); | 161 | ToServerRpcResponseActorMsg rpcMsg = new ToServerRpcResponseActorMsg(tenantId, deviceId, new ToServerRpcResponseMsg(requestId, body)); |
162 | forward(deviceId, rpcMsg); | 162 | forward(deviceId, rpcMsg); |
163 | } | 163 | } |
@@ -166,6 +166,8 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -166,6 +166,8 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
166 | ObjectNode entityNode = json.createObjectNode(); | 166 | ObjectNode entityNode = json.createObjectNode(); |
167 | TbMsgMetaData metaData = new TbMsgMetaData(); | 167 | TbMsgMetaData metaData = new TbMsgMetaData(); |
168 | metaData.putValue("requestUUID", msg.getId().toString()); | 168 | metaData.putValue("requestUUID", msg.getId().toString()); |
169 | + metaData.putValue("originHost", routingService.getCurrentServer().getHost()); | ||
170 | + metaData.putValue("originPort", Integer.toString(routingService.getCurrentServer().getPort())); | ||
169 | metaData.putValue("expirationTime", Long.toString(msg.getExpirationTime())); | 171 | metaData.putValue("expirationTime", Long.toString(msg.getExpirationTime())); |
170 | metaData.putValue("oneway", Boolean.toString(msg.isOneway())); | 172 | metaData.putValue("oneway", Boolean.toString(msg.isOneway())); |
171 | 173 | ||
@@ -176,7 +178,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -176,7 +178,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
176 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), metaData, TbMsgDataType.JSON | 178 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), metaData, TbMsgDataType.JSON |
177 | , json.writeValueAsString(entityNode) | 179 | , json.writeValueAsString(entityNode) |
178 | , null, null, 0L); | 180 | , null, null, 0L); |
179 | - actorService.onMsg(new ServiceToRuleEngineMsg(msg.getTenantId(), tbMsg)); | 181 | + actorService.onMsg(new SendToClusterMsg(msg.getDeviceId(), new ServiceToRuleEngineMsg(msg.getTenantId(), tbMsg))); |
180 | } catch (JsonProcessingException e) { | 182 | } catch (JsonProcessingException e) { |
181 | throw new RuntimeException(e); | 183 | throw new RuntimeException(e); |
182 | } | 184 | } |
@@ -199,7 +201,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -199,7 +201,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
199 | log.trace("[{}] timeout the request: [{}]", this.hashCode(), requestId); | 201 | log.trace("[{}] timeout the request: [{}]", this.hashCode(), requestId); |
200 | Consumer<FromDeviceRpcResponse> consumer = requestsMap.remove(requestId); | 202 | Consumer<FromDeviceRpcResponse> consumer = requestsMap.remove(requestId); |
201 | if (consumer != null) { | 203 | if (consumer != null) { |
202 | - consumer.accept(new FromDeviceRpcResponse(requestId, null, null, RpcError.TIMEOUT)); | 204 | + consumer.accept(new FromDeviceRpcResponse(requestId, null, RpcError.TIMEOUT)); |
203 | } | 205 | } |
204 | }, timeout, TimeUnit.MILLISECONDS); | 206 | }, timeout, TimeUnit.MILLISECONDS); |
205 | } | 207 | } |
@@ -29,13 +29,13 @@ public interface DeviceRpcService { | @@ -29,13 +29,13 @@ public interface DeviceRpcService { | ||
29 | 29 | ||
30 | void processRestAPIRpcRequestToRuleEngine(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); | 30 | void processRestAPIRpcRequestToRuleEngine(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); |
31 | 31 | ||
32 | - void processRestAPIRpcResponseFromRuleEngine(FromDeviceRpcResponse response); | 32 | + void processResponseToServerSideRPCRequestFromRuleEngine(ServerAddress requestOriginAddress, FromDeviceRpcResponse response); |
33 | 33 | ||
34 | - void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); | 34 | + void forwardServerSideRPCRequestToDeviceActor(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); |
35 | 35 | ||
36 | - void processRpcResponseFromDevice(FromDeviceRpcResponse response); | 36 | + void processResponseToServerSideRPCRequestFromDeviceActor(FromDeviceRpcResponse response); |
37 | 37 | ||
38 | - void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body); | 38 | + void processResponseToServerSideRPCRequestFromRemoteServer(ServerAddress serverAddress, byte[] data); |
39 | 39 | ||
40 | - void processRemoteResponseFromDevice(ServerAddress serverAddress, byte[] bytes); | 40 | + void sendReplyToRpcCallFromDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body); |
41 | } | 41 | } |
@@ -32,8 +32,6 @@ import java.util.UUID; | @@ -32,8 +32,6 @@ import java.util.UUID; | ||
32 | public class FromDeviceRpcResponse { | 32 | public class FromDeviceRpcResponse { |
33 | @Getter | 33 | @Getter |
34 | private final UUID id; | 34 | private final UUID id; |
35 | - @Getter | ||
36 | - private final ServerAddress serverAddress; | ||
37 | private final String response; | 35 | private final String response; |
38 | private final RpcError error; | 36 | private final RpcError error; |
39 | 37 |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.session; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.springframework.cache.annotation.CachePut; | ||
20 | +import org.springframework.cache.annotation.Cacheable; | ||
21 | +import org.springframework.stereotype.Service; | ||
22 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
23 | +import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; | ||
24 | + | ||
25 | +import java.util.ArrayList; | ||
26 | +import java.util.Collections; | ||
27 | + | ||
28 | +import static org.thingsboard.server.common.data.CacheConstants.SESSIONS_CACHE; | ||
29 | + | ||
30 | +/** | ||
31 | + * Created by ashvayka on 29.10.18. | ||
32 | + */ | ||
33 | +@Service | ||
34 | +@Slf4j | ||
35 | +public class DefaultDeviceSessionCacheService implements DeviceSessionCacheService { | ||
36 | + | ||
37 | + @Override | ||
38 | + @Cacheable(cacheNames = SESSIONS_CACHE, key = "#deviceId") | ||
39 | + public DeviceSessionsCacheEntry get(DeviceId deviceId) { | ||
40 | + log.debug("[{}] Fetching session data from cache", deviceId); | ||
41 | + return DeviceSessionsCacheEntry.newBuilder().addAllSessions(Collections.emptyList()).build(); | ||
42 | + } | ||
43 | + | ||
44 | + @Override | ||
45 | + @CachePut(cacheNames = SESSIONS_CACHE, key = "#deviceId") | ||
46 | + public DeviceSessionsCacheEntry put(DeviceId deviceId, DeviceSessionsCacheEntry sessions) { | ||
47 | + log.debug("[{}] Pushing session data from cache: {}", deviceId, sessions); | ||
48 | + return sessions; | ||
49 | + } | ||
50 | +} |
application/src/main/java/org/thingsboard/server/service/session/DeviceSessionCacheService.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.session; | ||
17 | + | ||
18 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
19 | +import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; | ||
20 | + | ||
21 | +/** | ||
22 | + * Created by ashvayka on 29.10.18. | ||
23 | + */ | ||
24 | +public interface DeviceSessionCacheService { | ||
25 | + | ||
26 | + DeviceSessionsCacheEntry get(DeviceId deviceId); | ||
27 | + | ||
28 | + DeviceSessionsCacheEntry put(DeviceId deviceId, DeviceSessionsCacheEntry sessions); | ||
29 | + | ||
30 | +} |
@@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; | @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; | ||
43 | import org.thingsboard.server.common.msg.TbMsg; | 43 | import org.thingsboard.server.common.msg.TbMsg; |
44 | import org.thingsboard.server.common.msg.TbMsgDataType; | 44 | import org.thingsboard.server.common.msg.TbMsgDataType; |
45 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 45 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
46 | +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | ||
46 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 47 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
47 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | 48 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
48 | import org.thingsboard.server.dao.attributes.AttributesService; | 49 | import org.thingsboard.server.dao.attributes.AttributesService; |
@@ -457,7 +458,7 @@ public class DefaultDeviceStateService implements DeviceStateService { | @@ -457,7 +458,7 @@ public class DefaultDeviceStateService implements DeviceStateService { | ||
457 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON | 458 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON |
458 | , json.writeValueAsString(state) | 459 | , json.writeValueAsString(state) |
459 | , null, null, 0L); | 460 | , null, null, 0L); |
460 | - actorService.onMsg(new ServiceToRuleEngineMsg(stateData.getTenantId(), tbMsg)); | 461 | + actorService.onMsg(new SendToClusterMsg(stateData.getDeviceId(), new ServiceToRuleEngineMsg(stateData.getTenantId(), tbMsg))); |
461 | } catch (Exception e) { | 462 | } catch (Exception e) { |
462 | log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); | 463 | log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); |
463 | } | 464 | } |
@@ -166,7 +166,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | @@ -166,7 +166,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
166 | 166 | ||
167 | private SubscriptionState getUpdatedSubscriptionState(EntityId entityId, SubscriptionState sub, EntityView entityView) { | 167 | private SubscriptionState getUpdatedSubscriptionState(EntityId entityId, SubscriptionState sub, EntityView entityView) { |
168 | Map<String, Long> keyStates; | 168 | Map<String, Long> keyStates; |
169 | - if(sub.isAllKeys()) { | 169 | + if (sub.isAllKeys()) { |
170 | keyStates = entityView.getKeys().getTimeseries().stream().collect(Collectors.toMap(k -> k, k -> 0L)); | 170 | keyStates = entityView.getKeys().getTimeseries().stream().collect(Collectors.toMap(k -> k, k -> 0L)); |
171 | } else { | 171 | } else { |
172 | keyStates = sub.getKeyStates().entrySet() | 172 | keyStates = sub.getKeyStates().entrySet() |
@@ -618,7 +618,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | @@ -618,7 +618,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
618 | builder.setEntityId(sub.getEntityId().getId().toString()); | 618 | builder.setEntityId(sub.getEntityId().getId().toString()); |
619 | builder.setType(sub.getType().name()); | 619 | builder.setType(sub.getType().name()); |
620 | builder.setAllKeys(sub.isAllKeys()); | 620 | builder.setAllKeys(sub.isAllKeys()); |
621 | - builder.setScope(sub.getScope()); | 621 | + if (sub.getScope() != null) { |
622 | + builder.setScope(sub.getScope()); | ||
623 | + } | ||
622 | sub.getKeyStates().entrySet().forEach(e -> builder.addKeyStates( | 624 | sub.getKeyStates().entrySet().forEach(e -> builder.addKeyStates( |
623 | ClusterAPIProtos.SubscriptionKetStateProto.newBuilder().setKey(e.getKey()).setTs(e.getValue()).build())); | 625 | ClusterAPIProtos.SubscriptionKetStateProto.newBuilder().setKey(e.getKey()).setTs(e.getValue()).build())); |
624 | rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE, builder.build().toByteArray()); | 626 | rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE, builder.build().toByteArray()); |
@@ -133,59 +133,48 @@ public class LocalTransportService extends AbstractTransportService implements R | @@ -133,59 +133,48 @@ public class LocalTransportService extends AbstractTransportService implements R | ||
133 | } | 133 | } |
134 | 134 | ||
135 | @Override | 135 | @Override |
136 | - public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) { | ||
137 | - if (checkLimits(sessionInfo, callback)) { | ||
138 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback); | ||
139 | - } | 136 | + protected void doProcess(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) { |
137 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback); | ||
140 | } | 138 | } |
141 | 139 | ||
142 | @Override | 140 | @Override |
143 | - public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { | ||
144 | - if (checkLimits(sessionInfo, callback)) { | ||
145 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback); | ||
146 | - } | 141 | + protected void doProcess(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { |
142 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback); | ||
147 | } | 143 | } |
148 | 144 | ||
149 | @Override | 145 | @Override |
150 | - public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) { | ||
151 | - if (checkLimits(sessionInfo, callback)) { | ||
152 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback); | ||
153 | - } | 146 | + protected void doProcess(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) { |
147 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback); | ||
154 | } | 148 | } |
155 | 149 | ||
156 | @Override | 150 | @Override |
157 | - public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) { | ||
158 | - if (checkLimits(sessionInfo, callback)) { | ||
159 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback); | ||
160 | - } | 151 | + protected void doProcess(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) { |
152 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback); | ||
161 | } | 153 | } |
162 | 154 | ||
163 | @Override | 155 | @Override |
164 | - public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) { | ||
165 | - if (checkLimits(sessionInfo, callback)) { | ||
166 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback); | ||
167 | - } | 156 | + public void process(SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto msg, TransportServiceCallback<Void> callback) { |
157 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscriptionInfo(msg).build(), callback); | ||
168 | } | 158 | } |
169 | 159 | ||
170 | @Override | 160 | @Override |
171 | - public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) { | ||
172 | - if (checkLimits(sessionInfo, callback)) { | ||
173 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback); | ||
174 | - } | 161 | + protected void doProcess(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) { |
162 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback); | ||
175 | } | 163 | } |
176 | 164 | ||
177 | @Override | 165 | @Override |
178 | - public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { | ||
179 | - if (checkLimits(sessionInfo, callback)) { | ||
180 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback); | ||
181 | - } | 166 | + protected void doProcess(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) { |
167 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback); | ||
182 | } | 168 | } |
183 | 169 | ||
184 | @Override | 170 | @Override |
185 | - public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) { | ||
186 | - if (checkLimits(sessionInfo, callback)) { | ||
187 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback); | ||
188 | - } | 171 | + protected void doProcess(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { |
172 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback); | ||
173 | + } | ||
174 | + | ||
175 | + @Override | ||
176 | + protected void doProcess(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) { | ||
177 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback); | ||
189 | } | 178 | } |
190 | 179 | ||
191 | @Override | 180 | @Override |
@@ -17,6 +17,11 @@ package org.thingsboard.server.service.transport; | @@ -17,6 +17,11 @@ package org.thingsboard.server.service.transport; | ||
17 | 17 | ||
18 | import akka.actor.ActorRef; | 18 | import akka.actor.ActorRef; |
19 | import com.fasterxml.jackson.databind.ObjectMapper; | 19 | import com.fasterxml.jackson.databind.ObjectMapper; |
20 | +import io.github.bucket4j.Bandwidth; | ||
21 | +import io.github.bucket4j.BlockingBucket; | ||
22 | +import io.github.bucket4j.Bucket4j; | ||
23 | +import io.github.bucket4j.local.LocalBucket; | ||
24 | +import io.github.bucket4j.local.LocalBucketBuilder; | ||
20 | import lombok.extern.slf4j.Slf4j; | 25 | import lombok.extern.slf4j.Slf4j; |
21 | import org.apache.kafka.clients.consumer.ConsumerRecords; | 26 | import org.apache.kafka.clients.consumer.ConsumerRecords; |
22 | import org.apache.kafka.clients.producer.Callback; | 27 | import org.apache.kafka.clients.producer.Callback; |
@@ -49,6 +54,7 @@ import java.util.Optional; | @@ -49,6 +54,7 @@ import java.util.Optional; | ||
49 | import java.util.UUID; | 54 | import java.util.UUID; |
50 | import java.util.concurrent.ExecutorService; | 55 | import java.util.concurrent.ExecutorService; |
51 | import java.util.concurrent.Executors; | 56 | import java.util.concurrent.Executors; |
57 | +import java.util.concurrent.TimeUnit; | ||
52 | import java.util.function.Consumer; | 58 | import java.util.function.Consumer; |
53 | 59 | ||
54 | /** | 60 | /** |
@@ -68,6 +74,13 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ | @@ -68,6 +74,13 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ | ||
68 | @Value("${transport.remote.rule_engine.auto_commit_interval}") | 74 | @Value("${transport.remote.rule_engine.auto_commit_interval}") |
69 | private int autoCommitInterval; | 75 | private int autoCommitInterval; |
70 | 76 | ||
77 | + @Value("${transport.remote.rule_engine.poll_records_pack_size}") | ||
78 | + private int pollRecordsPackSize; | ||
79 | + @Value("${transport.remote.rule_engine.max_poll_records_per_second}") | ||
80 | + private long pollRecordsPerSecond; | ||
81 | + @Value("${transport.remote.rule_engine.max_poll_records_per_minute}") | ||
82 | + private long pollRecordsPerMinute; | ||
83 | + | ||
71 | @Autowired | 84 | @Autowired |
72 | private TbKafkaSettings kafkaSettings; | 85 | private TbKafkaSettings kafkaSettings; |
73 | 86 | ||
@@ -109,15 +122,30 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ | @@ -109,15 +122,30 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ | ||
109 | ruleEngineConsumerBuilder.groupId("tb-node"); | 122 | ruleEngineConsumerBuilder.groupId("tb-node"); |
110 | ruleEngineConsumerBuilder.autoCommit(true); | 123 | ruleEngineConsumerBuilder.autoCommit(true); |
111 | ruleEngineConsumerBuilder.autoCommitIntervalMs(autoCommitInterval); | 124 | ruleEngineConsumerBuilder.autoCommitIntervalMs(autoCommitInterval); |
125 | + ruleEngineConsumerBuilder.maxPollRecords(pollRecordsPackSize); | ||
112 | ruleEngineConsumerBuilder.decoder(new ToRuleEngineMsgDecoder()); | 126 | ruleEngineConsumerBuilder.decoder(new ToRuleEngineMsgDecoder()); |
113 | 127 | ||
114 | ruleEngineConsumer = ruleEngineConsumerBuilder.build(); | 128 | ruleEngineConsumer = ruleEngineConsumerBuilder.build(); |
115 | ruleEngineConsumer.subscribe(); | 129 | ruleEngineConsumer.subscribe(); |
116 | 130 | ||
131 | + LocalBucketBuilder builder = Bucket4j.builder(); | ||
132 | + builder.addLimit(Bandwidth.simple(pollRecordsPerSecond, Duration.ofSeconds(1))); | ||
133 | + builder.addLimit(Bandwidth.simple(pollRecordsPerMinute, Duration.ofMinutes(1))); | ||
134 | + LocalBucket pollRateBucket = builder.build(); | ||
135 | + BlockingBucket blockingPollRateBucket = pollRateBucket.asScheduler(); | ||
136 | + | ||
117 | mainConsumerExecutor.execute(() -> { | 137 | mainConsumerExecutor.execute(() -> { |
118 | while (!stopped) { | 138 | while (!stopped) { |
119 | try { | 139 | try { |
120 | ConsumerRecords<String, byte[]> records = ruleEngineConsumer.poll(Duration.ofMillis(pollDuration)); | 140 | ConsumerRecords<String, byte[]> records = ruleEngineConsumer.poll(Duration.ofMillis(pollDuration)); |
141 | + int recordsCount = records.count(); | ||
142 | + if (recordsCount > 0) { | ||
143 | + while (!blockingPollRateBucket.tryConsume(recordsCount, TimeUnit.SECONDS.toNanos(5))) { | ||
144 | + log.info("Rule Engine consumer is busy. Required tokens: [{}]. Available tokens: [{}].", recordsCount, pollRateBucket.getAvailableTokens()); | ||
145 | + Thread.sleep(TimeUnit.SECONDS.toMillis(1)); | ||
146 | + } | ||
147 | + log.trace("Processing {} records", recordsCount); | ||
148 | + } | ||
121 | records.forEach(record -> { | 149 | records.forEach(record -> { |
122 | try { | 150 | try { |
123 | ToRuleEngineMsg toRuleEngineMsg = ruleEngineConsumer.decode(record); | 151 | ToRuleEngineMsg toRuleEngineMsg = ruleEngineConsumer.decode(record); |
@@ -22,6 +22,7 @@ option java_outer_classname = "ClusterAPIProtos"; | @@ -22,6 +22,7 @@ option java_outer_classname = "ClusterAPIProtos"; | ||
22 | service ClusterRpcService { | 22 | service ClusterRpcService { |
23 | rpc handleMsgs(stream ClusterMessage) returns (stream ClusterMessage) {} | 23 | rpc handleMsgs(stream ClusterMessage) returns (stream ClusterMessage) {} |
24 | } | 24 | } |
25 | + | ||
25 | message ClusterMessage { | 26 | message ClusterMessage { |
26 | MessageType messageType = 1; | 27 | MessageType messageType = 1; |
27 | MessageMataInfo messageMetaInfo = 2; | 28 | MessageMataInfo messageMetaInfo = 2; |
@@ -139,4 +140,4 @@ message DeviceStateServiceMsgProto { | @@ -139,4 +140,4 @@ message DeviceStateServiceMsgProto { | ||
139 | bool added = 5; | 140 | bool added = 5; |
140 | bool updated = 6; | 141 | bool updated = 6; |
141 | bool deleted = 7; | 142 | bool deleted = 7; |
142 | -} | ||
143 | +} |
@@ -92,7 +92,7 @@ core-dispatcher { | @@ -92,7 +92,7 @@ core-dispatcher { | ||
92 | throughput = 5 | 92 | throughput = 5 |
93 | } | 93 | } |
94 | 94 | ||
95 | -# This dispatcher is used for system rule actors | 95 | +# This dispatcher is used for system rule chains and rule node actors |
96 | system-rule-dispatcher { | 96 | system-rule-dispatcher { |
97 | type = Dispatcher | 97 | type = Dispatcher |
98 | executor = "fork-join-executor" | 98 | executor = "fork-join-executor" |
@@ -115,30 +115,7 @@ system-rule-dispatcher { | @@ -115,30 +115,7 @@ system-rule-dispatcher { | ||
115 | throughput = 5 | 115 | throughput = 5 |
116 | } | 116 | } |
117 | 117 | ||
118 | -# This dispatcher is used for system plugin actors | ||
119 | -system-plugin-dispatcher { | ||
120 | - type = Dispatcher | ||
121 | - executor = "fork-join-executor" | ||
122 | - fork-join-executor { | ||
123 | - # Min number of threads to cap factor-based parallelism number to | ||
124 | - parallelism-min = 2 | ||
125 | - # Max number of threads to cap factor-based parallelism number to | ||
126 | - parallelism-max = 12 | ||
127 | - | ||
128 | - # The parallelism factor is used to determine thread pool size using the | ||
129 | - # following formula: ceil(available processors * factor). Resulting size | ||
130 | - # is then bounded by the parallelism-min and parallelism-max values. | ||
131 | - parallelism-factor = 0.25 | ||
132 | - } | ||
133 | - # How long time the dispatcher will wait for new actors until it shuts down | ||
134 | - shutdown-timeout = 1s | ||
135 | - | ||
136 | - # Throughput defines the number of messages that are processed in a batch | ||
137 | - # before the thread is returned to the pool. Set to 1 for as fair as possible. | ||
138 | - throughput = 5 | ||
139 | -} | ||
140 | - | ||
141 | -# This dispatcher is used for tenant rule actors | 118 | +# This dispatcher is used for tenant rule chains and rule node actors |
142 | rule-dispatcher { | 119 | rule-dispatcher { |
143 | type = Dispatcher | 120 | type = Dispatcher |
144 | executor = "fork-join-executor" | 121 | executor = "fork-join-executor" |
@@ -160,50 +137,3 @@ rule-dispatcher { | @@ -160,50 +137,3 @@ rule-dispatcher { | ||
160 | # before the thread is returned to the pool. Set to 1 for as fair as possible. | 137 | # before the thread is returned to the pool. Set to 1 for as fair as possible. |
161 | throughput = 5 | 138 | throughput = 5 |
162 | } | 139 | } |
163 | - | ||
164 | -# This dispatcher is used for tenant plugin actors | ||
165 | -plugin-dispatcher { | ||
166 | - type = Dispatcher | ||
167 | - executor = "fork-join-executor" | ||
168 | - fork-join-executor { | ||
169 | - # Min number of threads to cap factor-based parallelism number to | ||
170 | - parallelism-min = 2 | ||
171 | - # Max number of threads to cap factor-based parallelism number to | ||
172 | - parallelism-max = 12 | ||
173 | - | ||
174 | - # The parallelism factor is used to determine thread pool size using the | ||
175 | - # following formula: ceil(available processors * factor). Resulting size | ||
176 | - # is then bounded by the parallelism-min and parallelism-max values. | ||
177 | - parallelism-factor = 0.25 | ||
178 | - } | ||
179 | - # How long time the dispatcher will wait for new actors until it shuts down | ||
180 | - shutdown-timeout = 1s | ||
181 | - | ||
182 | - # Throughput defines the number of messages that are processed in a batch | ||
183 | - # before the thread is returned to the pool. Set to 1 for as fair as possible. | ||
184 | - throughput = 5 | ||
185 | -} | ||
186 | - | ||
187 | - | ||
188 | -# This dispatcher is used for rule actors | ||
189 | -session-dispatcher { | ||
190 | - type = Dispatcher | ||
191 | - executor = "fork-join-executor" | ||
192 | - fork-join-executor { | ||
193 | - # Min number of threads to cap factor-based parallelism number to | ||
194 | - parallelism-min = 2 | ||
195 | - # Max number of threads to cap factor-based parallelism number to | ||
196 | - parallelism-max = 12 | ||
197 | - | ||
198 | - # The parallelism factor is used to determine thread pool size using the | ||
199 | - # following formula: ceil(available processors * factor). Resulting size | ||
200 | - # is then bounded by the parallelism-min and parallelism-max values. | ||
201 | - parallelism-factor = 0.25 | ||
202 | - } | ||
203 | - # How long time the dispatcher will wait for new actors until it shuts down | ||
204 | - shutdown-timeout = 1s | ||
205 | - | ||
206 | - # Throughput defines the number of messages that are processed in a batch | ||
207 | - # before the thread is returned to the pool. Set to 1 for as fair as possible. | ||
208 | - throughput = 5 | ||
209 | -} |
@@ -140,7 +140,7 @@ cassandra: | @@ -140,7 +140,7 @@ cassandra: | ||
140 | buffer_size: "${CASSANDRA_QUERY_BUFFER_SIZE:200000}" | 140 | buffer_size: "${CASSANDRA_QUERY_BUFFER_SIZE:200000}" |
141 | concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}" | 141 | concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}" |
142 | permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}" | 142 | permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}" |
143 | - rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:30000}" | 143 | + rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:10000}" |
144 | 144 | ||
145 | # SQL configuration parameters | 145 | # SQL configuration parameters |
146 | sql: | 146 | sql: |
@@ -202,6 +202,9 @@ caffeine: | @@ -202,6 +202,9 @@ caffeine: | ||
202 | devices: | 202 | devices: |
203 | timeToLiveInMinutes: 1440 | 203 | timeToLiveInMinutes: 1440 |
204 | maxSize: 100000 | 204 | maxSize: 100000 |
205 | + sessions: | ||
206 | + timeToLiveInMinutes: 1440 | ||
207 | + maxSize: 100000 | ||
205 | assets: | 208 | assets: |
206 | timeToLiveInMinutes: 1440 | 209 | timeToLiveInMinutes: 1440 |
207 | maxSize: 100000 | 210 | maxSize: 100000 |
@@ -222,7 +225,7 @@ redis: | @@ -222,7 +225,7 @@ redis: | ||
222 | updates: | 225 | updates: |
223 | # Enable/disable updates checking. | 226 | # Enable/disable updates checking. |
224 | enabled: "${UPDATES_ENABLED:true}" | 227 | enabled: "${UPDATES_ENABLED:true}" |
225 | - | 228 | + |
226 | # spring CORS configuration | 229 | # spring CORS configuration |
227 | spring.mvc.cors: | 230 | spring.mvc.cors: |
228 | mappings: | 231 | mappings: |
@@ -322,8 +325,8 @@ audit_log: | @@ -322,8 +325,8 @@ audit_log: | ||
322 | password: "${AUDIT_LOG_SINK_PASSWORD:}" | 325 | password: "${AUDIT_LOG_SINK_PASSWORD:}" |
323 | 326 | ||
324 | state: | 327 | state: |
325 | - defaultInactivityTimeoutInSec: 10 | ||
326 | - defaultStateCheckIntervalInSec: 10 | 328 | + defaultInactivityTimeoutInSec: "${DEFAULT_INACTIVITY_TIMEOUT:10}" |
329 | + defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:10}" | ||
327 | 330 | ||
328 | kafka: | 331 | kafka: |
329 | enabled: true | 332 | enabled: true |
@@ -390,8 +393,14 @@ transport: | @@ -390,8 +393,14 @@ transport: | ||
390 | topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}" | 393 | topic: "${TB_RULE_ENGINE_TOPIC:tb.rule-engine}" |
391 | poll_interval: "${TB_RULE_ENGINE_POLL_INTERVAL_MS:25}" | 394 | poll_interval: "${TB_RULE_ENGINE_POLL_INTERVAL_MS:25}" |
392 | auto_commit_interval: "${TB_RULE_ENGINE_AUTO_COMMIT_INTERVAL_MS:100}" | 395 | auto_commit_interval: "${TB_RULE_ENGINE_AUTO_COMMIT_INTERVAL_MS:100}" |
396 | + poll_records_pack_size: "${TB_RULE_ENGINE_MAX_POLL_RECORDS:1000}" | ||
397 | + max_poll_records_per_second: "${TB_RULE_ENGINE_MAX_POLL_RECORDS_PER_SECOND:10000}" | ||
398 | + max_poll_records_per_minute: "${TB_RULE_ENGINE_MAX_POLL_RECORDS_PER_MINUTE:120000}" | ||
393 | notifications: | 399 | notifications: |
394 | topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}" | 400 | topic: "${TB_TRANSPORT_NOTIFICATIONS_TOPIC:tb.transport.notifications}" |
401 | + sessions: | ||
402 | + inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}" | ||
403 | + report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:30000}" | ||
395 | rate_limits: | 404 | rate_limits: |
396 | enabled: "${TB_TRANSPORT_RATE_LIMITS_ENABLED:false}" | 405 | enabled: "${TB_TRANSPORT_RATE_LIMITS_ENABLED:false}" |
397 | tenant: "${TB_TRANSPORT_RATE_LIMITS_TENANT:1000:1,20000:60}" | 406 | tenant: "${TB_TRANSPORT_RATE_LIMITS_TENANT:1000:1,20000:60}" |
@@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; | @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; | ||
38 | import org.thingsboard.server.common.data.security.Authority; | 38 | import org.thingsboard.server.common.data.security.Authority; |
39 | import org.thingsboard.server.common.msg.TbMsg; | 39 | import org.thingsboard.server.common.msg.TbMsg; |
40 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 40 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
41 | +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | ||
41 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | 42 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
42 | import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; | 43 | import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; |
43 | import org.thingsboard.server.dao.attributes.AttributesService; | 44 | import org.thingsboard.server.dao.attributes.AttributesService; |
@@ -155,7 +156,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule | @@ -155,7 +156,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule | ||
155 | device.getId(), | 156 | device.getId(), |
156 | new TbMsgMetaData(), | 157 | new TbMsgMetaData(), |
157 | "{}", null, null, 0L); | 158 | "{}", null, null, 0L); |
158 | - actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); | 159 | + actorService.onMsg(new SendToClusterMsg(device.getId(), new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg))); |
159 | 160 | ||
160 | Thread.sleep(3000); | 161 | Thread.sleep(3000); |
161 | 162 | ||
@@ -270,7 +271,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule | @@ -270,7 +271,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule | ||
270 | device.getId(), | 271 | device.getId(), |
271 | new TbMsgMetaData(), | 272 | new TbMsgMetaData(), |
272 | "{}", null, null, 0L); | 273 | "{}", null, null, 0L); |
273 | - actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); | 274 | + actorService.onMsg(new SendToClusterMsg(device.getId(), new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg))); |
274 | 275 | ||
275 | Thread.sleep(3000); | 276 | Thread.sleep(3000); |
276 | 277 |
@@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; | @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; | ||
39 | import org.thingsboard.server.common.data.security.Authority; | 39 | import org.thingsboard.server.common.data.security.Authority; |
40 | import org.thingsboard.server.common.msg.TbMsg; | 40 | import org.thingsboard.server.common.msg.TbMsg; |
41 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 41 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
42 | +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | ||
42 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | 43 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
43 | import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; | 44 | import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; |
44 | import org.thingsboard.server.dao.attributes.AttributesService; | 45 | import org.thingsboard.server.dao.attributes.AttributesService; |
@@ -142,7 +143,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac | @@ -142,7 +143,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac | ||
142 | new TbMsgMetaData(), | 143 | new TbMsgMetaData(), |
143 | "{}", | 144 | "{}", |
144 | null, null, 0L); | 145 | null, null, 0L); |
145 | - actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); | 146 | + actorService.onMsg(new SendToClusterMsg(device.getId(), new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg))); |
146 | 147 | ||
147 | Thread.sleep(3000); | 148 | Thread.sleep(3000); |
148 | 149 |
@@ -19,6 +19,7 @@ public class CacheConstants { | @@ -19,6 +19,7 @@ public class CacheConstants { | ||
19 | public static final String DEVICE_CREDENTIALS_CACHE = "deviceCredentials"; | 19 | public static final String DEVICE_CREDENTIALS_CACHE = "deviceCredentials"; |
20 | public static final String RELATIONS_CACHE = "relations"; | 20 | public static final String RELATIONS_CACHE = "relations"; |
21 | public static final String DEVICE_CACHE = "devices"; | 21 | public static final String DEVICE_CACHE = "devices"; |
22 | + public static final String SESSIONS_CACHE = "sessions"; | ||
22 | public static final String ASSET_CACHE = "assets"; | 23 | public static final String ASSET_CACHE = "assets"; |
23 | public static final String ENTITY_VIEW_CACHE = "entityViews"; | 24 | public static final String ENTITY_VIEW_CACHE = "entityViews"; |
24 | } | 25 | } |
@@ -24,6 +24,7 @@ public enum ActionType { | @@ -24,6 +24,7 @@ public enum ActionType { | ||
24 | UPDATED(false), // log entity | 24 | UPDATED(false), // log entity |
25 | ATTRIBUTES_UPDATED(false), // log attributes/values | 25 | ATTRIBUTES_UPDATED(false), // log attributes/values |
26 | ATTRIBUTES_DELETED(false), // log attributes | 26 | ATTRIBUTES_DELETED(false), // log attributes |
27 | + TIMESERIES_DELETED(false), // log timeseries | ||
27 | RPC_CALL(false), // log method and params | 28 | RPC_CALL(false), // log method and params |
28 | CREDENTIALS_UPDATED(false), // log new credentials | 29 | CREDENTIALS_UPDATED(false), // log new credentials |
29 | ASSIGNED_TO_CUSTOMER(false), // log customer name | 30 | ASSIGNED_TO_CUSTOMER(false), // log customer name |
@@ -32,11 +33,11 @@ public enum ActionType { | @@ -32,11 +33,11 @@ public enum ActionType { | ||
32 | SUSPENDED(false), // log string id | 33 | SUSPENDED(false), // log string id |
33 | CREDENTIALS_READ(true), // log device id | 34 | CREDENTIALS_READ(true), // log device id |
34 | ATTRIBUTES_READ(true), // log attributes | 35 | ATTRIBUTES_READ(true), // log attributes |
35 | - RELATION_ADD_OR_UPDATE (false), | ||
36 | - RELATION_DELETED (false), | ||
37 | - RELATIONS_DELETED (false), | ||
38 | - ALARM_ACK (false), | ||
39 | - ALARM_CLEAR (false); | 36 | + RELATION_ADD_OR_UPDATE(false), |
37 | + RELATION_DELETED(false), | ||
38 | + RELATIONS_DELETED(false), | ||
39 | + ALARM_ACK(false), | ||
40 | + ALARM_CLEAR(false); | ||
40 | 41 | ||
41 | private final boolean isRead; | 42 | private final boolean isRead; |
42 | 43 |
@@ -96,13 +96,8 @@ public enum MsgType { | @@ -96,13 +96,8 @@ public enum MsgType { | ||
96 | */ | 96 | */ |
97 | DEVICE_ACTOR_TO_RULE_ENGINE_MSG, | 97 | DEVICE_ACTOR_TO_RULE_ENGINE_MSG, |
98 | 98 | ||
99 | - /** | ||
100 | - * Message that is sent from Rule Engine to the Device Actor when message is successfully pushed to queue. | ||
101 | - */ | ||
102 | - ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG, | ||
103 | - TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG, | ||
104 | SESSION_TIMEOUT_MSG, | 99 | SESSION_TIMEOUT_MSG, |
105 | - SESSION_CTRL_MSG, | 100 | + |
106 | STATS_PERSIST_TICK_MSG, | 101 | STATS_PERSIST_TICK_MSG, |
107 | 102 | ||
108 | 103 |
@@ -19,6 +19,5 @@ package org.thingsboard.server.common.msg.cluster; | @@ -19,6 +19,5 @@ package org.thingsboard.server.common.msg.cluster; | ||
19 | * Created by ashvayka on 23.09.18. | 19 | * Created by ashvayka on 23.09.18. |
20 | */ | 20 | */ |
21 | public enum ServerType { | 21 | public enum ServerType { |
22 | - //Should match content of enum in discovery.proto. | ||
23 | - CORE, JS_EVALUATOR | 22 | + CORE |
24 | } | 23 | } |
@@ -21,11 +21,13 @@ import org.thingsboard.server.common.msg.MsgType; | @@ -21,11 +21,13 @@ import org.thingsboard.server.common.msg.MsgType; | ||
21 | import org.thingsboard.server.common.msg.TbActorMsg; | 21 | import org.thingsboard.server.common.msg.TbActorMsg; |
22 | import org.thingsboard.server.common.msg.TbMsg; | 22 | import org.thingsboard.server.common.msg.TbMsg; |
23 | 23 | ||
24 | +import java.io.Serializable; | ||
25 | + | ||
24 | /** | 26 | /** |
25 | * Created by ashvayka on 15.03.18. | 27 | * Created by ashvayka on 15.03.18. |
26 | */ | 28 | */ |
27 | @Data | 29 | @Data |
28 | -public final class ServiceToRuleEngineMsg implements TbActorMsg { | 30 | +public final class ServiceToRuleEngineMsg implements TbActorMsg, Serializable { |
29 | 31 | ||
30 | private final TenantId tenantId; | 32 | private final TenantId tenantId; |
31 | private final TbMsg tbMsg; | 33 | private final TbMsg tbMsg; |
@@ -46,7 +46,8 @@ public class TBKafkaConsumerTemplate<T> { | @@ -46,7 +46,8 @@ public class TBKafkaConsumerTemplate<T> { | ||
46 | private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> decoder, | 46 | private TBKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> decoder, |
47 | TbKafkaRequestIdExtractor<T> requestIdExtractor, | 47 | TbKafkaRequestIdExtractor<T> requestIdExtractor, |
48 | String clientId, String groupId, String topic, | 48 | String clientId, String groupId, String topic, |
49 | - boolean autoCommit, int autoCommitIntervalMs) { | 49 | + boolean autoCommit, int autoCommitIntervalMs, |
50 | + int maxPollRecords) { | ||
50 | Properties props = settings.toProps(); | 51 | Properties props = settings.toProps(); |
51 | props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); | 52 | props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); |
52 | if (groupId != null) { | 53 | if (groupId != null) { |
@@ -56,6 +57,9 @@ public class TBKafkaConsumerTemplate<T> { | @@ -56,6 +57,9 @@ public class TBKafkaConsumerTemplate<T> { | ||
56 | props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitIntervalMs); | 57 | props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitIntervalMs); |
57 | props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer"); | 58 | props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer"); |
58 | props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.ByteArrayDeserializer"); | 59 | props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.ByteArrayDeserializer"); |
60 | + if (maxPollRecords > 0) { | ||
61 | + props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords); | ||
62 | + } | ||
59 | this.consumer = new KafkaConsumer<>(props); | 63 | this.consumer = new KafkaConsumer<>(props); |
60 | this.decoder = decoder; | 64 | this.decoder = decoder; |
61 | this.requestIdExtractor = requestIdExtractor; | 65 | this.requestIdExtractor = requestIdExtractor; |
@@ -26,6 +26,7 @@ import org.apache.kafka.clients.producer.ProducerConfig; | @@ -26,6 +26,7 @@ import org.apache.kafka.clients.producer.ProducerConfig; | ||
26 | import org.apache.kafka.clients.producer.ProducerRecord; | 26 | import org.apache.kafka.clients.producer.ProducerRecord; |
27 | import org.apache.kafka.clients.producer.RecordMetadata; | 27 | import org.apache.kafka.clients.producer.RecordMetadata; |
28 | import org.apache.kafka.common.PartitionInfo; | 28 | import org.apache.kafka.common.PartitionInfo; |
29 | +import org.apache.kafka.common.errors.TopicExistsException; | ||
29 | import org.apache.kafka.common.header.Header; | 30 | import org.apache.kafka.common.header.Header; |
30 | 31 | ||
31 | import java.util.List; | 32 | import java.util.List; |
@@ -75,7 +76,11 @@ public class TBKafkaProducerTemplate<T> { | @@ -75,7 +76,11 @@ public class TBKafkaProducerTemplate<T> { | ||
75 | CreateTopicsResult result = admin.createTopic(new NewTopic(defaultTopic, 100, (short) 1)); | 76 | CreateTopicsResult result = admin.createTopic(new NewTopic(defaultTopic, 100, (short) 1)); |
76 | result.all().get(); | 77 | result.all().get(); |
77 | } catch (Exception e) { | 78 | } catch (Exception e) { |
78 | - log.trace("Failed to create topic: {}", e.getMessage(), e); | 79 | + if ((e instanceof TopicExistsException) || (e.getCause() != null && e.getCause() instanceof TopicExistsException)) { |
80 | + log.trace("[{}] Topic already exists: ", defaultTopic); | ||
81 | + } else { | ||
82 | + log.trace("[{}] Failed to create topic: {}", defaultTopic, e.getMessage(), e); | ||
83 | + } | ||
79 | } | 84 | } |
80 | //Maybe this should not be cached, but we don't plan to change size of partitions | 85 | //Maybe this should not be cached, but we don't plan to change size of partitions |
81 | this.partitionInfoMap = new ConcurrentHashMap<>(); | 86 | this.partitionInfoMap = new ConcurrentHashMap<>(); |
@@ -43,10 +43,10 @@ import org.thingsboard.server.common.transport.TransportService; | @@ -43,10 +43,10 @@ import org.thingsboard.server.common.transport.TransportService; | ||
43 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 43 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
44 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 44 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
45 | import org.thingsboard.server.common.msg.EncryptionUtil; | 45 | import org.thingsboard.server.common.msg.EncryptionUtil; |
46 | +import org.thingsboard.server.common.transport.service.AbstractTransportService; | ||
46 | import org.thingsboard.server.gen.transport.TransportProtos; | 47 | import org.thingsboard.server.gen.transport.TransportProtos; |
47 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | 48 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
48 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; | 49 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; |
49 | -import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; | ||
50 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | 50 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
51 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; | 51 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; |
52 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | 52 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; |
@@ -141,9 +141,12 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -141,9 +141,12 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
141 | processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); | 141 | processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); |
142 | break; | 142 | break; |
143 | case PINGREQ: | 143 | case PINGREQ: |
144 | - //TODO: should we push the notification to the rule engine? | ||
145 | if (checkConnected(ctx)) { | 144 | if (checkConnected(ctx)) { |
146 | ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); | 145 | ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); |
146 | + transportService.reportActivity(sessionInfo); | ||
147 | + if (gatewaySessionHandler != null) { | ||
148 | + gatewaySessionHandler.reportActivity(); | ||
149 | + } | ||
147 | } | 150 | } |
148 | break; | 151 | break; |
149 | case DISCONNECT: | 152 | case DISCONNECT: |
@@ -394,7 +397,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -394,7 +397,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
394 | private void processDisconnect(ChannelHandlerContext ctx) { | 397 | private void processDisconnect(ChannelHandlerContext ctx) { |
395 | ctx.close(); | 398 | ctx.close(); |
396 | if (deviceSessionCtx.isConnected()) { | 399 | if (deviceSessionCtx.isConnected()) { |
397 | - transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.CLOSED), null); | 400 | + transportService.process(sessionInfo, AbstractTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); |
398 | transportService.deregisterSession(sessionInfo); | 401 | transportService.deregisterSession(sessionInfo); |
399 | if (gatewaySessionHandler != null) { | 402 | if (gatewaySessionHandler != null) { |
400 | gatewaySessionHandler.onGatewayDisconnect(); | 403 | gatewaySessionHandler.onGatewayDisconnect(); |
@@ -466,16 +469,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -466,16 +469,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
466 | } | 469 | } |
467 | } | 470 | } |
468 | 471 | ||
469 | - public static SessionEventMsg getSessionEventMsg(SessionEvent event) { | ||
470 | - return SessionEventMsg.newBuilder() | ||
471 | - .setSessionType(TransportProtos.SessionType.ASYNC) | ||
472 | - .setEvent(event).build(); | ||
473 | - } | ||
474 | - | ||
475 | @Override | 472 | @Override |
476 | public void operationComplete(Future<? super Void> future) throws Exception { | 473 | public void operationComplete(Future<? super Void> future) throws Exception { |
477 | if (deviceSessionCtx.isConnected()) { | 474 | if (deviceSessionCtx.isConnected()) { |
478 | - transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.CLOSED), null); | 475 | + transportService.process(sessionInfo, AbstractTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); |
479 | transportService.deregisterSession(sessionInfo); | 476 | transportService.deregisterSession(sessionInfo); |
480 | } | 477 | } |
481 | } | 478 | } |
@@ -495,7 +492,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -495,7 +492,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
495 | .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB()) | 492 | .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB()) |
496 | .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) | 493 | .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB()) |
497 | .build(); | 494 | .build(); |
498 | - transportService.process(sessionInfo, getSessionEventMsg(SessionEvent.OPEN), null); | 495 | + transportService.process(sessionInfo, AbstractTransportService.getSessionEventMsg(SessionEvent.OPEN), null); |
499 | transportService.registerAsyncSession(sessionInfo, this); | 496 | transportService.registerAsyncSession(sessionInfo, this); |
500 | checkGatewaySession(); | 497 | checkGatewaySession(); |
501 | ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); | 498 | ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); |
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.transport.TransportService; | @@ -34,6 +34,7 @@ import org.thingsboard.server.common.transport.TransportService; | ||
34 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 34 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
35 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 35 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
36 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 36 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
37 | +import org.thingsboard.server.common.transport.service.AbstractTransportService; | ||
37 | import org.thingsboard.server.gen.transport.TransportProtos; | 38 | import org.thingsboard.server.gen.transport.TransportProtos; |
38 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | 39 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
39 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; | 40 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; |
@@ -118,7 +119,7 @@ public class GatewaySessionHandler { | @@ -118,7 +119,7 @@ public class GatewaySessionHandler { | ||
118 | GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), mqttQoSMap); | 119 | GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), mqttQoSMap); |
119 | if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) { | 120 | if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) { |
120 | SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo(); | 121 | SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo(); |
121 | - transportService.process(deviceSessionInfo, MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); | 122 | + transportService.process(deviceSessionInfo, AbstractTransportService.getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); |
122 | transportService.process(deviceSessionInfo, TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), null); | 123 | transportService.process(deviceSessionInfo, TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), null); |
123 | transportService.process(deviceSessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), null); | 124 | transportService.process(deviceSessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), null); |
124 | transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx); | 125 | transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx); |
@@ -334,7 +335,7 @@ public class GatewaySessionHandler { | @@ -334,7 +335,7 @@ public class GatewaySessionHandler { | ||
334 | 335 | ||
335 | private void deregisterSession(String deviceName, GatewayDeviceSessionCtx deviceSessionCtx) { | 336 | private void deregisterSession(String deviceName, GatewayDeviceSessionCtx deviceSessionCtx) { |
336 | transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); | 337 | transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); |
337 | - transportService.process(deviceSessionCtx.getSessionInfo(), MqttTransportHandler.getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); | 338 | + transportService.process(deviceSessionCtx.getSessionInfo(), AbstractTransportService.getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); |
338 | log.debug("[{}] Removed device [{}] from the gateway session", sessionId, deviceName); | 339 | log.debug("[{}] Removed device [{}] from the gateway session", sessionId, deviceName); |
339 | } | 340 | } |
340 | 341 | ||
@@ -360,11 +361,15 @@ public class GatewaySessionHandler { | @@ -360,11 +361,15 @@ public class GatewaySessionHandler { | ||
360 | return context; | 361 | return context; |
361 | } | 362 | } |
362 | 363 | ||
363 | - public MqttTransportAdaptor getAdaptor() { | 364 | + MqttTransportAdaptor getAdaptor() { |
364 | return context.getAdaptor(); | 365 | return context.getAdaptor(); |
365 | } | 366 | } |
366 | 367 | ||
367 | - public int nextMsgId() { | 368 | + int nextMsgId() { |
368 | return deviceSessionCtx.nextMsgId(); | 369 | return deviceSessionCtx.nextMsgId(); |
369 | } | 370 | } |
371 | + | ||
372 | + public void reportActivity() { | ||
373 | + devices.forEach((id, deviceCtx) -> transportService.reportActivity(deviceCtx.getSessionInfo())); | ||
374 | + } | ||
370 | } | 375 | } |
@@ -61,10 +61,14 @@ public interface TransportService { | @@ -61,10 +61,14 @@ public interface TransportService { | ||
61 | 61 | ||
62 | void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback); | 62 | void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback); |
63 | 63 | ||
64 | + void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto msg, TransportServiceCallback<Void> callback); | ||
65 | + | ||
64 | void registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); | 66 | void registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); |
65 | 67 | ||
66 | void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout); | 68 | void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout); |
67 | 69 | ||
70 | + void reportActivity(SessionInfoProto sessionInfo); | ||
71 | + | ||
68 | void deregisterSession(SessionInfoProto sessionInfo); | 72 | void deregisterSession(SessionInfoProto sessionInfo); |
69 | 73 | ||
70 | } | 74 | } |
@@ -47,9 +47,14 @@ public abstract class AbstractTransportService implements TransportService { | @@ -47,9 +47,14 @@ public abstract class AbstractTransportService implements TransportService { | ||
47 | private String perTenantLimitsConf; | 47 | private String perTenantLimitsConf; |
48 | @Value("${transport.rate_limits.tenant}") | 48 | @Value("${transport.rate_limits.tenant}") |
49 | private String perDevicesLimitsConf; | 49 | private String perDevicesLimitsConf; |
50 | + @Value("${transport.sessions.inactivity_timeout}") | ||
51 | + private long sessionInactivityTimeout; | ||
52 | + @Value("${transport.sessions.report_timeout}") | ||
53 | + private long sessionReportTimeout; | ||
50 | 54 | ||
51 | protected ScheduledExecutorService schedulerExecutor; | 55 | protected ScheduledExecutorService schedulerExecutor; |
52 | protected ExecutorService transportCallbackExecutor; | 56 | protected ExecutorService transportCallbackExecutor; |
57 | + | ||
53 | private ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>(); | 58 | private ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>(); |
54 | 59 | ||
55 | //TODO: Implement cleanup of this maps. | 60 | //TODO: Implement cleanup of this maps. |
@@ -59,7 +64,121 @@ public abstract class AbstractTransportService implements TransportService { | @@ -59,7 +64,121 @@ public abstract class AbstractTransportService implements TransportService { | ||
59 | @Override | 64 | @Override |
60 | public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) { | 65 | public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) { |
61 | sessions.putIfAbsent(toId(sessionInfo), new SessionMetaData(sessionInfo, TransportProtos.SessionType.ASYNC, listener)); | 66 | sessions.putIfAbsent(toId(sessionInfo), new SessionMetaData(sessionInfo, TransportProtos.SessionType.ASYNC, listener)); |
62 | - //TODO: monitor sessions periodically: PING REQ/RESP, etc. | 67 | + } |
68 | + | ||
69 | + @Override | ||
70 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SessionEventMsg msg, TransportServiceCallback<Void> callback) { | ||
71 | + if (checkLimits(sessionInfo, callback)) { | ||
72 | + reportActivityInternal(sessionInfo); | ||
73 | + doProcess(sessionInfo, msg, callback); | ||
74 | + } | ||
75 | + } | ||
76 | + | ||
77 | + @Override | ||
78 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { | ||
79 | + if (checkLimits(sessionInfo, callback)) { | ||
80 | + reportActivityInternal(sessionInfo); | ||
81 | + doProcess(sessionInfo, msg, callback); | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + @Override | ||
86 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostAttributeMsg msg, TransportServiceCallback<Void> callback) { | ||
87 | + if (checkLimits(sessionInfo, callback)) { | ||
88 | + reportActivityInternal(sessionInfo); | ||
89 | + doProcess(sessionInfo, msg, callback); | ||
90 | + } | ||
91 | + } | ||
92 | + | ||
93 | + @Override | ||
94 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) { | ||
95 | + if (checkLimits(sessionInfo, callback)) { | ||
96 | + reportActivityInternal(sessionInfo); | ||
97 | + doProcess(sessionInfo, msg, callback); | ||
98 | + } | ||
99 | + } | ||
100 | + | ||
101 | + @Override | ||
102 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) { | ||
103 | + if (checkLimits(sessionInfo, callback)) { | ||
104 | + SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); | ||
105 | + sessionMetaData.setSubscribedToAttributes(!msg.getUnsubscribe()); | ||
106 | + doProcess(sessionInfo, msg, callback); | ||
107 | + } | ||
108 | + } | ||
109 | + | ||
110 | + @Override | ||
111 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) { | ||
112 | + if (checkLimits(sessionInfo, callback)) { | ||
113 | + SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); | ||
114 | + sessionMetaData.setSubscribedToRPC(!msg.getUnsubscribe()); | ||
115 | + doProcess(sessionInfo, msg, callback); | ||
116 | + } | ||
117 | + } | ||
118 | + | ||
119 | + @Override | ||
120 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { | ||
121 | + if (checkLimits(sessionInfo, callback)) { | ||
122 | + reportActivityInternal(sessionInfo); | ||
123 | + doProcess(sessionInfo, msg, callback); | ||
124 | + } | ||
125 | + } | ||
126 | + | ||
127 | + @Override | ||
128 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) { | ||
129 | + if (checkLimits(sessionInfo, callback)) { | ||
130 | + reportActivityInternal(sessionInfo); | ||
131 | + doProcess(sessionInfo, msg, callback); | ||
132 | + } | ||
133 | + } | ||
134 | + | ||
135 | + @Override | ||
136 | + public void reportActivity(TransportProtos.SessionInfoProto sessionInfo) { | ||
137 | + reportActivityInternal(sessionInfo); | ||
138 | + } | ||
139 | + | ||
140 | + protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SessionEventMsg msg, TransportServiceCallback<Void> callback); | ||
141 | + | ||
142 | + protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostTelemetryMsg msg, TransportServiceCallback<Void> callback); | ||
143 | + | ||
144 | + protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostAttributeMsg msg, TransportServiceCallback<Void> callback); | ||
145 | + | ||
146 | + protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback); | ||
147 | + | ||
148 | + protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback); | ||
149 | + | ||
150 | + protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback); | ||
151 | + | ||
152 | + protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback); | ||
153 | + | ||
154 | + protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback); | ||
155 | + | ||
156 | + private SessionMetaData reportActivityInternal(TransportProtos.SessionInfoProto sessionInfo) { | ||
157 | + UUID sessionId = toId(sessionInfo); | ||
158 | + SessionMetaData sessionMetaData = sessions.get(sessionId); | ||
159 | + if (sessionMetaData != null) { | ||
160 | + sessionMetaData.updateLastActivityTime(); | ||
161 | + } | ||
162 | + return sessionMetaData; | ||
163 | + } | ||
164 | + | ||
165 | + private void checkInactivityAndReportActivity() { | ||
166 | + long expTime = System.currentTimeMillis() - sessionInactivityTimeout; | ||
167 | + sessions.forEach((uuid, sessionMD) -> { | ||
168 | + if (sessionMD.getLastActivityTime() < expTime) { | ||
169 | + if (log.isDebugEnabled()) { | ||
170 | + log.debug("[{}] Session has expired due to last activity time: {}", toId(sessionMD.getSessionInfo()), sessionMD.getLastActivityTime()); | ||
171 | + } | ||
172 | + process(sessionMD.getSessionInfo(), getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); | ||
173 | + sessions.remove(uuid); | ||
174 | + sessionMD.getListener().onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto.getDefaultInstance()); | ||
175 | + } else { | ||
176 | + process(sessionMD.getSessionInfo(), TransportProtos.SubscriptionInfoProto.newBuilder() | ||
177 | + .setAttributeSubscription(sessionMD.isSubscribedToAttributes()) | ||
178 | + .setRpcSubscription(sessionMD.isSubscribedToRPC()) | ||
179 | + .setLastActivityTime(sessionMD.getLastActivityTime()).build(), null); | ||
180 | + } | ||
181 | + }); | ||
63 | } | 182 | } |
64 | 183 | ||
65 | @Override | 184 | @Override |
@@ -131,7 +250,7 @@ public abstract class AbstractTransportService implements TransportService { | @@ -131,7 +250,7 @@ public abstract class AbstractTransportService implements TransportService { | ||
131 | } | 250 | } |
132 | } | 251 | } |
133 | 252 | ||
134 | - protected UUID toId(TransportProtos.SessionInfoProto sessionInfo) { | 253 | + private UUID toId(TransportProtos.SessionInfoProto sessionInfo) { |
135 | return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); | 254 | return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); |
136 | } | 255 | } |
137 | 256 | ||
@@ -147,6 +266,7 @@ public abstract class AbstractTransportService implements TransportService { | @@ -147,6 +266,7 @@ public abstract class AbstractTransportService implements TransportService { | ||
147 | } | 266 | } |
148 | this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(); | 267 | this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(); |
149 | this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); | 268 | this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); |
269 | + this.schedulerExecutor.scheduleAtFixedRate(this::checkInactivityAndReportActivity, sessionReportTimeout, sessionReportTimeout, TimeUnit.MILLISECONDS); | ||
150 | } | 270 | } |
151 | 271 | ||
152 | public void destroy() { | 272 | public void destroy() { |
@@ -161,4 +281,10 @@ public abstract class AbstractTransportService implements TransportService { | @@ -161,4 +281,10 @@ public abstract class AbstractTransportService implements TransportService { | ||
161 | transportCallbackExecutor.shutdownNow(); | 281 | transportCallbackExecutor.shutdownNow(); |
162 | } | 282 | } |
163 | } | 283 | } |
284 | + | ||
285 | + public static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) { | ||
286 | + return TransportProtos.SessionEventMsg.newBuilder() | ||
287 | + .setSessionType(TransportProtos.SessionType.ASYNC) | ||
288 | + .setEvent(event).build(); | ||
289 | + } | ||
164 | } | 290 | } |
@@ -217,91 +217,84 @@ public class RemoteTransportService extends AbstractTransportService { | @@ -217,91 +217,84 @@ public class RemoteTransportService extends AbstractTransportService { | ||
217 | } | 217 | } |
218 | 218 | ||
219 | @Override | 219 | @Override |
220 | - public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) { | ||
221 | - if (checkLimits(sessionInfo, callback)) { | ||
222 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
223 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
224 | - .setSessionEvent(msg).build() | ||
225 | - ).build(); | ||
226 | - send(sessionInfo, toRuleEngineMsg, callback); | ||
227 | - } | 220 | + public void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback) { |
221 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
222 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
223 | + .setSubscriptionInfo(msg).build() | ||
224 | + ).build(); | ||
225 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
228 | } | 226 | } |
229 | 227 | ||
230 | @Override | 228 | @Override |
231 | - public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { | ||
232 | - if (checkLimits(sessionInfo, callback)) { | ||
233 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
234 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
235 | - .setPostTelemetry(msg).build() | ||
236 | - ).build(); | ||
237 | - send(sessionInfo, toRuleEngineMsg, callback); | ||
238 | - } | 229 | + protected void doProcess(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) { |
230 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
231 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
232 | + .setSessionEvent(msg).build() | ||
233 | + ).build(); | ||
234 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
239 | } | 235 | } |
240 | 236 | ||
241 | @Override | 237 | @Override |
242 | - public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) { | ||
243 | - if (checkLimits(sessionInfo, callback)) { | ||
244 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
245 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
246 | - .setPostAttributes(msg).build() | ||
247 | - ).build(); | ||
248 | - send(sessionInfo, toRuleEngineMsg, callback); | ||
249 | - } | 238 | + protected void doProcess(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { |
239 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
240 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
241 | + .setPostTelemetry(msg).build() | ||
242 | + ).build(); | ||
243 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
250 | } | 244 | } |
251 | 245 | ||
252 | @Override | 246 | @Override |
253 | - public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) { | ||
254 | - if (checkLimits(sessionInfo, callback)) { | ||
255 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
256 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
257 | - .setGetAttributes(msg).build() | ||
258 | - ).build(); | ||
259 | - send(sessionInfo, toRuleEngineMsg, callback); | ||
260 | - } | 247 | + protected void doProcess(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) { |
248 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
249 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
250 | + .setPostAttributes(msg).build() | ||
251 | + ).build(); | ||
252 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
261 | } | 253 | } |
262 | 254 | ||
263 | @Override | 255 | @Override |
264 | - public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) { | ||
265 | - if (checkLimits(sessionInfo, callback)) { | ||
266 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
267 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
268 | - .setSubscribeToAttributes(msg).build() | ||
269 | - ).build(); | ||
270 | - send(sessionInfo, toRuleEngineMsg, callback); | ||
271 | - } | 256 | + protected void doProcess(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) { |
257 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
258 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
259 | + .setGetAttributes(msg).build() | ||
260 | + ).build(); | ||
261 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
272 | } | 262 | } |
273 | 263 | ||
274 | @Override | 264 | @Override |
275 | - public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) { | ||
276 | - if (checkLimits(sessionInfo, callback)) { | ||
277 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
278 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
279 | - .setSubscribeToRPC(msg).build() | ||
280 | - ).build(); | ||
281 | - send(sessionInfo, toRuleEngineMsg, callback); | ||
282 | - } | 265 | + protected void doProcess(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) { |
266 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
267 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
268 | + .setSubscribeToAttributes(msg).build() | ||
269 | + ).build(); | ||
270 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
283 | } | 271 | } |
284 | 272 | ||
285 | @Override | 273 | @Override |
286 | - public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { | ||
287 | - if (checkLimits(sessionInfo, callback)) { | ||
288 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
289 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
290 | - .setToDeviceRPCCallResponse(msg).build() | ||
291 | - ).build(); | ||
292 | - send(sessionInfo, toRuleEngineMsg, callback); | ||
293 | - } | 274 | + protected void doProcess(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) { |
275 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
276 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
277 | + .setSubscribeToRPC(msg).build() | ||
278 | + ).build(); | ||
279 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
294 | } | 280 | } |
295 | 281 | ||
296 | @Override | 282 | @Override |
297 | - public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) { | ||
298 | - if (checkLimits(sessionInfo, callback)) { | ||
299 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
300 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
301 | - .setToServerRPCCallRequest(msg).build() | ||
302 | - ).build(); | ||
303 | - send(sessionInfo, toRuleEngineMsg, callback); | ||
304 | - } | 283 | + protected void doProcess(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { |
284 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
285 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
286 | + .setToDeviceRPCCallResponse(msg).build() | ||
287 | + ).build(); | ||
288 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
289 | + } | ||
290 | + | ||
291 | + @Override | ||
292 | + protected void doProcess(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) { | ||
293 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
294 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
295 | + .setToServerRPCCallRequest(msg).build() | ||
296 | + ).build(); | ||
297 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
305 | } | 298 | } |
306 | 299 | ||
307 | private static class TransportCallbackAdaptor implements Callback { | 300 | private static class TransportCallbackAdaptor implements Callback { |
@@ -23,10 +23,25 @@ import org.thingsboard.server.gen.transport.TransportProtos; | @@ -23,10 +23,25 @@ import org.thingsboard.server.gen.transport.TransportProtos; | ||
23 | * Created by ashvayka on 15.10.18. | 23 | * Created by ashvayka on 15.10.18. |
24 | */ | 24 | */ |
25 | @Data | 25 | @Data |
26 | -public class SessionMetaData { | 26 | +class SessionMetaData { |
27 | 27 | ||
28 | private final TransportProtos.SessionInfoProto sessionInfo; | 28 | private final TransportProtos.SessionInfoProto sessionInfo; |
29 | private final TransportProtos.SessionType sessionType; | 29 | private final TransportProtos.SessionType sessionType; |
30 | private final SessionMsgListener listener; | 30 | private final SessionMsgListener listener; |
31 | 31 | ||
32 | + private volatile long lastActivityTime; | ||
33 | + private volatile boolean subscribedToAttributes; | ||
34 | + private volatile boolean subscribedToRPC; | ||
35 | + | ||
36 | + SessionMetaData(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SessionType sessionType, SessionMsgListener listener) { | ||
37 | + this.sessionInfo = sessionInfo; | ||
38 | + this.sessionType = sessionType; | ||
39 | + this.listener = listener; | ||
40 | + this.lastActivityTime = System.currentTimeMillis(); | ||
41 | + } | ||
42 | + | ||
43 | + void updateLastActivityTime() { | ||
44 | + this.lastActivityTime = System.currentTimeMillis(); | ||
45 | + } | ||
46 | + | ||
32 | } | 47 | } |
@@ -172,6 +172,22 @@ message ToServerRpcResponseMsg { | @@ -172,6 +172,22 @@ message ToServerRpcResponseMsg { | ||
172 | string error = 3; | 172 | string error = 3; |
173 | } | 173 | } |
174 | 174 | ||
175 | +//Used to report session state to tb-node and persist this state in the cache on the tb-node level. | ||
176 | +message SubscriptionInfoProto { | ||
177 | + int64 lastActivityTime = 1; | ||
178 | + bool attributeSubscription = 2; | ||
179 | + bool rpcSubscription = 3; | ||
180 | +} | ||
181 | + | ||
182 | +message SessionSubscriptionInfoProto { | ||
183 | + SessionInfoProto sessionInfo = 1; | ||
184 | + SubscriptionInfoProto subscriptionInfo = 2; | ||
185 | +} | ||
186 | + | ||
187 | +message DeviceSessionsCacheEntry { | ||
188 | + repeated SessionSubscriptionInfoProto sessions = 1; | ||
189 | +} | ||
190 | + | ||
175 | message TransportToDeviceActorMsg { | 191 | message TransportToDeviceActorMsg { |
176 | SessionInfoProto sessionInfo = 1; | 192 | SessionInfoProto sessionInfo = 1; |
177 | SessionEventMsg sessionEvent = 2; | 193 | SessionEventMsg sessionEvent = 2; |
@@ -182,6 +198,7 @@ message TransportToDeviceActorMsg { | @@ -182,6 +198,7 @@ message TransportToDeviceActorMsg { | ||
182 | SubscribeToRPCMsg subscribeToRPC = 7; | 198 | SubscribeToRPCMsg subscribeToRPC = 7; |
183 | ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 8; | 199 | ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 8; |
184 | ToServerRpcRequestMsg toServerRPCCallRequest = 9; | 200 | ToServerRpcRequestMsg toServerRPCCallRequest = 9; |
201 | + SubscriptionInfoProto subscriptionInfo = 10; | ||
185 | } | 202 | } |
186 | 203 | ||
187 | message DeviceActorToTransportMsg { | 204 | message DeviceActorToTransportMsg { |
@@ -214,4 +231,4 @@ message TransportApiRequestMsg { | @@ -214,4 +231,4 @@ message TransportApiRequestMsg { | ||
214 | message TransportApiResponseMsg { | 231 | message TransportApiResponseMsg { |
215 | ValidateDeviceCredentialsResponseMsg validateTokenResponseMsg = 1; | 232 | ValidateDeviceCredentialsResponseMsg validateTokenResponseMsg = 1; |
216 | GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; | 233 | GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; |
217 | -} | ||
234 | +} |
@@ -43,6 +43,8 @@ public interface EntityViewService { | @@ -43,6 +43,8 @@ public interface EntityViewService { | ||
43 | 43 | ||
44 | EntityView findEntityViewById(EntityViewId entityViewId); | 44 | EntityView findEntityViewById(EntityViewId entityViewId); |
45 | 45 | ||
46 | + EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name); | ||
47 | + | ||
46 | TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); | 48 | TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); |
47 | 49 | ||
48 | TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type); | 50 | TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type); |
@@ -29,8 +29,6 @@ import org.springframework.cache.annotation.Cacheable; | @@ -29,8 +29,6 @@ import org.springframework.cache.annotation.Cacheable; | ||
29 | import org.springframework.cache.annotation.Caching; | 29 | import org.springframework.cache.annotation.Caching; |
30 | import org.springframework.stereotype.Service; | 30 | import org.springframework.stereotype.Service; |
31 | import org.thingsboard.server.common.data.Customer; | 31 | import org.thingsboard.server.common.data.Customer; |
32 | -import org.thingsboard.server.common.data.DataConstants; | ||
33 | -import org.thingsboard.server.common.data.Device; | ||
34 | import org.thingsboard.server.common.data.EntitySubtype; | 32 | import org.thingsboard.server.common.data.EntitySubtype; |
35 | import org.thingsboard.server.common.data.EntityType; | 33 | import org.thingsboard.server.common.data.EntityType; |
36 | import org.thingsboard.server.common.data.EntityView; | 34 | import org.thingsboard.server.common.data.EntityView; |
@@ -40,12 +38,10 @@ import org.thingsboard.server.common.data.id.CustomerId; | @@ -40,12 +38,10 @@ import org.thingsboard.server.common.data.id.CustomerId; | ||
40 | import org.thingsboard.server.common.data.id.EntityId; | 38 | import org.thingsboard.server.common.data.id.EntityId; |
41 | import org.thingsboard.server.common.data.id.EntityViewId; | 39 | import org.thingsboard.server.common.data.id.EntityViewId; |
42 | import org.thingsboard.server.common.data.id.TenantId; | 40 | import org.thingsboard.server.common.data.id.TenantId; |
43 | -import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
44 | import org.thingsboard.server.common.data.page.TextPageData; | 41 | import org.thingsboard.server.common.data.page.TextPageData; |
45 | import org.thingsboard.server.common.data.page.TextPageLink; | 42 | import org.thingsboard.server.common.data.page.TextPageLink; |
46 | import org.thingsboard.server.common.data.relation.EntityRelation; | 43 | import org.thingsboard.server.common.data.relation.EntityRelation; |
47 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; | 44 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
48 | -import org.thingsboard.server.dao.attributes.AttributesService; | ||
49 | import org.thingsboard.server.dao.customer.CustomerDao; | 45 | import org.thingsboard.server.dao.customer.CustomerDao; |
50 | import org.thingsboard.server.dao.entity.AbstractEntityService; | 46 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
51 | import org.thingsboard.server.dao.exception.DataValidationException; | 47 | import org.thingsboard.server.dao.exception.DataValidationException; |
@@ -56,15 +52,13 @@ import org.thingsboard.server.dao.tenant.TenantDao; | @@ -56,15 +52,13 @@ import org.thingsboard.server.dao.tenant.TenantDao; | ||
56 | import javax.annotation.Nullable; | 52 | import javax.annotation.Nullable; |
57 | import java.util.ArrayList; | 53 | import java.util.ArrayList; |
58 | import java.util.Arrays; | 54 | import java.util.Arrays; |
59 | -import java.util.Collection; | ||
60 | import java.util.Collections; | 55 | import java.util.Collections; |
61 | import java.util.Comparator; | 56 | import java.util.Comparator; |
62 | import java.util.List; | 57 | import java.util.List; |
63 | -import java.util.concurrent.ExecutionException; | 58 | +import java.util.Optional; |
64 | import java.util.stream.Collectors; | 59 | import java.util.stream.Collectors; |
65 | 60 | ||
66 | import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE; | 61 | import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE; |
67 | -import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; | ||
68 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; | 62 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
69 | import static org.thingsboard.server.dao.service.Validator.validateId; | 63 | import static org.thingsboard.server.dao.service.Validator.validateId; |
70 | import static org.thingsboard.server.dao.service.Validator.validatePageLink; | 64 | import static org.thingsboard.server.dao.service.Validator.validatePageLink; |
@@ -96,6 +90,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -96,6 +90,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
96 | 90 | ||
97 | @Caching(evict = { | 91 | @Caching(evict = { |
98 | @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.entityId}"), | 92 | @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.entityId}"), |
93 | + @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.name}"), | ||
99 | @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")}) | 94 | @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")}) |
100 | @Override | 95 | @Override |
101 | public EntityView saveEntityView(EntityView entityView) { | 96 | public EntityView saveEntityView(EntityView entityView) { |
@@ -137,6 +132,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -137,6 +132,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
137 | return entityViewDao.findById(entityViewId.getId()); | 132 | return entityViewDao.findById(entityViewId.getId()); |
138 | } | 133 | } |
139 | 134 | ||
135 | + @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #name}") | ||
136 | + @Override | ||
137 | + public EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name) { | ||
138 | + log.trace("Executing findEntityViewByTenantIdAndName [{}][{}]", tenantId, name); | ||
139 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
140 | + Optional<EntityView> entityViewOpt = entityViewDao.findEntityViewByTenantIdAndName(tenantId.getId(), name); | ||
141 | + return entityViewOpt.orElse(null); | ||
142 | + } | ||
143 | + | ||
140 | @Override | 144 | @Override |
141 | public TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink) { | 145 | public TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink) { |
142 | log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); | 146 | log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); |
@@ -255,6 +259,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -255,6 +259,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
255 | deleteEntityRelations(entityViewId); | 259 | deleteEntityRelations(entityViewId); |
256 | EntityView entityView = entityViewDao.findById(entityViewId.getId()); | 260 | EntityView entityView = entityViewDao.findById(entityViewId.getId()); |
257 | cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId())); | 261 | cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId())); |
262 | + cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getName())); | ||
258 | entityViewDao.removeById(entityViewId.getId()); | 263 | entityViewDao.removeById(entityViewId.getId()); |
259 | } | 264 | } |
260 | 265 |
@@ -34,6 +34,7 @@ import javax.persistence.Entity; | @@ -34,6 +34,7 @@ import javax.persistence.Entity; | ||
34 | import javax.persistence.EnumType; | 34 | import javax.persistence.EnumType; |
35 | import javax.persistence.Enumerated; | 35 | import javax.persistence.Enumerated; |
36 | import javax.persistence.Table; | 36 | import javax.persistence.Table; |
37 | +import javax.persistence.UniqueConstraint; | ||
37 | 38 | ||
38 | @Data | 39 | @Data |
39 | @EqualsAndHashCode(callSuper = true) | 40 | @EqualsAndHashCode(callSuper = true) |
@@ -53,7 +54,7 @@ public class ComponentDescriptorEntity extends BaseSqlEntity<ComponentDescriptor | @@ -53,7 +54,7 @@ public class ComponentDescriptorEntity extends BaseSqlEntity<ComponentDescriptor | ||
53 | @Column(name = ModelConstants.COMPONENT_DESCRIPTOR_NAME_PROPERTY) | 54 | @Column(name = ModelConstants.COMPONENT_DESCRIPTOR_NAME_PROPERTY) |
54 | private String name; | 55 | private String name; |
55 | 56 | ||
56 | - @Column(name = ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY) | 57 | + @Column(name = ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, unique = true) |
57 | private String clazz; | 58 | private String clazz; |
58 | 59 | ||
59 | @Type(type = "json") | 60 | @Type(type = "json") |
@@ -35,7 +35,6 @@ import org.thingsboard.server.dao.model.type.ComponentTypeCodec; | @@ -35,7 +35,6 @@ import org.thingsboard.server.dao.model.type.ComponentTypeCodec; | ||
35 | import org.thingsboard.server.dao.model.type.DeviceCredentialsTypeCodec; | 35 | import org.thingsboard.server.dao.model.type.DeviceCredentialsTypeCodec; |
36 | import org.thingsboard.server.dao.model.type.EntityTypeCodec; | 36 | import org.thingsboard.server.dao.model.type.EntityTypeCodec; |
37 | import org.thingsboard.server.dao.model.type.JsonCodec; | 37 | import org.thingsboard.server.dao.model.type.JsonCodec; |
38 | -import org.thingsboard.server.dao.util.BufferedRateLimiter; | ||
39 | 38 | ||
40 | import java.util.concurrent.ConcurrentHashMap; | 39 | import java.util.concurrent.ConcurrentHashMap; |
41 | import java.util.concurrent.ConcurrentMap; | 40 | import java.util.concurrent.ConcurrentMap; |
@@ -49,7 +48,7 @@ public abstract class CassandraAbstractDao { | @@ -49,7 +48,7 @@ public abstract class CassandraAbstractDao { | ||
49 | private ConcurrentMap<String, PreparedStatement> preparedStatementMap = new ConcurrentHashMap<>(); | 48 | private ConcurrentMap<String, PreparedStatement> preparedStatementMap = new ConcurrentHashMap<>(); |
50 | 49 | ||
51 | @Autowired | 50 | @Autowired |
52 | - private BufferedRateLimiter rateLimiter; | 51 | + private CassandraBufferedRateExecutor rateLimiter; |
53 | 52 | ||
54 | private Session session; | 53 | private Session session; |
55 | 54 | ||
@@ -115,12 +114,12 @@ public abstract class CassandraAbstractDao { | @@ -115,12 +114,12 @@ public abstract class CassandraAbstractDao { | ||
115 | if (statement.getConsistencyLevel() == null) { | 114 | if (statement.getConsistencyLevel() == null) { |
116 | statement.setConsistencyLevel(level); | 115 | statement.setConsistencyLevel(level); |
117 | } | 116 | } |
118 | - return new RateLimitedResultSetFuture(getSession(), rateLimiter, statement); | 117 | + return rateLimiter.submit(new CassandraStatementTask(getSession(), statement)); |
119 | } | 118 | } |
120 | 119 | ||
121 | private static String statementToString(Statement statement) { | 120 | private static String statementToString(Statement statement) { |
122 | if (statement instanceof BoundStatement) { | 121 | if (statement instanceof BoundStatement) { |
123 | - return ((BoundStatement)statement).preparedStatement().getQueryString(); | 122 | + return ((BoundStatement) statement).preparedStatement().getQueryString(); |
124 | } else { | 123 | } else { |
125 | return statement.toString(); | 124 | return statement.toString(); |
126 | } | 125 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.nosql; | ||
17 | + | ||
18 | +import com.datastax.driver.core.ResultSet; | ||
19 | +import com.datastax.driver.core.ResultSetFuture; | ||
20 | +import com.google.common.util.concurrent.SettableFuture; | ||
21 | +import lombok.extern.slf4j.Slf4j; | ||
22 | +import org.springframework.beans.factory.annotation.Value; | ||
23 | +import org.springframework.scheduling.annotation.Scheduled; | ||
24 | +import org.springframework.stereotype.Component; | ||
25 | +import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; | ||
26 | +import org.thingsboard.server.dao.util.AsyncTaskContext; | ||
27 | +import org.thingsboard.server.dao.util.NoSqlAnyDao; | ||
28 | + | ||
29 | +import javax.annotation.PreDestroy; | ||
30 | + | ||
31 | +/** | ||
32 | + * Created by ashvayka on 24.10.18. | ||
33 | + */ | ||
34 | +@Component | ||
35 | +@Slf4j | ||
36 | +@NoSqlAnyDao | ||
37 | +public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<CassandraStatementTask, ResultSetFuture, ResultSet> { | ||
38 | + | ||
39 | + public CassandraBufferedRateExecutor( | ||
40 | + @Value("${cassandra.query.buffer_size}") int queueLimit, | ||
41 | + @Value("${cassandra.query.concurrent_limit}") int concurrencyLimit, | ||
42 | + @Value("${cassandra.query.permit_max_wait_time}") long maxWaitTime, | ||
43 | + @Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads, | ||
44 | + @Value("${cassandra.query.callback_threads:2}") int callbackThreads, | ||
45 | + @Value("${cassandra.query.poll_ms:50}") long pollMs) { | ||
46 | + super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs); | ||
47 | + } | ||
48 | + | ||
49 | + @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") | ||
50 | + public void printStats() { | ||
51 | + log.info("Permits queueSize [{}] totalAdded [{}] totalLaunched [{}] totalReleased [{}] totalFailed [{}] totalExpired [{}] totalRejected [{}] currBuffer [{}] ", | ||
52 | + getQueueSize(), | ||
53 | + totalAdded.getAndSet(0), totalLaunched.getAndSet(0), totalReleased.getAndSet(0), | ||
54 | + totalFailed.getAndSet(0), totalExpired.getAndSet(0), totalRejected.getAndSet(0), | ||
55 | + concurrencyLevel.get()); | ||
56 | + } | ||
57 | + | ||
58 | + @PreDestroy | ||
59 | + public void stop() { | ||
60 | + super.stop(); | ||
61 | + } | ||
62 | + | ||
63 | + @Override | ||
64 | + protected SettableFuture<ResultSet> create() { | ||
65 | + return SettableFuture.create(); | ||
66 | + } | ||
67 | + | ||
68 | + @Override | ||
69 | + protected ResultSetFuture wrap(CassandraStatementTask task, SettableFuture<ResultSet> future) { | ||
70 | + return new TbResultSetFuture(future); | ||
71 | + } | ||
72 | + | ||
73 | + @Override | ||
74 | + protected ResultSetFuture execute(AsyncTaskContext<CassandraStatementTask, ResultSet> taskCtx) { | ||
75 | + CassandraStatementTask task = taskCtx.getTask(); | ||
76 | + return task.getSession().executeAsync(task.getStatement()); | ||
77 | + } | ||
78 | + | ||
79 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.nosql; | ||
17 | + | ||
18 | +import com.datastax.driver.core.Session; | ||
19 | +import com.datastax.driver.core.Statement; | ||
20 | +import lombok.Data; | ||
21 | +import org.thingsboard.server.dao.util.AsyncTask; | ||
22 | + | ||
23 | +/** | ||
24 | + * Created by ashvayka on 24.10.18. | ||
25 | + */ | ||
26 | +@Data | ||
27 | +public class CassandraStatementTask implements AsyncTask { | ||
28 | + | ||
29 | + private final Session session; | ||
30 | + private final Statement statement; | ||
31 | + | ||
32 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.nosql; | ||
17 | + | ||
18 | +import com.datastax.driver.core.ResultSet; | ||
19 | +import com.datastax.driver.core.ResultSetFuture; | ||
20 | +import com.google.common.util.concurrent.SettableFuture; | ||
21 | + | ||
22 | +import java.util.concurrent.ExecutionException; | ||
23 | +import java.util.concurrent.Executor; | ||
24 | +import java.util.concurrent.TimeUnit; | ||
25 | +import java.util.concurrent.TimeoutException; | ||
26 | + | ||
27 | +/** | ||
28 | + * Created by ashvayka on 24.10.18. | ||
29 | + */ | ||
30 | +public class TbResultSetFuture implements ResultSetFuture { | ||
31 | + | ||
32 | + private final SettableFuture<ResultSet> mainFuture; | ||
33 | + | ||
34 | + public TbResultSetFuture(SettableFuture<ResultSet> mainFuture) { | ||
35 | + this.mainFuture = mainFuture; | ||
36 | + } | ||
37 | + | ||
38 | + @Override | ||
39 | + public ResultSet getUninterruptibly() { | ||
40 | + return getSafe(); | ||
41 | + } | ||
42 | + | ||
43 | + @Override | ||
44 | + public ResultSet getUninterruptibly(long timeout, TimeUnit unit) throws TimeoutException { | ||
45 | + return getSafe(timeout, unit); | ||
46 | + } | ||
47 | + | ||
48 | + @Override | ||
49 | + public boolean cancel(boolean mayInterruptIfRunning) { | ||
50 | + return mainFuture.cancel(mayInterruptIfRunning); | ||
51 | + } | ||
52 | + | ||
53 | + @Override | ||
54 | + public boolean isCancelled() { | ||
55 | + return mainFuture.isCancelled(); | ||
56 | + } | ||
57 | + | ||
58 | + @Override | ||
59 | + public boolean isDone() { | ||
60 | + return mainFuture.isDone(); | ||
61 | + } | ||
62 | + | ||
63 | + @Override | ||
64 | + public ResultSet get() throws InterruptedException, ExecutionException { | ||
65 | + return mainFuture.get(); | ||
66 | + } | ||
67 | + | ||
68 | + @Override | ||
69 | + public ResultSet get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { | ||
70 | + return mainFuture.get(timeout, unit); | ||
71 | + } | ||
72 | + | ||
73 | + @Override | ||
74 | + public void addListener(Runnable listener, Executor executor) { | ||
75 | + mainFuture.addListener(listener, executor); | ||
76 | + } | ||
77 | + | ||
78 | + private ResultSet getSafe() { | ||
79 | + try { | ||
80 | + return mainFuture.get(); | ||
81 | + } catch (InterruptedException | ExecutionException e) { | ||
82 | + throw new IllegalStateException(e); | ||
83 | + } | ||
84 | + } | ||
85 | + | ||
86 | + private ResultSet getSafe(long timeout, TimeUnit unit) throws TimeoutException { | ||
87 | + try { | ||
88 | + return mainFuture.get(timeout, unit); | ||
89 | + } catch (InterruptedException | ExecutionException e) { | ||
90 | + throw new IllegalStateException(e); | ||
91 | + } | ||
92 | + } | ||
93 | + | ||
94 | +} |
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries; | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries; | ||
17 | 17 | ||
18 | import com.google.common.base.Function; | 18 | import com.google.common.base.Function; |
19 | import com.google.common.collect.Lists; | 19 | import com.google.common.collect.Lists; |
20 | +import com.google.common.util.concurrent.FutureCallback; | ||
20 | import com.google.common.util.concurrent.Futures; | 21 | import com.google.common.util.concurrent.Futures; |
21 | import com.google.common.util.concurrent.ListenableFuture; | 22 | import com.google.common.util.concurrent.ListenableFuture; |
22 | import com.google.common.util.concurrent.ListeningExecutorService; | 23 | import com.google.common.util.concurrent.ListeningExecutorService; |
@@ -31,6 +32,7 @@ import org.springframework.stereotype.Component; | @@ -31,6 +32,7 @@ import org.springframework.stereotype.Component; | ||
31 | import org.thingsboard.server.common.data.UUIDConverter; | 32 | import org.thingsboard.server.common.data.UUIDConverter; |
32 | import org.thingsboard.server.common.data.id.EntityId; | 33 | import org.thingsboard.server.common.data.id.EntityId; |
33 | import org.thingsboard.server.common.data.kv.Aggregation; | 34 | import org.thingsboard.server.common.data.kv.Aggregation; |
35 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; | ||
34 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 36 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
35 | import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | 37 | import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; |
36 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | 38 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; |
@@ -41,9 +43,9 @@ import org.thingsboard.server.dao.model.sql.TsKvEntity; | @@ -41,9 +43,9 @@ import org.thingsboard.server.dao.model.sql.TsKvEntity; | ||
41 | import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; | 43 | import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; |
42 | import org.thingsboard.server.dao.model.sql.TsKvLatestEntity; | 44 | import org.thingsboard.server.dao.model.sql.TsKvLatestEntity; |
43 | import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; | 45 | import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; |
46 | +import org.thingsboard.server.dao.timeseries.SimpleListenableFuture; | ||
44 | import org.thingsboard.server.dao.timeseries.TimeseriesDao; | 47 | import org.thingsboard.server.dao.timeseries.TimeseriesDao; |
45 | import org.thingsboard.server.dao.timeseries.TsInsertExecutorType; | 48 | import org.thingsboard.server.dao.timeseries.TsInsertExecutorType; |
46 | -import org.thingsboard.server.dao.util.SqlDao; | ||
47 | import org.thingsboard.server.dao.util.SqlTsDao; | 49 | import org.thingsboard.server.dao.util.SqlTsDao; |
48 | 50 | ||
49 | import javax.annotation.Nullable; | 51 | import javax.annotation.Nullable; |
@@ -53,6 +55,7 @@ import java.util.ArrayList; | @@ -53,6 +55,7 @@ import java.util.ArrayList; | ||
53 | import java.util.List; | 55 | import java.util.List; |
54 | import java.util.Optional; | 56 | import java.util.Optional; |
55 | import java.util.concurrent.CompletableFuture; | 57 | import java.util.concurrent.CompletableFuture; |
58 | +import java.util.concurrent.ExecutionException; | ||
56 | import java.util.concurrent.Executors; | 59 | import java.util.concurrent.Executors; |
57 | import java.util.stream.Collectors; | 60 | import java.util.stream.Collectors; |
58 | 61 | ||
@@ -64,6 +67,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; | @@ -64,6 +67,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; | ||
64 | @SqlTsDao | 67 | @SqlTsDao |
65 | public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao { | 68 | public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao { |
66 | 69 | ||
70 | + private static final String DESC_ORDER = "DESC"; | ||
71 | + | ||
67 | @Value("${sql.ts_inserts_executor_type}") | 72 | @Value("${sql.ts_inserts_executor_type}") |
68 | private String insertExecutorType; | 73 | private String insertExecutorType; |
69 | 74 | ||
@@ -326,14 +331,72 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | @@ -326,14 +331,72 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | ||
326 | 331 | ||
327 | @Override | 332 | @Override |
328 | public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { | 333 | public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { |
329 | - TsKvLatestEntity latestEntity = new TsKvLatestEntity(); | ||
330 | - latestEntity.setEntityType(entityId.getEntityType()); | ||
331 | - latestEntity.setEntityId(fromTimeUUID(entityId.getId())); | ||
332 | - latestEntity.setKey(query.getKey()); | ||
333 | - return service.submit(() -> { | ||
334 | - tsKvLatestRepository.delete(latestEntity); | ||
335 | - return null; | 334 | + ListenableFuture<TsKvEntry> latestFuture = findLatest(entityId, query.getKey()); |
335 | + | ||
336 | + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> { | ||
337 | + long ts = tsKvEntry.getTs(); | ||
338 | + return ts > query.getStartTs() && ts <= query.getEndTs(); | ||
339 | + }, service); | ||
340 | + | ||
341 | + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
342 | + if (isRemove) { | ||
343 | + TsKvLatestEntity latestEntity = new TsKvLatestEntity(); | ||
344 | + latestEntity.setEntityType(entityId.getEntityType()); | ||
345 | + latestEntity.setEntityId(fromTimeUUID(entityId.getId())); | ||
346 | + latestEntity.setKey(query.getKey()); | ||
347 | + return service.submit(() -> { | ||
348 | + tsKvLatestRepository.delete(latestEntity); | ||
349 | + return null; | ||
350 | + }); | ||
351 | + } | ||
352 | + return Futures.immediateFuture(null); | ||
353 | + }, service); | ||
354 | + | ||
355 | + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>(); | ||
356 | + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() { | ||
357 | + @Override | ||
358 | + public void onSuccess(@Nullable Void result) { | ||
359 | + if (query.getRewriteLatestIfDeleted()) { | ||
360 | + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
361 | + if (isRemove) { | ||
362 | + return getNewLatestEntryFuture(entityId, query); | ||
363 | + } | ||
364 | + return Futures.immediateFuture(null); | ||
365 | + }, service); | ||
366 | + | ||
367 | + try { | ||
368 | + resultFuture.set(savedLatestFuture.get()); | ||
369 | + } catch (InterruptedException | ExecutionException e) { | ||
370 | + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e); | ||
371 | + } | ||
372 | + } else { | ||
373 | + resultFuture.set(null); | ||
374 | + } | ||
375 | + } | ||
376 | + | ||
377 | + @Override | ||
378 | + public void onFailure(Throwable t) { | ||
379 | + log.warn("[{}] Failed to process remove of the latest value", entityId, t); | ||
380 | + } | ||
336 | }); | 381 | }); |
382 | + return resultFuture; | ||
383 | + } | ||
384 | + | ||
385 | + private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) { | ||
386 | + long startTs = 0; | ||
387 | + long endTs = query.getStartTs() - 1; | ||
388 | + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1, | ||
389 | + Aggregation.NONE, DESC_ORDER); | ||
390 | + ListenableFuture<List<TsKvEntry>> future = findAllAsync(entityId, findNewLatestQuery); | ||
391 | + | ||
392 | + return Futures.transformAsync(future, entryList -> { | ||
393 | + if (entryList.size() == 1) { | ||
394 | + return saveLatest(entityId, entryList.get(0)); | ||
395 | + } else { | ||
396 | + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey()); | ||
397 | + } | ||
398 | + return Futures.immediateFuture(null); | ||
399 | + }, service); | ||
337 | } | 400 | } |
338 | 401 | ||
339 | @Override | 402 | @Override |
@@ -47,7 +47,7 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | @@ -47,7 +47,7 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | ||
47 | @Modifying | 47 | @Modifying |
48 | @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " + | 48 | @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " + |
49 | "AND tskv.entityType = :entityType AND tskv.key = :entityKey " + | 49 | "AND tskv.entityType = :entityType AND tskv.key = :entityKey " + |
50 | - "AND tskv.ts > :startTs AND tskv.ts < :endTs") | 50 | + "AND tskv.ts > :startTs AND tskv.ts <= :endTs") |
51 | void delete(@Param("entityId") String entityId, | 51 | void delete(@Param("entityId") String entityId, |
52 | @Param("entityType") EntityType entityType, | 52 | @Param("entityType") EntityType entityType, |
53 | @Param("entityKey") String key, | 53 | @Param("entityKey") String key, |
@@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | @@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
48 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 48 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
49 | import org.thingsboard.server.dao.model.ModelConstants; | 49 | import org.thingsboard.server.dao.model.ModelConstants; |
50 | import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; | 50 | import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; |
51 | -import org.thingsboard.server.dao.util.NoSqlDao; | ||
52 | import org.thingsboard.server.dao.util.NoSqlTsDao; | 51 | import org.thingsboard.server.dao.util.NoSqlTsDao; |
53 | 52 | ||
54 | import javax.annotation.Nullable; | 53 | import javax.annotation.Nullable; |
@@ -62,6 +61,7 @@ import java.util.Arrays; | @@ -62,6 +61,7 @@ import java.util.Arrays; | ||
62 | import java.util.Collections; | 61 | import java.util.Collections; |
63 | import java.util.List; | 62 | import java.util.List; |
64 | import java.util.Optional; | 63 | import java.util.Optional; |
64 | +import java.util.concurrent.ExecutionException; | ||
65 | import java.util.stream.Collectors; | 65 | import java.util.stream.Collectors; |
66 | 66 | ||
67 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; | 67 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; |
@@ -434,14 +434,14 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -434,14 +434,14 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
434 | public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { | 434 | public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { |
435 | ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey()); | 435 | ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey()); |
436 | 436 | ||
437 | - ListenableFuture<Boolean> booleanFuture = Futures.transformAsync(latestEntryFuture, latestEntry -> { | 437 | + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestEntryFuture, latestEntry -> { |
438 | long ts = latestEntry.getTs(); | 438 | long ts = latestEntry.getTs(); |
439 | - if (ts >= query.getStartTs() && ts <= query.getEndTs()) { | ||
440 | - return Futures.immediateFuture(true); | 439 | + if (ts > query.getStartTs() && ts <= query.getEndTs()) { |
440 | + return true; | ||
441 | } else { | 441 | } else { |
442 | log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey()); | 442 | log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey()); |
443 | } | 443 | } |
444 | - return Futures.immediateFuture(false); | 444 | + return false; |
445 | }, readResultsProcessingExecutor); | 445 | }, readResultsProcessingExecutor); |
446 | 446 | ||
447 | ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | 447 | ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { |
@@ -451,18 +451,34 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -451,18 +451,34 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
451 | return Futures.immediateFuture(null); | 451 | return Futures.immediateFuture(null); |
452 | }, readResultsProcessingExecutor); | 452 | }, readResultsProcessingExecutor); |
453 | 453 | ||
454 | - if (query.getRewriteLatestIfDeleted()) { | ||
455 | - ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
456 | - if (isRemove) { | ||
457 | - return getNewLatestEntryFuture(entityId, query); | 454 | + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>(); |
455 | + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() { | ||
456 | + @Override | ||
457 | + public void onSuccess(@Nullable Void result) { | ||
458 | + if (query.getRewriteLatestIfDeleted()) { | ||
459 | + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
460 | + if (isRemove) { | ||
461 | + return getNewLatestEntryFuture(entityId, query); | ||
462 | + } | ||
463 | + return Futures.immediateFuture(null); | ||
464 | + }, readResultsProcessingExecutor); | ||
465 | + | ||
466 | + try { | ||
467 | + resultFuture.set(savedLatestFuture.get()); | ||
468 | + } catch (InterruptedException | ExecutionException e) { | ||
469 | + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e); | ||
470 | + } | ||
471 | + } else { | ||
472 | + resultFuture.set(null); | ||
458 | } | 473 | } |
459 | - return Futures.immediateFuture(null); | ||
460 | - }, readResultsProcessingExecutor); | 474 | + } |
461 | 475 | ||
462 | - return Futures.transformAsync(Futures.allAsList(Arrays.asList(savedLatestFuture, removedLatestFuture)), | ||
463 | - list -> Futures.immediateFuture(null), readResultsProcessingExecutor); | ||
464 | - } | ||
465 | - return removedLatestFuture; | 476 | + @Override |
477 | + public void onFailure(Throwable t) { | ||
478 | + log.warn("[{}] Failed to process remove of the latest value", entityId, t); | ||
479 | + } | ||
480 | + }); | ||
481 | + return resultFuture; | ||
466 | } | 482 | } |
467 | 483 | ||
468 | private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) { | 484 | private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) { |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.util; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.FutureCallback; | ||
19 | +import com.google.common.util.concurrent.Futures; | ||
20 | +import com.google.common.util.concurrent.ListenableFuture; | ||
21 | +import com.google.common.util.concurrent.SettableFuture; | ||
22 | +import lombok.extern.slf4j.Slf4j; | ||
23 | + | ||
24 | +import javax.annotation.Nullable; | ||
25 | +import java.util.UUID; | ||
26 | +import java.util.concurrent.BlockingQueue; | ||
27 | +import java.util.concurrent.ExecutorService; | ||
28 | +import java.util.concurrent.Executors; | ||
29 | +import java.util.concurrent.LinkedBlockingDeque; | ||
30 | +import java.util.concurrent.ScheduledExecutorService; | ||
31 | +import java.util.concurrent.TimeUnit; | ||
32 | +import java.util.concurrent.TimeoutException; | ||
33 | +import java.util.concurrent.atomic.AtomicInteger; | ||
34 | + | ||
35 | +/** | ||
36 | + * Created by ashvayka on 24.10.18. | ||
37 | + */ | ||
38 | +@Slf4j | ||
39 | +public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extends ListenableFuture<V>, V> implements BufferedRateExecutor<T, F> { | ||
40 | + | ||
41 | + private final long maxWaitTime; | ||
42 | + private final long pollMs; | ||
43 | + private final BlockingQueue<AsyncTaskContext<T, V>> queue; | ||
44 | + private final ExecutorService dispatcherExecutor; | ||
45 | + private final ExecutorService callbackExecutor; | ||
46 | + private final ScheduledExecutorService timeoutExecutor; | ||
47 | + private final int concurrencyLimit; | ||
48 | + | ||
49 | + protected final AtomicInteger concurrencyLevel = new AtomicInteger(); | ||
50 | + protected final AtomicInteger totalAdded = new AtomicInteger(); | ||
51 | + protected final AtomicInteger totalLaunched = new AtomicInteger(); | ||
52 | + protected final AtomicInteger totalReleased = new AtomicInteger(); | ||
53 | + protected final AtomicInteger totalFailed = new AtomicInteger(); | ||
54 | + protected final AtomicInteger totalExpired = new AtomicInteger(); | ||
55 | + protected final AtomicInteger totalRejected = new AtomicInteger(); | ||
56 | + | ||
57 | + public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads, int callbackThreads, long pollMs) { | ||
58 | + this.maxWaitTime = maxWaitTime; | ||
59 | + this.pollMs = pollMs; | ||
60 | + this.concurrencyLimit = concurrencyLimit; | ||
61 | + this.queue = new LinkedBlockingDeque<>(queueLimit); | ||
62 | + this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads); | ||
63 | + this.callbackExecutor = Executors.newFixedThreadPool(callbackThreads); | ||
64 | + this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(); | ||
65 | + for (int i = 0; i < dispatcherThreads; i++) { | ||
66 | + dispatcherExecutor.submit(this::dispatch); | ||
67 | + } | ||
68 | + } | ||
69 | + | ||
70 | + @Override | ||
71 | + public F submit(T task) { | ||
72 | + SettableFuture<V> settableFuture = create(); | ||
73 | + F result = wrap(task, settableFuture); | ||
74 | + try { | ||
75 | + totalAdded.incrementAndGet(); | ||
76 | + queue.add(new AsyncTaskContext<>(UUID.randomUUID(), task, settableFuture, System.currentTimeMillis())); | ||
77 | + } catch (IllegalStateException e) { | ||
78 | + totalRejected.incrementAndGet(); | ||
79 | + settableFuture.setException(e); | ||
80 | + } | ||
81 | + return result; | ||
82 | + } | ||
83 | + | ||
84 | + public void stop() { | ||
85 | + if (dispatcherExecutor != null) { | ||
86 | + dispatcherExecutor.shutdownNow(); | ||
87 | + } | ||
88 | + if (callbackExecutor != null) { | ||
89 | + callbackExecutor.shutdownNow(); | ||
90 | + } | ||
91 | + if (timeoutExecutor != null) { | ||
92 | + timeoutExecutor.shutdownNow(); | ||
93 | + } | ||
94 | + } | ||
95 | + | ||
96 | + protected abstract SettableFuture<V> create(); | ||
97 | + | ||
98 | + protected abstract F wrap(T task, SettableFuture<V> future); | ||
99 | + | ||
100 | + protected abstract ListenableFuture<V> execute(AsyncTaskContext<T, V> taskCtx); | ||
101 | + | ||
102 | + private void dispatch() { | ||
103 | + log.info("Buffered rate executor thread started"); | ||
104 | + while (!Thread.interrupted()) { | ||
105 | + int curLvl = concurrencyLevel.get(); | ||
106 | + AsyncTaskContext<T, V> taskCtx = null; | ||
107 | + try { | ||
108 | + if (curLvl <= concurrencyLimit) { | ||
109 | + taskCtx = queue.take(); | ||
110 | + final AsyncTaskContext<T, V> finalTaskCtx = taskCtx; | ||
111 | + logTask("Processing", finalTaskCtx); | ||
112 | + concurrencyLevel.incrementAndGet(); | ||
113 | + long timeout = finalTaskCtx.getCreateTime() + maxWaitTime - System.currentTimeMillis(); | ||
114 | + if (timeout > 0) { | ||
115 | + totalLaunched.incrementAndGet(); | ||
116 | + ListenableFuture<V> result = execute(finalTaskCtx); | ||
117 | + result = Futures.withTimeout(result, timeout, TimeUnit.MILLISECONDS, timeoutExecutor); | ||
118 | + Futures.addCallback(result, new FutureCallback<V>() { | ||
119 | + @Override | ||
120 | + public void onSuccess(@Nullable V result) { | ||
121 | + logTask("Releasing", finalTaskCtx); | ||
122 | + totalReleased.incrementAndGet(); | ||
123 | + concurrencyLevel.decrementAndGet(); | ||
124 | + finalTaskCtx.getFuture().set(result); | ||
125 | + } | ||
126 | + | ||
127 | + @Override | ||
128 | + public void onFailure(Throwable t) { | ||
129 | + if (t instanceof TimeoutException) { | ||
130 | + logTask("Expired During Execution", finalTaskCtx); | ||
131 | + } else { | ||
132 | + logTask("Failed", finalTaskCtx); | ||
133 | + } | ||
134 | + totalFailed.incrementAndGet(); | ||
135 | + concurrencyLevel.decrementAndGet(); | ||
136 | + finalTaskCtx.getFuture().setException(t); | ||
137 | + log.debug("[{}] Failed to execute task: {}", finalTaskCtx.getId(), finalTaskCtx.getTask(), t); | ||
138 | + } | ||
139 | + }, callbackExecutor); | ||
140 | + } else { | ||
141 | + logTask("Expired Before Execution", finalTaskCtx); | ||
142 | + totalExpired.incrementAndGet(); | ||
143 | + concurrencyLevel.decrementAndGet(); | ||
144 | + taskCtx.getFuture().setException(new TimeoutException()); | ||
145 | + } | ||
146 | + } else { | ||
147 | + Thread.sleep(pollMs); | ||
148 | + } | ||
149 | + } catch (InterruptedException e) { | ||
150 | + break; | ||
151 | + } catch (Throwable e) { | ||
152 | + if (taskCtx != null) { | ||
153 | + log.debug("[{}] Failed to execute task: {}", taskCtx.getId(), taskCtx, e); | ||
154 | + totalFailed.incrementAndGet(); | ||
155 | + concurrencyLevel.decrementAndGet(); | ||
156 | + } else { | ||
157 | + log.debug("Failed to queue task:", e); | ||
158 | + } | ||
159 | + } | ||
160 | + } | ||
161 | + log.info("Buffered rate executor thread stopped"); | ||
162 | + } | ||
163 | + | ||
164 | + private void logTask(String action, AsyncTaskContext<T, V> taskCtx) { | ||
165 | + if (log.isTraceEnabled()) { | ||
166 | + log.trace("[{}] {} task: {}", taskCtx.getId(), action, taskCtx); | ||
167 | + } else { | ||
168 | + log.debug("[{}] {} task", taskCtx.getId(), action); | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + protected int getQueueSize() { | ||
173 | + return queue.size(); | ||
174 | + } | ||
175 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.util; | ||
17 | + | ||
18 | +/** | ||
19 | + * Created by ashvayka on 24.10.18. | ||
20 | + */ | ||
21 | +public interface AsyncTask { | ||
22 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.util; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.SettableFuture; | ||
19 | +import lombok.Data; | ||
20 | + | ||
21 | +import java.util.UUID; | ||
22 | + | ||
23 | +/** | ||
24 | + * Created by ashvayka on 24.10.18. | ||
25 | + */ | ||
26 | +@Data | ||
27 | +public class AsyncTaskContext<T extends AsyncTask, V> { | ||
28 | + | ||
29 | + private final UUID id; | ||
30 | + private final T task; | ||
31 | + private final SettableFuture<V> future; | ||
32 | + private final long createTime; | ||
33 | + | ||
34 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.util; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
19 | + | ||
20 | +/** | ||
21 | + * Created by ashvayka on 24.10.18. | ||
22 | + */ | ||
23 | +public interface BufferedRateExecutor<T extends AsyncTask, F extends ListenableFuture> { | ||
24 | + | ||
25 | + F submit(T task); | ||
26 | + | ||
27 | +} |
dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateLimiter.java
deleted
100644 → 0
1 | -/** | ||
2 | - * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.thingsboard.server.dao.util; | ||
17 | - | ||
18 | -import com.google.common.util.concurrent.Futures; | ||
19 | -import com.google.common.util.concurrent.ListenableFuture; | ||
20 | -import com.google.common.util.concurrent.ListeningExecutorService; | ||
21 | -import com.google.common.util.concurrent.MoreExecutors; | ||
22 | -import lombok.extern.slf4j.Slf4j; | ||
23 | -import org.springframework.beans.factory.annotation.Value; | ||
24 | -import org.springframework.scheduling.annotation.Scheduled; | ||
25 | -import org.springframework.stereotype.Component; | ||
26 | -import org.thingsboard.server.dao.exception.BufferLimitException; | ||
27 | - | ||
28 | -import java.util.concurrent.BlockingQueue; | ||
29 | -import java.util.concurrent.CountDownLatch; | ||
30 | -import java.util.concurrent.Executors; | ||
31 | -import java.util.concurrent.LinkedBlockingQueue; | ||
32 | -import java.util.concurrent.TimeUnit; | ||
33 | -import java.util.concurrent.atomic.AtomicInteger; | ||
34 | - | ||
35 | -@Component | ||
36 | -@Slf4j | ||
37 | -@NoSqlAnyDao | ||
38 | -public class BufferedRateLimiter implements AsyncRateLimiter { | ||
39 | - | ||
40 | - private final ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); | ||
41 | - | ||
42 | - private final int permitsLimit; | ||
43 | - private final int maxPermitWaitTime; | ||
44 | - private final AtomicInteger permits; | ||
45 | - private final BlockingQueue<LockedFuture> queue; | ||
46 | - | ||
47 | - private final AtomicInteger maxQueueSize = new AtomicInteger(); | ||
48 | - private final AtomicInteger maxGrantedPermissions = new AtomicInteger(); | ||
49 | - private final AtomicInteger totalGranted = new AtomicInteger(); | ||
50 | - private final AtomicInteger totalReleased = new AtomicInteger(); | ||
51 | - private final AtomicInteger totalRequested = new AtomicInteger(); | ||
52 | - | ||
53 | - public BufferedRateLimiter(@Value("${cassandra.query.buffer_size}") int queueLimit, | ||
54 | - @Value("${cassandra.query.concurrent_limit}") int permitsLimit, | ||
55 | - @Value("${cassandra.query.permit_max_wait_time}") int maxPermitWaitTime) { | ||
56 | - this.permitsLimit = permitsLimit; | ||
57 | - this.maxPermitWaitTime = maxPermitWaitTime; | ||
58 | - this.permits = new AtomicInteger(); | ||
59 | - this.queue = new LinkedBlockingQueue<>(queueLimit); | ||
60 | - } | ||
61 | - | ||
62 | - @Override | ||
63 | - public ListenableFuture<Void> acquireAsync() { | ||
64 | - totalRequested.incrementAndGet(); | ||
65 | - if (queue.isEmpty()) { | ||
66 | - if (permits.incrementAndGet() <= permitsLimit) { | ||
67 | - if (permits.get() > maxGrantedPermissions.get()) { | ||
68 | - maxGrantedPermissions.set(permits.get()); | ||
69 | - } | ||
70 | - totalGranted.incrementAndGet(); | ||
71 | - return Futures.immediateFuture(null); | ||
72 | - } | ||
73 | - permits.decrementAndGet(); | ||
74 | - } | ||
75 | - | ||
76 | - return putInQueue(); | ||
77 | - } | ||
78 | - | ||
79 | - @Override | ||
80 | - public void release() { | ||
81 | - permits.decrementAndGet(); | ||
82 | - totalReleased.incrementAndGet(); | ||
83 | - reprocessQueue(); | ||
84 | - } | ||
85 | - | ||
86 | - private void reprocessQueue() { | ||
87 | - while (permits.get() < permitsLimit) { | ||
88 | - if (permits.incrementAndGet() <= permitsLimit) { | ||
89 | - if (permits.get() > maxGrantedPermissions.get()) { | ||
90 | - maxGrantedPermissions.set(permits.get()); | ||
91 | - } | ||
92 | - LockedFuture lockedFuture = queue.poll(); | ||
93 | - if (lockedFuture != null) { | ||
94 | - totalGranted.incrementAndGet(); | ||
95 | - lockedFuture.latch.countDown(); | ||
96 | - } else { | ||
97 | - permits.decrementAndGet(); | ||
98 | - break; | ||
99 | - } | ||
100 | - } else { | ||
101 | - permits.decrementAndGet(); | ||
102 | - } | ||
103 | - } | ||
104 | - } | ||
105 | - | ||
106 | - private LockedFuture createLockedFuture() { | ||
107 | - CountDownLatch latch = new CountDownLatch(1); | ||
108 | - ListenableFuture<Void> future = pool.submit(() -> { | ||
109 | - latch.await(); | ||
110 | - return null; | ||
111 | - }); | ||
112 | - return new LockedFuture(latch, future, System.currentTimeMillis()); | ||
113 | - } | ||
114 | - | ||
115 | - private ListenableFuture<Void> putInQueue() { | ||
116 | - | ||
117 | - int size = queue.size(); | ||
118 | - if (size > maxQueueSize.get()) { | ||
119 | - maxQueueSize.set(size); | ||
120 | - } | ||
121 | - | ||
122 | - if (queue.remainingCapacity() > 0) { | ||
123 | - try { | ||
124 | - LockedFuture lockedFuture = createLockedFuture(); | ||
125 | - if (!queue.offer(lockedFuture, 1, TimeUnit.SECONDS)) { | ||
126 | - lockedFuture.cancelFuture(); | ||
127 | - return Futures.immediateFailedFuture(new BufferLimitException()); | ||
128 | - } | ||
129 | - if(permits.get() < permitsLimit) { | ||
130 | - reprocessQueue(); | ||
131 | - } | ||
132 | - if(permits.get() < permitsLimit) { | ||
133 | - reprocessQueue(); | ||
134 | - } | ||
135 | - return lockedFuture.future; | ||
136 | - } catch (InterruptedException e) { | ||
137 | - return Futures.immediateFailedFuture(new BufferLimitException()); | ||
138 | - } | ||
139 | - } | ||
140 | - return Futures.immediateFailedFuture(new BufferLimitException()); | ||
141 | - } | ||
142 | - | ||
143 | - @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") | ||
144 | - public void printStats() { | ||
145 | - int expiredCount = 0; | ||
146 | - for (LockedFuture lockedFuture : queue) { | ||
147 | - if (lockedFuture.isExpired()) { | ||
148 | - lockedFuture.cancelFuture(); | ||
149 | - expiredCount++; | ||
150 | - } | ||
151 | - } | ||
152 | - log.info("Permits maxBuffer [{}] maxPermits [{}] expired [{}] currPermits [{}] currBuffer [{}] " + | ||
153 | - "totalPermits [{}] totalRequests [{}] totalReleased [{}]", | ||
154 | - maxQueueSize.getAndSet(0), maxGrantedPermissions.getAndSet(0), expiredCount, | ||
155 | - permits.get(), queue.size(), | ||
156 | - totalGranted.getAndSet(0), totalRequested.getAndSet(0), totalReleased.getAndSet(0)); | ||
157 | - } | ||
158 | - | ||
159 | - private class LockedFuture { | ||
160 | - final CountDownLatch latch; | ||
161 | - final ListenableFuture<Void> future; | ||
162 | - final long createTime; | ||
163 | - | ||
164 | - public LockedFuture(CountDownLatch latch, ListenableFuture<Void> future, long createTime) { | ||
165 | - this.latch = latch; | ||
166 | - this.future = future; | ||
167 | - this.createTime = createTime; | ||
168 | - } | ||
169 | - | ||
170 | - void cancelFuture() { | ||
171 | - future.cancel(false); | ||
172 | - latch.countDown(); | ||
173 | - } | ||
174 | - | ||
175 | - boolean isExpired() { | ||
176 | - return (System.currentTimeMillis() - createTime) > maxPermitWaitTime; | ||
177 | - } | ||
178 | - | ||
179 | - } | ||
180 | - | ||
181 | - | ||
182 | -} |
@@ -78,7 +78,7 @@ CREATE TABLE IF NOT EXISTS attribute_kv ( | @@ -78,7 +78,7 @@ CREATE TABLE IF NOT EXISTS attribute_kv ( | ||
78 | CREATE TABLE IF NOT EXISTS component_descriptor ( | 78 | CREATE TABLE IF NOT EXISTS component_descriptor ( |
79 | id varchar(31) NOT NULL CONSTRAINT component_descriptor_pkey PRIMARY KEY, | 79 | id varchar(31) NOT NULL CONSTRAINT component_descriptor_pkey PRIMARY KEY, |
80 | actions varchar(255), | 80 | actions varchar(255), |
81 | - clazz varchar, | 81 | + clazz varchar UNIQUE, |
82 | configuration_descriptor varchar, | 82 | configuration_descriptor varchar, |
83 | name varchar(255), | 83 | name varchar(255), |
84 | scope varchar(255), | 84 | scope varchar(255), |
@@ -152,7 +152,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -152,7 +152,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
152 | } | 152 | } |
153 | 153 | ||
154 | @Test | 154 | @Test |
155 | - public void testDeleteDeviceTsData() throws Exception { | 155 | + public void testDeleteDeviceTsDataWithoutOverwritingLatest() throws Exception { |
156 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); | 156 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); |
157 | 157 | ||
158 | saveEntries(deviceId, 10000); | 158 | saveEntries(deviceId, 10000); |
@@ -172,6 +172,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -172,6 +172,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
172 | } | 172 | } |
173 | 173 | ||
174 | @Test | 174 | @Test |
175 | + public void testDeleteDeviceTsDataWithOverwritingLatest() throws Exception { | ||
176 | + DeviceId deviceId = new DeviceId(UUIDs.timeBased()); | ||
177 | + | ||
178 | + saveEntries(deviceId, 10000); | ||
179 | + saveEntries(deviceId, 20000); | ||
180 | + saveEntries(deviceId, 30000); | ||
181 | + saveEntries(deviceId, 40000); | ||
182 | + | ||
183 | + tsService.remove(deviceId, Collections.singletonList( | ||
184 | + new BaseDeleteTsKvQuery(STRING_KEY, 25000, 45000, true))).get(); | ||
185 | + | ||
186 | + List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList( | ||
187 | + new BaseReadTsKvQuery(STRING_KEY, 5000, 45000, 10000, 10, Aggregation.NONE))).get(); | ||
188 | + Assert.assertEquals(2, list.size()); | ||
189 | + | ||
190 | + List<TsKvEntry> latest = tsService.findLatest(deviceId, Collections.singletonList(STRING_KEY)).get(); | ||
191 | + Assert.assertEquals(20000, latest.get(0).getTs()); | ||
192 | + } | ||
193 | + | ||
194 | + @Test | ||
175 | public void testFindDeviceTsData() throws Exception { | 195 | public void testFindDeviceTsData() throws Exception { |
176 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); | 196 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); |
177 | List<TsKvEntry> entries = new ArrayList<>(); | 197 | List<TsKvEntry> entries = new ArrayList<>(); |
1 | -/** | ||
2 | - * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.thingsboard.server.dao.util; | ||
17 | - | ||
18 | -import com.google.common.util.concurrent.FutureCallback; | ||
19 | -import com.google.common.util.concurrent.Futures; | ||
20 | -import com.google.common.util.concurrent.ListenableFuture; | ||
21 | -import com.google.common.util.concurrent.ListeningExecutorService; | ||
22 | -import com.google.common.util.concurrent.MoreExecutors; | ||
23 | -import org.junit.Test; | ||
24 | -import org.thingsboard.server.dao.exception.BufferLimitException; | ||
25 | - | ||
26 | -import javax.annotation.Nullable; | ||
27 | -import java.util.concurrent.ExecutionException; | ||
28 | -import java.util.concurrent.Executors; | ||
29 | -import java.util.concurrent.TimeUnit; | ||
30 | -import java.util.concurrent.atomic.AtomicInteger; | ||
31 | - | ||
32 | -import static org.junit.Assert.assertEquals; | ||
33 | -import static org.junit.Assert.assertFalse; | ||
34 | -import static org.junit.Assert.assertTrue; | ||
35 | -import static org.junit.Assert.fail; | ||
36 | - | ||
37 | - | ||
38 | -public class BufferedRateLimiterTest { | ||
39 | - | ||
40 | - @Test | ||
41 | - public void finishedFutureReturnedIfPermitsAreGranted() { | ||
42 | - BufferedRateLimiter limiter = new BufferedRateLimiter(10, 10, 100); | ||
43 | - ListenableFuture<Void> actual = limiter.acquireAsync(); | ||
44 | - assertTrue(actual.isDone()); | ||
45 | - } | ||
46 | - | ||
47 | - @Test | ||
48 | - public void notFinishedFutureReturnedIfPermitsAreNotGranted() { | ||
49 | - BufferedRateLimiter limiter = new BufferedRateLimiter(10, 1, 100); | ||
50 | - ListenableFuture<Void> actual1 = limiter.acquireAsync(); | ||
51 | - ListenableFuture<Void> actual2 = limiter.acquireAsync(); | ||
52 | - assertTrue(actual1.isDone()); | ||
53 | - assertFalse(actual2.isDone()); | ||
54 | - } | ||
55 | - | ||
56 | - @Test | ||
57 | - public void failedFutureReturnedIfQueueIsfull() { | ||
58 | - BufferedRateLimiter limiter = new BufferedRateLimiter(1, 1, 100); | ||
59 | - ListenableFuture<Void> actual1 = limiter.acquireAsync(); | ||
60 | - ListenableFuture<Void> actual2 = limiter.acquireAsync(); | ||
61 | - ListenableFuture<Void> actual3 = limiter.acquireAsync(); | ||
62 | - | ||
63 | - assertTrue(actual1.isDone()); | ||
64 | - assertFalse(actual2.isDone()); | ||
65 | - assertTrue(actual3.isDone()); | ||
66 | - try { | ||
67 | - actual3.get(); | ||
68 | - fail(); | ||
69 | - } catch (Exception e) { | ||
70 | - assertTrue(e instanceof ExecutionException); | ||
71 | - Throwable actualCause = e.getCause(); | ||
72 | - assertTrue(actualCause instanceof BufferLimitException); | ||
73 | - assertEquals("Rate Limit Buffer is full", actualCause.getMessage()); | ||
74 | - } | ||
75 | - } | ||
76 | - | ||
77 | - @Test | ||
78 | - public void releasedPermitTriggerTasksFromQueue() throws InterruptedException { | ||
79 | - BufferedRateLimiter limiter = new BufferedRateLimiter(10, 2, 100); | ||
80 | - ListenableFuture<Void> actual1 = limiter.acquireAsync(); | ||
81 | - ListenableFuture<Void> actual2 = limiter.acquireAsync(); | ||
82 | - ListenableFuture<Void> actual3 = limiter.acquireAsync(); | ||
83 | - ListenableFuture<Void> actual4 = limiter.acquireAsync(); | ||
84 | - assertTrue(actual1.isDone()); | ||
85 | - assertTrue(actual2.isDone()); | ||
86 | - assertFalse(actual3.isDone()); | ||
87 | - assertFalse(actual4.isDone()); | ||
88 | - limiter.release(); | ||
89 | - TimeUnit.MILLISECONDS.sleep(100L); | ||
90 | - assertTrue(actual3.isDone()); | ||
91 | - assertFalse(actual4.isDone()); | ||
92 | - limiter.release(); | ||
93 | - TimeUnit.MILLISECONDS.sleep(100L); | ||
94 | - assertTrue(actual4.isDone()); | ||
95 | - } | ||
96 | - | ||
97 | - @Test | ||
98 | - public void permitsReleasedInConcurrentMode() throws InterruptedException { | ||
99 | - BufferedRateLimiter limiter = new BufferedRateLimiter(10, 2, 100); | ||
100 | - AtomicInteger actualReleased = new AtomicInteger(); | ||
101 | - AtomicInteger actualRejected = new AtomicInteger(); | ||
102 | - ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5)); | ||
103 | - for (int i = 0; i < 100; i++) { | ||
104 | - ListenableFuture<ListenableFuture<Void>> submit = pool.submit(limiter::acquireAsync); | ||
105 | - Futures.addCallback(submit, new FutureCallback<ListenableFuture<Void>>() { | ||
106 | - @Override | ||
107 | - public void onSuccess(@Nullable ListenableFuture<Void> result) { | ||
108 | - Futures.addCallback(result, new FutureCallback<Void>() { | ||
109 | - @Override | ||
110 | - public void onSuccess(@Nullable Void result) { | ||
111 | - try { | ||
112 | - TimeUnit.MILLISECONDS.sleep(100); | ||
113 | - } catch (InterruptedException e) { | ||
114 | - e.printStackTrace(); | ||
115 | - } | ||
116 | - limiter.release(); | ||
117 | - actualReleased.incrementAndGet(); | ||
118 | - } | ||
119 | - | ||
120 | - @Override | ||
121 | - public void onFailure(Throwable t) { | ||
122 | - actualRejected.incrementAndGet(); | ||
123 | - } | ||
124 | - }); | ||
125 | - } | ||
126 | - | ||
127 | - @Override | ||
128 | - public void onFailure(Throwable t) { | ||
129 | - } | ||
130 | - }); | ||
131 | - } | ||
132 | - | ||
133 | - TimeUnit.SECONDS.sleep(2); | ||
134 | - assertTrue("Unexpected released count " + actualReleased.get(), | ||
135 | - actualReleased.get() > 10 && actualReleased.get() < 20); | ||
136 | - assertTrue("Unexpected rejected count " + actualRejected.get(), | ||
137 | - actualRejected.get() > 80 && actualRejected.get() < 90); | ||
138 | - | ||
139 | - } | ||
140 | - | ||
141 | - | ||
142 | -} |
1 | -# cassandra environment variables | ||
2 | -CASSANDRA_DATA_DIR=/home/docker/cassandra_volume | ||
3 | 1 | ||
4 | -# postgres environment variables | ||
5 | -POSTGRES_DATA_DIR=/home/docker/postgres_volume | ||
6 | -POSTGRES_DB=thingsboard | 2 | +DOCKER_REPO=thingsboard |
7 | 3 | ||
8 | -# hsqldb environment variables | ||
9 | -HSQLDB_DATA_DIR=/home/docker/hsqldb_volume | 4 | +JS_EXECUTOR_DOCKER_NAME=tb-js-executor |
5 | +TB_NODE_DOCKER_NAME=tb-node | ||
6 | +WEB_UI_DOCKER_NAME=tb-web-ui | ||
7 | +MQTT_TRANSPORT_DOCKER_NAME=tb-mqtt-transport | ||
8 | +HTTP_TRANSPORT_DOCKER_NAME=tb-http-transport | ||
9 | +COAP_TRANSPORT_DOCKER_NAME=tb-coap-transport | ||
10 | 10 | ||
11 | -# environment variables for schema init and insert system and demo data | ||
12 | -ADD_SCHEMA_AND_SYSTEM_DATA=false | ||
13 | -ADD_DEMO_DATA=false | ||
11 | +TB_VERSION=latest | ||
12 | + | ||
13 | +# Database used by ThingsBoard, can be either postgres (PostgreSQL) or cassandra (Cassandra). | ||
14 | +# According to the database type corresponding docker service will be deployed (see docker-compose.postgres.yml, docker-compose.cassandra.yml for details). | ||
15 | + | ||
16 | +DATABASE=postgres | ||
17 | + | ||
18 | +KAFKA_TOPICS="js.eval.requests:100:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.transport.api.requests:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.rule-engine:30:1" |
docker/.gitignore
renamed from
msa/docker/.gitignore
docker/README.md
0 → 100644
1 | +# Docker configuration for ThingsBoard Microservices | ||
2 | + | ||
3 | +This folder containing scripts and Docker Compose configurations to run ThingsBoard in Microservices mode. | ||
4 | + | ||
5 | +## Prerequisites | ||
6 | + | ||
7 | +ThingsBoard Microservices are running in dockerized environment. | ||
8 | +Before starting please make sure [Docker CE](https://docs.docker.com/install/) and [Docker Compose](https://docs.docker.com/compose/install/) are installed in your system. | ||
9 | + | ||
10 | +## Installation | ||
11 | + | ||
12 | +Before performing initial installation you can configure the type of database to be used with ThinsBoard. | ||
13 | +In order to set database type change the value of `DATABASE` variable in `.env` file to one of the following: | ||
14 | + | ||
15 | +- `postgres` - use PostgreSQL database; | ||
16 | +- `cassandra` - use Cassandra database; | ||
17 | + | ||
18 | +**NOTE**: According to the database type corresponding docker service will be deployed (see `docker-compose.postgres.yml`, `docker-compose.cassandra.yml` for details). | ||
19 | + | ||
20 | +Execute the following command to run installation: | ||
21 | + | ||
22 | +` | ||
23 | +$ ./docker-install-tb.sh --loadDemo | ||
24 | +` | ||
25 | + | ||
26 | +Where: | ||
27 | + | ||
28 | +- `--loadDemo` - optional argument. Whether to load additional demo data. | ||
29 | + | ||
30 | +## Running | ||
31 | + | ||
32 | +Execute the following command to start services: | ||
33 | + | ||
34 | +` | ||
35 | +$ ./docker-start-services.sh | ||
36 | +` | ||
37 | + | ||
38 | +After a while when all services will be successfully started you can open `http://{your-host-ip}` in you browser (for ex. `http://localhost`). | ||
39 | +You should see ThingsBoard login page. | ||
40 | + | ||
41 | +Use the following default credentials: | ||
42 | + | ||
43 | +- **Systen Administrator**: sysadmin@thingsboard.org / sysadmin | ||
44 | + | ||
45 | +If you installed DataBase with demo data (using `--loadDemo` flag) you can also use the following credentials: | ||
46 | + | ||
47 | +- **Tenant Administrator**: tenant@thingsboard.org / tenant | ||
48 | +- **Customer User**: customer@thingsboard.org / customer | ||
49 | + | ||
50 | +In case of any issues you can examine service logs for errors. | ||
51 | +For example to see ThingsBoard node logs execute the following command: | ||
52 | + | ||
53 | +` | ||
54 | +$ docker-compose logs -f tb1 | ||
55 | +` | ||
56 | + | ||
57 | +Or use `docker-compose ps` to see the state of all the containers. | ||
58 | +Use `docker-compose logs --f` to inspect the logs of all running services. | ||
59 | +See [docker-compose logs](https://docs.docker.com/compose/reference/logs/) command reference for details. | ||
60 | + | ||
61 | +Execute the following command to stop services: | ||
62 | + | ||
63 | +` | ||
64 | +$ ./docker-stop-services.sh | ||
65 | +` | ||
66 | + | ||
67 | +Execute the following command to stop and completely remove deployed docker containers: | ||
68 | + | ||
69 | +` | ||
70 | +$ ./docker-remove-services.sh | ||
71 | +` | ||
72 | + | ||
73 | +Execute the following command to update particular or all services (pull newer docker image and rebuild container): | ||
74 | + | ||
75 | +` | ||
76 | +$ ./docker-update-service.sh [SERVICE...] | ||
77 | +` | ||
78 | + | ||
79 | +Where: | ||
80 | + | ||
81 | +- `[SERVICE...]` - list of services to update (defined in docker-compose configurations). If not specified all services will be updated. | ||
82 | + | ||
83 | +## Upgrading | ||
84 | + | ||
85 | +In case when database upgrade is needed, execute the following commands: | ||
86 | + | ||
87 | +``` | ||
88 | +$ ./docker-stop-services.sh | ||
89 | +$ ./docker-upgrade-tb.sh --fromVersion=[FROM_VERSION] | ||
90 | +$ ./docker-start-services.sh | ||
91 | +``` | ||
92 | + | ||
93 | +Where: | ||
94 | + | ||
95 | +- `FROM_VERSION` - from which version upgrade should be started. See [Upgrade Instructions](https://thingsboard.io/docs/user-guide/install/upgrade-instructions) for valid `fromVersion` values. |
docker/cassandra-setup/Makefile
deleted
100644 → 0
1 | -VERSION=2.1.0 | ||
2 | -PROJECT=thingsboard | ||
3 | -APP=cassandra-setup | ||
4 | - | ||
5 | -build: | ||
6 | - cp ../../application/target/thingsboard.deb . | ||
7 | - docker build --pull -t ${PROJECT}/${APP}:${VERSION} -t ${PROJECT}/${APP}:latest . | ||
8 | - rm thingsboard.deb | ||
9 | - | ||
10 | -push: build | ||
11 | - docker push ${PROJECT}/${APP}:${VERSION} | ||
12 | - docker push ${PROJECT}/${APP}:latest |
docker/cassandra-upgrade/Makefile
deleted
100644 → 0
1 | -VERSION=2.1.0 | ||
2 | -PROJECT=thingsboard | ||
3 | -APP=cassandra-upgrade | ||
4 | - | ||
5 | -build: | ||
6 | - cp ../../application/target/thingsboard.deb . | ||
7 | - docker build --pull -t ${PROJECT}/${APP}:${VERSION} -t ${PROJECT}/${APP}:latest . | ||
8 | - rm thingsboard.deb | ||
9 | - | ||
10 | -push: build | ||
11 | - docker push ${PROJECT}/${APP}:${VERSION} | ||
12 | - docker push ${PROJECT}/${APP}:latest |
docker/cassandra/Makefile
deleted
100644 → 0
docker/check-dirs.sh
renamed from
msa/docker/check-dirs.sh
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | # limitations under the License. | 15 | # limitations under the License. |
16 | # | 16 | # |
17 | 17 | ||
18 | -dirsArray=("./haproxy/certs.d" "./haproxy/letsencrypt" "./tb-node/db" "./tb-node/log") | 18 | +dirsArray=("./haproxy/certs.d" "./haproxy/letsencrypt" "./tb-node/postgres" "./tb-node/cassandra" "./tb-node/log/tb1" "./tb-node/log/tb2") |
19 | 19 | ||
20 | for dir in ${dirsArray[@]} | 20 | for dir in ${dirsArray[@]} |
21 | do | 21 | do |
docker/cluster-mode-thirdparty.yml
deleted
100644 → 0
1 | -# | ||
2 | -# Copyright © 2016-2018 The Thingsboard Authors | ||
3 | -# | ||
4 | -# Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -# you may not use this file except in compliance with the License. | ||
6 | -# You may obtain a copy of the License at | ||
7 | -# | ||
8 | -# http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -# | ||
10 | -# Unless required by applicable law or agreed to in writing, software | ||
11 | -# distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -# See the License for the specific language governing permissions and | ||
14 | -# limitations under the License. | ||
15 | -# | ||
16 | - | ||
17 | -version: '3.3' | ||
18 | -services: | ||
19 | - zookeeper: | ||
20 | - image: wurstmeister/zookeeper | ||
21 | - networks: | ||
22 | - - core | ||
23 | - ports: | ||
24 | - - "2181:2181" | ||
25 | - | ||
26 | - cassandra: | ||
27 | - image: cassandra:3.11.2 | ||
28 | - networks: | ||
29 | - - core | ||
30 | - ports: | ||
31 | - - "7199:7199" | ||
32 | - - "9160:9160" | ||
33 | - - "9042:9042" | ||
34 | - | ||
35 | - redis: | ||
36 | - image: redis:4.0 | ||
37 | - networks: | ||
38 | - - core | ||
39 | - command: redis-server --maxclients 2000 | ||
40 | - ports: | ||
41 | - - "6379:6379" | ||
42 | - | ||
43 | -networks: | ||
44 | - core: | ||
45 | - |
docker/compose-utils.sh
0 → 100755
1 | +#!/bin/bash | ||
2 | +# | ||
3 | +# Copyright © 2016-2018 The Thingsboard Authors | ||
4 | +# | ||
5 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | +# you may not use this file except in compliance with the License. | ||
7 | +# You may obtain a copy of the License at | ||
8 | +# | ||
9 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | +# | ||
11 | +# Unless required by applicable law or agreed to in writing, software | ||
12 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | +# See the License for the specific language governing permissions and | ||
15 | +# limitations under the License. | ||
16 | +# | ||
17 | + | ||
18 | +function additionalComposeArgs() { | ||
19 | + source .env | ||
20 | + ADDITIONAL_COMPOSE_ARGS="" | ||
21 | + case $DATABASE in | ||
22 | + postgres) | ||
23 | + ADDITIONAL_COMPOSE_ARGS="-f docker-compose.postgres.yml" | ||
24 | + ;; | ||
25 | + cassandra) | ||
26 | + ADDITIONAL_COMPOSE_ARGS="-f docker-compose.cassandra.yml" | ||
27 | + ;; | ||
28 | + *) | ||
29 | + echo "Unknown DATABASE value specified: '${DATABASE}'. Should be either postgres or cassandra." >&2 | ||
30 | + exit 1 | ||
31 | + esac | ||
32 | + echo $ADDITIONAL_COMPOSE_ARGS | ||
33 | +} | ||
34 | + | ||
35 | +function additionalStartupServices() { | ||
36 | + source .env | ||
37 | + ADDITIONAL_STARTUP_SERVICES="" | ||
38 | + case $DATABASE in | ||
39 | + postgres) | ||
40 | + ADDITIONAL_STARTUP_SERVICES=postgres | ||
41 | + ;; | ||
42 | + cassandra) | ||
43 | + ADDITIONAL_STARTUP_SERVICES=cassandra | ||
44 | + ;; | ||
45 | + *) | ||
46 | + echo "Unknown DATABASE value specified: '${DATABASE}'. Should be either postgres or cassandra." >&2 | ||
47 | + exit 1 | ||
48 | + esac | ||
49 | + echo $ADDITIONAL_STARTUP_SERVICES | ||
50 | +} |
docker/docker-compose.cassandra.yml
renamed from
docker/docker-compose.static.yml
@@ -14,16 +14,27 @@ | @@ -14,16 +14,27 @@ | ||
14 | # limitations under the License. | 14 | # limitations under the License. |
15 | # | 15 | # |
16 | 16 | ||
17 | -version: '2' | 17 | +version: '2.2' |
18 | 18 | ||
19 | services: | 19 | services: |
20 | cassandra: | 20 | cassandra: |
21 | + restart: always | ||
22 | + image: "cassandra:3.11.3" | ||
21 | ports: | 23 | ports: |
22 | - - "9042:9042" | ||
23 | - - "9160:9160" | ||
24 | - zk: | ||
25 | - ports: | ||
26 | - - "2181:2181" | ||
27 | - postgres: | ||
28 | - ports: | ||
29 | - - "5432:5432" | ||
24 | + - "9042" | ||
25 | + volumes: | ||
26 | + - ./tb-node/cassandra:/var/lib/cassandra | ||
27 | + tb1: | ||
28 | + env_file: | ||
29 | + - tb-node.cassandra.env | ||
30 | + depends_on: | ||
31 | + - kafka | ||
32 | + - redis | ||
33 | + - cassandra | ||
34 | + tb2: | ||
35 | + env_file: | ||
36 | + - tb-node.cassandra.env | ||
37 | + depends_on: | ||
38 | + - kafka | ||
39 | + - redis | ||
40 | + - cassandra |
docker/docker-compose.postgres.yml
renamed from
docker/docker-compose-tests.yml
@@ -14,14 +14,29 @@ | @@ -14,14 +14,29 @@ | ||
14 | # limitations under the License. | 14 | # limitations under the License. |
15 | # | 15 | # |
16 | 16 | ||
17 | -version: '3.3' | 17 | +version: '2.2' |
18 | + | ||
18 | services: | 19 | services: |
19 | - redis: | ||
20 | - image: redis:4.0 | ||
21 | - networks: | ||
22 | - - core | 20 | + postgres: |
21 | + restart: always | ||
22 | + image: "postgres:9.6" | ||
23 | ports: | 23 | ports: |
24 | - - "6379:6379" | ||
25 | - | ||
26 | -networks: | ||
27 | - core: | 24 | + - "5432" |
25 | + environment: | ||
26 | + POSTGRES_DB: thingsboard | ||
27 | + volumes: | ||
28 | + - ./tb-node/postgres:/var/lib/postgresql/data | ||
29 | + tb1: | ||
30 | + env_file: | ||
31 | + - tb-node.postgres.env | ||
32 | + depends_on: | ||
33 | + - kafka | ||
34 | + - redis | ||
35 | + - postgres | ||
36 | + tb2: | ||
37 | + env_file: | ||
38 | + - tb-node.postgres.env | ||
39 | + depends_on: | ||
40 | + - kafka | ||
41 | + - redis | ||
42 | + - postgres |
@@ -14,40 +14,163 @@ | @@ -14,40 +14,163 @@ | ||
14 | # limitations under the License. | 14 | # limitations under the License. |
15 | # | 15 | # |
16 | 16 | ||
17 | -version: '2' | 17 | + |
18 | +version: '2.2' | ||
18 | 19 | ||
19 | services: | 20 | services: |
20 | - tb: | ||
21 | - image: "thingsboard/application:2.1.0" | 21 | + zookeeper: |
22 | + restart: always | ||
23 | + image: "zookeeper:3.5" | ||
22 | ports: | 24 | ports: |
23 | - - "8080:8080" | ||
24 | - - "1883:1883" | ||
25 | - - "5683:5683/udp" | 25 | + - "2181" |
26 | + kafka: | ||
27 | + restart: always | ||
28 | + image: "wurstmeister/kafka" | ||
29 | + ports: | ||
30 | + - "9092:9092" | ||
26 | env_file: | 31 | env_file: |
27 | - - tb.env | 32 | + - kafka.env |
33 | + depends_on: | ||
34 | + - zookeeper | ||
35 | + redis: | ||
36 | + image: redis:4.0 | ||
37 | + ports: | ||
38 | + - "6379" | ||
39 | + tb-js-executor: | ||
40 | + restart: always | ||
41 | + image: "${DOCKER_REPO}/${JS_EXECUTOR_DOCKER_NAME}:${TB_VERSION}" | ||
42 | + scale: 20 | ||
43 | + env_file: | ||
44 | + - tb-js-executor.env | ||
45 | + depends_on: | ||
46 | + - kafka | ||
47 | + tb1: | ||
48 | + restart: always | ||
49 | + image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}" | ||
50 | + ports: | ||
51 | + - "8080" | ||
52 | + logging: | ||
53 | + driver: "json-file" | ||
54 | + options: | ||
55 | + max-size: "200m" | ||
56 | + max-file: "30" | ||
28 | environment: | 57 | environment: |
29 | - - ADD_SCHEMA_AND_SYSTEM_DATA=${ADD_SCHEMA_AND_SYSTEM_DATA} | ||
30 | - - ADD_DEMO_DATA=${ADD_DEMO_DATA} | 58 | + TB_HOST: tb1 |
59 | + env_file: | ||
60 | + - tb-node.env | ||
31 | volumes: | 61 | volumes: |
32 | - - "${HSQLDB_DATA_DIR}:/usr/share/thingsboard/data/sql" | ||
33 | - entrypoint: /run-application.sh | ||
34 | - cassandra: | ||
35 | - image: "cassandra:3.11.2" | 62 | + - ./tb-node/conf:/config |
63 | + - ./tb-node/log:/var/log/thingsboard | ||
64 | + depends_on: | ||
65 | + - kafka | ||
66 | + - redis | ||
67 | + tb2: | ||
68 | + restart: always | ||
69 | + image: "${DOCKER_REPO}/${TB_NODE_DOCKER_NAME}:${TB_VERSION}" | ||
36 | ports: | 70 | ports: |
37 | - - "9042" | ||
38 | - - "9160" | 71 | + - "8080" |
72 | + logging: | ||
73 | + driver: "json-file" | ||
74 | + options: | ||
75 | + max-size: "200m" | ||
76 | + max-file: "30" | ||
77 | + environment: | ||
78 | + TB_HOST: tb2 | ||
79 | + env_file: | ||
80 | + - tb-node.env | ||
39 | volumes: | 81 | volumes: |
40 | - - "${CASSANDRA_DATA_DIR}:/var/lib/cassandra" | ||
41 | - zk: | ||
42 | - image: "zookeeper:3.4.10" | 82 | + - ./tb-node/conf:/config |
83 | + - ./tb-node/log:/var/log/thingsboard | ||
84 | + depends_on: | ||
85 | + - kafka | ||
86 | + - redis | ||
87 | + tb-mqtt-transport1: | ||
88 | + restart: always | ||
89 | + image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" | ||
43 | ports: | 90 | ports: |
44 | - - "2181" | 91 | + - "1883" |
92 | + env_file: | ||
93 | + - tb-mqtt-transport.env | ||
94 | + depends_on: | ||
95 | + - kafka | ||
96 | + tb-mqtt-transport2: | ||
45 | restart: always | 97 | restart: always |
46 | - postgres: | ||
47 | - image: "postgres:9.6" | 98 | + image: "${DOCKER_REPO}/${MQTT_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" |
48 | ports: | 99 | ports: |
49 | - - "5432" | ||
50 | - environment: | ||
51 | - - POSTGRES_DB=${POSTGRES_DB} | 100 | + - "1883" |
101 | + env_file: | ||
102 | + - tb-mqtt-transport.env | ||
103 | + depends_on: | ||
104 | + - kafka | ||
105 | + tb-http-transport1: | ||
106 | + restart: always | ||
107 | + image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" | ||
108 | + ports: | ||
109 | + - "8081" | ||
110 | + env_file: | ||
111 | + - tb-http-transport.env | ||
112 | + depends_on: | ||
113 | + - kafka | ||
114 | + tb-http-transport2: | ||
115 | + restart: always | ||
116 | + image: "${DOCKER_REPO}/${HTTP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" | ||
117 | + ports: | ||
118 | + - "8081" | ||
119 | + env_file: | ||
120 | + - tb-http-transport.env | ||
121 | + depends_on: | ||
122 | + - kafka | ||
123 | + tb-coap-transport: | ||
124 | + restart: always | ||
125 | + image: "${DOCKER_REPO}/${COAP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" | ||
126 | + ports: | ||
127 | + - "5683:5683/udp" | ||
128 | + env_file: | ||
129 | + - tb-coap-transport.env | ||
130 | + depends_on: | ||
131 | + - kafka | ||
132 | + tb-web-ui1: | ||
133 | + restart: always | ||
134 | + image: "${DOCKER_REPO}/${WEB_UI_DOCKER_NAME}:${TB_VERSION}" | ||
135 | + ports: | ||
136 | + - "8080" | ||
137 | + env_file: | ||
138 | + - tb-web-ui.env | ||
139 | + tb-web-ui2: | ||
140 | + restart: always | ||
141 | + image: "${DOCKER_REPO}/${WEB_UI_DOCKER_NAME}:${TB_VERSION}" | ||
142 | + ports: | ||
143 | + - "8080" | ||
144 | + env_file: | ||
145 | + - tb-web-ui.env | ||
146 | + haproxy: | ||
147 | + restart: always | ||
148 | + container_name: haproxy-certbot | ||
149 | + image: xalauc/haproxy-certbot:1.7.9 | ||
52 | volumes: | 150 | volumes: |
53 | - - "${POSTGRES_DATA_DIR}:/var/lib/postgresql/data" | 151 | + - ./haproxy/config:/config |
152 | + - ./haproxy/letsencrypt:/etc/letsencrypt | ||
153 | + - ./haproxy/certs.d:/usr/local/etc/haproxy/certs.d | ||
154 | + ports: | ||
155 | + - "80:80" | ||
156 | + - "8080" | ||
157 | + - "443:443" | ||
158 | + - "1883:1883" | ||
159 | + - "9999:9999" | ||
160 | + cap_add: | ||
161 | + - NET_ADMIN | ||
162 | + environment: | ||
163 | + HTTP_PORT: 80 | ||
164 | + HTTPS_PORT: 443 | ||
165 | + MQTT_PORT: 1883 | ||
166 | + TB_API_PORT: 8080 | ||
167 | + FORCE_HTTPS_REDIRECT: "false" | ||
168 | + links: | ||
169 | + - tb1 | ||
170 | + - tb2 | ||
171 | + - tb-web-ui1 | ||
172 | + - tb-web-ui2 | ||
173 | + - tb-mqtt-transport1 | ||
174 | + - tb-mqtt-transport2 | ||
175 | + - tb-http-transport1 | ||
176 | + - tb-http-transport2 |
docker/docker-install-tb.sh
renamed from
msa/docker/docker-install-tb.sh
@@ -39,6 +39,18 @@ else | @@ -39,6 +39,18 @@ else | ||
39 | loadDemo=false | 39 | loadDemo=false |
40 | fi | 40 | fi |
41 | 41 | ||
42 | -docker-compose run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=${loadDemo} tb | 42 | +set -e |
43 | + | ||
44 | +source compose-utils.sh | ||
45 | + | ||
46 | +ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? | ||
47 | + | ||
48 | +ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $? | ||
49 | + | ||
50 | +if [ ! -z "${ADDITIONAL_STARTUP_SERVICES// }" ]; then | ||
51 | + docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES | ||
52 | +fi | ||
53 | + | ||
54 | +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e INSTALL_TB=true -e LOAD_DEMO=${loadDemo} tb1 | ||
43 | 55 | ||
44 | 56 |
docker/docker-remove-services.sh
renamed from
docker/cassandra-setup/Dockerfile
100644 → 100755
1 | +#!/bin/bash | ||
1 | # | 2 | # |
2 | # Copyright © 2016-2018 The Thingsboard Authors | 3 | # Copyright © 2016-2018 The Thingsboard Authors |
3 | # | 4 | # |
@@ -14,11 +15,10 @@ | @@ -14,11 +15,10 @@ | ||
14 | # limitations under the License. | 15 | # limitations under the License. |
15 | # | 16 | # |
16 | 17 | ||
17 | -FROM openjdk:8-jre | 18 | +set -e |
18 | 19 | ||
19 | -ADD install.sh /install.sh | ||
20 | -ADD thingsboard.deb /thingsboard.deb | 20 | +source compose-utils.sh |
21 | 21 | ||
22 | -RUN apt-get update \ | ||
23 | - && apt-get install -y nmap \ | ||
24 | - && chmod +x /install.sh | 22 | +ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? |
23 | + | ||
24 | +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS down -v |
docker/docker-start-services.sh
renamed from
docker/cassandra-upgrade/upgrade.sh
@@ -15,14 +15,12 @@ | @@ -15,14 +15,12 @@ | ||
15 | # limitations under the License. | 15 | # limitations under the License. |
16 | # | 16 | # |
17 | 17 | ||
18 | +./check-dirs.sh | ||
18 | 19 | ||
19 | -dpkg -i /thingsboard.deb | 20 | +set -e |
20 | 21 | ||
21 | -until nmap $CASSANDRA_HOST -p $CASSANDRA_PORT | grep "$CASSANDRA_PORT/tcp open" | ||
22 | -do | ||
23 | - echo "Wait for cassandra db to start..." | ||
24 | - sleep 10 | ||
25 | -done | 22 | +source compose-utils.sh |
26 | 23 | ||
27 | -echo "Upgrading 'Thingsboard' schema..." | ||
28 | -/usr/share/thingsboard/bin/install/upgrade.sh --fromVersion=$UPGRADE_FROM_VERSION | 24 | +ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? |
25 | + | ||
26 | +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d |
docker/docker-stop-services.sh
renamed from
docker/cassandra-upgrade/Dockerfile
100644 → 100755
1 | +#!/bin/bash | ||
1 | # | 2 | # |
2 | # Copyright © 2016-2018 The Thingsboard Authors | 3 | # Copyright © 2016-2018 The Thingsboard Authors |
3 | # | 4 | # |
@@ -14,11 +15,10 @@ | @@ -14,11 +15,10 @@ | ||
14 | # limitations under the License. | 15 | # limitations under the License. |
15 | # | 16 | # |
16 | 17 | ||
17 | -FROM openjdk:8-jre | 18 | +set -e |
18 | 19 | ||
19 | -ADD upgrade.sh /upgrade.sh | ||
20 | -ADD thingsboard.deb /thingsboard.deb | 20 | +source compose-utils.sh |
21 | 21 | ||
22 | -RUN apt-get update \ | ||
23 | - && apt-get install -y nmap \ | ||
24 | - && chmod +x /upgrade.sh | 22 | +ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? |
23 | + | ||
24 | +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS stop |
docker/docker-update-service.sh
renamed from
docker/tb/Dockerfile
100644 → 100755
1 | +#!/bin/bash | ||
1 | # | 2 | # |
2 | # Copyright © 2016-2018 The Thingsboard Authors | 3 | # Copyright © 2016-2018 The Thingsboard Authors |
3 | # | 4 | # |
@@ -14,13 +15,11 @@ | @@ -14,13 +15,11 @@ | ||
14 | # limitations under the License. | 15 | # limitations under the License. |
15 | # | 16 | # |
16 | 17 | ||
17 | -FROM openjdk:8-jre | 18 | +set -e |
18 | 19 | ||
19 | -ADD run-application.sh /run-application.sh | ||
20 | -ADD thingsboard.deb /thingsboard.deb | 20 | +source compose-utils.sh |
21 | 21 | ||
22 | -RUN apt-get update \ | ||
23 | - && apt-get install --no-install-recommends -y nmap \ | ||
24 | - && apt-get clean \ | ||
25 | - && rm -r /var/lib/apt/lists/* \ | ||
26 | - && chmod +x /run-application.sh | 22 | +ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? |
23 | + | ||
24 | +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS pull $@ | ||
25 | +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d --no-deps --build $@ |
docker/docker-upgrade-tb.sh
renamed from
msa/docker/docker-upgrade-tb.sh
@@ -38,4 +38,18 @@ else | @@ -38,4 +38,18 @@ else | ||
38 | fromVersion="${FROM_VERSION// }" | 38 | fromVersion="${FROM_VERSION// }" |
39 | fi | 39 | fi |
40 | 40 | ||
41 | -docker-compose run --no-deps --rm -e UPGRADE_TB=true -e FROM_VERSION=${fromVersion} tb | 41 | +set -e |
42 | + | ||
43 | +source compose-utils.sh | ||
44 | + | ||
45 | +ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? | ||
46 | + | ||
47 | +ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $? | ||
48 | + | ||
49 | +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS pull tb1 | ||
50 | + | ||
51 | +if [ ! -z "${ADDITIONAL_STARTUP_SERVICES// }" ]; then | ||
52 | + docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES | ||
53 | +fi | ||
54 | + | ||
55 | +docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e UPGRADE_TB=true -e FROM_VERSION=${fromVersion} tb1 |
docker/haproxy/config/haproxy.cfg
renamed from
msa/docker/haproxy/config/haproxy.cfg
@@ -23,6 +23,13 @@ defaults | @@ -23,6 +23,13 @@ defaults | ||
23 | timeout connect 5000ms | 23 | timeout connect 5000ms |
24 | timeout client 50000ms | 24 | timeout client 50000ms |
25 | timeout server 50000ms | 25 | timeout server 50000ms |
26 | + timeout tunnel 1h # timeout to use with WebSocket and CONNECT | ||
27 | + | ||
28 | + default-server init-addr none | ||
29 | + | ||
30 | +#enable resolving throught docker dns and avoid crashing if service is down while proxy is starting | ||
31 | +resolvers docker_resolver | ||
32 | + nameserver dns 127.0.0.11:53 | ||
26 | 33 | ||
27 | listen stats | 34 | listen stats |
28 | bind *:9999 | 35 | bind *:9999 |
@@ -39,8 +46,8 @@ listen mqtt-in | @@ -39,8 +46,8 @@ listen mqtt-in | ||
39 | timeout server 3h | 46 | timeout server 3h |
40 | option tcplog | 47 | option tcplog |
41 | balance leastconn | 48 | balance leastconn |
42 | - server tbMqtt1 tb-mqtt-transport1:1883 check | ||
43 | - server tbMqtt2 tb-mqtt-transport2:1883 check | 49 | + server tbMqtt1 tb-mqtt-transport1:1883 check inter 5s resolvers docker_resolver resolve-prefer ipv4 |
50 | + server tbMqtt2 tb-mqtt-transport2:1883 check inter 5s resolvers docker_resolver resolve-prefer ipv4 | ||
44 | 51 | ||
45 | frontend http-in | 52 | frontend http-in |
46 | bind *:${HTTP_PORT} | 53 | bind *:${HTTP_PORT} |
@@ -51,7 +58,7 @@ frontend http-in | @@ -51,7 +58,7 @@ frontend http-in | ||
51 | 58 | ||
52 | acl transport_http_acl path_beg /api/v1/ | 59 | acl transport_http_acl path_beg /api/v1/ |
53 | acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/ | 60 | acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/ |
54 | - redirect scheme https if !letsencrypt_http_acl !transport_http_acl | 61 | + redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true } |
55 | use_backend letsencrypt_http if letsencrypt_http_acl | 62 | use_backend letsencrypt_http if letsencrypt_http_acl |
56 | use_backend tb-http-backend if transport_http_acl | 63 | use_backend tb-http-backend if transport_http_acl |
57 | 64 | ||
@@ -69,6 +76,11 @@ frontend https_in | @@ -69,6 +76,11 @@ frontend https_in | ||
69 | 76 | ||
70 | default_backend tb-web-backend | 77 | default_backend tb-web-backend |
71 | 78 | ||
79 | +frontend http-api-in | ||
80 | + bind *:${TB_API_PORT} | ||
81 | + | ||
82 | + default_backend tb-api-backend | ||
83 | + | ||
72 | backend letsencrypt_http | 84 | backend letsencrypt_http |
73 | server letsencrypt_http_srv 127.0.0.1:8080 | 85 | server letsencrypt_http_srv 127.0.0.1:8080 |
74 | 86 | ||
@@ -76,13 +88,20 @@ backend tb-web-backend | @@ -76,13 +88,20 @@ backend tb-web-backend | ||
76 | balance leastconn | 88 | balance leastconn |
77 | option tcp-check | 89 | option tcp-check |
78 | option log-health-checks | 90 | option log-health-checks |
79 | - server tbWeb1 tb-web-ui1:8080 check | ||
80 | - server tbWeb2 tb-web-ui2:8080 check | 91 | + server tbWeb1 tb-web-ui1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 |
92 | + server tbWeb2 tb-web-ui2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 | ||
81 | http-request set-header X-Forwarded-Port %[dst_port] | 93 | http-request set-header X-Forwarded-Port %[dst_port] |
82 | 94 | ||
83 | backend tb-http-backend | 95 | backend tb-http-backend |
84 | balance leastconn | 96 | balance leastconn |
85 | option tcp-check | 97 | option tcp-check |
86 | option log-health-checks | 98 | option log-health-checks |
87 | - server tbHttp1 tb-http-transport1:8081 check | ||
88 | - server tbHttp2 tb-http-transport2:8081 check | 99 | + server tbHttp1 tb-http-transport1:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 |
100 | + server tbHttp2 tb-http-transport2:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 | ||
101 | + | ||
102 | +backend tb-api-backend | ||
103 | + balance leastconn | ||
104 | + option tcp-check | ||
105 | + option log-health-checks | ||
106 | + server tbApi1 tb1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 | ||
107 | + server tbApi2 tb2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 |
docker/k8s/cassandra-setup.yaml
deleted
100644 → 0
1 | -# | ||
2 | -# Copyright © 2016-2018 The Thingsboard Authors | ||
3 | -# | ||
4 | -# Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -# you may not use this file except in compliance with the License. | ||
6 | -# You may obtain a copy of the License at | ||
7 | -# | ||
8 | -# http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -# | ||
10 | -# Unless required by applicable law or agreed to in writing, software | ||
11 | -# distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -# See the License for the specific language governing permissions and | ||
14 | -# limitations under the License. | ||
15 | -# | ||
16 | - | ||
17 | -apiVersion: v1 | ||
18 | -kind: Pod | ||
19 | -metadata: | ||
20 | - name: cassandra-setup | ||
21 | -spec: | ||
22 | - containers: | ||
23 | - - name: cassandra-setup | ||
24 | - imagePullPolicy: Always | ||
25 | - image: thingsboard/cassandra-setup:2.1.0 | ||
26 | - env: | ||
27 | - - name: ADD_DEMO_DATA | ||
28 | - value: "true" | ||
29 | - - name : CASSANDRA_HOST | ||
30 | - value: "cassandra-headless" | ||
31 | - - name : CASSANDRA_PORT | ||
32 | - value: "9042" | ||
33 | - - name : DATABASE_ENTITIES_TYPE | ||
34 | - value: "cassandra" | ||
35 | - - name : DATABASE_TS_TYPE | ||
36 | - value: "cassandra" | ||
37 | - - name : CASSANDRA_URL | ||
38 | - value: "cassandra-headless:9042" | ||
39 | - command: | ||
40 | - - sh | ||
41 | - - -c | ||
42 | - - /install.sh | ||
43 | - restartPolicy: Never |
docker/k8s/cassandra-upgrade.yaml
deleted
100644 → 0
1 | -# | ||
2 | -# Copyright © 2016-2018 The Thingsboard Authors | ||
3 | -# | ||
4 | -# Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -# you may not use this file except in compliance with the License. | ||
6 | -# You may obtain a copy of the License at | ||
7 | -# | ||
8 | -# http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -# | ||
10 | -# Unless required by applicable law or agreed to in writing, software | ||
11 | -# distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -# See the License for the specific language governing permissions and | ||
14 | -# limitations under the License. | ||
15 | -# | ||
16 | - | ||
17 | -apiVersion: v1 | ||
18 | -kind: Pod | ||
19 | -metadata: | ||
20 | - name: cassandra-upgrade | ||
21 | -spec: | ||
22 | - containers: | ||
23 | - - name: cassandra-upgrade | ||
24 | - imagePullPolicy: Always | ||
25 | - image: thingsboard/cassandra-upgrade:2.1.0 | ||
26 | - env: | ||
27 | - - name: ADD_DEMO_DATA | ||
28 | - value: "true" | ||
29 | - - name : CASSANDRA_HOST | ||
30 | - value: "cassandra-headless" | ||
31 | - - name : CASSANDRA_PORT | ||
32 | - value: "9042" | ||
33 | - - name : DATABASE_ENTITIES_TYPE | ||
34 | - value: "cassandra" | ||
35 | - - name : DATABASE_TS_TYPE | ||
36 | - value: "cassandra" | ||
37 | - - name : CASSANDRA_URL | ||
38 | - value: "cassandra-headless:9042" | ||
39 | - - name : UPGRADE_FROM_VERSION | ||
40 | - value: "1.4.0" | ||
41 | - command: | ||
42 | - - sh | ||
43 | - - -c | ||
44 | - - /upgrade.sh | ||
45 | - restartPolicy: Never |
docker/k8s/cassandra.yaml
deleted
100644 → 0
1 | -# | ||
2 | -# Copyright © 2016-2018 The Thingsboard Authors | ||
3 | -# | ||
4 | -# Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -# you may not use this file except in compliance with the License. | ||
6 | -# You may obtain a copy of the License at | ||
7 | -# | ||
8 | -# http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -# | ||
10 | -# Unless required by applicable law or agreed to in writing, software | ||
11 | -# distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -# See the License for the specific language governing permissions and | ||
14 | -# limitations under the License. | ||
15 | -# | ||
16 | - | ||
17 | -apiVersion: v1 | ||
18 | -kind: Service | ||
19 | -metadata: | ||
20 | - name: cassandra-headless | ||
21 | - labels: | ||
22 | - app: cassandra-headless | ||
23 | -spec: | ||
24 | - ports: | ||
25 | - - port: 9042 | ||
26 | - name: cql | ||
27 | - clusterIP: None | ||
28 | - selector: | ||
29 | - app: cassandra | ||
30 | ---- | ||
31 | -apiVersion: "apps/v1beta1" | ||
32 | -kind: StatefulSet | ||
33 | -metadata: | ||
34 | - name: cassandra | ||
35 | -spec: | ||
36 | - serviceName: cassandra-headless | ||
37 | - replicas: 2 | ||
38 | - template: | ||
39 | - metadata: | ||
40 | - labels: | ||
41 | - app: cassandra | ||
42 | - spec: | ||
43 | - nodeSelector: | ||
44 | - machinetype: other | ||
45 | - affinity: | ||
46 | - podAntiAffinity: | ||
47 | - requiredDuringSchedulingIgnoredDuringExecution: | ||
48 | - - labelSelector: | ||
49 | - matchExpressions: | ||
50 | - - key: "app" | ||
51 | - operator: In | ||
52 | - values: | ||
53 | - - cassandra-headless | ||
54 | - topologyKey: "kubernetes.io/hostname" | ||
55 | - containers: | ||
56 | - - name: cassandra | ||
57 | - image: thingsboard/cassandra:2.1.0 | ||
58 | - imagePullPolicy: Always | ||
59 | - ports: | ||
60 | - - containerPort: 7000 | ||
61 | - name: intra-node | ||
62 | - - containerPort: 7001 | ||
63 | - name: tls-intra-node | ||
64 | - - containerPort: 7199 | ||
65 | - name: jmx | ||
66 | - - containerPort: 9042 | ||
67 | - name: cql | ||
68 | - - containerPort: 9160 | ||
69 | - name: thrift | ||
70 | - securityContext: | ||
71 | - capabilities: | ||
72 | - add: | ||
73 | - - IPC_LOCK | ||
74 | - lifecycle: | ||
75 | - preStop: | ||
76 | - exec: | ||
77 | - command: ["/bin/sh", "-c", "PID=$(pidof java) && kill $PID && while ps -p $PID > /dev/null; do sleep 1; done"] | ||
78 | - env: | ||
79 | - - name: MAX_HEAP_SIZE | ||
80 | - value: 2048M | ||
81 | - - name: HEAP_NEWSIZE | ||
82 | - value: 100M | ||
83 | - - name: CASSANDRA_SEEDS | ||
84 | - value: "cassandra-0.cassandra-headless.default.svc.cluster.local" | ||
85 | - - name: CASSANDRA_CLUSTER_NAME | ||
86 | - value: "Thingsboard-Cluster" | ||
87 | - - name: CASSANDRA_DC | ||
88 | - value: "DC1-Thingsboard-Cluster" | ||
89 | - - name: CASSANDRA_RACK | ||
90 | - value: "Rack-Thingsboard-Cluster" | ||
91 | - - name: CASSANDRA_AUTO_BOOTSTRAP | ||
92 | - value: "false" | ||
93 | - - name: POD_IP | ||
94 | - valueFrom: | ||
95 | - fieldRef: | ||
96 | - fieldPath: status.podIP | ||
97 | - - name: POD_NAMESPACE | ||
98 | - valueFrom: | ||
99 | - fieldRef: | ||
100 | - fieldPath: metadata.namespace | ||
101 | - readinessProbe: | ||
102 | - exec: | ||
103 | - command: | ||
104 | - - /bin/bash | ||
105 | - - -c | ||
106 | - - /ready-probe.sh | ||
107 | - initialDelaySeconds: 15 | ||
108 | - timeoutSeconds: 5 | ||
109 | - volumeMounts: | ||
110 | - - name: cassandra-data | ||
111 | - mountPath: /var/lib/cassandra/data | ||
112 | - - name: cassandra-commitlog | ||
113 | - mountPath: /var/lib/cassandra/commitlog | ||
114 | - volumeClaimTemplates: | ||
115 | - - metadata: | ||
116 | - name: cassandra-data | ||
117 | - annotations: | ||
118 | - volume.beta.kubernetes.io/storage-class: fast | ||
119 | - spec: | ||
120 | - accessModes: [ "ReadWriteOnce" ] | ||
121 | - resources: | ||
122 | - requests: | ||
123 | - storage: 3Gi | ||
124 | - - metadata: | ||
125 | - name: cassandra-commitlog | ||
126 | - annotations: | ||
127 | - volume.beta.kubernetes.io/storage-class: fast | ||
128 | - spec: | ||
129 | - accessModes: [ "ReadWriteOnce" ] | ||
130 | - resources: | ||
131 | - requests: | ||
132 | - storage: 2Gi |
docker/k8s/common.yaml
deleted
100644 → 0
1 | -# | ||
2 | -# Copyright © 2016-2018 The Thingsboard Authors | ||
3 | -# | ||
4 | -# Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -# you may not use this file except in compliance with the License. | ||
6 | -# You may obtain a copy of the License at | ||
7 | -# | ||
8 | -# http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -# | ||
10 | -# Unless required by applicable law or agreed to in writing, software | ||
11 | -# distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -# See the License for the specific language governing permissions and | ||
14 | -# limitations under the License. | ||
15 | -# | ||
16 | - | ||
17 | ---- | ||
18 | -apiVersion: storage.k8s.io/v1beta1 | ||
19 | -kind: StorageClass | ||
20 | -metadata: | ||
21 | - name: slow | ||
22 | -provisioner: kubernetes.io/gce-pd | ||
23 | -parameters: | ||
24 | - type: pd-standard | ||
25 | ---- | ||
26 | -apiVersion: storage.k8s.io/v1beta1 | ||
27 | -kind: StorageClass | ||
28 | -metadata: | ||
29 | - name: fast | ||
30 | -provisioner: kubernetes.io/gce-pd | ||
31 | -parameters: | ||
32 | - type: pd-ssd | ||
33 | ---- |
docker/k8s/redis.yaml
deleted
100644 → 0
1 | -# | ||
2 | -# Copyright © 2016-2018 The Thingsboard Authors | ||
3 | -# | ||
4 | -# Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -# you may not use this file except in compliance with the License. | ||
6 | -# You may obtain a copy of the License at | ||
7 | -# | ||
8 | -# http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -# | ||
10 | -# Unless required by applicable law or agreed to in writing, software | ||
11 | -# distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -# See the License for the specific language governing permissions and | ||
14 | -# limitations under the License. | ||
15 | -# | ||
16 | - | ||
17 | ---- | ||
18 | -apiVersion: v1 | ||
19 | -kind: Service | ||
20 | -metadata: | ||
21 | - labels: | ||
22 | - name: redis-service | ||
23 | - name: redis-service | ||
24 | -spec: | ||
25 | - ports: | ||
26 | - - name: redis-service | ||
27 | - protocol: TCP | ||
28 | - port: 6379 | ||
29 | - targetPort: 6379 | ||
30 | - selector: | ||
31 | - app: redis | ||
32 | ---- | ||
33 | -apiVersion: v1 | ||
34 | -kind: ConfigMap | ||
35 | -metadata: | ||
36 | - name: redis-conf | ||
37 | -data: | ||
38 | - redis.conf: | | ||
39 | - appendonly yes | ||
40 | - protected-mode no | ||
41 | - bind 0.0.0.0 | ||
42 | - port 6379 | ||
43 | - dir /var/lib/redis | ||
44 | ---- | ||
45 | -apiVersion: apps/v1beta1 | ||
46 | -kind: StatefulSet | ||
47 | -metadata: | ||
48 | - name: redis | ||
49 | -spec: | ||
50 | - serviceName: redis-service | ||
51 | - replicas: 1 | ||
52 | - template: | ||
53 | - metadata: | ||
54 | - labels: | ||
55 | - app: redis | ||
56 | - spec: | ||
57 | - terminationGracePeriodSeconds: 10 | ||
58 | - containers: | ||
59 | - - name: redis | ||
60 | - image: redis:4.0.9 | ||
61 | - command: | ||
62 | - - redis-server | ||
63 | - args: | ||
64 | - - /etc/redis/redis.conf | ||
65 | - resources: | ||
66 | - requests: | ||
67 | - cpu: 100m | ||
68 | - memory: 100Mi | ||
69 | - ports: | ||
70 | - - containerPort: 6379 | ||
71 | - name: redis | ||
72 | - volumeMounts: | ||
73 | - - name: redis-data | ||
74 | - mountPath: /var/lib/redis | ||
75 | - - name: redis-conf | ||
76 | - mountPath: /etc/redis | ||
77 | - volumes: | ||
78 | - - name: redis-conf | ||
79 | - configMap: | ||
80 | - name: redis-conf | ||
81 | - items: | ||
82 | - - key: redis.conf | ||
83 | - path: redis.conf | ||
84 | - volumeClaimTemplates: | ||
85 | - - metadata: | ||
86 | - name: redis-data | ||
87 | - annotations: | ||
88 | - volume.beta.kubernetes.io/storage-class: fast | ||
89 | - spec: | ||
90 | - accessModes: [ "ReadWriteOnce" ] | ||
91 | - resources: | ||
92 | - requests: | ||
93 | - storage: 1Gi |
docker/k8s/tb.yaml
deleted
100644 → 0
1 | -# | ||
2 | -# Copyright © 2016-2018 The Thingsboard Authors | ||
3 | -# | ||
4 | -# Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -# you may not use this file except in compliance with the License. | ||
6 | -# You may obtain a copy of the License at | ||
7 | -# | ||
8 | -# http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -# | ||
10 | -# Unless required by applicable law or agreed to in writing, software | ||
11 | -# distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -# See the License for the specific language governing permissions and | ||
14 | -# limitations under the License. | ||
15 | -# | ||
16 | - | ||
17 | ---- | ||
18 | -apiVersion: v1 | ||
19 | -kind: Service | ||
20 | -metadata: | ||
21 | - name: tb-service | ||
22 | - labels: | ||
23 | - app: tb-service | ||
24 | -spec: | ||
25 | - ports: | ||
26 | - - port: 8080 | ||
27 | - name: ui | ||
28 | - - port: 1883 | ||
29 | - name: mqtt | ||
30 | - - port: 5683 | ||
31 | - name: coap | ||
32 | - selector: | ||
33 | - app: tb | ||
34 | - type: LoadBalancer | ||
35 | ---- | ||
36 | -apiVersion: policy/v1beta1 | ||
37 | -kind: PodDisruptionBudget | ||
38 | -metadata: | ||
39 | - name: tb-budget | ||
40 | -spec: | ||
41 | - selector: | ||
42 | - matchLabels: | ||
43 | - app: tb | ||
44 | - minAvailable: 3 | ||
45 | ---- | ||
46 | -apiVersion: v1 | ||
47 | -kind: ConfigMap | ||
48 | -metadata: | ||
49 | - name: tb-config | ||
50 | -data: | ||
51 | - zookeeper.enabled: "true" | ||
52 | - zookeeper.url: "zk-headless" | ||
53 | - cassandra.url: "cassandra-headless:9042" | ||
54 | - cassandra.host: "cassandra-headless" | ||
55 | - cassandra.port: "9042" | ||
56 | - database.type: "cassandra" | ||
57 | - cache.type: "redis" | ||
58 | - redis.host: "redis-service" | ||
59 | ---- | ||
60 | -apiVersion: apps/v1beta1 | ||
61 | -kind: StatefulSet | ||
62 | -metadata: | ||
63 | - name: tb | ||
64 | -spec: | ||
65 | - serviceName: "tb-service" | ||
66 | - replicas: 3 | ||
67 | - template: | ||
68 | - metadata: | ||
69 | - labels: | ||
70 | - app: tb | ||
71 | - spec: | ||
72 | - nodeSelector: | ||
73 | - machinetype: tb | ||
74 | - affinity: | ||
75 | - podAntiAffinity: | ||
76 | - requiredDuringSchedulingIgnoredDuringExecution: | ||
77 | - - labelSelector: | ||
78 | - matchExpressions: | ||
79 | - - key: "app" | ||
80 | - operator: In | ||
81 | - values: | ||
82 | - - tb-service | ||
83 | - topologyKey: "kubernetes.io/hostname" | ||
84 | - containers: | ||
85 | - - name: tb | ||
86 | - imagePullPolicy: Always | ||
87 | - image: thingsboard/application:2.1.0 | ||
88 | - ports: | ||
89 | - - containerPort: 8080 | ||
90 | - name: ui | ||
91 | - - containerPort: 1883 | ||
92 | - name: mqtt | ||
93 | - - containerPort: 5683 | ||
94 | - name: coap | ||
95 | - - containerPort: 9001 | ||
96 | - name: rpc | ||
97 | - env: | ||
98 | - - name: ZOOKEEPER_ENABLED | ||
99 | - valueFrom: | ||
100 | - configMapKeyRef: | ||
101 | - name: tb-config | ||
102 | - key: zookeeper.enabled | ||
103 | - - name: ZOOKEEPER_URL | ||
104 | - valueFrom: | ||
105 | - configMapKeyRef: | ||
106 | - name: tb-config | ||
107 | - key: zookeeper.url | ||
108 | - - name : CASSANDRA_HOST | ||
109 | - valueFrom: | ||
110 | - configMapKeyRef: | ||
111 | - name: tb-config | ||
112 | - key: cassandra.host | ||
113 | - - name : CASSANDRA_PORT | ||
114 | - valueFrom: | ||
115 | - configMapKeyRef: | ||
116 | - name: tb-config | ||
117 | - key: cassandra.port | ||
118 | - - name : CASSANDRA_URL | ||
119 | - valueFrom: | ||
120 | - configMapKeyRef: | ||
121 | - name: tb-config | ||
122 | - key: cassandra.url | ||
123 | - - name: DATABASE_ENTITIES_TYPE | ||
124 | - valueFrom: | ||
125 | - configMapKeyRef: | ||
126 | - name: tb-config | ||
127 | - key: database.type | ||
128 | - - name: DATABASE_TS_TYPE | ||
129 | - valueFrom: | ||
130 | - configMapKeyRef: | ||
131 | - name: tb-config | ||
132 | - key: database.type | ||
133 | - - name : RPC_HOST | ||
134 | - valueFrom: | ||
135 | - fieldRef: | ||
136 | - fieldPath: status.podIP | ||
137 | - - name: CACHE_TYPE | ||
138 | - valueFrom: | ||
139 | - configMapKeyRef: | ||
140 | - name: tb-config | ||
141 | - key: cache.type | ||
142 | - - name: REDIS_HOST | ||
143 | - valueFrom: | ||
144 | - configMapKeyRef: | ||
145 | - name: tb-config | ||
146 | - key: redis.host | ||
147 | - command: | ||
148 | - - sh | ||
149 | - - -c | ||
150 | - - /run-application.sh | ||
151 | - livenessProbe: | ||
152 | - httpGet: | ||
153 | - path: /login | ||
154 | - port: ui-port | ||
155 | - initialDelaySeconds: 120 | ||
156 | - timeoutSeconds: 10 |