Browse Source

Merge branch 'feature/usage-records'

pull/3695/head
Andrii Shvaika 6 years ago
parent
commit
3088e6be6f
  1. 26
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  2. 135
      application/src/main/java/org/thingsboard/server/service/apiusage/TenantApiUsageState.java
  3. 24
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  4. 8
      common/data/src/main/java/org/thingsboard/server/common/data/ApiFeature.java
  5. 34
      common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageRecordKey.java
  6. 32
      common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageState.java
  7. 26
      common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageStateValue.java
  8. 6
      common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java
  9. 3
      common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java
  10. 2
      common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java
  11. 8
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  12. 44
      dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java
  13. 5
      dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java
  14. 8
      dao/src/main/resources/sql/schema-entities-hsql.sql
  15. 8
      dao/src/main/resources/sql/schema-entities.sql
  16. 3
      dao/src/test/java/org/thingsboard/server/dao/service/BaseApiUsageStateServiceTest.java
  17. 1
      transport/coap/src/main/resources/tb-coap-transport.yml
  18. 1
      transport/http/src/main/resources/tb-http-transport.yml
  19. 1
      transport/mqtt/src/main/resources/tb-mqtt-transport.yml

26
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java

@ -23,16 +23,18 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.util.Pair; import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.ApiFeature;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.ApiUsageStateId; import org.thingsboard.server.common.data.id.ApiUsageStateId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
@ -57,8 +59,10 @@ import org.thingsboard.server.service.telemetry.InternalTelemetryService;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -137,7 +141,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB()));
TenantApiUsageState tenantState; TenantApiUsageState tenantState;
List<TsKvEntry> updatedEntries; List<TsKvEntry> updatedEntries;
Map<ApiFeature, Boolean> result = new HashMap<>(); Map<ApiFeature, ApiUsageStateValue> result;
updateLock.lock(); updateLock.lock();
try { try {
tenantState = getOrFetchState(tenantId); tenantState = getOrFetchState(tenantId);
@ -148,17 +152,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
tenantState.setHour(newHourTs); tenantState.setHour(newHourTs);
} }
updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length); updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length);
Set<ApiFeature> apiFeatures = new HashSet<>();
for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) { for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) {
ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey()); ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey());
long newValue = tenantState.add(recordKey, kvProto.getValue()); long newValue = tenantState.add(recordKey, kvProto.getValue());
updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue))); updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue)));
long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue());
updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue))); updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue)));
Pair<ApiFeature, Boolean> update = tenantState.checkStateUpdatedDueToThreshold(recordKey); apiFeatures.add(recordKey.getApiFeature());
if (update != null) {
result.put(update.getFirst(), update.getSecond());
}
} }
result = tenantState.checkStateUpdatedDueToThreshold(apiFeatures);
} finally { } finally {
updateLock.unlock(); updateLock.unlock();
} }
@ -196,7 +199,9 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
state = otherTenantStates.get(tenantId); state = otherTenantStates.get(tenantId);
if (state == null) { if (state == null) {
state = apiUsageStateService.findTenantApiUsageState(tenantId); state = apiUsageStateService.findTenantApiUsageState(tenantId);
otherTenantStates.put(tenantId, state); if (state != null) {
otherTenantStates.put(tenantId, state);
}
} }
} finally { } finally {
updateLock.unlock(); updateLock.unlock();
@ -245,7 +250,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
TenantProfileData oldProfileData = state.getTenantProfileData(); TenantProfileData oldProfileData = state.getTenantProfileData();
state.setTenantProfileId(tenantProfile.getId()); state.setTenantProfileId(tenantProfile.getId());
state.setTenantProfileData(tenantProfile.getProfileData()); state.setTenantProfileData(tenantProfile.getProfileData());
Map<ApiFeature, Boolean> result = state.checkStateUpdatedDueToThresholds(); Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds();
if (!result.isEmpty()) { if (!result.isEmpty()) {
persistAndNotify(state, result); persistAndNotify(state, result);
} }
@ -269,14 +274,15 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
} }
} }
private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, Boolean> result) { private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) {
log.info("[{}] Detected update of the API state: {}", state.getTenantId(), result); log.info("[{}] Detected update of the API state: {}", state.getTenantId(), result);
apiUsageStateService.update(state.getApiUsageState()); apiUsageStateService.update(state.getApiUsageState());
clusterService.onApiStateChange(state.getApiUsageState(), null); clusterService.onApiStateChange(state.getApiUsageState(), null);
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
List<TsKvEntry> stateTelemetry = new ArrayList<>(); List<TsKvEntry> stateTelemetry = new ArrayList<>();
result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new BooleanDataEntry(apiFeature.getApiStateKey(), aState))))); result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name())))));
tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK);
//TODO: notify tenant admin via email!
} }
private void checkStartOfNextCycle() { private void checkStartOfNextCycle() {

135
application/src/main/java/org/thingsboard/server/service/apiusage/TenantApiUsageState.java

@ -18,17 +18,21 @@ package org.thingsboard.server.service.apiusage;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.springframework.data.util.Pair; import org.springframework.data.util.Pair;
import org.thingsboard.server.common.data.ApiFeature;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.common.msg.tools.SchedulerUtils; import org.thingsboard.server.common.msg.tools.SchedulerUtils;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
public class TenantApiUsageState { public class TenantApiUsageState {
@ -103,99 +107,80 @@ public class TenantApiUsageState {
return tenantProfileData.getConfiguration().getProfileThreshold(key); return tenantProfileData.getConfiguration().getProfileThreshold(key);
} }
public TenantId getTenantId() { public long getProfileWarnThreshold(ApiUsageRecordKey key) {
return apiUsageState.getTenantId(); return tenantProfileData.getConfiguration().getWarnThreshold(key);
}
public boolean isTransportEnabled() {
return apiUsageState.isTransportEnabled();
}
public boolean isDbStorageEnabled() {
return apiUsageState.isDbStorageEnabled();
}
public boolean isRuleEngineEnabled() {
return apiUsageState.isReExecEnabled();
}
public boolean isJsExecEnabled() {
return apiUsageState.isJsExecEnabled();
}
public void setTransportEnabled(boolean transportEnabled) {
apiUsageState.setTransportEnabled(transportEnabled);
} }
public void setDbStorageEnabled(boolean dbStorageEnabled) { public TenantId getTenantId() {
apiUsageState.setDbStorageEnabled(dbStorageEnabled); return apiUsageState.getTenantId();
}
public void setRuleEngineEnabled(boolean ruleEngineEnabled) {
apiUsageState.setReExecEnabled(ruleEngineEnabled);
}
public void setJsExecEnabled(boolean jsExecEnabled) {
apiUsageState.setJsExecEnabled(jsExecEnabled);
} }
public boolean isFeatureEnabled(ApiUsageRecordKey recordKey) { public ApiUsageStateValue getFeatureValue(ApiFeature feature) {
switch (recordKey) { switch (feature) {
case TRANSPORT_MSG_COUNT: case TRANSPORT:
case TRANSPORT_DP_COUNT: return apiUsageState.getTransportState();
return isTransportEnabled(); case RE:
case RE_EXEC_COUNT: return apiUsageState.getReExecState();
return isRuleEngineEnabled(); case DB:
case STORAGE_DP_COUNT: return apiUsageState.getDbStorageState();
return isDbStorageEnabled(); case JS:
case JS_EXEC_COUNT: return apiUsageState.getJsExecState();
return isJsExecEnabled();
default: default:
return true; return ApiUsageStateValue.ENABLED;
} }
} }
public ApiFeature setFeatureValue(ApiUsageRecordKey recordKey, boolean value) { public boolean setFeatureValue(ApiFeature feature, ApiUsageStateValue value) {
ApiFeature feature = null; ApiUsageStateValue currentValue = getFeatureValue(feature);
boolean currentValue = isFeatureEnabled(recordKey); switch (feature) {
switch (recordKey) { case TRANSPORT:
case TRANSPORT_MSG_COUNT: apiUsageState.setTransportState(value);
case TRANSPORT_DP_COUNT:
feature = ApiFeature.TRANSPORT;
setTransportEnabled(value);
break; break;
case RE_EXEC_COUNT: case RE:
feature = ApiFeature.RE; apiUsageState.setReExecState(value);
setRuleEngineEnabled(value);
break; break;
case STORAGE_DP_COUNT: case DB:
feature = ApiFeature.DB; apiUsageState.setDbStorageState(value);
setDbStorageEnabled(value);
break; break;
case JS_EXEC_COUNT: case JS:
feature = ApiFeature.JS; apiUsageState.setJsExecState(value);
setJsExecEnabled(value);
break; break;
} }
return currentValue == value ? null : feature; return !currentValue.equals(value);
} }
public Map<ApiFeature, Boolean> checkStateUpdatedDueToThresholds() { public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThresholds() {
Map<ApiFeature, Boolean> result = new HashMap<>(); return checkStateUpdatedDueToThreshold(new HashSet<>(Arrays.asList(ApiFeature.values())));
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { }
Pair<ApiFeature, Boolean> featureUpdate = checkStateUpdatedDueToThreshold(key);
if (featureUpdate != null) { public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(Set<ApiFeature> features) {
result.put(featureUpdate.getFirst(), featureUpdate.getSecond()); Map<ApiFeature, ApiUsageStateValue> result = new HashMap<>();
for (ApiFeature feature : features) {
Pair<ApiFeature, ApiUsageStateValue> tmp = checkStateUpdatedDueToThreshold(feature);
if (tmp != null) {
result.put(tmp.getFirst(), tmp.getSecond());
} }
} }
return result; return result;
} }
public Pair<ApiFeature, Boolean> checkStateUpdatedDueToThreshold(ApiUsageRecordKey recordKey) { public Pair<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(ApiFeature feature) {
long value = get(recordKey); ApiUsageStateValue featureValue = ApiUsageStateValue.ENABLED;
long threshold = getProfileThreshold(recordKey); for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.getKeys(feature)) {
boolean featureValue = threshold == 0 || value < threshold; long value = get(recordKey);
ApiFeature feature = setFeatureValue(recordKey, featureValue); long threshold = getProfileThreshold(recordKey);
return feature != null ? Pair.of(feature, featureValue) : null; long warnThreshold = getProfileWarnThreshold(recordKey);
ApiUsageStateValue tmpValue;
if (threshold == 0 || value < warnThreshold) {
tmpValue = ApiUsageStateValue.ENABLED;
} else if (value < threshold) {
tmpValue = ApiUsageStateValue.WARNING;
} else {
tmpValue = ApiUsageStateValue.DISABLED;
}
featureValue = ApiUsageStateValue.toMoreRestricted(featureValue, tmpValue);
}
return setFeatureValue(feature, featureValue) ? Pair.of(feature, featureValue) : null;
} }
} }

