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,16 +23,18 @@ import org.springframework.beans.factory.annotation.Value;
23 import org.springframework.context.annotation.Lazy; 23 import org.springframework.context.annotation.Lazy;
24 import org.springframework.data.util.Pair; 24 import org.springframework.data.util.Pair;
25 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.data.ApiFeature;
26 import org.thingsboard.server.common.data.ApiUsageRecordKey; 27 import org.thingsboard.server.common.data.ApiUsageRecordKey;
27 import org.thingsboard.server.common.data.ApiUsageState; 28 import org.thingsboard.server.common.data.ApiUsageState;
  29 +import org.thingsboard.server.common.data.ApiUsageStateValue;
28 import org.thingsboard.server.common.data.Tenant; 30 import org.thingsboard.server.common.data.Tenant;
29 import org.thingsboard.server.common.data.TenantProfile; 31 import org.thingsboard.server.common.data.TenantProfile;
30 import org.thingsboard.server.common.data.id.ApiUsageStateId; 32 import org.thingsboard.server.common.data.id.ApiUsageStateId;
31 import org.thingsboard.server.common.data.id.TenantId; 33 import org.thingsboard.server.common.data.id.TenantId;
32 import org.thingsboard.server.common.data.id.TenantProfileId; 34 import org.thingsboard.server.common.data.id.TenantProfileId;
33 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
34 -import org.thingsboard.server.common.data.kv.BooleanDataEntry;  
35 import org.thingsboard.server.common.data.kv.LongDataEntry; 36 import org.thingsboard.server.common.data.kv.LongDataEntry;
  37 +import org.thingsboard.server.common.data.kv.StringDataEntry;
36 import org.thingsboard.server.common.data.kv.TsKvEntry; 38 import org.thingsboard.server.common.data.kv.TsKvEntry;
37 import org.thingsboard.server.common.data.page.PageDataIterable; 39 import org.thingsboard.server.common.data.page.PageDataIterable;
38 import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; 40 import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
@@ -57,8 +59,10 @@ import org.thingsboard.server.service.telemetry.InternalTelemetryService; @@ -57,8 +59,10 @@ import org.thingsboard.server.service.telemetry.InternalTelemetryService;
57 import javax.annotation.PostConstruct; 59 import javax.annotation.PostConstruct;
58 import java.util.ArrayList; 60 import java.util.ArrayList;
59 import java.util.HashMap; 61 import java.util.HashMap;
  62 +import java.util.HashSet;
60 import java.util.List; 63 import java.util.List;
61 import java.util.Map; 64 import java.util.Map;
  65 +import java.util.Set;
62 import java.util.UUID; 66 import java.util.UUID;
63 import java.util.concurrent.ConcurrentHashMap; 67 import java.util.concurrent.ConcurrentHashMap;
64 import java.util.concurrent.ExecutionException; 68 import java.util.concurrent.ExecutionException;
@@ -137,7 +141,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -137,7 +141,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
137 TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); 141 TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB()));
138 TenantApiUsageState tenantState; 142 TenantApiUsageState tenantState;
139 List<TsKvEntry> updatedEntries; 143 List<TsKvEntry> updatedEntries;
140 - Map<ApiFeature, Boolean> result = new HashMap<>(); 144 + Map<ApiFeature, ApiUsageStateValue> result;
141 updateLock.lock(); 145 updateLock.lock();
142 try { 146 try {
143 tenantState = getOrFetchState(tenantId); 147 tenantState = getOrFetchState(tenantId);
@@ -148,17 +152,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -148,17 +152,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
148 tenantState.setHour(newHourTs); 152 tenantState.setHour(newHourTs);
149 } 153 }
150 updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length); 154 updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length);
  155 + Set<ApiFeature> apiFeatures = new HashSet<>();
