Commit 3088e6be6f58d64a45c319f5a2b07019703a58d1

Authored by Andrii Shvaika
2 parents f32aed7d ac866faa

Merge branch 'feature/usage-records'

Showing 19 changed files with 239 additions and 136 deletions
... ... @@ -23,16 +23,18 @@ import org.springframework.beans.factory.annotation.Value;
23 23 import org.springframework.context.annotation.Lazy;
24 24 import org.springframework.data.util.Pair;
25 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.data.ApiFeature;
26 27 import org.thingsboard.server.common.data.ApiUsageRecordKey;
27 28 import org.thingsboard.server.common.data.ApiUsageState;
  29 +import org.thingsboard.server.common.data.ApiUsageStateValue;
28 30 import org.thingsboard.server.common.data.Tenant;
29 31 import org.thingsboard.server.common.data.TenantProfile;
30 32 import org.thingsboard.server.common.data.id.ApiUsageStateId;
31 33 import org.thingsboard.server.common.data.id.TenantId;
32 34 import org.thingsboard.server.common.data.id.TenantProfileId;
33 35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
34   -import org.thingsboard.server.common.data.kv.BooleanDataEntry;
35 36 import org.thingsboard.server.common.data.kv.LongDataEntry;
  37 +import org.thingsboard.server.common.data.kv.StringDataEntry;
36 38 import org.thingsboard.server.common.data.kv.TsKvEntry;
37 39 import org.thingsboard.server.common.data.page.PageDataIterable;
38 40 import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
... ... @@ -57,8 +59,10 @@ import org.thingsboard.server.service.telemetry.InternalTelemetryService;
57 59 import javax.annotation.PostConstruct;
58 60 import java.util.ArrayList;
59 61 import java.util.HashMap;
  62 +import java.util.HashSet;
60 63 import java.util.List;
61 64 import java.util.Map;
  65 +import java.util.Set;
62 66 import java.util.UUID;
63 67 import java.util.concurrent.ConcurrentHashMap;
64 68 import java.util.concurrent.ExecutionException;
... ... @@ -137,7 +141,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
137 141 TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB()));
138 142 TenantApiUsageState tenantState;
139 143 List<TsKvEntry> updatedEntries;
140   - Map<ApiFeature, Boolean> result = new HashMap<>();
  144 + Map<ApiFeature, ApiUsageStateValue> result;
141 145 updateLock.lock();
142 146 try {
143 147 tenantState = getOrFetchState(tenantId);
... ... @@ -148,17 +152,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
148 152 tenantState.setHour(newHourTs);
149 153 }
150 154 updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length);
  155 + Set<ApiFeature> apiFeatures = new HashSet<>();
151 156 for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) {
152 157 ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey());
153 158 long newValue = tenantState.add(recordKey, kvProto.getValue());
154 159 updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue)));
155 160 long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue());
156 161 updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue)));
157   - Pair<ApiFeature, Boolean> update = tenantState.checkStateUpdatedDueToThreshold(recordKey);
158   - if (update != null) {
159   - result.put(update.getFirst(), update.getSecond());
160   - }
  162 + apiFeatures.add(recordKey.getApiFeature());
161 163 }
  164 + result = tenantState.checkStateUpdatedDueToThreshold(apiFeatures);