24
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -28,6 +28,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import org.thingsboard.server.service.install.sql.SqlDbHelper; import org.thingsboard.server.service.install.sql.SqlDbHelper;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@ -96,6 +97,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
@Autowired @Autowired
private DeviceProfileService deviceProfileService; private DeviceProfileService deviceProfileService;
@Autowired
private ApiUsageStateService apiUsageStateService;
@Override @Override
public void upgradeDatabase(String fromVersion) throws Exception { public void upgradeDatabase(String fromVersion) throws Exception {
@ -352,6 +356,22 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
} catch (Exception e) { } catch (Exception e) {
} }
try {
conn.createStatement().execute("CREATE TABLE IF NOT EXISTS api_usage_state (" +
" id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY," +
" created_time bigint NOT NULL," +
" tenant_id uuid," +
" entity_type varchar(32)," +
" entity_id uuid," +
" transport varchar(32)," +
" db_storage varchar(32)," +
" re_exec varchar(32)," +
" js_exec varchar(32)," +
" CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)\n" +
");");
} catch (Exception e) {
}
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql"); schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql");
loadSql(schemaUpdateFile, conn); loadSql(schemaUpdateFile, conn);
@ -367,6 +387,10 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
do { do {
pageData = tenantService.findTenants(pageLink); pageData = tenantService.findTenants(pageLink);
for (Tenant tenant : pageData.getData()) { for (Tenant tenant : pageData.getData()) {
try {
apiUsageStateService.createDefaultApiUsageState(tenant.getId());
} catch (Exception e) {
}
List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get();
try { try {
deviceProfileService.createDefaultDeviceProfile(tenant.getId()); deviceProfileService.createDefaultDeviceProfile(tenant.getId());

8
application/src/main/java/org/thingsboard/server/service/apiusage/ApiFeature.java → common/data/src/main/java/org/thingsboard/server/common/data/ApiFeature.java

@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.apiusage; package org.thingsboard.server.common.data;
import lombok.Getter; import lombok.Getter;
public enum ApiFeature { public enum ApiFeature {
TRANSPORT("transportApiState"), DB("dbApiState"), RE("ruleEngineApiState"), JS("jsExecutionApiState"); TRANSPORT("transportApiState"),
DB("dbApiState"),
RE("ruleEngineApiState"),
JS("jsExecutionApiState");
@Getter @Getter
private final String apiStateKey; private final String apiStateKey;
@ -26,4 +29,5 @@ public enum ApiFeature {
ApiFeature(String apiStateKey) { ApiFeature(String apiStateKey) {
this.apiStateKey = apiStateKey; this.apiStateKey = apiStateKey;
} }
} }

34
common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageRecordKey.java

@ -19,20 +19,42 @@ import lombok.Getter;
public enum ApiUsageRecordKey { public enum ApiUsageRecordKey {
TRANSPORT_MSG_COUNT("transportMsgCount", "transportMsgLimit"), TRANSPORT_MSG_COUNT(ApiFeature.TRANSPORT, "transportMsgCount", "transportMsgLimit"),
TRANSPORT_DP_COUNT("transportDataPointsCount", "transportDataPointsLimit"), TRANSPORT_DP_COUNT(ApiFeature.TRANSPORT, "transportDataPointsCount", "transportDataPointsLimit"),
STORAGE_DP_COUNT("storageDataPointsCount", "storageDataPointsLimit"), STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit"),
RE_EXEC_COUNT("ruleEngineExecutionCount", "ruleEngineExecutionLimit"), RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit"),
JS_EXEC_COUNT("jsExecutionCount", "jsExecutionLimit"); JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit");
private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT};
private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT};
private static final ApiUsageRecordKey[] DB_RECORD_KEYS = {STORAGE_DP_COUNT};
private static final ApiUsageRecordKey[] TRANSPORT_RECORD_KEYS = {TRANSPORT_MSG_COUNT, TRANSPORT_DP_COUNT};
@Getter
private final ApiFeature apiFeature;
@Getter @Getter
private final String apiCountKey; private final String apiCountKey;
@Getter @Getter
private final String apiLimitKey; private final String apiLimitKey;
ApiUsageRecordKey(String apiCountKey, String apiLimitKey) { ApiUsageRecordKey(ApiFeature apiFeature, String apiCountKey, String apiLimitKey) {
this.apiFeature = apiFeature;
this.apiCountKey = apiCountKey; this.apiCountKey = apiCountKey;
this.apiLimitKey = apiLimitKey; this.apiLimitKey = apiLimitKey;
} }
public static ApiUsageRecordKey[] getKeys(ApiFeature feature) {
switch (feature) {
case TRANSPORT:
return TRANSPORT_RECORD_KEYS;
case DB:
return DB_RECORD_KEYS;
case RE:
return RE_RECORD_KEYS;
case JS:
return JS_RECORD_KEYS;
default:
return new ApiUsageRecordKey[]{};
}
}
} }

32
common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageState.java

@ -37,16 +37,16 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
private EntityId entityId; private EntityId entityId;
@Getter @Getter
@Setter @Setter
private boolean transportEnabled = true; private ApiUsageStateValue transportState;
@Getter @Getter
@Setter @Setter
private boolean dbStorageEnabled = true; private ApiUsageStateValue dbStorageState;
@Getter @Getter
@Setter @Setter
private boolean reExecEnabled = true; private ApiUsageStateValue reExecState;
@Getter @Getter
@Setter @Setter
private boolean jsExecEnabled = true; private ApiUsageStateValue jsExecState;
public ApiUsageState() { public ApiUsageState() {
super(); super();
@ -60,9 +60,25 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
super(ur); super(ur);
this.tenantId = ur.getTenantId(); this.tenantId = ur.getTenantId();
this.entityId = ur.getEntityId(); this.entityId = ur.getEntityId();
this.transportEnabled = ur.isTransportEnabled(); this.transportState = ur.getTransportState();
this.dbStorageEnabled = ur.isDbStorageEnabled(); this.dbStorageState = ur.getDbStorageState();
this.reExecEnabled = ur.isReExecEnabled(); this.reExecState = ur.getReExecState();
this.jsExecEnabled = ur.isJsExecEnabled(); this.jsExecState = ur.getJsExecState();
}
public boolean isTransportEnabled() {
return !ApiUsageStateValue.DISABLED.equals(transportState);
}
public boolean isReExecEnabled() {
return !ApiUsageStateValue.DISABLED.equals(reExecState);
}
public boolean isDbStorageEnabled() {
return !ApiUsageStateValue.DISABLED.equals(dbStorageState);
}
public boolean isJsExecEnabled() {
return !ApiUsageStateValue.DISABLED.equals(jsExecState);
} }
} }

26
common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageStateValue.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data;
public enum ApiUsageStateValue {
ENABLED, WARNING, DISABLED;
public static ApiUsageStateValue toMoreRestricted(ApiUsageStateValue a, ApiUsageStateValue b) {
return a.ordinal() > b.ordinal() ? a : b;
}
}

6
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java

@ -39,6 +39,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
private long maxDPStorageDays; private long maxDPStorageDays;
private int maxRuleNodeExecutionsPerMessage; private int maxRuleNodeExecutionsPerMessage;
private double warnThreshold;
@Override @Override
public long getProfileThreshold(ApiUsageRecordKey key) { public long getProfileThreshold(ApiUsageRecordKey key) {
switch (key) { switch (key) {
@ -56,6 +58,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
return 0L; return 0L;
} }
@Override
public long getWarnThreshold(ApiUsageRecordKey key) {
return (long) (getProfileThreshold(key) * (warnThreshold > 0.0 ? warnThreshold : 0.8));
}
@Override @Override
public TenantProfileType getType() { public TenantProfileType getType() {

3
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java

@ -37,6 +37,9 @@ public interface TenantProfileConfiguration {
@JsonIgnore @JsonIgnore
long getProfileThreshold(ApiUsageRecordKey key); long getProfileThreshold(ApiUsageRecordKey key);
@JsonIgnore
long getWarnThreshold(ApiUsageRecordKey key);
@JsonIgnore @JsonIgnore
int getMaxRuleNodeExecsPerMessage(); int getMaxRuleNodeExecsPerMessage();

2
common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java

@ -26,7 +26,7 @@ public class TbQueueCoreSettings {
@Value("${queue.core.topic}") @Value("${queue.core.topic}")
private String topic; private String topic;
@Value("${queue.core.usage-stats-topic}") @Value("${queue.core.usage-stats-topic:tb_usage_stats}")
private String usageStatsTopic; private String usageStatsTopic;
@Value("${queue.core.partitions}") @Value("${queue.core.partitions}")

8
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -446,10 +446,10 @@ public class ModelConstants {
public static final String API_USAGE_STATE_TENANT_ID_COLUMN = TENANT_ID_PROPERTY; public static final String API_USAGE_STATE_TENANT_ID_COLUMN = TENANT_ID_PROPERTY;
public static final String API_USAGE_STATE_ENTITY_TYPE_COLUMN = ENTITY_TYPE_COLUMN; public static final String API_USAGE_STATE_ENTITY_TYPE_COLUMN = ENTITY_TYPE_COLUMN;
public static final String API_USAGE_STATE_ENTITY_ID_COLUMN = ENTITY_ID_COLUMN; public static final String API_USAGE_STATE_ENTITY_ID_COLUMN = ENTITY_ID_COLUMN;
public static final String API_USAGE_STATE_TRANSPORT_ENABLED_COLUMN = "transport_enabled"; public static final String API_USAGE_STATE_TRANSPORT_COLUMN = "transport";
public static final String API_USAGE_STATE_DB_STORAGE_ENABLED_COLUMN = "db_storage_enabled"; public static final String API_USAGE_STATE_DB_STORAGE_COLUMN = "db_storage";
public static final String API_USAGE_STATE_RE_EXEC_ENABLED_COLUMN = "re_exec_enabled"; public static final String API_USAGE_STATE_RE_EXEC_COLUMN = "re_exec";
public static final String API_USAGE_STATE_JS_EXEC_ENABLED_COLUMN = "js_exec_enabled"; public static final String API_USAGE_STATE_JS_EXEC_COLUMN = "js_exec";
/** /**
* Cassandra attributes and timeseries constants. * Cassandra attributes and timeseries constants.

44
dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java

@ -17,10 +17,9 @@ package org.thingsboard.server.dao.model.sql;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.ApiUsageStateId; import org.thingsboard.server.common.data.id.ApiUsageStateId;
@ -31,6 +30,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table; import javax.persistence.Table;
import java.util.UUID; import java.util.UUID;
@ -46,21 +47,22 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
@Column(name = ModelConstants.API_USAGE_STATE_TENANT_ID_COLUMN) @Column(name = ModelConstants.API_USAGE_STATE_TENANT_ID_COLUMN)
private UUID tenantId; private UUID tenantId;
@Column(name = ModelConstants.API_USAGE_STATE_ENTITY_TYPE_COLUMN) @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_TYPE_COLUMN)
private String entityType; private String entityType;
@Column(name = ModelConstants.API_USAGE_STATE_ENTITY_ID_COLUMN) @Column(name = ModelConstants.API_USAGE_STATE_ENTITY_ID_COLUMN)
private UUID entityId; private UUID entityId;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.API_USAGE_STATE_TRANSPORT_ENABLED_COLUMN) @Column(name = ModelConstants.API_USAGE_STATE_TRANSPORT_COLUMN)
private boolean transportEnabled = true; private ApiUsageStateValue transportState = ApiUsageStateValue.ENABLED;
@Column(name = ModelConstants.API_USAGE_STATE_DB_STORAGE_ENABLED_COLUMN) @Enumerated(EnumType.STRING)
private boolean dbStorageEnabled = true; @Column(name = ModelConstants.API_USAGE_STATE_DB_STORAGE_COLUMN)
@Column(name = ModelConstants.API_USAGE_STATE_RE_EXEC_ENABLED_COLUMN) private ApiUsageStateValue dbStorageState = ApiUsageStateValue.ENABLED;
private boolean reExecEnabled = true; @Enumerated(EnumType.STRING)
@Column(name = ModelConstants.API_USAGE_STATE_JS_EXEC_ENABLED_COLUMN) @Column(name = ModelConstants.API_USAGE_STATE_RE_EXEC_COLUMN)
private boolean jsExecEnabled = true; private ApiUsageStateValue reExecState = ApiUsageStateValue.ENABLED;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.API_USAGE_STATE_JS_EXEC_COLUMN)
private ApiUsageStateValue jsExecState = ApiUsageStateValue.ENABLED;
public ApiUsageStateEntity() { public ApiUsageStateEntity() {
} }
@ -77,10 +79,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
this.entityType = ur.getEntityId().getEntityType().name(); this.entityType = ur.getEntityId().getEntityType().name();
this.entityId = ur.getEntityId().getId(); this.entityId = ur.getEntityId().getId();
} }
this.transportEnabled = ur.isTransportEnabled(); this.transportState = ur.getTransportState();
this.dbStorageEnabled = ur.isDbStorageEnabled(); this.dbStorageState = ur.getDbStorageState();
this.reExecEnabled = ur.isReExecEnabled(); this.reExecState = ur.getReExecState();
this.jsExecEnabled = ur.isJsExecEnabled(); this.jsExecState = ur.getJsExecState();
} }
@Override @Override
@ -93,10 +95,10 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
if (entityId != null) { if (entityId != null) {
ur.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); ur.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId));
} }
ur.setTransportEnabled(transportEnabled); ur.setTransportState(transportState);
ur.setDbStorageEnabled(dbStorageEnabled); ur.setDbStorageState(dbStorageState);
ur.setReExecEnabled(reExecEnabled); ur.setReExecState(reExecState);
ur.setJsExecEnabled(jsExecEnabled); ur.setJsExecState(jsExecState);
return ur; return ur;
} }

5
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java

@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
@ -71,6 +72,10 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
ApiUsageState apiUsageState = new ApiUsageState(); ApiUsageState apiUsageState = new ApiUsageState();
apiUsageState.setTenantId(tenantId); apiUsageState.setTenantId(tenantId);
apiUsageState.setEntityId(tenantId); apiUsageState.setEntityId(tenantId);
apiUsageState.setTransportState(ApiUsageStateValue.ENABLED);
apiUsageState.setReExecState(ApiUsageStateValue.ENABLED);
apiUsageState.setJsExecState(ApiUsageStateValue.ENABLED);
apiUsageState.setDbStorageState(ApiUsageStateValue.ENABLED);
apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId); apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId);
ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState); ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);

8
dao/src/main/resources/sql/schema-entities-hsql.sql

@ -412,9 +412,9 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
tenant_id uuid, tenant_id uuid,
entity_type varchar(32), entity_type varchar(32),
entity_id uuid, entity_id uuid,
transport_enabled boolean, transport varchar(32),
db_storage_enabled boolean, db_storage varchar(32),
re_exec_enabled boolean, re_exec varchar(32),
js_exec_enabled boolean, js_exec varchar(32),
CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
); );

8
dao/src/main/resources/sql/schema-entities.sql

@ -438,10 +438,10 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
tenant_id uuid, tenant_id uuid,
entity_type varchar(32), entity_type varchar(32),
entity_id uuid, entity_id uuid,
transport_enabled boolean, transport varchar(32),
db_storage_enabled boolean, db_storage varchar(32),
re_exec_enabled boolean, re_exec varchar(32),
js_exec_enabled boolean, js_exec varchar(32),
CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id) CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
); );

3
dao/src/test/java/org/thingsboard/server/dao/service/BaseApiUsageStateServiceTest.java

@ -19,6 +19,7 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -53,7 +54,7 @@ public abstract class BaseApiUsageStateServiceTest extends AbstractServiceTest {
ApiUsageState apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId); ApiUsageState apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);
Assert.assertNotNull(apiUsageState); Assert.assertNotNull(apiUsageState);
Assert.assertTrue(apiUsageState.isTransportEnabled()); Assert.assertTrue(apiUsageState.isTransportEnabled());
apiUsageState.setTransportEnabled(false); apiUsageState.setTransportState(ApiUsageStateValue.DISABLED);
apiUsageState = apiUsageStateService.update(apiUsageState); apiUsageState = apiUsageStateService.update(apiUsageState);
Assert.assertNotNull(apiUsageState); Assert.assertNotNull(apiUsageState);
apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId); apiUsageState = apiUsageStateService.findTenantApiUsageState(tenantId);

1
transport/coap/src/main/resources/tb-coap-transport.yml

@ -145,6 +145,7 @@ queue:
poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
stats: stats:
enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"

1
transport/http/src/main/resources/tb-http-transport.yml

@ -138,6 +138,7 @@ queue:
poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
stats: stats:
enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"

1
transport/mqtt/src/main/resources/tb-mqtt-transport.yml

@ -167,6 +167,7 @@ queue:
poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}"
usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
stats: stats:
enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}"
print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}"

Loading…
Cancel
Save