Commit 9328bbc0b7819341ec121e321c624fbd9ce51a64

Authored by Andrew Shvayka
1 parent 87d05f7c

Aggregation Implementation

@@ -151,20 +151,9 @@ public final class PluginProcessingContext implements PluginContext { @@ -151,20 +151,9 @@ public final class PluginProcessingContext implements PluginContext {
151 } 151 }
152 152
153 @Override 153 @Override
154 - public List<TsKvEntry> loadTimeseries(DeviceId deviceId, TsKvQuery query) { 154 + public void loadTimeseries(DeviceId deviceId, List<TsKvQuery> queries, PluginCallback<List<TsKvEntry>> callback) {
155 validate(deviceId); 155 validate(deviceId);
156 - try {  
157 - return pluginCtx.tsService.findAll(DataConstants.DEVICE, deviceId, query).get();  
158 - } catch (Exception e) {  
159 - log.error("TODO", e);  
160 - throw new RuntimeException(e);  
161 - }  
162 - }  
163 -  
164 - @Override  
165 - public void loadTimeseries(DeviceId deviceId, TsKvQuery query, PluginCallback<List<TsKvEntry>> callback) {  
166 - validate(deviceId);  
167 - ListenableFuture<List<TsKvEntry>> future = pluginCtx.tsService.findAll(DataConstants.DEVICE, deviceId, query); 156 + ListenableFuture<List<TsKvEntry>> future = pluginCtx.tsService.findAll(DataConstants.DEVICE, deviceId, queries);
168 Futures.addCallback(future, getCallback(callback, v -> v), executor); 157 Futures.addCallback(future, getCallback(callback, v -> v), executor);
169 } 158 }
170 159
@@ -96,7 +96,21 @@ public class BaseTimeseriesDao extends AbstractDao implements TimeseriesDao { @@ -96,7 +96,21 @@ public class BaseTimeseriesDao extends AbstractDao implements TimeseriesDao {
96 } 96 }
97 97
98 @Override 98 @Override
99 - public ListenableFuture<List<TsKvEntry>> findAllAsync(String entityType, UUID entityId, TsKvQuery query) { 99 + public ListenableFuture<List<TsKvEntry>> findAllAsync(String entityType, UUID entityId, List<TsKvQuery> queries) {
  100 + List<ListenableFuture<List<TsKvEntry>>> futures = queries.stream().map(query -> findAllAsync(entityType, entityId, query)).collect(Collectors.toList());
  101 + return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() {
  102 + @Nullable
  103 + @Override
  104 + public List<TsKvEntry> apply(@Nullable List<List<TsKvEntry>> results) {
  105 + List<TsKvEntry> result = new ArrayList<TsKvEntry>();
  106 + results.forEach(r -> result.addAll(r));
  107 + return result;
  108 + }
  109 + }, readResultsProcessingExecutor);
  110 + }
  111 +
  112 +
  113 + private ListenableFuture<List<TsKvEntry>> findAllAsync(String entityType, UUID entityId, TsKvQuery query) {
100 if (query.getAggregation() == Aggregation.NONE) { 114 if (query.getAggregation() == Aggregation.NONE) {
101 return findAllAsyncWithLimit(entityType, entityId, query); 115 return findAllAsyncWithLimit(entityType, entityId, query);
102 } else { 116 } else {
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.timeseries; @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.timeseries;
18 import com.datastax.driver.core.ResultSet; 18 import com.datastax.driver.core.ResultSet;
19 import com.datastax.driver.core.ResultSetFuture; 19 import com.datastax.driver.core.ResultSetFuture;
20 import com.datastax.driver.core.Row; 20 import com.datastax.driver.core.Row;
  21 +import com.google.common.base.Function;
21 import com.google.common.collect.Lists; 22 import com.google.common.collect.Lists;
22 import com.google.common.util.concurrent.Futures; 23 import com.google.common.util.concurrent.Futures;
23 import com.google.common.util.concurrent.ListenableFuture; 24 import com.google.common.util.concurrent.ListenableFuture;
@@ -32,6 +33,7 @@ import org.springframework.beans.factory.annotation.Value; @@ -32,6 +33,7 @@ import org.springframework.beans.factory.annotation.Value;
32 import org.springframework.stereotype.Service; 33 import org.springframework.stereotype.Service;
33 import org.thingsboard.server.dao.service.Validator; 34 import org.thingsboard.server.dao.service.Validator;
34 35
  36 +import javax.annotation.Nullable;
35 import javax.annotation.PostConstruct; 37 import javax.annotation.PostConstruct;
36 import javax.annotation.PreDestroy; 38 import javax.annotation.PreDestroy;
37 import java.time.Instant; 39 import java.time.Instant;
@@ -40,6 +42,7 @@ import java.time.ZoneOffset; @@ -40,6 +42,7 @@ import java.time.ZoneOffset;
40 import java.util.*; 42 import java.util.*;
41 import java.util.concurrent.ExecutorService; 43 import java.util.concurrent.ExecutorService;
42 import java.util.concurrent.Executors; 44 import java.util.concurrent.Executors;
  45 +import java.util.stream.Collectors;
43 46
44 import static org.apache.commons.lang3.StringUtils.isBlank; 47 import static org.apache.commons.lang3.StringUtils.isBlank;
45 48
@@ -56,10 +59,10 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -56,10 +59,10 @@ public class BaseTimeseriesService implements TimeseriesService {
56 private TimeseriesDao timeseriesDao; 59 private TimeseriesDao timeseriesDao;
57 60
58 @Override 61 @Override
59 - public ListenableFuture<List<TsKvEntry>> findAll(String entityType, UUIDBased entityId, TsKvQuery query) { 62 + public ListenableFuture<List<TsKvEntry>> findAll(String entityType, UUIDBased entityId, List<TsKvQuery> queries) {
60 validate(entityType, entityId); 63 validate(entityType, entityId);
61 - validate(query);  
62 - return timeseriesDao.findAllAsync(entityType, entityId.getId(), query); 64 + queries.forEach(query -> validate(query));
  65 + return timeseriesDao.findAllAsync(entityType, entityId.getId(), queries);
63 } 66 }
64 67
65 @Override 68 @Override
@@ -132,7 +135,7 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -132,7 +135,7 @@ public class BaseTimeseriesService implements TimeseriesService {
132 throw new IncorrectParameterException("TsKvQuery can't be null"); 135 throw new IncorrectParameterException("TsKvQuery can't be null");
133 } else if (isBlank(query.getKey())) { 136 } else if (isBlank(query.getKey())) {
134 throw new IncorrectParameterException("Incorrect TsKvQuery. Key can't be empty"); 137 throw new IncorrectParameterException("Incorrect TsKvQuery. Key can't be empty");
135 - } else if (query.getAggregation() == null){ 138 + } else if (query.getAggregation() == null) {
136 throw new IncorrectParameterException("Incorrect TsKvQuery. Aggregation can't be empty"); 139 throw new IncorrectParameterException("Incorrect TsKvQuery. Aggregation can't be empty");
137 } 140 }
138 } 141 }
@@ -33,9 +33,7 @@ public interface TimeseriesDao { @@ -33,9 +33,7 @@ public interface TimeseriesDao {
33 33
34 long toPartitionTs(long ts); 34 long toPartitionTs(long ts);
35 35
36 - ListenableFuture<List<TsKvEntry>> findAllAsync(String entityType, UUID entityId, TsKvQuery query);  
37 -  
38 -// List<TsKvEntry> find(String entityType, UUID entityId, TsKvQuery query, Optional<Long> minPartition, Optional<Long> maxPartition); 36 + ListenableFuture<List<TsKvEntry>> findAllAsync(String entityType, UUID entityId, List<TsKvQuery> queries);
39 37
40 ResultSetFuture findLatest(String entityType, UUID entityId, String key); 38 ResultSetFuture findLatest(String entityType, UUID entityId, String key);
41 39
@@ -33,7 +33,7 @@ import java.util.Set; @@ -33,7 +33,7 @@ import java.util.Set;
33 */ 33 */
34 public interface TimeseriesService { 34 public interface TimeseriesService {
35 35
36 - ListenableFuture<List<TsKvEntry>> findAll(String entityType, UUIDBased entityId, TsKvQuery query); 36 + ListenableFuture<List<TsKvEntry>> findAll(String entityType, UUIDBased entityId, List<TsKvQuery> queries);
37 37
38 ListenableFuture<List<ResultSet>> findLatest(String entityType, UUIDBased entityId, Collection<String> keys); 38 ListenableFuture<List<ResultSet>> findLatest(String entityType, UUIDBased entityId, Collection<String> keys);
39 39
@@ -82,9 +82,7 @@ public interface PluginContext { @@ -82,9 +82,7 @@ public interface PluginContext {
82 82
83 void saveTsData(DeviceId deviceId, List<TsKvEntry> entry, PluginCallback<Void> callback); 83 void saveTsData(DeviceId deviceId, List<TsKvEntry> entry, PluginCallback<Void> callback);
84 84
85 - List<TsKvEntry> loadTimeseries(DeviceId deviceId, TsKvQuery query);  
86 -  
87 - void loadTimeseries(DeviceId deviceId, TsKvQuery query, PluginCallback<List<TsKvEntry>> callback); 85 + void loadTimeseries(DeviceId deviceId, List<TsKvQuery> queries, PluginCallback<List<TsKvEntry>> callback);
88 86
89 void loadLatestTimeseries(DeviceId deviceId, Collection<String> keys, PluginCallback<List<TsKvEntry>> callback); 87 void loadLatestTimeseries(DeviceId deviceId, Collection<String> keys, PluginCallback<List<TsKvEntry>> callback);
90 88
@@ -15,9 +15,16 @@ @@ -15,9 +15,16 @@
15 */ 15 */
16 package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; 16 package org.thingsboard.server.extensions.core.plugin.telemetry.cmd;
17 17
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import lombok.NoArgsConstructor;
  21 +
18 /** 22 /**
19 * @author Andrew Shvayka 23 * @author Andrew Shvayka
20 */ 24 */
  25 +@NoArgsConstructor
  26 +@AllArgsConstructor
  27 +@Data
21 public class GetHistoryCmd implements TelemetryPluginCmd { 28 public class GetHistoryCmd implements TelemetryPluginCmd {
22 29
23 private int cmdId; 30 private int cmdId;
@@ -25,46 +32,7 @@ public class GetHistoryCmd implements TelemetryPluginCmd { @@ -25,46 +32,7 @@ public class GetHistoryCmd implements TelemetryPluginCmd {
25 private String keys; 32 private String keys;
26 private long startTs; 33 private long startTs;
27 private long endTs; 34 private long endTs;
  35 + private int limit;
  36 + private String agg;
28 37
29 - @Override  
30 - public int getCmdId() {  
31 - return cmdId;  
32 - }  
33 -  
34 - @Override  
35 - public void setCmdId(int cmdId) {  
36 - this.cmdId = cmdId;  
37 - }  
38 -  
39 - public String getDeviceId() {  
40 - return deviceId;  
41 - }  
42 -  
43 - public void setDeviceId(String deviceId) {  
44 - this.deviceId = deviceId;  
45 - }  
46 -  
47 - public String getKeys() {  
48 - return keys;  
49 - }  
50 -  
51 - public void setKeys(String keys) {  
52 - this.keys = keys;  
53 - }  
54 -  
55 - public long getStartTs() {  
56 - return startTs;  
57 - }  
58 -  
59 - public void setStartTs(long startTs) {  
60 - this.startTs = startTs;  
61 - }  
62 -  
63 - public long getEndTs() {  
64 - return endTs;  
65 - }  
66 -  
67 - public void setEndTs(long endTs) {  
68 - this.endTs = endTs;  
69 - }  
70 } 38 }
@@ -16,11 +16,13 @@ @@ -16,11 +16,13 @@
16 package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; 16 package org.thingsboard.server.extensions.core.plugin.telemetry.cmd;
17 17
18 import lombok.AllArgsConstructor; 18 import lombok.AllArgsConstructor;
  19 +import lombok.Data;
19 import lombok.NoArgsConstructor; 20 import lombok.NoArgsConstructor;
20 import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType; 21 import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType;
21 22
22 @NoArgsConstructor 23 @NoArgsConstructor
23 @AllArgsConstructor 24 @AllArgsConstructor
  25 +@Data
24 public abstract class SubscriptionCmd implements TelemetryPluginCmd { 26 public abstract class SubscriptionCmd implements TelemetryPluginCmd {
25 27
26 private int cmdId; 28 private int cmdId;
@@ -31,46 +33,6 @@ public abstract class SubscriptionCmd implements TelemetryPluginCmd { @@ -31,46 +33,6 @@ public abstract class SubscriptionCmd implements TelemetryPluginCmd {
31 33
32 public abstract SubscriptionType getType(); 34 public abstract SubscriptionType getType();
33 35
34 - public int getCmdId() {  
35 - return cmdId;  
36 - }  
37 -  
38 - public void setCmdId(int cmdId) {  
39 - this.cmdId = cmdId;  
40 - }  
41 -  
42 - public String getDeviceId() {  
43 - return deviceId;  
44 - }  
45 -  
46 - public void setDeviceId(String deviceId) {  
47 - this.deviceId = deviceId;  
48 - }  
49 -  
50 - public String getKeys() {  
51 - return keys;  
52 - }  
53 -  
54 - public void setTags(String tags) {  
55 - this.keys = tags;  
56 - }  
57 -  
58 - public boolean isUnsubscribe() {  
59 - return unsubscribe;  
60 - }  
61 -  
62 - public void setUnsubscribe(boolean unsubscribe) {  
63 - this.unsubscribe = unsubscribe;  
64 - }  
65 -  
66 - public String getScope() {  
67 - return scope;  
68 - }  
69 -  
70 - public void setKeys(String keys) {  
71 - this.keys = keys;  
72 - }  
73 -  
74 @Override 36 @Override
75 public String toString() { 37 public String toString() {
76 return "SubscriptionCmd [deviceId=" + deviceId + ", tags=" + keys + ", unsubscribe=" + unsubscribe + "]"; 38 return "SubscriptionCmd [deviceId=" + deviceId + ", tags=" + keys + ", unsubscribe=" + unsubscribe + "]";
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; 16 package org.thingsboard.server.extensions.core.plugin.telemetry.cmd;
17 17
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
18 import lombok.NoArgsConstructor; 20 import lombok.NoArgsConstructor;
19 import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType; 21 import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType;
20 22
@@ -22,17 +24,13 @@ import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionT @@ -22,17 +24,13 @@ import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionT
22 * @author Andrew Shvayka 24 * @author Andrew Shvayka
23 */ 25 */
24 @NoArgsConstructor 26 @NoArgsConstructor
  27 +@AllArgsConstructor
  28 +@Data
25 public class TimeseriesSubscriptionCmd extends SubscriptionCmd { 29 public class TimeseriesSubscriptionCmd extends SubscriptionCmd {
26 30
27 private long timeWindow; 31 private long timeWindow;
28 -  
29 - public long getTimeWindow() {  
30 - return timeWindow;  
31 - }  
32 -  
33 - public void setTimeWindow(long timeWindow) {  
34 - this.timeWindow = timeWindow;  
35 - } 32 + private int limit;
  33 + private String agg;
36 34
37 @Override 35 @Override
38 public SubscriptionType getType() { 36 public SubscriptionType getType() {
@@ -158,40 +158,14 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler { @@ -158,40 +158,14 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler {
158 log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), cmd.getDeviceId()); 158 log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), cmd.getDeviceId());
159 long endTs = System.currentTimeMillis(); 159 long endTs = System.currentTimeMillis();
160 startTs = endTs - cmd.getTimeWindow(); 160 startTs = endTs - cmd.getTimeWindow();
161 - for (String key : keys) {  
162 - TsKvQuery query = new BaseTsKvQuery(key, startTs, endTs);  
163 - data.addAll(ctx.loadTimeseries(deviceId, query));  
164 - }  
165 - sendWsMsg(ctx, sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data));  
166 161
167 - Map<String, Long> subState = new HashMap<>(keys.size());  
168 - keys.forEach(key -> subState.put(key, startTs));  
169 - data.forEach(v -> subState.put(v.getKey(), v.getTs()));  
170 - SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), deviceId, SubscriptionType.TIMESERIES, false, subState);  
171 - subscriptionManager.addLocalWsSubscription(ctx, sessionId, deviceId, sub); 162 + List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, cmd.getLimit(), Aggregation.valueOf(cmd.getAgg()))).collect(Collectors.toList());
  163 + ctx.loadTimeseries(deviceId, queries, getSubscriptionCallback(sessionRef, cmd, sessionId, deviceId, startTs, keys));
172 } else { 164 } else {
173 List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); 165 List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet()));
174 startTs = System.currentTimeMillis(); 166 startTs = System.currentTimeMillis();
175 log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), cmd.getDeviceId()); 167 log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), cmd.getDeviceId());
176 - ctx.loadLatestTimeseries(deviceId, keys, new PluginCallback<List<TsKvEntry>>() {  
177 - @Override  
178 - public void onSuccess(PluginContext ctx, List<TsKvEntry> data) {  
179 - sendWsMsg(ctx, sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data));  
180 -  
181 - Map<String, Long> subState = new HashMap<>(keys.size());  
182 - keys.forEach(key -> subState.put(key, startTs));  
183 - data.forEach(v -> subState.put(v.getKey(), v.getTs()));  
184 - SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), deviceId, SubscriptionType.TIMESERIES, false, subState);  
185 - subscriptionManager.addLocalWsSubscription(ctx, sessionId, deviceId, sub);  
186 - }  
187 -  
188 - @Override  
189 - public void onFailure(PluginContext ctx, Exception e) {  
190 - SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR,  
191 - "Failed to fetch data!");  
192 - sendWsMsg(ctx, sessionRef, update);  
193 - }  
194 - }); 168 + ctx.loadLatestTimeseries(deviceId, keys, getSubscriptionCallback(sessionRef, cmd, sessionId, deviceId, startTs, keys));
195 } 169 }
196 } else { 170 } else {
197 ctx.loadLatestTimeseries(deviceId, new PluginCallback<List<TsKvEntry>>() { 171 ctx.loadLatestTimeseries(deviceId, new PluginCallback<List<TsKvEntry>>() {
@@ -216,6 +190,28 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler { @@ -216,6 +190,28 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler {
216 } 190 }
217 } 191 }
218 192
  193 + private PluginCallback<List<TsKvEntry>> getSubscriptionCallback(final PluginWebsocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final DeviceId deviceId, final long startTs, final List<String> keys) {
  194 + return new PluginCallback<List<TsKvEntry>>() {
  195 + @Override
  196 + public void onSuccess(PluginContext ctx, List<TsKvEntry> data) {
  197 + sendWsMsg(ctx, sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data));
  198 +
  199 + Map<String, Long> subState = new HashMap<>(keys.size());
  200 + keys.forEach(key -> subState.put(key, startTs));
  201 + data.forEach(v -> subState.put(v.getKey(), v.getTs()));
  202 + SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), deviceId, SubscriptionType.TIMESERIES, false, subState);
  203 + subscriptionManager.addLocalWsSubscription(ctx, sessionId, deviceId, sub);
  204 + }
  205 +
  206 + @Override
  207 + public void onFailure(PluginContext ctx, Exception e) {
  208 + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR,
  209 + "Failed to fetch data!");
  210 + sendWsMsg(ctx, sessionRef, update);
  211 + }
  212 + };
  213 + }
  214 +