162 165 } finally {
163 166 updateLock.unlock();
164 167 }
... ... @@ -196,7 +199,9 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
196 199 state = otherTenantStates.get(tenantId);
197 200 if (state == null) {
198 201 state = apiUsageStateService.findTenantApiUsageState(tenantId);
199   - otherTenantStates.put(tenantId, state);
  202 + if (state != null) {
  203 + otherTenantStates.put(tenantId, state);
  204 + }
200 205 }
201 206 } finally {
202 207 updateLock.unlock();
... ... @@ -245,7 +250,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
245 250 TenantProfileData oldProfileData = state.getTenantProfileData();
246 251 state.setTenantProfileId(tenantProfile.getId());
247 252 state.setTenantProfileData(tenantProfile.getProfileData());
248   - Map<ApiFeature, Boolean> result = state.checkStateUpdatedDueToThresholds();
  253 + Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds();
249 254 if (!result.isEmpty()) {
250 255 persistAndNotify(state, result);
251 256 }
... ... @@ -269,14 +274,15 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
269 274 }
270 275 }
271 276
272   - private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, Boolean> result) {
  277 + private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) {
273 278 log.info("[{}] Detected update of the API state: {}", state.getTenantId(), result);
274 279 apiUsageStateService.update(state.getApiUsageState());
275 280 clusterService.onApiStateChange(state.getApiUsageState(), null);
276 281 long ts = System.currentTimeMillis();
277 282 List<TsKvEntry> stateTelemetry = new ArrayList<>();
278   - result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new BooleanDataEntry(apiFeature.getApiStateKey(), aState)))));
  283 + result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name())))));
279 284 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK);
  285 + //TODO: notify tenant admin via email!
