Usage records
This commit is contained in:
parent
b38b47f409
commit
03581c2941
@ -32,7 +32,6 @@ import org.springframework.data.redis.core.RedisTemplate;
|
|||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.rule.engine.api.MailService;
|
import org.thingsboard.rule.engine.api.MailService;
|
||||||
import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache;
|
|
||||||
import org.thingsboard.server.actors.service.ActorService;
|
import org.thingsboard.server.actors.service.ActorService;
|
||||||
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
|
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
|
||||||
import org.thingsboard.server.common.data.DataConstants;
|
import org.thingsboard.server.common.data.DataConstants;
|
||||||
@ -65,6 +64,8 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
|||||||
import org.thingsboard.server.dao.user.UserService;
|
import org.thingsboard.server.dao.user.UserService;
|
||||||
import org.thingsboard.server.queue.discovery.PartitionService;
|
import org.thingsboard.server.queue.discovery.PartitionService;
|
||||||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
|
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
|
||||||
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
|
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
|
||||||
import org.thingsboard.server.service.component.ComponentDiscoveryService;
|
import org.thingsboard.server.service.component.ComponentDiscoveryService;
|
||||||
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
|
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
|
||||||
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
|
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
|
||||||
@ -106,6 +107,14 @@ public class ActorSystemContext {
|
|||||||
return debugPerTenantLimits;
|
return debugPerTenantLimits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Getter
|
||||||
|
private TbApiUsageStateService apiUsageStateService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Getter
|
||||||
|
private TbApiUsageClient apiUsageClient;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import org.thingsboard.server.actors.TbActorRef;
|
|||||||
import org.thingsboard.server.actors.TbEntityActorId;
|
import org.thingsboard.server.actors.TbEntityActorId;
|
||||||
import org.thingsboard.server.actors.service.DefaultActorService;
|
import org.thingsboard.server.actors.service.DefaultActorService;
|
||||||
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
|
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
|
||||||
|
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||||
@ -46,6 +47,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
|
|||||||
import org.thingsboard.server.queue.TbQueueCallback;
|
import org.thingsboard.server.queue.TbQueueCallback;
|
||||||
import org.thingsboard.server.queue.common.MultipleTbQueueTbMsgCallbackWrapper;
|
import org.thingsboard.server.queue.common.MultipleTbQueueTbMsgCallbackWrapper;
|
||||||
import org.thingsboard.server.queue.common.TbQueueTbMsgCallbackWrapper;
|
import org.thingsboard.server.queue.common.TbQueueTbMsgCallbackWrapper;
|
||||||
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
import org.thingsboard.server.service.queue.TbClusterService;
|
import org.thingsboard.server.service.queue.TbClusterService;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -68,15 +70,16 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
|
|||||||
private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes;
|
private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes;
|
||||||
private final RuleChainService service;
|
private final RuleChainService service;
|
||||||
private final TbClusterService clusterService;
|
private final TbClusterService clusterService;
|
||||||
|
private final TbApiUsageClient apiUsageClient;
|
||||||
private String ruleChainName;
|
private String ruleChainName;
|
||||||
|
|
||||||
private RuleNodeId firstId;
|
private RuleNodeId firstId;
|
||||||
private RuleNodeCtx firstNode;
|
private RuleNodeCtx firstNode;
|
||||||
private boolean started;
|
private boolean started;
|
||||||
|
|
||||||
RuleChainActorMessageProcessor(TenantId tenantId, RuleChain ruleChain, ActorSystemContext systemContext
|
RuleChainActorMessageProcessor(TenantId tenantId, RuleChain ruleChain, ActorSystemContext systemContext, TbActorRef parent, TbActorRef self) {
|
||||||
, TbActorRef parent, TbActorRef self) {
|
|
||||||
super(systemContext, tenantId, ruleChain.getId());
|
super(systemContext, tenantId, ruleChain.getId());
|
||||||
|
this.apiUsageClient = systemContext.getApiUsageClient();
|
||||||
this.ruleChainName = ruleChain.getName();
|
this.ruleChainName = ruleChain.getName();
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.self = self;
|
this.self = self;
|
||||||
@ -331,6 +334,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
|
|||||||
|
|
||||||
private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
|
private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
|
||||||
if (nodeCtx != null) {
|
if (nodeCtx != null) {
|
||||||
|
apiUsageClient.report(tenantId, ApiUsageRecordKey.RE_EXEC_COUNT);
|
||||||
nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType));
|
nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType));
|
||||||
} else {
|
} else {
|
||||||
log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);
|
log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);
|
||||||
|
|||||||
@ -64,6 +64,12 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void destroyRuleChains() {
|
||||||
|
for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChains(tenantId, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
|
||||||
|
ctx.stop(new TbEntityActorId(ruleChain.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void visit(RuleChain entity, TbActorRef actorRef) {
|
protected void visit(RuleChain entity, TbActorRef actorRef) {
|
||||||
if (entity != null && entity.isRoot()) {
|
if (entity != null && entity.isRoot()) {
|
||||||
rootChain = entity;
|
rootChain = entity;
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import org.thingsboard.server.actors.ActorSystemContext;
|
|||||||
import org.thingsboard.server.actors.TbActorCtx;
|
import org.thingsboard.server.actors.TbActorCtx;
|
||||||
import org.thingsboard.server.actors.TbActorRef;
|
import org.thingsboard.server.actors.TbActorRef;
|
||||||
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
|
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
|
||||||
|
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||||
import org.thingsboard.server.common.data.id.RuleNodeId;
|
import org.thingsboard.server.common.data.id.RuleNodeId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
|
||||||
@ -28,6 +29,7 @@ import org.thingsboard.server.common.data.rule.RuleNode;
|
|||||||
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
|
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
|
||||||
import org.thingsboard.server.common.msg.queue.RuleNodeException;
|
import org.thingsboard.server.common.msg.queue.RuleNodeException;
|
||||||
import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
|
import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
|
||||||
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andrew Shvayka
|
* @author Andrew Shvayka
|
||||||
@ -36,6 +38,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
|
|||||||
|
|
||||||
private final String ruleChainName;
|
private final String ruleChainName;
|
||||||
private final TbActorRef self;
|
private final TbActorRef self;
|
||||||
|
private final TbApiUsageClient apiUsageClient;
|
||||||
private RuleNode ruleNode;
|
private RuleNode ruleNode;
|
||||||
private TbNode tbNode;
|
private TbNode tbNode;
|
||||||
private DefaultTbContext defaultCtx;
|
private DefaultTbContext defaultCtx;
|
||||||
@ -44,6 +47,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
|
|||||||
RuleNodeActorMessageProcessor(TenantId tenantId, String ruleChainName, RuleNodeId ruleNodeId, ActorSystemContext systemContext
|
RuleNodeActorMessageProcessor(TenantId tenantId, String ruleChainName, RuleNodeId ruleNodeId, ActorSystemContext systemContext
|
||||||
, TbActorRef parent, TbActorRef self) {
|
, TbActorRef parent, TbActorRef self) {
|
||||||
super(systemContext, tenantId, ruleNodeId);
|
super(systemContext, tenantId, ruleNodeId);
|
||||||
|
this.apiUsageClient = systemContext.getApiUsageClient();
|
||||||
this.ruleChainName = ruleChainName;
|
this.ruleChainName = ruleChainName;
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
|
this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
|
||||||
@ -92,6 +96,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
|
|||||||
|
|
||||||
public void onRuleToSelfMsg(RuleNodeToSelfMsg msg) throws Exception {
|
public void onRuleToSelfMsg(RuleNodeToSelfMsg msg) throws Exception {
|
||||||
checkActive(msg.getMsg());
|
checkActive(msg.getMsg());
|
||||||
|
apiUsageClient.report(tenantId, ApiUsageRecordKey.RE_EXEC_COUNT);
|
||||||
if (ruleNode.isDebugMode()) {
|
if (ruleNode.isDebugMode()) {
|
||||||
systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self");
|
systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import org.thingsboard.server.actors.device.DeviceActorCreator;
|
|||||||
import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
|
import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
|
||||||
import org.thingsboard.server.actors.service.ContextBasedCreator;
|
import org.thingsboard.server.actors.service.ContextBasedCreator;
|
||||||
import org.thingsboard.server.actors.service.DefaultActorService;
|
import org.thingsboard.server.actors.service.DefaultActorService;
|
||||||
|
import org.thingsboard.server.common.data.ApiUsageState;
|
||||||
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;
|
||||||
@ -57,6 +58,7 @@ public class TenantActor extends RuleChainManagerActor {
|
|||||||
|
|
||||||
private boolean isRuleEngineForCurrentTenant;
|
private boolean isRuleEngineForCurrentTenant;
|
||||||
private boolean isCore;
|
private boolean isCore;
|
||||||
|
private ApiUsageState apiUsageState;
|
||||||
|
|
||||||
private TenantActor(ActorSystemContext systemContext, TenantId tenantId) {
|
private TenantActor(ActorSystemContext systemContext, TenantId tenantId) {
|
||||||
super(systemContext, tenantId);
|
super(systemContext, tenantId);
|
||||||
@ -74,19 +76,24 @@ public class TenantActor extends RuleChainManagerActor {
|
|||||||
cantFindTenant = true;
|
cantFindTenant = true;
|
||||||
log.info("[{}] Started tenant actor for missing tenant.", tenantId);
|
log.info("[{}] Started tenant actor for missing tenant.", tenantId);
|
||||||
} else {
|
} else {
|
||||||
|
apiUsageState = new ApiUsageState(systemContext.getApiUsageStateService().getApiUsageState(tenant.getId()));
|
||||||
|
|
||||||
// This Service may be started for specific tenant only.
|
// This Service may be started for specific tenant only.
|
||||||
Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();
|
Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();
|
||||||
|
|
||||||
TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenant.getTenantProfileId());
|
TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenant.getTenantProfileId());
|
||||||
|
|
||||||
isRuleEngineForCurrentTenant = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
|
|
||||||
isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
|
isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
|
||||||
|
isRuleEngineForCurrentTenant = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
|
||||||
if (isRuleEngineForCurrentTenant) {
|
if (isRuleEngineForCurrentTenant) {
|
||||||
try {
|
try {
|
||||||
if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenantProfile.isIsolatedTbRuleEngine())) {
|
if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenantProfile.isIsolatedTbRuleEngine())) {
|
||||||
|
if (apiUsageState.isReExecEnabled()) {
|
||||||
log.info("[{}] Going to init rule chains", tenantId);
|
log.info("[{}] Going to init rule chains", tenantId);
|
||||||
initRuleChains();
|
initRuleChains();
|
||||||
|
} else {
|
||||||
|
log.info("[{}] Skip init of the rule chains due to API limits", tenantId);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
isRuleEngineForCurrentTenant = false;
|
isRuleEngineForCurrentTenant = false;
|
||||||
}
|
}
|
||||||
@ -98,8 +105,6 @@ public class TenantActor extends RuleChainManagerActor {
|
|||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("[{}] Unknown failure", tenantId, e);
|
log.warn("[{}] Unknown failure", tenantId, e);
|
||||||
// TODO: throw this in 3.1?
|
|
||||||
// throw new TbActorException("Failed to init actor", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +120,7 @@ public class TenantActor extends RuleChainManagerActor {
|
|||||||
if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
|
if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
|
||||||
QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg;
|
QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg;
|
||||||
queueMsg.getTbMsg().getCallback().onSuccess();
|
queueMsg.getTbMsg().getCallback().onSuccess();
|
||||||
} else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)){
|
} else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) {
|
||||||
TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg;
|
TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg;
|
||||||
transportMsg.getCallback().onSuccess();
|
transportMsg.getCallback().onSuccess();
|
||||||
}
|
}
|
||||||
@ -173,6 +178,7 @@ public class TenantActor extends RuleChainManagerActor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TbMsg tbMsg = msg.getTbMsg();
|
TbMsg tbMsg = msg.getTbMsg();
|
||||||
|
if (apiUsageState.isReExecEnabled()) {
|
||||||
if (tbMsg.getRuleChainId() == null) {
|
if (tbMsg.getRuleChainId() == null) {
|
||||||
if (getRootChainActor() != null) {
|
if (getRootChainActor() != null) {
|
||||||
getRootChainActor().tell(msg);
|
getRootChainActor().tell(msg);
|
||||||
@ -189,11 +195,17 @@ public class TenantActor extends RuleChainManagerActor {
|
|||||||
tbMsg.getCallback().onSuccess();
|
tbMsg.getCallback().onSuccess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.trace("[{}] Ack message because Rule Engine is disabled", tenantId);
|
||||||
|
tbMsg.getCallback().onSuccess();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRuleChainMsg(RuleChainAwareMsg msg) {
|
private void onRuleChainMsg(RuleChainAwareMsg msg) {
|
||||||
|
if (apiUsageState.isReExecEnabled()) {
|
||||||
getOrCreateActor(msg.getRuleChainId()).tell(msg);
|
getOrCreateActor(msg.getRuleChainId()).tell(msg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void onToDeviceActorMsg(DeviceAwareMsg msg, boolean priority) {
|
private void onToDeviceActorMsg(DeviceAwareMsg msg, boolean priority) {
|
||||||
if (!isCore) {
|
if (!isCore) {
|
||||||
@ -208,6 +220,17 @@ public class TenantActor extends RuleChainManagerActor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
|
private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
|
||||||
|
if (msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) {
|
||||||
|
ApiUsageState old = apiUsageState;
|
||||||
|
apiUsageState = new ApiUsageState(systemContext.getApiUsageStateService().getApiUsageState(tenantId));
|
||||||
|
if (old.isReExecEnabled() && !apiUsageState.isReExecEnabled()) {
|
||||||
|
log.info("[{}] Received API state update. Going to DISABLE Rule Engine execution.", tenantId);
|
||||||
|
destroyRuleChains();
|
||||||
|
} else if (!old.isReExecEnabled() && apiUsageState.isReExecEnabled()) {
|
||||||
|
log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId);
|
||||||
|
initRuleChains();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isRuleEngineForCurrentTenant) {
|
if (isRuleEngineForCurrentTenant) {
|
||||||
TbActorRef target = getEntityActorRef(msg.getEntityId());
|
TbActorRef target = getEntityActorRef(msg.getEntityId());
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
|
|||||||
@ -15,6 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.apiusage;
|
package org.thingsboard.server.service.apiusage;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
public enum ApiFeature {
|
public enum ApiFeature {
|
||||||
TRANSPORT, DB, RE, JS
|
TRANSPORT("transportApiState"), DB("dbApiState"), RE("ruleEngineApiState"), JS("jsExecutionApiState");
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final String apiStateKey;
|
||||||
|
|
||||||
|
ApiFeature(String apiStateKey) {
|
||||||
|
this.apiStateKey = apiStateKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,18 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.apiusage;
|
package org.thingsboard.server.service.apiusage;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
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.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.TenantProfile;
|
import org.thingsboard.server.common.data.TenantProfile;
|
||||||
|
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.TsKvEntry;
|
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
||||||
|
import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
|
||||||
|
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||||
import org.thingsboard.server.common.msg.queue.ServiceType;
|
import org.thingsboard.server.common.msg.queue.ServiceType;
|
||||||
import org.thingsboard.server.common.msg.queue.TbCallback;
|
import org.thingsboard.server.common.msg.queue.TbCallback;
|
||||||
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
|
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
|
||||||
@ -34,7 +40,6 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
|||||||
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
|
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
|
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
|
import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
|
||||||
import org.thingsboard.server.queue.TbQueueCallback;
|
|
||||||
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
|
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
|
||||||
import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
|
import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
|
||||||
import org.thingsboard.server.queue.discovery.PartitionService;
|
import org.thingsboard.server.queue.discovery.PartitionService;
|
||||||
@ -42,6 +47,7 @@ import org.thingsboard.server.queue.scheduler.SchedulerComponent;
|
|||||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||||
import org.thingsboard.server.service.profile.TbTenantProfileCache;
|
import org.thingsboard.server.service.profile.TbTenantProfileCache;
|
||||||
import org.thingsboard.server.service.queue.TbClusterService;
|
import org.thingsboard.server.service.queue.TbClusterService;
|
||||||
|
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -60,11 +66,23 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
@Service
|
@Service
|
||||||
public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
||||||
|
|
||||||
public static final String HOURLY = "HOURLY_";
|
public static final String HOURLY = "Hourly";
|
||||||
|
public static final FutureCallback<Void> VOID_CALLBACK = new FutureCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(@Nullable Void result) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
private final TbClusterService clusterService;
|
private final TbClusterService clusterService;
|
||||||
private final PartitionService partitionService;
|
private final PartitionService partitionService;
|
||||||
private final ApiUsageStateService apiUsageStateService;
|
private final ApiUsageStateService apiUsageStateService;
|
||||||
private final TimeseriesService tsService;
|
private final TimeseriesService tsService;
|
||||||
|
private final TelemetrySubscriptionService tsWsService;
|
||||||
private final SchedulerComponent scheduler;
|
private final SchedulerComponent scheduler;
|
||||||
private final TbTenantProfileCache tenantProfileCache;
|
private final TbTenantProfileCache tenantProfileCache;
|
||||||
|
|
||||||
@ -84,13 +102,14 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
|||||||
public DefaultTbApiUsageStateService(TbClusterService clusterService,
|
public DefaultTbApiUsageStateService(TbClusterService clusterService,
|
||||||
PartitionService partitionService,
|
PartitionService partitionService,
|
||||||
ApiUsageStateService apiUsageStateService,
|
ApiUsageStateService apiUsageStateService,
|
||||||
TimeseriesService tsService,
|
TimeseriesService tsService, TelemetrySubscriptionService tsWsService,
|
||||||
SchedulerComponent scheduler,
|
SchedulerComponent scheduler,
|
||||||
TbTenantProfileCache tenantProfileCache) {
|
TbTenantProfileCache tenantProfileCache) {
|
||||||
this.clusterService = clusterService;
|
this.clusterService = clusterService;
|
||||||
this.partitionService = partitionService;
|
this.partitionService = partitionService;
|
||||||
this.apiUsageStateService = apiUsageStateService;
|
this.apiUsageStateService = apiUsageStateService;
|
||||||
this.tsService = tsService;
|
this.tsService = tsService;
|
||||||
|
this.tsWsService = tsWsService;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.tenantProfileCache = tenantProfileCache;
|
this.tenantProfileCache = tenantProfileCache;
|
||||||
}
|
}
|
||||||
@ -122,9 +141,9 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
|||||||
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.name(), 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(HOURLY + recordKey.name(), newHourlyValue)));
|
updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue)));
|
||||||
Pair<ApiFeature, Boolean> update = tenantState.checkStateUpdatedDueToThreshold(recordKey);
|
Pair<ApiFeature, Boolean> update = tenantState.checkStateUpdatedDueToThreshold(recordKey);
|
||||||
if (update != null) {
|
if (update != null) {
|
||||||
result.put(update.getFirst(), update.getSecond());
|
result.put(update.getFirst(), update.getSecond());
|
||||||
@ -133,10 +152,11 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
|||||||
} finally {
|
} finally {
|
||||||
updateLock.unlock();
|
updateLock.unlock();
|
||||||
}
|
}
|
||||||
tsService.save(tenantId, tenantState.getApiUsageState().getId(), updatedEntries, 0L);
|
tsWsService.saveAndNotify(tenantId, tenantState.getApiUsageState().getId(), updatedEntries, 0L, VOID_CALLBACK);
|
||||||
if (!result.isEmpty()) {
|
if (!result.isEmpty()) {
|
||||||
persistAndNotify(tenantState, result);
|
persistAndNotify(tenantState, result);
|
||||||
}
|
}
|
||||||
|
callback.onSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -149,12 +169,17 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiUsageState getApiUsageState(TenantId tenantId) {
|
public ApiUsageState getApiUsageState(TenantId tenantId) {
|
||||||
if (partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) {
|
TenantApiUsageState tenantState = myTenantStates.get(tenantId);
|
||||||
TenantApiUsageState state = getOrFetchState(tenantId);
|
if (tenantState != null) {
|
||||||
return state.getApiUsageState();
|
return tenantState.getApiUsageState();
|
||||||
} else {
|
} else {
|
||||||
ApiUsageState state = otherTenantStates.get(tenantId);
|
ApiUsageState state = otherTenantStates.get(tenantId);
|
||||||
if (state == null) {
|
if (state != null) {
|
||||||
|
return state;
|
||||||
|
} else {
|
||||||
|
if (partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) {
|
||||||
|
return getOrFetchState(tenantId).getApiUsageState();
|
||||||
|
} else {
|
||||||
updateLock.lock();
|
updateLock.lock();
|
||||||
try {
|
try {
|
||||||
state = otherTenantStates.get(tenantId);
|
state = otherTenantStates.get(tenantId);
|
||||||
@ -165,10 +190,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
|||||||
} finally {
|
} finally {
|
||||||
updateLock.unlock();
|
updateLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApiUsageStateUpdate(TenantId tenantId) {
|
||||||
|
otherTenantStates.remove(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTenantProfileUpdate(TenantProfileId tenantProfileId) {
|
public void onTenantProfileUpdate(TenantProfileId tenantProfileId) {
|
||||||
@ -199,29 +230,38 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onApiUsageStateUpdate(TenantId tenantId) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTenantState(TenantApiUsageState state, TenantProfile tenantProfile) {
|
private void updateTenantState(TenantApiUsageState state, TenantProfile tenantProfile) {
|
||||||
|
TenantProfileData oldProfileData = state.getTenantProfileData();
|
||||||
state.setTenantProfileData(tenantProfile.getProfileData());
|
state.setTenantProfileData(tenantProfile.getProfileData());
|
||||||
Map<ApiFeature, Boolean> result = state.checkStateUpdatedDueToThresholds();
|
Map<ApiFeature, Boolean> result = state.checkStateUpdatedDueToThresholds();
|
||||||
if (!result.isEmpty()) {
|
if (!result.isEmpty()) {
|
||||||
persistAndNotify(state, result);
|
persistAndNotify(state, result);
|
||||||
}
|
}
|
||||||
|
updateProfileThresholds(state.getTenantId(), state.getApiUsageState().getId(),
|
||||||
|
oldProfileData.getConfiguration(), tenantProfile.getProfileData().getConfiguration());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProfileThresholds(TenantId tenantId, ApiUsageStateId id,
|
||||||
|
TenantProfileConfiguration oldData, TenantProfileConfiguration newData) {
|
||||||
|
long ts = System.currentTimeMillis();
|
||||||
|
List<TsKvEntry> profileThresholds = new ArrayList<>();
|
||||||
|
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
|
||||||
|
if (oldData == null || oldData.getProfileThreshold(key) != newData.getProfileThreshold(key)) {
|
||||||
|
profileThresholds.add(new BasicTsKvEntry(ts, new LongDataEntry(key.getApiLimitKey(), newData.getProfileThreshold(key))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!profileThresholds.isEmpty()) {
|
||||||
|
tsWsService.saveAndNotify(tenantId, id, profileThresholds, 0L, VOID_CALLBACK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, Boolean> result) {
|
private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, Boolean> result) {
|
||||||
// TODO:
|
|
||||||
// 1. Broadcast to everyone notifications about enabled/disabled features.
|
|
||||||
// 2. Report rule engine and js executor metrics
|
|
||||||
// 4. UI for configuration of the thresholds
|
|
||||||
// 5. Max rule node executions per message.
|
|
||||||
apiUsageStateService.update(state.getApiUsageState());
|
apiUsageStateService.update(state.getApiUsageState());
|
||||||
if (result.containsKey(ApiFeature.TRANSPORT)) {
|
|
||||||
clusterService.onApiStateChange(state.getApiUsageState(), null);
|
clusterService.onApiStateChange(state.getApiUsageState(), null);
|
||||||
}
|
long ts = System.currentTimeMillis();
|
||||||
|
List<TsKvEntry> stateTelemetry = new ArrayList<>();
|
||||||
|
result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new BooleanDataEntry(apiFeature.getApiStateKey(), aState)))));
|
||||||
|
tsWsService.saveAndNotify(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, 0L, VOID_CALLBACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkStartOfNextCycle() {
|
private void checkStartOfNextCycle() {
|
||||||
@ -252,15 +292,15 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
|
|||||||
TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
|
TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
|
||||||
tenantState = new TenantApiUsageState(tenantProfile, dbStateEntity);
|
tenantState = new TenantApiUsageState(tenantProfile, dbStateEntity);
|
||||||
try {
|
try {
|
||||||
List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, dbStateEntity.getEntityId()).get();
|
List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, dbStateEntity.getId()).get();
|
||||||
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
|
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
|
||||||
boolean cycleEntryFound = false;
|
boolean cycleEntryFound = false;
|
||||||
boolean hourlyEntryFound = false;
|
boolean hourlyEntryFound = false;
|
||||||
for (TsKvEntry tsKvEntry : dbValues) {
|
for (TsKvEntry tsKvEntry : dbValues) {
|
||||||
if (tsKvEntry.getKey().equals(key.name())) {
|
if (tsKvEntry.getKey().equals(key.getApiCountKey())) {
|
||||||
cycleEntryFound = true;
|
cycleEntryFound = true;
|
||||||
tenantState.put(key, tsKvEntry.getLongValue().get());
|
tenantState.put(key, tsKvEntry.getLongValue().get());
|
||||||
} else if (tsKvEntry.getKey().equals(HOURLY + key.name())) {
|
} else if (tsKvEntry.getKey().equals(key.getApiCountKey() + HOURLY)) {
|
||||||
hourlyEntryFound = true;
|
hourlyEntryFound = true;
|
||||||
if (tsKvEntry.getTs() == tenantState.getCurrentHourTs()) {
|
if (tsKvEntry.getTs() == tenantState.getCurrentHourTs()) {
|
||||||
tenantState.putHourly(key, tsKvEntry.getLongValue().get());
|
tenantState.putHourly(key, tsKvEntry.getLongValue().get());
|
||||||
|
|||||||
@ -21,11 +21,10 @@ import org.springframework.data.util.Pair;
|
|||||||
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.TenantProfile;
|
import org.thingsboard.server.common.data.TenantProfile;
|
||||||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
|
||||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
|
||||||
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.msg.tools.SchedulerUtils;
|
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -101,30 +100,13 @@ public class TenantApiUsageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getProfileThreshold(ApiUsageRecordKey key) {
|
public long getProfileThreshold(ApiUsageRecordKey key) {
|
||||||
DefaultTenantProfileConfiguration config = (DefaultTenantProfileConfiguration) tenantProfileData.getConfiguration();
|
return tenantProfileData.getConfiguration().getProfileThreshold(key);
|
||||||
switch (key) {
|
|
||||||
case TRANSPORT_MSG_COUNT:
|
|
||||||
return config.getMaxTransportMessages();
|
|
||||||
case TRANSPORT_DP_COUNT:
|
|
||||||
return config.getMaxTransportDataPoints();
|
|
||||||
case JS_EXEC_COUNT:
|
|
||||||
return config.getMaxJSExecutions();
|
|
||||||
case RE_EXEC_COUNT:
|
|
||||||
return config.getMaxREExecutions();
|
|
||||||
case STORAGE_DP_COUNT:
|
|
||||||
return config.getMaxDPStorageDays();
|
|
||||||
}
|
|
||||||
return 0L;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TenantId getTenantId() {
|
public TenantId getTenantId() {
|
||||||
return apiUsageState.getTenantId();
|
return apiUsageState.getTenantId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityId getEntityId() {
|
|
||||||
return apiUsageState.getEntityId();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTransportEnabled() {
|
public boolean isTransportEnabled() {
|
||||||
return apiUsageState.isTransportEnabled();
|
return apiUsageState.isTransportEnabled();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,8 +129,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
tenantProfileService.findOrCreateDefaultTenantProfile(TenantId.SYS_TENANT_ID);
|
tenantProfileService.findOrCreateDefaultTenantProfile(TenantId.SYS_TENANT_ID);
|
||||||
|
|
||||||
TenantProfileData tenantProfileData = new TenantProfileData();
|
TenantProfileData tenantProfileData = new TenantProfileData();
|
||||||
DefaultTenantProfileConfiguration configuration = new DefaultTenantProfileConfiguration();
|
tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
|
||||||
tenantProfileData.setConfiguration(configuration);
|
|
||||||
|
|
||||||
TenantProfile isolatedTbCoreProfile = new TenantProfile();
|
TenantProfile isolatedTbCoreProfile = new TenantProfile();
|
||||||
isolatedTbCoreProfile.setDefault(false);
|
isolatedTbCoreProfile.setDefault(false);
|
||||||
|
|||||||
@ -21,7 +21,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
||||||
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.queue.usagestats.TbUsageStatsClient;
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
|
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -36,13 +37,15 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AbstractJsInvokeService implements JsInvokeService {
|
public abstract class AbstractJsInvokeService implements JsInvokeService {
|
||||||
|
|
||||||
private final TbUsageStatsClient apiUsageStatsClient;
|
private final TbApiUsageStateService apiUsageStateService;
|
||||||
|
private final TbApiUsageClient apiUsageClient;
|
||||||
protected ScheduledExecutorService timeoutExecutorService;
|
protected ScheduledExecutorService timeoutExecutorService;
|
||||||
protected Map<UUID, String> scriptIdToNameMap = new ConcurrentHashMap<>();
|
protected Map<UUID, String> scriptIdToNameMap = new ConcurrentHashMap<>();
|
||||||
protected Map<UUID, BlackListInfo> blackListedFunctions = new ConcurrentHashMap<>();
|
protected Map<UUID, DisableListInfo> disabledFunctions = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
protected AbstractJsInvokeService(TbUsageStatsClient apiUsageStatsClient) {
|
protected AbstractJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) {
|
||||||
this.apiUsageStatsClient = apiUsageStatsClient;
|
this.apiUsageStateService = apiUsageStateService;
|
||||||
|
this.apiUsageClient = apiUsageClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(long maxRequestsTimeout) {
|
public void init(long maxRequestsTimeout) {
|
||||||
@ -59,24 +62,32 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames) {
|
public ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames) {
|
||||||
|
if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) {
|
||||||
UUID scriptId = UUID.randomUUID();
|
UUID scriptId = UUID.randomUUID();
|
||||||
String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_');
|
String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_');
|
||||||
String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
|
String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
|
||||||
return doEval(scriptId, functionName, jsScript);
|
return doEval(scriptId, functionName, jsScript);
|
||||||
|
} else {
|
||||||
|
return Futures.immediateFailedFuture(new RuntimeException("JS Execution is disabled due to API limits!"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Object> invokeFunction(TenantId tenantId, UUID scriptId, Object... args) {
|
public ListenableFuture<Object> invokeFunction(TenantId tenantId, UUID scriptId, Object... args) {
|
||||||
|
if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) {
|
||||||
String functionName = scriptIdToNameMap.get(scriptId);
|
String functionName = scriptIdToNameMap.get(scriptId);
|
||||||
if (functionName == null) {
|
if (functionName == null) {
|
||||||
return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!"));
|
return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!"));
|
||||||
}
|
}
|
||||||
if (!isBlackListed(scriptId)) {
|
if (!isDisabled(scriptId)) {
|
||||||
apiUsageStatsClient.report(tenantId, ApiUsageRecordKey.JS_EXEC_COUNT, 1);
|
apiUsageClient.report(tenantId, ApiUsageRecordKey.JS_EXEC_COUNT, 1);
|
||||||
return doInvokeFunction(scriptId, functionName, args);
|
return doInvokeFunction(scriptId, functionName, args);
|
||||||
} else {
|
} else {
|
||||||
return Futures.immediateFailedFuture(
|
return Futures.immediateFailedFuture(
|
||||||
new RuntimeException("Script is blacklisted due to maximum error count " + getMaxErrors() + "!"));
|
new RuntimeException("Script invocation is blocked due to maximum error count " + getMaxErrors() + "!"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Futures.immediateFailedFuture(new RuntimeException("JS Execution is disabled due to API limits!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +97,7 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
|
|||||||
if (functionName != null) {
|
if (functionName != null) {
|
||||||
try {
|
try {
|
||||||
scriptIdToNameMap.remove(scriptId);
|
scriptIdToNameMap.remove(scriptId);
|
||||||
blackListedFunctions.remove(scriptId);
|
disabledFunctions.remove(scriptId);
|
||||||
doRelease(scriptId, functionName);
|
doRelease(scriptId, functionName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return Futures.immediateFailedFuture(e);
|
return Futures.immediateFailedFuture(e);
|
||||||
@ -106,7 +117,7 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
|
|||||||
protected abstract long getMaxBlacklistDuration();
|
protected abstract long getMaxBlacklistDuration();
|
||||||
|
|
||||||
protected void onScriptExecutionError(UUID scriptId) {
|
protected void onScriptExecutionError(UUID scriptId) {
|
||||||
blackListedFunctions.computeIfAbsent(scriptId, key -> new BlackListInfo()).incrementAndGet();
|
disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()).incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) {
|
private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) {
|
||||||
@ -116,11 +127,11 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
|
|||||||
throw new RuntimeException("No script factory implemented for scriptType: " + scriptType);
|
throw new RuntimeException("No script factory implemented for scriptType: " + scriptType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isBlackListed(UUID scriptId) {
|
private boolean isDisabled(UUID scriptId) {
|
||||||
BlackListInfo errorCount = blackListedFunctions.get(scriptId);
|
DisableListInfo errorCount = disabledFunctions.get(scriptId);
|
||||||
if (errorCount != null) {
|
if (errorCount != null) {
|
||||||
if (errorCount.getExpirationTime() <= System.currentTimeMillis()) {
|
if (errorCount.getExpirationTime() <= System.currentTimeMillis()) {
|
||||||
blackListedFunctions.remove(scriptId);
|
disabledFunctions.remove(scriptId);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return errorCount.get() >= getMaxErrors();
|
return errorCount.get() >= getMaxErrors();
|
||||||
@ -130,11 +141,11 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BlackListInfo {
|
private class DisableListInfo {
|
||||||
private final AtomicInteger counter;
|
private final AtomicInteger counter;
|
||||||
private long expirationTime;
|
private long expirationTime;
|
||||||
|
|
||||||
private BlackListInfo() {
|
private DisableListInfo() {
|
||||||
this.counter = new AtomicInteger(0);
|
this.counter = new AtomicInteger(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,10 +24,10 @@ import delight.nashornsandbox.NashornSandboxes;
|
|||||||
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.thingsboard.server.queue.usagestats.TbUsageStatsClient;
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
|
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
@ -65,8 +65,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
|
|||||||
@Value("${js.local.stats.enabled:false}")
|
@Value("${js.local.stats.enabled:false}")
|
||||||
private boolean statsEnabled;
|
private boolean statsEnabled;
|
||||||
|
|
||||||
public AbstractNashornJsInvokeService(TbUsageStatsClient apiUsageStatsClient, JsExecutorService jsExecutor) {
|
public AbstractNashornJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient, JsExecutorService jsExecutor) {
|
||||||
super(apiUsageStatsClient);
|
super(apiUsageStateService, apiUsageClient);
|
||||||
this.jsExecutor = jsExecutor;
|
this.jsExecutor = jsExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.server.queue.usagestats.TbUsageStatsClient;
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
|
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ public class NashornJsInvokeService extends AbstractNashornJsInvokeService {
|
|||||||
@Value("${js.local.max_black_list_duration_sec:60}")
|
@Value("${js.local.max_black_list_duration_sec:60}")
|
||||||
private int maxBlackListDurationSec;
|
private int maxBlackListDurationSec;
|
||||||
|
|
||||||
public NashornJsInvokeService(TbUsageStatsClient apiUsageStatsClient, JsExecutorService jsExecutor) {
|
public NashornJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient, JsExecutorService jsExecutor) {
|
||||||
super(apiUsageStatsClient, jsExecutor);
|
super(apiUsageStateService, apiUsageClient, jsExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -30,7 +30,8 @@ import org.thingsboard.server.gen.js.JsInvokeProtos;
|
|||||||
import org.thingsboard.server.queue.TbQueueRequestTemplate;
|
import org.thingsboard.server.queue.TbQueueRequestTemplate;
|
||||||
import org.thingsboard.server.queue.common.TbProtoJsQueueMsg;
|
import org.thingsboard.server.queue.common.TbProtoJsQueueMsg;
|
||||||
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
|
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
|
||||||
import org.thingsboard.server.queue.usagestats.TbUsageStatsClient;
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
|
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@ -69,8 +70,8 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
|
|||||||
private final AtomicInteger queueFailedMsgs = new AtomicInteger(0);
|
private final AtomicInteger queueFailedMsgs = new AtomicInteger(0);
|
||||||
private final AtomicInteger queueTimeoutMsgs = new AtomicInteger(0);
|
private final AtomicInteger queueTimeoutMsgs = new AtomicInteger(0);
|
||||||
|
|
||||||
public RemoteJsInvokeService(TbUsageStatsClient apiUsageStatsClient) {
|
public RemoteJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) {
|
||||||
super(apiUsageStatsClient);
|
super(apiUsageStateService, apiUsageClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(fixedDelayString = "${js.remote.stats.print_interval_ms}")
|
@Scheduled(fixedDelayString = "${js.remote.stats.print_interval_ms}")
|
||||||
|
|||||||
@ -15,12 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.common.data;
|
package org.thingsboard.server.common.data;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
public enum ApiUsageRecordKey {
|
public enum ApiUsageRecordKey {
|
||||||
|
|
||||||
TRANSPORT_MSG_COUNT,
|
TRANSPORT_MSG_COUNT("transportMsgCount", "transportMsgLimit"),
|
||||||
TRANSPORT_DP_COUNT,
|
TRANSPORT_DP_COUNT("transportDataPointsCount", "transportDataPointsLimit"),
|
||||||
STORAGE_DP_COUNT,
|
STORAGE_DP_COUNT("storageDataPointsCount", "storageDataPointsLimit"),
|
||||||
RE_EXEC_COUNT,
|
RE_EXEC_COUNT("ruleEngineExecutionCount", "ruleEngineExecutionLimit"),
|
||||||
JS_EXEC_COUNT
|
JS_EXEC_COUNT("jsExecutionCount", "jsExecutionLimit");
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final String apiCountKey;
|
||||||
|
@Getter
|
||||||
|
private final String apiLimitKey;
|
||||||
|
|
||||||
|
ApiUsageRecordKey(String apiCountKey, String apiLimitKey) {
|
||||||
|
this.apiCountKey = apiCountKey;
|
||||||
|
this.apiLimitKey = apiLimitKey;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
package org.thingsboard.server.common.data.tenant.profile;
|
package org.thingsboard.server.common.data.tenant.profile;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||||
import org.thingsboard.server.common.data.TenantProfileType;
|
import org.thingsboard.server.common.data.TenantProfileType;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@ -38,6 +39,24 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
|
|||||||
private long maxDPStorageDays;
|
private long maxDPStorageDays;
|
||||||
private int maxRuleNodeExecutionsPerMessage;
|
private int maxRuleNodeExecutionsPerMessage;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getProfileThreshold(ApiUsageRecordKey key) {
|
||||||
|
switch (key) {
|
||||||
|
case TRANSPORT_MSG_COUNT:
|
||||||
|
return maxTransportMessages;
|
||||||
|
case TRANSPORT_DP_COUNT:
|
||||||
|
return maxTransportDataPoints;
|
||||||
|
case JS_EXEC_COUNT:
|
||||||
|
return maxJSExecutions;
|
||||||
|
case RE_EXEC_COUNT:
|
||||||
|
return maxREExecutions;
|
||||||
|
case STORAGE_DP_COUNT:
|
||||||
|
return maxDPStorageDays;
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TenantProfileType getType() {
|
public TenantProfileType getType() {
|
||||||
return TenantProfileType.DEFAULT;
|
return TenantProfileType.DEFAULT;
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
|||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||||
import org.thingsboard.server.common.data.TenantProfileType;
|
import org.thingsboard.server.common.data.TenantProfileType;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@ -33,4 +34,7 @@ public interface TenantProfileConfiguration {
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
TenantProfileType getType();
|
TenantProfileType getType();
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
long getProfileThreshold(ApiUsageRecordKey key);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DefaultTbUsageStatsClient implements TbUsageStatsClient {
|
public class DefaultTbApiUsageClient implements TbApiUsageClient {
|
||||||
|
|
||||||
@Value("${usage.stats.report.enabled:true}")
|
@Value("${usage.stats.report.enabled:true}")
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
@ -53,7 +53,7 @@ public class DefaultTbUsageStatsClient implements TbUsageStatsClient {
|
|||||||
private final TbQueueProducerProvider producerProvider;
|
private final TbQueueProducerProvider producerProvider;
|
||||||
private TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> msgProducer;
|
private TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> msgProducer;
|
||||||
|
|
||||||
public DefaultTbUsageStatsClient(PartitionService partitionService, SchedulerComponent scheduler, TbQueueProducerProvider producerProvider) {
|
public DefaultTbApiUsageClient(PartitionService partitionService, SchedulerComponent scheduler, TbQueueProducerProvider producerProvider) {
|
||||||
this.partitionService = partitionService;
|
this.partitionService = partitionService;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.producerProvider = producerProvider;
|
this.producerProvider = producerProvider;
|
||||||
@ -109,4 +109,8 @@ public class DefaultTbUsageStatsClient implements TbUsageStatsClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void report(TenantId tenantId, ApiUsageRecordKey key) {
|
||||||
|
report(tenantId, key, 1L);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -18,9 +18,10 @@ package org.thingsboard.server.queue.usagestats;
|
|||||||
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
public interface TbUsageStatsClient {
|
public interface TbApiUsageClient {
|
||||||
|
|
||||||
void report(TenantId tenantId, ApiUsageRecordKey key, long value);
|
void report(TenantId tenantId, ApiUsageRecordKey key, long value);
|
||||||
|
|
||||||
|
void report(TenantId tenantId, ApiUsageRecordKey key);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -78,7 +78,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
|
|||||||
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
|
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
|
||||||
import org.thingsboard.server.queue.provider.TbTransportQueueFactory;
|
import org.thingsboard.server.queue.provider.TbTransportQueueFactory;
|
||||||
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
|
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
|
||||||
import org.thingsboard.server.queue.usagestats.TbUsageStatsClient;
|
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||||
import org.thingsboard.server.queue.util.TbTransportComponent;
|
import org.thingsboard.server.queue.util.TbTransportComponent;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@ -123,7 +123,7 @@ public class DefaultTransportService implements TransportService {
|
|||||||
private final StatsFactory statsFactory;
|
private final StatsFactory statsFactory;
|
||||||
private final TransportDeviceProfileCache deviceProfileCache;
|
private final TransportDeviceProfileCache deviceProfileCache;
|
||||||
private final TransportTenantProfileCache tenantProfileCache;
|
private final TransportTenantProfileCache tenantProfileCache;
|
||||||
private final TbUsageStatsClient apiUsageStatsClient;
|
private final TbApiUsageClient apiUsageClient;
|
||||||
private final TransportRateLimitService rateLimitService;
|
private final TransportRateLimitService rateLimitService;
|
||||||
private final DataDecodingEncodingService dataDecodingEncodingService;
|
private final DataDecodingEncodingService dataDecodingEncodingService;
|
||||||
private final SchedulerComponent scheduler;
|
private final SchedulerComponent scheduler;
|
||||||
@ -152,7 +152,7 @@ public class DefaultTransportService implements TransportService {
|
|||||||
StatsFactory statsFactory,
|
StatsFactory statsFactory,
|
||||||
TransportDeviceProfileCache deviceProfileCache,
|
TransportDeviceProfileCache deviceProfileCache,
|
||||||
TransportTenantProfileCache tenantProfileCache,
|
TransportTenantProfileCache tenantProfileCache,
|
||||||
TbUsageStatsClient apiUsageStatsClient, TransportRateLimitService rateLimitService,
|
TbApiUsageClient apiUsageClient, TransportRateLimitService rateLimitService,
|
||||||
DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler) {
|
DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler) {
|
||||||
this.serviceInfoProvider = serviceInfoProvider;
|
this.serviceInfoProvider = serviceInfoProvider;
|
||||||
this.queueProvider = queueProvider;
|
this.queueProvider = queueProvider;
|
||||||
@ -161,7 +161,7 @@ public class DefaultTransportService implements TransportService {
|
|||||||
this.statsFactory = statsFactory;
|
this.statsFactory = statsFactory;
|
||||||
this.deviceProfileCache = deviceProfileCache;
|
this.deviceProfileCache = deviceProfileCache;
|
||||||
this.tenantProfileCache = tenantProfileCache;
|
this.tenantProfileCache = tenantProfileCache;
|
||||||
this.apiUsageStatsClient = apiUsageStatsClient;
|
this.apiUsageClient = apiUsageClient;
|
||||||
this.rateLimitService = rateLimitService;
|
this.rateLimitService = rateLimitService;
|
||||||
this.dataDecodingEncodingService = dataDecodingEncodingService;
|
this.dataDecodingEncodingService = dataDecodingEncodingService;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
@ -647,6 +647,7 @@ public class DefaultTransportService implements TransportService {
|
|||||||
if (stateOpt.isPresent()) {
|
if (stateOpt.isPresent()) {
|
||||||
ApiUsageState apiUsageState = stateOpt.get();
|
ApiUsageState apiUsageState = stateOpt.get();
|
||||||
rateLimitService.update(apiUsageState.getTenantId(), apiUsageState.isTransportEnabled());
|
rateLimitService.update(apiUsageState.getTenantId(), apiUsageState.isTransportEnabled());
|
||||||
|
//TODO: if transport is disabled, we should close all sessions and not to check credentials.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (toSessionMsg.hasEntityDeleteMsg()) {
|
} else if (toSessionMsg.hasEntityDeleteMsg()) {
|
||||||
@ -821,8 +822,8 @@ public class DefaultTransportService implements TransportService {
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(T msg) {
|
public void onSuccess(T msg) {
|
||||||
try {
|
try {
|
||||||
apiUsageStatsClient.report(tenantId, ApiUsageRecordKey.TRANSPORT_MSG_COUNT, 1);
|
apiUsageClient.report(tenantId, ApiUsageRecordKey.TRANSPORT_MSG_COUNT, 1);
|
||||||
apiUsageStatsClient.report(tenantId, ApiUsageRecordKey.TRANSPORT_DP_COUNT, dataPoints);
|
apiUsageClient.report(tenantId, ApiUsageRecordKey.TRANSPORT_DP_COUNT, dataPoints);
|
||||||
} finally {
|
} finally {
|
||||||
callback.onSuccess(msg);
|
callback.onSuccess(msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,4 +51,5 @@ public interface TenantProfileRepository extends PagingAndSortingRepository<Tena
|
|||||||
"FROM TenantProfileEntity t " +
|
"FROM TenantProfileEntity t " +
|
||||||
"WHERE t.isDefault = true")
|
"WHERE t.isDefault = true")
|
||||||
EntityInfo findDefaultTenantProfileInfo();
|
EntityInfo findDefaultTenantProfileInfo();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,4 +37,5 @@ public interface TenantProfileDao extends Dao<TenantProfile> {
|
|||||||
TenantProfile findDefaultTenantProfile(TenantId tenantId);
|
TenantProfile findDefaultTenantProfile(TenantId tenantId);
|
||||||
|
|
||||||
EntityInfo findDefaultTenantProfileInfo(TenantId tenantId);
|
EntityInfo findDefaultTenantProfileInfo(TenantId tenantId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,15 +17,26 @@ package org.thingsboard.server.dao.usagerecord;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.ApiUsageState;
|
||||||
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.ApiUsageState;
|
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.kv.BasicTsKvEntry;
|
||||||
|
import org.thingsboard.server.common.data.kv.LongDataEntry;
|
||||||
|
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
||||||
|
import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
|
||||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
import org.thingsboard.server.dao.service.DataValidator;
|
import org.thingsboard.server.dao.service.DataValidator;
|
||||||
import org.thingsboard.server.dao.tenant.TenantDao;
|
import org.thingsboard.server.dao.tenant.TenantDao;
|
||||||
|
import org.thingsboard.server.dao.tenant.TenantProfileDao;
|
||||||
|
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.thingsboard.server.dao.service.Validator.validateId;
|
import static org.thingsboard.server.dao.service.Validator.validateId;
|
||||||
|
|
||||||
@ -35,11 +46,15 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
|
|||||||
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
||||||
|
|
||||||
private final ApiUsageStateDao apiUsageStateDao;
|
private final ApiUsageStateDao apiUsageStateDao;
|
||||||
|
private final TenantProfileDao tenantProfileDao;
|
||||||
private final TenantDao tenantDao;
|
private final TenantDao tenantDao;
|
||||||
|
private final TimeseriesService tsService;
|
||||||
|
|
||||||
public ApiUsageStateServiceImpl(TenantDao tenantDao, ApiUsageStateDao apiUsageStateDao) {
|
public ApiUsageStateServiceImpl(TenantDao tenantDao, ApiUsageStateDao apiUsageStateDao, TenantProfileDao tenantProfileDao, TimeseriesService tsService) {
|
||||||
this.tenantDao = tenantDao;
|
this.tenantDao = tenantDao;
|
||||||
this.apiUsageStateDao = apiUsageStateDao;
|
this.apiUsageStateDao = apiUsageStateDao;
|
||||||
|
this.tenantProfileDao = tenantProfileDao;
|
||||||
|
this.tsService = tsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -57,7 +72,18 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
|
|||||||
apiUsageState.setTenantId(tenantId);
|
apiUsageState.setTenantId(tenantId);
|
||||||
apiUsageState.setEntityId(tenantId);
|
apiUsageState.setEntityId(tenantId);
|
||||||
apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId);
|
apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId);
|
||||||
return apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
|
|
||||||
|
ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
|
||||||
|
|
||||||
|
Tenant tenant = tenantDao.findById(tenantId, tenantId.getId());
|
||||||
|
TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId());
|
||||||
|
TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration();
|
||||||
|
List<TsKvEntry> profileThresholds = new ArrayList<>();
|
||||||
|
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
|
||||||
|
profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key))));
|
||||||
|
}
|
||||||
|
tsService.save(tenantId, saved.getId(), profileThresholds, 0L);
|
||||||
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -80,7 +106,8 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
|
|||||||
log.trace("Executing findApiUsageStateById, tenantId [{}], apiUsageStateId [{}]", tenantId, id);
|
log.trace("Executing findApiUsageStateById, tenantId [{}], apiUsageStateId [{}]", tenantId, id);
|
||||||
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
|
||||||
validateId(id, "Incorrect apiUsageStateId " + id);
|
validateId(id, "Incorrect apiUsageStateId " + id);
|
||||||
return apiUsageStateDao.findById(tenantId, id.getId()); }
|
return apiUsageStateDao.findById(tenantId, id.getId());
|
||||||
|
}
|
||||||
|
|
||||||
private DataValidator<ApiUsageState> apiUsageStateValidator =
|
private DataValidator<ApiUsageState> apiUsageStateValidator =
|
||||||
new DataValidator<ApiUsageState>() {
|
new DataValidator<ApiUsageState>() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user