219 private void handleWsHistoryCmd(PluginContext ctx, PluginWebsocketSessionRef sessionRef, GetHistoryCmd cmd) { 215 private void handleWsHistoryCmd(PluginContext ctx, PluginWebsocketSessionRef sessionRef, GetHistoryCmd cmd) {
220 String sessionId = sessionRef.getSessionId(); 216 String sessionId = sessionRef.getSessionId();
221 WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); 217 WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId);
@@ -246,12 +242,19 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler { @@ -246,12 +242,19 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler {
246 return; 242 return;
247 } 243 }
248 List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); 244 List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet()));
249 - List<TsKvEntry> data = new ArrayList<>();  
250 - for (String key : keys) {  
251 - TsKvQuery query = new BaseTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs());  
252 - data.addAll(ctx.loadTimeseries(deviceId, query));  
253 - }  
254 - sendWsMsg(ctx, sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data)); 245 + List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getLimit(), Aggregation.valueOf(cmd.getAgg()))).collect(Collectors.toList());
  246 + ctx.loadTimeseries(deviceId, queries, new PluginCallback<List<TsKvEntry>>() {
  247 + @Override
  248 + public void onSuccess(PluginContext ctx, List<TsKvEntry> data) {
  249 + sendWsMsg(ctx, sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data));
  250 + }
  251 +
  252 + @Override
  253 + public void onFailure(PluginContext ctx, Exception e) {
  254 + sendWsMsg(ctx, sessionRef, new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR,
  255 + "Failed to fetch data!"));
  256 + }
  257 + });
255 } 258 }
256 259
257 private boolean validateSessionMetadata(PluginContext ctx, PluginWebsocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { 260 private boolean validateSessionMetadata(PluginContext ctx, PluginWebsocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) {