151 for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) { 156 for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) {
152 ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey()); 157 ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey());
153 long newValue = tenantState.add(recordKey, kvProto.getValue()); 158 long newValue = tenantState.add(recordKey, kvProto.getValue());
154 updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue))); 159 updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue)));
155 long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); 160 long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue());
156 updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue))); 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 } finally { 165 } finally {
163 updateLock.unlock(); 166 updateLock.unlock();
164 } 167 }
@@ -196,7 +199,9 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -196,7 +199,9 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
196 state = otherTenantStates.get(tenantId); 199 state = otherTenantStates.get(tenantId);
197 if (state == null) { 200 if (state == null) {
198 state = apiUsageStateService.findTenantApiUsageState(tenantId); 201 state = apiUsageStateService.findTenantApiUsageState(tenantId);
199 - otherTenantStates.put(tenantId, state); 202 + if (state != null) {
  203 + otherTenantStates.put(tenantId, state);
  204 + }
200 } 205 }
201 } finally { 206 } finally {
202 updateLock.unlock(); 207 updateLock.unlock();
@@ -245,7 +250,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -245,7 +250,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
245 TenantProfileData oldProfileData = state.getTenantProfileData(); 250 TenantProfileData oldProfileData = state.getTenantProfileData();
246 state.setTenantProfileId(tenantProfile.getId()); 251 state.setTenantProfileId(tenantProfile.getId());
247 state.setTenantProfileData(tenantProfile.getProfileData()); 252 state.setTenantProfileData(tenantProfile.getProfileData());
248 - Map<ApiFeature, Boolean> result = state.checkStateUpdatedDueToThresholds(); 253 + Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds();
249 if (!result.isEmpty()) { 254 if (!result.isEmpty()) {
250 persistAndNotify(state, result); 255 persistAndNotify(state, result);
251 } 256 }
@@ -269,14 +274,15 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -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 log.info("[{}] Detected update of the API state: {}", state.getTenantId(), result); 278 log.info("[{}] Detected update of the API state: {}", state.getTenantId(), result);
274 apiUsageStateService.update(state.getApiUsageState()); 279 apiUsageStateService.update(state.getApiUsageState());
275 clusterService.onApiStateChange(state.getApiUsageState(), null); 280 clusterService.onApiStateChange(state.getApiUsageState(), null);
276 long ts = System.currentTimeMillis(); 281 long ts = System.currentTimeMillis();
277 List<TsKvEntry> stateTelemetry = new ArrayList<>(); 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 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); 284 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK);
  285 + //TODO: notify tenant admin via email!