280 286 }
281 287
282 288 private void checkStartOfNextCycle() {
... ...
... ... @@ -18,17 +18,21 @@ package org.thingsboard.server.service.apiusage;
18 18 import lombok.Getter;
19 19 import lombok.Setter;
20 20 import org.springframework.data.util.Pair;
  21 +import org.thingsboard.server.common.data.ApiFeature;
21 22 import org.thingsboard.server.common.data.ApiUsageRecordKey;
22 23 import org.thingsboard.server.common.data.ApiUsageState;
  24 +import org.thingsboard.server.common.data.ApiUsageStateValue;
23 25 import org.thingsboard.server.common.data.TenantProfile;
24 26 import org.thingsboard.server.common.data.id.TenantId;
25 27 import org.thingsboard.server.common.data.id.TenantProfileId;
26   -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
27 28 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
28 29 import org.thingsboard.server.common.msg.tools.SchedulerUtils;
29 30
  31 +import java.util.Arrays;
30 32 import java.util.HashMap;
  33 +import java.util.HashSet;
31 34 import java.util.Map;
  35 +import java.util.Set;
32 36 import java.util.concurrent.ConcurrentHashMap;
33 37
34 38 public class TenantApiUsageState {
... ... @@ -103,99 +107,80 @@ public class TenantApiUsageState {
103 107 return tenantProfileData.getConfiguration().getProfileThreshold(key);
104 108 }
105 109
106   - public TenantId getTenantId() {
107   - return apiUsageState.getTenantId();
108   - }
109   -
110   - public boolean isTransportEnabled() {
111   - return apiUsageState.isTransportEnabled();
112   - }
113   -
114   - public boolean isDbStorageEnabled() {
115   - return apiUsageState.isDbStorageEnabled();
116   - }
117   -
118   - public boolean isRuleEngineEnabled() {
119   - return apiUsageState.isReExecEnabled();
120   - }
121   -
122   - public boolean isJsExecEnabled() {
123   - return apiUsageState.isJsExecEnabled();
124   - }
125   -
126   - public void setTransportEnabled(boolean transportEnabled) {
127   - apiUsageState.setTransportEnabled(transportEnabled);
  110 + public long getProfileWarnThreshold(ApiUsageRecordKey key) {
  111 + return tenantProfileData.getConfiguration().getWarnThreshold(key);
128 112 }
129 113
130   - public void setDbStorageEnabled(boolean dbStorageEnabled) {
131   - apiUsageState.setDbStorageEnabled(dbStorageEnabled);
132   - }
133   -
134   - public void setRuleEngineEnabled(boolean ruleEngineEnabled) {
135   - apiUsageState.setReExecEnabled(ruleEngineEnabled);
136   - }
137   -
138   - public void setJsExecEnabled(boolean jsExecEnabled) {
139   - apiUsageState.setJsExecEnabled(jsExecEnabled);
  114 + public TenantId getTenantId() {
  115 + return apiUsageState.getTenantId();
140 116 }
141 117
142   - public boolean isFeatureEnabled(ApiUsageRecordKey recordKey) {
143   - switch (recordKey) {
144   - case TRANSPORT_MSG_COUNT:
145   - case TRANSPORT_DP_COUNT:
146   - return isTransportEnabled();
147   - case RE_EXEC_COUNT:
148   - return isRuleEngineEnabled();
149   - case STORAGE_DP_COUNT:
150   - return isDbStorageEnabled();
151   - case JS_EXEC_COUNT:
152   - return isJsExecEnabled();
  118 + public ApiUsageStateValue getFeatureValue(ApiFeature feature) {
  119 + switch (feature) {
  120 + case TRANSPORT:
  121 + return apiUsageState.getTransportState();
  122 + case RE:
  123 + return apiUsageState.getReExecState();
  124 + case DB:
  125 + return apiUsageState.getDbStorageState();
  126 + case JS:
  127 + return apiUsageState.getJsExecState();
153 128 default:
154   - return true;
  129 + return ApiUsageStateValue.ENABLED;
155 130 }
156 131 }
157 132
158   - public ApiFeature setFeatureValue(ApiUsageRecordKey recordKey, boolean value) {
159   - ApiFeature feature = null;
160   - boolean currentValue = isFeatureEnabled(recordKey);
161   - switch (recordKey) {
162   - case TRANSPORT_MSG_COUNT:
163   - case TRANSPORT_DP_COUNT:
164   - feature = ApiFeature.TRANSPORT;
165   - setTransportEnabled(value);
  133 + public boolean setFeatureValue(ApiFeature feature, ApiUsageStateValue value) {
  134 + ApiUsageStateValue currentValue = getFeatureValue(feature);
  135 + switch (feature) {
  136 + case TRANSPORT:
  137 + apiUsageState.setTransportState(value);
166 138 break;
167   - case RE_EXEC_COUNT:
168   - feature = ApiFeature.RE;
169   - setRuleEngineEnabled(value);
  139 + case RE:
  140 + apiUsageState.setReExecState(value);
170 141 break;
171   - case STORAGE_DP_COUNT:
172   - feature = ApiFeature.DB;
173   - setDbStorageEnabled(value);
  142 + case DB:
  143 + apiUsageState.setDbStorageState(value);
174 144 break;
175   - case JS_EXEC_COUNT:
176   - feature = ApiFeature.JS;
177   - setJsExecEnabled(value);
  145 + case JS:
  146 + apiUsageState.setJsExecState(value);
178 147 break;
179 148 }
180   - return currentValue == value ? null : feature;
  149 + return !currentValue.equals(value);
181 150 }
182 151
183   - public Map<ApiFeature, Boolean> checkStateUpdatedDueToThresholds() {
184   - Map<ApiFeature, Boolean> result = new HashMap<>();
185   - for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
186   - Pair<ApiFeature, Boolean> featureUpdate = checkStateUpdatedDueToThreshold(key);
187   - if (featureUpdate != null) {
188   - result.put(featureUpdate.getFirst(), featureUpdate.getSecond());
  152 + public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThresholds() {
  153 + return checkStateUpdatedDueToThreshold(new HashSet<>(Arrays.asList(ApiFeature.values())));
  154 + }
  155 +
  156 + public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(Set<ApiFeature> features) {
  157 + Map<ApiFeature, ApiUsageStateValue> result = new HashMap<>();
  158 + for (ApiFeature feature : features) {
  159 + Pair<ApiFeature, ApiUsageStateValue> tmp = checkStateUpdatedDueToThreshold(feature);
  160 + if (tmp != null) {
  161 + result.put(tmp.getFirst(), tmp.getSecond());
189 162 }
190 163 }
191 164 return result;
192 165 }
193 166
194   - public Pair<ApiFeature, Boolean> checkStateUpdatedDueToThreshold(ApiUsageRecordKey recordKey) {
195   - long value = get(recordKey);
196   - long threshold = getProfileThreshold(recordKey);
197   - boolean featureValue = threshold == 0 || value < threshold;
198   - ApiFeature feature = setFeatureValue(recordKey, featureValue);
199   - return feature != null ? Pair.of(feature, featureValue) : null;
  167 + public Pair<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(ApiFeature feature) {
  168 + ApiUsageStateValue featureValue = ApiUsageStateValue.ENABLED;
  169 + for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.getKeys(feature)) {
  170 + long value = get(recordKey);
  171 + long threshold = getProfileThreshold(recordKey);
  172 + long warnThreshold = getProfileWarnThreshold(recordKey);
  173 + ApiUsageStateValue tmpValue;
  174 + if (threshold == 0 || value < warnThreshold) {
  175 + tmpValue = ApiUsageStateValue.ENABLED;
  176 + } else if (value < threshold) {
  177 + tmpValue = ApiUsageStateValue.WARNING;
  178 + } else {
  179 + tmpValue = ApiUsageStateValue.DISABLED;
  180 + }
  181 + featureValue = ApiUsageStateValue.toMoreRestricted(featureValue, tmpValue);
  182 + }
  183 + return setFeatureValue(feature, featureValue) ? Pair.of(feature, featureValue) : null;
200 184 }
  185 +
201 186 }
... ...
... ... @@ -28,6 +28,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
28 28 import org.thingsboard.server.dao.device.DeviceProfileService;
29 29 import org.thingsboard.server.dao.device.DeviceService;
30 30 import org.thingsboard.server.dao.tenant.TenantService;
  31 +import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
31 32 import org.thingsboard.server.service.install.sql.SqlDbHelper;
32 33
33 34 import java.nio.charset.Charset;
... ... @@ -96,6 +97,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
96 97 @Autowired
97 98 private DeviceProfileService deviceProfileService;
98 99
  100 + @Autowired
  101 + private ApiUsageStateService apiUsageStateService;
  102 +
99 103
100 104 @Override
101 105 public void upgradeDatabase(String fromVersion) throws Exception {
... ... @@ -352,6 +356,22 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
352 356 } catch (Exception e) {
353 357 }
354 358
  359 + try {
  360 + conn.createStatement().execute("CREATE TABLE IF NOT EXISTS api_usage_state (" +
  361 + " id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY," +
  362 + " created_time bigint NOT NULL," +
  363 + " tenant_id uuid," +
  364 + " entity_type varchar(32)," +
  365 + " entity_id uuid," +
  366 + " transport varchar(32)," +
  367 + " db_storage varchar(32)," +
  368 + " re_exec varchar(32)," +
  369 + " js_exec varchar(32)," +
  370 + " CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)\n" +
  371 + ");");
  372 + } catch (Exception e) {
  373 + }
  374 +
355 375 schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql");
356 376 loadSql(schemaUpdateFile, conn);
357 377
... ... @@ -367,6 +387,10 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
367 387 do {
368 388 pageData = tenantService.findTenants(pageLink);
369 389 for (Tenant tenant : pageData.getData()) {
  390 + try {
  391 + apiUsageStateService.createDefaultApiUsageState(tenant.getId());
  392 + } catch (Exception e) {
  393 + }
370 394 List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get();
371 395 try {
372 396 deviceProfileService.createDefaultDeviceProfile(tenant.getId());
... ...
common/data/src/main/java/org/thingsboard/server/common/data/ApiFeature.java renamed from application/src/main/java/org/thingsboard/server/service/apiusage/ApiFeature.java
... ... @@ -13,12 +13,15 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.service.apiusage;
  16 +package org.thingsboard.server.common.data;
17 17
18 18 import lombok.Getter;
19 19
20 20 public enum ApiFeature {
21   - TRANSPORT("transportApiState"), DB("dbApiState"), RE("ruleEngineApiState"), JS("jsExecutionApiState");
  21 + TRANSPORT("transportApiState"),
  22 + DB("dbApiState"),
  23 + RE("ruleEngineApiState"),
  24 + JS("jsExecutionApiState");
22 25
23 26 @Getter
24 27 private final String apiStateKey;
... ... @@ -26,4 +29,5 @@ public enum ApiFeature {
26 29 ApiFeature(String apiStateKey) {
27 30 this.apiStateKey = apiStateKey;
28 31 }
  32 +
29 33 }
... ...
... ... @@ -19,20 +19,42 @@ import lombok.Getter;
19 19
20 20 public enum ApiUsageRecordKey {
21 21
22   - TRANSPORT_MSG_COUNT("transportMsgCount", "transportMsgLimit"),
23   - TRANSPORT_DP_COUNT("transportDataPointsCount", "transportDataPointsLimit"),
24   - STORAGE_DP_COUNT("storageDataPointsCount", "storageDataPointsLimit"),
25   - RE_EXEC_COUNT("ruleEngineExecutionCount", "ruleEngineExecutionLimit"),
26   - JS_EXEC_COUNT("jsExecutionCount", "jsExecutionLimit");
  22 + TRANSPORT_MSG_COUNT(ApiFeature.TRANSPORT, "transportMsgCount", "transportMsgLimit"),
  23 + TRANSPORT_DP_COUNT(ApiFeature.TRANSPORT, "transportDataPointsCount", "transportDataPointsLimit"),
  24 + STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit"),
  25 + RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit"),
  26 + JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit");
  27 + private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT};
  28 + private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT};
  29 + private static final ApiUsageRecordKey[] DB_RECORD_KEYS = {STORAGE_DP_COUNT};
  30 + private static final ApiUsageRecordKey[] TRANSPORT_RECORD_KEYS = {TRANSPORT_MSG_COUNT, TRANSPORT_DP_COUNT};
27 31
28 32 @Getter
  33 + private final ApiFeature apiFeature;
  34 + @Getter
29 35 private final String apiCountKey;
30 36 @Getter
31 37 private final String apiLimitKey;
32 38
33   - ApiUsageRecordKey(String apiCountKey, String apiLimitKey) {
  39 + ApiUsageRecordKey(ApiFeature apiFeature, String apiCountKey, String apiLimitKey) {
  40 + this.apiFeature = apiFeature;
34 41 this.apiCountKey = apiCountKey;
35 42 this.apiLimitKey = apiLimitKey;
36 43 }
37 44
  45 + public static ApiUsageRecordKey[] getKeys(ApiFeature feature) {
  46 + switch (feature) {
  47 + case TRANSPORT:
  48 + return TRANSPORT_RECORD_KEYS;
  49 + case DB:
  50 + return DB_RECORD_KEYS;
  51 + case RE:
  52 + return RE_RECORD_KEYS;
  53 + case JS:
  54 + return JS_RECORD_KEYS;
  55 + default:
  56 + return new ApiUsageRecordKey[]{};
  57 + }
  58 + }
  59 +
38 60 }
... ...
... ... @@ -37,16 +37,16 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
37 37 private EntityId entityId;
38 38 @Getter
39 39 @Setter
40   - private boolean transportEnabled = true;
  40 + private ApiUsageStateValue transportState;
41 41 @Getter
42 42 @Setter
43   - private boolean dbStorageEnabled = true;
  43 + private ApiUsageStateValue dbStorageState;
44 44 @Getter
45 45 @Setter
46   - private boolean reExecEnabled = true;
  46 + private ApiUsageStateValue reExecState;
47 47 @Getter
48 48 @Setter
49   - private boolean jsExecEnabled = true;
  49 + private ApiUsageStateValue jsExecState;
50 50
51 51 public ApiUsageState() {
52 52 super();
... ... @@ -60,9 +60,25 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
60 60 super(ur);
61 61 this.tenantId = ur.getTenantId();
62 62 this.entityId = ur.getEntityId();
63   - this.transportEnabled = ur.isTransportEnabled();
64   - this.dbStorageEnabled = ur.isDbStorageEnabled();
65   - this.reExecEnabled = ur.isReExecEnabled();
66   - this.jsExecEnabled = ur.isJsExecEnabled();
  63 + this.transportState = ur.getTransportState();
  64 + this.dbStorageState = ur.getDbStorageState();
  65 + this.reExecState = ur.getReExecState();
  66 + this.jsExecState = ur.getJsExecState();
  67 + }
  68 +
  69 + public boolean isTransportEnabled() {
  70 + return !ApiUsageStateValue.DISABLED.equals(transportState);
  71 + }
  72 +
  73 + public boolean isReExecEnabled() {
  74 + return !ApiUsageStateValue.DISABLED.equals(reExecState);
  75 + }
  76 +
  77 + public boolean isDbStorageEnabled() {
  78 + return !ApiUsageStateValue.DISABLED.equals(dbStorageState);
  79 + }
  80 +
  81 + public boolean isJsExecEnabled() {
  82 + return !ApiUsageStateValue.DISABLED.equals(jsExecState);
67 83 }
68 84 }
... ...
  1 +/**
  2 + * Copyright © 2016-2020 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.common.data;
  17 +
  18 +public enum ApiUsageStateValue {
  19 +
  20 + ENABLED, WARNING, DISABLED;
  21 +
  22 +
  23 + public static ApiUsageStateValue toMoreRestricted(ApiUsageStateValue a, ApiUsageStateValue b) {
  24 + return a.ordinal() > b.ordinal() ? a : b;
  25 + }
  26 +}
... ...
... ... @@ -39,6 +39,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
39 39 private long maxDPStorageDays;
40 40 private int maxRuleNodeExecutionsPerMessage;
41 41
  42 + private double warnThreshold;
  43 +
42 44 @Override
43 45 public long getProfileThreshold(ApiUsageRecordKey key) {
44 46 switch (key) {
... ... @@ -56,6 +58,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
56 58 return 0L;
57 59 }
58 60
  61 + @Override
  62 + public long getWarnThreshold(ApiUsageRecordKey key) {
  63 + return (long) (getProfileThreshold(key) * (warnThreshold > 0.0 ? warnThreshold : 0.8));
  64 + }
59 65
60 66 @Override
61 67 public TenantProfileType getType() {
... ...
... ... @@ -38,6 +38,9 @@ public interface TenantProfileConfiguration {
38 38 long getProfileThreshold(ApiUsageRecordKey key);
39 39
40 40 @JsonIgnore
  41 + long getWarnThreshold(ApiUsageRecordKey key);
  42 +
  43 + @JsonIgnore
41 44 int getMaxRuleNodeExecsPerMessage();
42 45
43 46 }
... ...
... ... @@ -26,7 +26,7 @@ public class TbQueueCoreSettings {
26 26 @Value("${queue.core.topic}")
27 27 private String topic;
28 28
29   - @Value("${queue.core.usage-stats-topic}")
  29 + @Value("${queue.core.usage-stats-topic:tb_usage_stats}")
30 30 private String usageStatsTopic;
31 31
32 32 @Value("${queue.core.partitions}")
... ...
... ... @@ -446,10 +446,10 @@ public class ModelConstants {
446 446 public static final String API_USAGE_STATE_TENANT_ID_COLUMN = TENANT_ID_PROPERTY;
447 447 public static final String API_USAGE_STATE_ENTITY_TYPE_COLUMN = ENTITY_TYPE_COLUMN;
448 448 public static final String API_USAGE_STATE_ENTITY_ID_COLUMN = ENTITY_ID_COLUMN;
449   - public static final String API_USAGE_STATE_TRANSPORT_ENABLED_COLUMN = "transport_enabled";
450   - public static final String API_USAGE_STATE_DB_STORAGE_ENABLED_COLUMN = "db_storage_enabled";
451   - public static final String API_USAGE_STATE_RE_EXEC_ENABLED_COLUMN = "re_exec_enabled";
452   - public static final String API_USAGE_STATE_JS_EXEC_ENABLED_COLUMN = "js_exec_enabled";
  449 + public static final String API_USAGE_STATE_TRANSPORT_COLUMN = "transport";
  450 + public static final String API_USAGE_STATE_DB_STORAGE_COLUMN = "db_storage";
  451 + public static final String API_USAGE_STATE_RE_EXEC_COLUMN = "re_exec";
  452 + public static final String API_USAGE_STATE_JS_EXEC_COLUMN = "js_exec";
453 453
454 454 /**
455 455 * Cassandra attributes and timeseries constants.
... ...
... ... @@ -17,10 +17,9 @@ package org.thingsboard.server.dao.model.sql;
17 17
18 18 import lombok.Data;
19 19 import lombok.EqualsAndHashCode;
20   -import lombok.Getter;
21   -import lombok.Setter;
22 20 import org.hibernate.annotations.TypeDef;
23 21 import org.thingsboard.server.common.data.ApiUsageState;
  22 +import org.thingsboard.server.common.data.ApiUsageStateValue;
24 23 import org.thingsboard.server.common.data.id.EntityIdFactory;
25 24 import org.thingsboard.server.common.data.id.TenantId;
26 25 import org.thingsboard.server.common.data.id.ApiUsageStateId;
... ... @@ -31,6 +30,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
31 30
32 31 import javax.persistence.Column;
33 32 import javax.persistence.Entity;
  33 +import javax.persistence.EnumType;
  34 +import javax.persistence.Enumerated;
34 35 import javax.persistence.Table;
35 36 import java.util.UUID;
36 37
... ... @@ -46,21 +47,22 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
46 47
47 48 @Column(name = ModelConstants.API_USAGE_STATE_TENANT_ID_COLUMN)
48 49 private UUID tenantId;
49   -
50 50 @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_TYPE_COLUMN)
51 51 private String entityType;
52   -
53 52 @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_ID_COLUMN)
54 53 private UUID entityId;
55   -
56   - @Column(name = ModelConstants.API_USAGE_STATE_TRANSPORT_ENABLED_COLUMN)
57   - private boolean transportEnabled = true;
58   - @Column(name = ModelConstants.API_USAGE_STATE_DB_STORAGE_ENABLED_COLUMN)
59   - private boolean dbStorageEnabled = true;
60   - @Column(name = ModelConstants.API_USAGE_STATE_RE_EXEC_ENABLED_COLUMN)
61   - private boolean reExecEnabled = true;
62   - @Column(name = ModelConstants.API_USAGE_STATE_JS_EXEC_ENABLED_COLUMN)
63   - private boolean jsExecEnabled = true;
  54 + @Enumerated(EnumType.STRING)
  55 + @Column(name = ModelConstants.API_USAGE_STATE_TRANSPORT_COLUMN)
  56 + private ApiUsageStateValue transportState = ApiUsageStateValue.ENABLED;
  57 + @Enumerated(EnumType.STRING)
  58 + @Column(name = ModelConstants.API_USAGE_STATE_DB_STORAGE_COLUMN)
  59 + private ApiUsageStateValue dbStorageState = ApiUsageStateValue.ENABLED;
  60 + @Enumerated(EnumType.STRING)
  61 + @Column(name = ModelConstants.API_USAGE_STATE_RE_EXEC_COLUMN)
  62 + private ApiUsageStateValue reExecState = ApiUsageStateValue.ENABLED;
  63 + @Enumerated(EnumType.STRING)
  64 + @Column(name = ModelConstants.API_USAGE_STATE_JS_EXEC_COLUMN)
  65 + private ApiUsageStateValue jsExecState = ApiUsageStateValue.ENABLED;
64 66
65 67 public ApiUsageStateEntity() {
66 68 }
... ... @@ -77,10 +79,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
77 79 this.entityType = ur.getEntityId().getEntityType().name();
78 80 this.entityId = ur.getEntityId().getId();
79 81 }
80   - this.transportEnabled = ur.isTransportEnabled();
81   - this.dbStorageEnabled = ur.isDbStorageEnabled();
82   - this.reExecEnabled = ur.isReExecEnabled();
83   - this.jsExecEnabled = ur.isJsExecEnabled();
  82 + this.transportState = ur.getTransportState();
  83 + this.dbStorageState = ur.getDbStorageState();
  84 + this.reExecState = ur.getReExecState();
  85 + this.jsExecState = ur.getJsExecState();
84 86 }
85 87
86 88 @Override
... ... @@ -93,10 +95,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
93 95 if (entityId != null) {
94 96 ur.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId));
95 97 }
96   - ur.setTransportEnabled(transportEnabled);
97   - ur.setDbStorageEnabled(dbStorageEnabled);
98   - ur.setReExecEnabled(reExecEnabled);
99   - ur.setJsExecEnabled(jsExecEnabled);
  98 + ur.setTransportState(transportState);
  99 + ur.setDbStorageState(dbStorageState);
  100 + ur.setReExecState(reExecState);
  101 + ur.setJsExecState(jsExecState);
100 102 return ur;
101 103 }
102 104
... ...
... ... @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.stereotype.Service;
20 20 import org.thingsboard.server.common.data.ApiUsageRecordKey;
21 21 import org.thingsboard.server.common.data.ApiUsageState;
  22 +import org.thingsboard.server.common.data.ApiUsageStateValue;
22 23 import org.thingsboard.server.common.data.EntityType;
23 24 import org.thingsboard.server.common.data.Tenant;
24 25 import org.thingsboard.server.common.data.TenantProfile;
... ... @@ -71,6 +72,10 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
71 72 ApiUsageState apiUsageState = new ApiUsageState();
72 73 apiUsageState.setTenantId(tenantId);
73 74 apiUsageState.setEntityId(tenantId);
  75 + apiUsageState.setTransportState(ApiUsageStateValue.ENABLED);
  76 + apiUsageState.setReExecState(ApiUsageStateValue.ENABLED);
  77 + apiUsageState.setJsExecState(ApiUsageStateValue.ENABLED);
  78 + apiUsageState.setDbStorageState(ApiUsageStateValue.ENABLED);
74 79 apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId);
75 80
76 81 ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
... ...
... ... @@ -412,9 +412,9 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
412 412 tenant_id uuid,
413 413 entity_type varchar(32),
414 414 entity_id uuid,
415   - transport_enabled boolean,
416   - db_storage_enabled boolean,
417   - re_exec_enabled boolean,
418   - js_exec_enabled boolean,
  415 + transport varchar(32),
  416 + db_storage varchar(32),
  417 + re_exec varchar(32),
  418 + js_exec varchar(32),
419 419 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
420 420 );
... ...
... ... @@ -438,10 +438,10 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
438 438 tenant_id uuid,
439 439 entity_type varchar(32),
440 440 entity_id uuid,
441   - transport_enabled boolean,
442   - db_storage_enabled boolean,
443   - re_exec_enabled boolean,
444   - js_exec_enabled boolean,
  441 + transport varchar(32),
  442 + db_storage varchar(32),
  443 + re_exec varchar(32),
  444 + js_exec varchar(32),
445 445 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
446 446 );
447 447
... ...
... ... @@ -19,6 +19,7 @@ import org.junit.After;
19 19 import org.junit.Assert;
20 20 import org.junit.Before;
21 21 import org.junit.Test;
  22 +import org.thingsboard.server.common.data.ApiUsageStateValue;
22 23 import org.thingsboard.server.common.data.Tenant;
23 24 import org.thingsboard.server.common.data.ApiUsageState;
24 25 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -53,7 +54,7 @@ public abstract class BaseApiUsageStateServiceTest extends AbstractServiceTest {
53 54 ApiUsageState apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);
54 55 Assert.assertNotNull(apiUsageState);
55 56 Assert.assertTrue(apiUsageState.isTransportEnabled());
56   - apiUsageState.setTransportEnabled(false);
  57 + apiUsageState.setTransportState(ApiUsageStateValue.DISABLED);
57 58 apiUsageState = apiUsageStateService.update(apiUsageState);
58 59 Assert.assertNotNull(apiUsageState);
59 60 apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);
... ...
... ... @@ -145,6 +145,7 @@ queue:
145 145 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
146 146 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
147 147 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  148 + usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
148 149 stats:
149 150 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
150 151 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"
... ...
... ... @@ -138,6 +138,7 @@ queue:
138 138 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
139 139 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
140 140 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  141 + usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
141 142 stats:
142 143 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
143 144 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"
... ...
... ... @@ -167,6 +167,7 @@ queue:
167 167 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
168 168 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
169 169 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  170 + usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
170 171 stats:
171 172 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
172 173 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"
... ...