280 } 286 }
281 287
282 private void checkStartOfNextCycle() { 288 private void checkStartOfNextCycle() {
@@ -18,17 +18,21 @@ package org.thingsboard.server.service.apiusage; @@ -18,17 +18,21 @@ package org.thingsboard.server.service.apiusage;
18 import lombok.Getter; 18 import lombok.Getter;
19 import lombok.Setter; 19 import lombok.Setter;
20 import org.springframework.data.util.Pair; 20 import org.springframework.data.util.Pair;
  21 +import org.thingsboard.server.common.data.ApiFeature;
21 import org.thingsboard.server.common.data.ApiUsageRecordKey; 22 import org.thingsboard.server.common.data.ApiUsageRecordKey;
22 import org.thingsboard.server.common.data.ApiUsageState; 23 import org.thingsboard.server.common.data.ApiUsageState;
  24 +import org.thingsboard.server.common.data.ApiUsageStateValue;
23 import org.thingsboard.server.common.data.TenantProfile; 25 import org.thingsboard.server.common.data.TenantProfile;
24 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.data.id.TenantProfileId; 27 import org.thingsboard.server.common.data.id.TenantProfileId;
26 -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;  
27 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; 28 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
28 import org.thingsboard.server.common.msg.tools.SchedulerUtils; 29 import org.thingsboard.server.common.msg.tools.SchedulerUtils;
29 30
  31 +import java.util.Arrays;
30 import java.util.HashMap; 32 import java.util.HashMap;
  33 +import java.util.HashSet;
31 import java.util.Map; 34 import java.util.Map;
  35 +import java.util.Set;
32 import java.util.concurrent.ConcurrentHashMap; 36 import java.util.concurrent.ConcurrentHashMap;
33 37
34 public class TenantApiUsageState { 38 public class TenantApiUsageState {
@@ -103,99 +107,80 @@ public class TenantApiUsageState { @@ -103,99 +107,80 @@ public class TenantApiUsageState {
103 return tenantProfileData.getConfiguration().getProfileThreshold(key); 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 default: 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 break; 138 break;
167 - case RE_EXEC_COUNT:  
168 - feature = ApiFeature.RE;  
169 - setRuleEngineEnabled(value); 139 + case RE:
  140 + apiUsageState.setReExecState(value);
170 break; 141 break;
171 - case STORAGE_DP_COUNT:  
172 - feature = ApiFeature.DB;  
173 - setDbStorageEnabled(value); 142 + case DB:
  143 + apiUsageState.setDbStorageState(value);
174 break; 144 break;
175 - case JS_EXEC_COUNT:  
176 - feature = ApiFeature.JS;  
177 - setJsExecEnabled(value); 145 + case JS:
  146 + apiUsageState.setJsExecState(value);
178 break; 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 return result; 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,6 +28,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
28 import org.thingsboard.server.dao.device.DeviceProfileService; 28 import org.thingsboard.server.dao.device.DeviceProfileService;
29 import org.thingsboard.server.dao.device.DeviceService; 29 import org.thingsboard.server.dao.device.DeviceService;
30 import org.thingsboard.server.dao.tenant.TenantService; 30 import org.thingsboard.server.dao.tenant.TenantService;
  31 +import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
31 import org.thingsboard.server.service.install.sql.SqlDbHelper; 32 import org.thingsboard.server.service.install.sql.SqlDbHelper;
32 33
33 import java.nio.charset.Charset; 34 import java.nio.charset.Charset;
@@ -96,6 +97,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @@ -96,6 +97,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
96 @Autowired 97 @Autowired
97 private DeviceProfileService deviceProfileService; 98 private DeviceProfileService deviceProfileService;
98 99
  100 + @Autowired
  101 + private ApiUsageStateService apiUsageStateService;
  102 +
99 103
100 @Override 104 @Override
101 public void upgradeDatabase(String fromVersion) throws Exception { 105 public void upgradeDatabase(String fromVersion) throws Exception {
@@ -352,6 +356,22 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @@ -352,6 +356,22 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
352 } catch (Exception e) { 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 schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql"); 375 schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql");
356 loadSql(schemaUpdateFile, conn); 376 loadSql(schemaUpdateFile, conn);
357 377
@@ -367,6 +387,10 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @@ -367,6 +387,10 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
367 do { 387 do {
368 pageData = tenantService.findTenants(pageLink); 388 pageData = tenantService.findTenants(pageLink);
369 for (Tenant tenant : pageData.getData()) { 389 for (Tenant tenant : pageData.getData()) {
  390 + try {
  391 + apiUsageStateService.createDefaultApiUsageState(tenant.getId());
  392 + } catch (Exception e) {
  393 + }
370 List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); 394 List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get();
371 try { 395 try {
372 deviceProfileService.createDefaultDeviceProfile(tenant.getId()); 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,12 +13,15 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.service.apiusage; 16 +package org.thingsboard.server.common.data;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
19 19
20 public enum ApiFeature { 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 @Getter 26 @Getter
24 private final String apiStateKey; 27 private final String apiStateKey;
@@ -26,4 +29,5 @@ public enum ApiFeature { @@ -26,4 +29,5 @@ public enum ApiFeature {
26 ApiFeature(String apiStateKey) { 29 ApiFeature(String apiStateKey) {
27 this.apiStateKey = apiStateKey; 30 this.apiStateKey = apiStateKey;
28 } 31 }
  32 +
29 } 33 }
@@ -19,20 +19,42 @@ import lombok.Getter; @@ -19,20 +19,42 @@ import lombok.Getter;
19 19
20 public enum ApiUsageRecordKey { 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 @Getter 32 @Getter
  33 + private final ApiFeature apiFeature;
  34 + @Getter
29 private final String apiCountKey; 35 private final String apiCountKey;
30 @Getter 36 @Getter
31 private final String apiLimitKey; 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 this.apiCountKey = apiCountKey; 41 this.apiCountKey = apiCountKey;
35 this.apiLimitKey = apiLimitKey; 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,16 +37,16 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
37 private EntityId entityId; 37 private EntityId entityId;
38 @Getter 38 @Getter
39 @Setter 39 @Setter
40 - private boolean transportEnabled = true; 40 + private ApiUsageStateValue transportState;
41 @Getter 41 @Getter
42 @Setter 42 @Setter
43 - private boolean dbStorageEnabled = true; 43 + private ApiUsageStateValue dbStorageState;
44 @Getter 44 @Getter
45 @Setter 45 @Setter
46 - private boolean reExecEnabled = true; 46 + private ApiUsageStateValue reExecState;
47 @Getter 47 @Getter
48 @Setter 48 @Setter
49 - private boolean jsExecEnabled = true; 49 + private ApiUsageStateValue jsExecState;
50 50
51 public ApiUsageState() { 51 public ApiUsageState() {
52 super(); 52 super();
@@ -60,9 +60,25 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan @@ -60,9 +60,25 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
60 super(ur); 60 super(ur);
61 this.tenantId = ur.getTenantId(); 61 this.tenantId = ur.getTenantId();
62 this.entityId = ur.getEntityId(); 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,6 +39,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
39 private long maxDPStorageDays; 39 private long maxDPStorageDays;
40 private int maxRuleNodeExecutionsPerMessage; 40 private int maxRuleNodeExecutionsPerMessage;
41 41
  42 + private double warnThreshold;
  43 +
42 @Override 44 @Override
43 public long getProfileThreshold(ApiUsageRecordKey key) { 45 public long getProfileThreshold(ApiUsageRecordKey key) {
44 switch (key) { 46 switch (key) {
@@ -56,6 +58,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura @@ -56,6 +58,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
56 return 0L; 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 @Override 66 @Override
61 public TenantProfileType getType() { 67 public TenantProfileType getType() {
@@ -38,6 +38,9 @@ public interface TenantProfileConfiguration { @@ -38,6 +38,9 @@ public interface TenantProfileConfiguration {
38 long getProfileThreshold(ApiUsageRecordKey key); 38 long getProfileThreshold(ApiUsageRecordKey key);
39 39
40 @JsonIgnore 40 @JsonIgnore
  41 + long getWarnThreshold(ApiUsageRecordKey key);
  42 +
  43 + @JsonIgnore
41 int getMaxRuleNodeExecsPerMessage(); 44 int getMaxRuleNodeExecsPerMessage();
42 45
43 } 46 }
@@ -26,7 +26,7 @@ public class TbQueueCoreSettings { @@ -26,7 +26,7 @@ public class TbQueueCoreSettings {
26 @Value("${queue.core.topic}") 26 @Value("${queue.core.topic}")
27 private String topic; 27 private String topic;
28 28
29 - @Value("${queue.core.usage-stats-topic}") 29 + @Value("${queue.core.usage-stats-topic:tb_usage_stats}")
30 private String usageStatsTopic; 30 private String usageStatsTopic;
31 31
32 @Value("${queue.core.partitions}") 32 @Value("${queue.core.partitions}")
@@ -446,10 +446,10 @@ public class ModelConstants { @@ -446,10 +446,10 @@ public class ModelConstants {
446 public static final String API_USAGE_STATE_TENANT_ID_COLUMN = TENANT_ID_PROPERTY; 446 public static final String API_USAGE_STATE_TENANT_ID_COLUMN = TENANT_ID_PROPERTY;
447 public static final String API_USAGE_STATE_ENTITY_TYPE_COLUMN = ENTITY_TYPE_COLUMN; 447 public static final String API_USAGE_STATE_ENTITY_TYPE_COLUMN = ENTITY_TYPE_COLUMN;
448 public static final String API_USAGE_STATE_ENTITY_ID_COLUMN = ENTITY_ID_COLUMN; 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 * Cassandra attributes and timeseries constants. 455 * Cassandra attributes and timeseries constants.
@@ -17,10 +17,9 @@ package org.thingsboard.server.dao.model.sql; @@ -17,10 +17,9 @@ package org.thingsboard.server.dao.model.sql;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import lombok.EqualsAndHashCode; 19 import lombok.EqualsAndHashCode;
20 -import lombok.Getter;  
21 -import lombok.Setter;  
22 import org.hibernate.annotations.TypeDef; 20 import org.hibernate.annotations.TypeDef;
23 import org.thingsboard.server.common.data.ApiUsageState; 21 import org.thingsboard.server.common.data.ApiUsageState;
  22 +import org.thingsboard.server.common.data.ApiUsageStateValue;
24 import org.thingsboard.server.common.data.id.EntityIdFactory; 23 import org.thingsboard.server.common.data.id.EntityIdFactory;
25 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
26 import org.thingsboard.server.common.data.id.ApiUsageStateId; 25 import org.thingsboard.server.common.data.id.ApiUsageStateId;
@@ -31,6 +30,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; @@ -31,6 +30,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
31 30
32 import javax.persistence.Column; 31 import javax.persistence.Column;
33 import javax.persistence.Entity; 32 import javax.persistence.Entity;
  33 +import javax.persistence.EnumType;
  34 +import javax.persistence.Enumerated;
34 import javax.persistence.Table; 35 import javax.persistence.Table;
35 import java.util.UUID; 36 import java.util.UUID;
36 37
@@ -46,21 +47,22 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements @@ -46,21 +47,22 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
46 47
47 @Column(name = ModelConstants.API_USAGE_STATE_TENANT_ID_COLUMN) 48 @Column(name = ModelConstants.API_USAGE_STATE_TENANT_ID_COLUMN)
48 private UUID tenantId; 49 private UUID tenantId;
49 -  
50 @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_TYPE_COLUMN) 50 @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_TYPE_COLUMN)
51 private String entityType; 51 private String entityType;
52 -  
53 @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_ID_COLUMN) 52 @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_ID_COLUMN)
54 private UUID entityId; 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 public ApiUsageStateEntity() { 67 public ApiUsageStateEntity() {
66 } 68 }
@@ -77,10 +79,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements @@ -77,10 +79,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
77 this.entityType = ur.getEntityId().getEntityType().name(); 79 this.entityType = ur.getEntityId().getEntityType().name();
78 this.entityId = ur.getEntityId().getId(); 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 @Override 88 @Override
@@ -93,10 +95,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements @@ -93,10 +95,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
93 if (entityId != null) { 95 if (entityId != null) {
94 ur.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); 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 return ur; 102 return ur;
101 } 103 }
102 104
@@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
19 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
20 import org.thingsboard.server.common.data.ApiUsageRecordKey; 20 import org.thingsboard.server.common.data.ApiUsageRecordKey;
21 import org.thingsboard.server.common.data.ApiUsageState; 21 import org.thingsboard.server.common.data.ApiUsageState;
  22 +import org.thingsboard.server.common.data.ApiUsageStateValue;
22 import org.thingsboard.server.common.data.EntityType; 23 import org.thingsboard.server.common.data.EntityType;
23 import org.thingsboard.server.common.data.Tenant; 24 import org.thingsboard.server.common.data.Tenant;
24 import org.thingsboard.server.common.data.TenantProfile; 25 import org.thingsboard.server.common.data.TenantProfile;
@@ -71,6 +72,10 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @@ -71,6 +72,10 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
71 ApiUsageState apiUsageState = new ApiUsageState(); 72 ApiUsageState apiUsageState = new ApiUsageState();
72 apiUsageState.setTenantId(tenantId); 73 apiUsageState.setTenantId(tenantId);
73 apiUsageState.setEntityId(tenantId); 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 apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId); 79 apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId);
75 80
76 ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState); 81 ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
@@ -412,9 +412,9 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( @@ -412,9 +412,9 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
412 tenant_id uuid, 412 tenant_id uuid,
413 entity_type varchar(32), 413 entity_type varchar(32),
414 entity_id uuid, 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 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) 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,10 +438,10 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
438 tenant_id uuid, 438 tenant_id uuid,
439 entity_type varchar(32), 439 entity_type varchar(32),
440 entity_id uuid, 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 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) 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,6 +19,7 @@ import org.junit.After;
19 import org.junit.Assert; 19 import org.junit.Assert;
20 import org.junit.Before; 20 import org.junit.Before;
21 import org.junit.Test; 21 import org.junit.Test;
  22 +import org.thingsboard.server.common.data.ApiUsageStateValue;
22 import org.thingsboard.server.common.data.Tenant; 23 import org.thingsboard.server.common.data.Tenant;
23 import org.thingsboard.server.common.data.ApiUsageState; 24 import org.thingsboard.server.common.data.ApiUsageState;
24 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
@@ -53,7 +54,7 @@ public abstract class BaseApiUsageStateServiceTest extends AbstractServiceTest { @@ -53,7 +54,7 @@ public abstract class BaseApiUsageStateServiceTest extends AbstractServiceTest {
53 ApiUsageState apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId); 54 ApiUsageState apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);
54 Assert.assertNotNull(apiUsageState); 55 Assert.assertNotNull(apiUsageState);
55 Assert.assertTrue(apiUsageState.isTransportEnabled()); 56 Assert.assertTrue(apiUsageState.isTransportEnabled());
56 - apiUsageState.setTransportEnabled(false); 57 + apiUsageState.setTransportState(ApiUsageStateValue.DISABLED);
57 apiUsageState = apiUsageStateService.update(apiUsageState); 58 apiUsageState = apiUsageStateService.update(apiUsageState);
58 Assert.assertNotNull(apiUsageState); 59 Assert.assertNotNull(apiUsageState);
59 apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId); 60 apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);
@@ -145,6 +145,7 @@ queue: @@ -145,6 +145,7 @@ queue:
145 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" 145 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
146 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" 146 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
147 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" 147 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  148 + usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
148 stats: 149 stats:
149 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" 150 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
150 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" 151 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"
@@ -138,6 +138,7 @@ queue: @@ -138,6 +138,7 @@ queue:
138 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" 138 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
139 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" 139 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
140 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" 140 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  141 + usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
141 stats: 142 stats:
142 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" 143 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
143 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" 144 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"
@@ -167,6 +167,7 @@ queue: @@ -167,6 +167,7 @@ queue:
167 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" 167 poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
168 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" 168 partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
169 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" 169 pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  170 + usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
170 stats: 171 stats:
171 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" 172 enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
172 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" 173 print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"