Rate limits to tenant profile BE refactoring
This commit is contained in:
parent
cb071ac0a2
commit
487d7165cc
@ -48,22 +48,20 @@ public class RateLimitProcessingFilter extends GenericFilterBean {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TbTenantProfileCache tenantProfileCache;
|
private TbTenantProfileCache tenantProfileCache;
|
||||||
|
|
||||||
private ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
|
private final ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
|
||||||
private ConcurrentMap<CustomerId, TbRateLimits> perCustomerLimits = new ConcurrentHashMap<>();
|
private final ConcurrentMap<CustomerId, TbRateLimits> perCustomerLimits = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||||
SecurityUser user = getCurrentUser();
|
SecurityUser user = getCurrentUser();
|
||||||
if (user != null && !user.isSystemAdmin()) {
|
if (user != null && !user.isSystemAdmin()) {
|
||||||
|
|
||||||
var profileConfiguration = tenantProfileCache.get(user.getTenantId()).getDefaultTenantProfileConfiguration();
|
var profileConfiguration = tenantProfileCache.get(user.getTenantId()).getDefaultTenantProfileConfiguration();
|
||||||
|
if (profileConfiguration != null) {
|
||||||
if(profileConfiguration != null) {
|
if (user.isTenantAdmin() && StringUtils.isNotEmpty(profileConfiguration.getTenantServerRestLimitsConfiguration())) {
|
||||||
if (user.isTenantAdmin() && StringUtils.isNotEmpty(profileConfiguration.getRateLimitsTenantConfiguration())) {
|
|
||||||
|
|
||||||
TbRateLimits rateLimits = perTenantLimits.get(user.getTenantId());
|
TbRateLimits rateLimits = perTenantLimits.get(user.getTenantId());
|
||||||
if(rateLimits == null || !rateLimits.getCurrentConfig().equals(profileConfiguration.getRateLimitsTenantConfiguration())) {
|
if (rateLimits == null || !rateLimits.getConfiguration().equals(profileConfiguration.getTenantServerRestLimitsConfiguration())) {
|
||||||
rateLimits = new TbRateLimits(profileConfiguration.getRateLimitsTenantConfiguration());
|
// fixme: or maybe handle component lifecycle event ?
|
||||||
|
rateLimits = new TbRateLimits(profileConfiguration.getTenantServerRestLimitsConfiguration());
|
||||||
perTenantLimits.put(user.getTenantId(), rateLimits);
|
perTenantLimits.put(user.getTenantId(), rateLimits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,12 +69,10 @@ public class RateLimitProcessingFilter extends GenericFilterBean {
|
|||||||
errorResponseHandler.handle(new TbRateLimitsException(EntityType.TENANT), (HttpServletResponse) response);
|
errorResponseHandler.handle(new TbRateLimitsException(EntityType.TENANT), (HttpServletResponse) response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
} else if (user.isCustomerUser() && StringUtils.isNotEmpty(profileConfiguration.getCustomerServerRestLimitsConfiguration())) {
|
||||||
if (user.isCustomerUser() && StringUtils.isNotEmpty(profileConfiguration.getRateLimitsCustomerConfiguration())) {
|
|
||||||
|
|
||||||
TbRateLimits rateLimits = perCustomerLimits.get(user.getCustomerId());
|
TbRateLimits rateLimits = perCustomerLimits.get(user.getCustomerId());
|
||||||
if(rateLimits == null || !rateLimits.getCurrentConfig().equals(profileConfiguration.getRateLimitsCustomerConfiguration())) {
|
if (rateLimits == null || !rateLimits.getConfiguration().equals(profileConfiguration.getCustomerServerRestLimitsConfiguration())) {
|
||||||
rateLimits = new TbRateLimits(profileConfiguration.getRateLimitsCustomerConfiguration());
|
rateLimits = new TbRateLimits(profileConfiguration.getCustomerServerRestLimitsConfiguration());
|
||||||
perCustomerLimits.put(user.getCustomerId(), rateLimits);
|
perCustomerLimits.put(user.getCustomerId(), rateLimits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,12 +16,12 @@
|
|||||||
package org.thingsboard.server.controller.plugin;
|
package org.thingsboard.server.controller.plugin;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.socket.CloseStatus;
|
import org.springframework.web.socket.CloseStatus;
|
||||||
import org.springframework.web.socket.PongMessage;
|
import org.springframework.web.socket.PongMessage;
|
||||||
import org.springframework.web.socket.TextMessage;
|
import org.springframework.web.socket.TextMessage;
|
||||||
@ -139,8 +139,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantProfileConfiguration();
|
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantProfileConfiguration();
|
||||||
|
internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, tenantProfileConfiguration.getWsMsgQueueLimitPerSession()));
|
||||||
internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, tenantProfileConfiguration.getWsLimitQueuePerWsSession()));
|
|
||||||
|
|
||||||
externalSessionMap.put(externalSessionId, internalSessionId);
|
externalSessionMap.put(externalSessionId, internalSessionId);
|
||||||
processInWebSocketService(sessionRef, SessionEvent.onEstablished());
|
processInWebSocketService(sessionRef, SessionEvent.onEstablished());
|
||||||
@ -298,14 +297,13 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
String externalId = sessionRef.getSessionId();
|
String externalId = sessionRef.getSessionId();
|
||||||
log.debug("[{}] Processing {}", externalId, msg);
|
log.debug("[{}] Processing {}", externalId, msg);
|
||||||
String internalId = externalSessionMap.get(externalId);
|
String internalId = externalSessionMap.get(externalId);
|
||||||
|
|
||||||
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantProfileConfiguration();
|
|
||||||
|
|
||||||
if (internalId != null) {
|
if (internalId != null) {
|
||||||
SessionMetaData sessionMd = internalSessionMap.get(internalId);
|
SessionMetaData sessionMd = internalSessionMap.get(internalId);
|
||||||
if (sessionMd != null) {
|
if (sessionMd != null) {
|
||||||
if (!StringUtils.isEmpty(tenantProfileConfiguration.getWsLimitUpdatesPerSession())) {
|
var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantProfileConfiguration();
|
||||||
TbRateLimits rateLimits = perSessionUpdateLimits.computeIfAbsent(sessionRef.getSessionId(), sid -> new TbRateLimits(tenantProfileConfiguration.getWsLimitUpdatesPerSession()));
|
if (StringUtils.isNotEmpty(tenantProfileConfiguration.getWsUpdatesPerSessionRateLimit())) {
|
||||||
|
// fixme: is this ok not to update rate limits config if it was change in profile?
|
||||||
|
TbRateLimits rateLimits = perSessionUpdateLimits.computeIfAbsent(sessionRef.getSessionId(), sid -> new TbRateLimits(tenantProfileConfiguration.getWsUpdatesPerSessionRateLimit()));
|
||||||
if (!rateLimits.tryConsume()) {
|
if (!rateLimits.tryConsume()) {
|
||||||
if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) {
|
if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) {
|
||||||
log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached"
|
log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached"
|
||||||
@ -364,15 +362,15 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
var tenantProfileConfiguration =
|
var tenantProfileConfiguration =
|
||||||
tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantProfileConfiguration();
|
tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantProfileConfiguration();
|
||||||
|
|
||||||
if(tenantProfileConfiguration == null) {
|
if (tenantProfileConfiguration == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String sessionId = session.getId();
|
String sessionId = session.getId();
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant() > 0) {
|
if (tenantProfileConfiguration.getMaxWsSessionsPerTenant() > 0) {
|
||||||
Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (tenantSessions) {
|
synchronized (tenantSessions) {
|
||||||
if (tenantSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant()) {
|
if (tenantSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerTenant()) {
|
||||||
tenantSessions.add(sessionId);
|
tenantSessions.add(sessionId);
|
||||||
} else {
|
} else {
|
||||||
log.info("[{}][{}][{}] Failed to start session. Max tenant sessions limit reached"
|
log.info("[{}][{}][{}] Failed to start session. Max tenant sessions limit reached"
|
||||||
@ -384,10 +382,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sessionRef.getSecurityCtx().isCustomerUser()) {
|
if (sessionRef.getSecurityCtx().isCustomerUser()) {
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer() > 0) {
|
if (tenantProfileConfiguration.getMaxWsSessionsPerCustomer() > 0) {
|
||||||
Set<String> customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (customerSessions) {
|
synchronized (customerSessions) {
|
||||||
if (customerSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer()) {
|
if (customerSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerCustomer()) {
|
||||||
customerSessions.add(sessionId);
|
customerSessions.add(sessionId);
|
||||||
} else {
|
} else {
|
||||||
log.info("[{}][{}][{}] Failed to start session. Max customer sessions limit reached"
|
log.info("[{}][{}][{}] Failed to start session. Max customer sessions limit reached"
|
||||||
@ -397,11 +395,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser() > 0
|
if (tenantProfileConfiguration.getMaxWsSessionsPerRegularUser() > 0
|
||||||
&& UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
&& UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
||||||
Set<String> regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (regularUserSessions) {
|
synchronized (regularUserSessions) {
|
||||||
if (regularUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser()) {
|
if (regularUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerRegularUser()) {
|
||||||
regularUserSessions.add(sessionId);
|
regularUserSessions.add(sessionId);
|
||||||
} else {
|
} else {
|
||||||
log.info("[{}][{}][{}] Failed to start session. Max regular user sessions limit reached"
|
log.info("[{}][{}][{}] Failed to start session. Max regular user sessions limit reached"
|
||||||
@ -411,11 +409,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser() > 0
|
if (tenantProfileConfiguration.getMaxWsSessionsPerPublicUser() > 0
|
||||||
&& UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
&& UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
||||||
Set<String> publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (publicUserSessions) {
|
synchronized (publicUserSessions) {
|
||||||
if (publicUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser()) {
|
if (publicUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerPublicUser()) {
|
||||||
publicUserSessions.add(sessionId);
|
publicUserSessions.add(sessionId);
|
||||||
} else {
|
} else {
|
||||||
log.info("[{}][{}][{}] Failed to start session. Max public user sessions limit reached"
|
log.info("[{}][{}][{}] Failed to start session. Max public user sessions limit reached"
|
||||||
@ -435,26 +433,26 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
String sessionId = session.getId();
|
String sessionId = session.getId();
|
||||||
perSessionUpdateLimits.remove(sessionRef.getSessionId());
|
perSessionUpdateLimits.remove(sessionRef.getSessionId());
|
||||||
blacklistedSessions.remove(sessionRef.getSessionId());
|
blacklistedSessions.remove(sessionRef.getSessionId());
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant() > 0) {
|
if (tenantProfileConfiguration.getMaxWsSessionsPerTenant() > 0) {
|
||||||
Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (tenantSessions) {
|
synchronized (tenantSessions) {
|
||||||
tenantSessions.remove(sessionId);
|
tenantSessions.remove(sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sessionRef.getSecurityCtx().isCustomerUser()) {
|
if (sessionRef.getSecurityCtx().isCustomerUser()) {
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer() > 0) {
|
if (tenantProfileConfiguration.getMaxWsSessionsPerCustomer() > 0) {
|
||||||
Set<String> customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (customerSessions) {
|
synchronized (customerSessions) {
|
||||||
customerSessions.remove(sessionId);
|
customerSessions.remove(sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
if (tenantProfileConfiguration.getMaxWsSessionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
||||||
Set<String> regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (regularUserSessions) {
|
synchronized (regularUserSessions) {
|
||||||
regularUserSessions.remove(sessionId);
|
regularUserSessions.remove(sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
if (tenantProfileConfiguration.getMaxWsSessionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
||||||
Set<String> publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (publicUserSessions) {
|
synchronized (publicUserSessions) {
|
||||||
publicUserSessions.remove(sessionId);
|
publicUserSessions.remove(sessionId);
|
||||||
|
|||||||
@ -216,6 +216,9 @@ public class ThingsboardInstallService {
|
|||||||
dataUpdateService.updateData("3.3.2");
|
dataUpdateService.updateData("3.3.2");
|
||||||
log.info("Updating system data...");
|
log.info("Updating system data...");
|
||||||
systemDataLoaderService.updateSystemWidgets();
|
systemDataLoaderService.updateSystemWidgets();
|
||||||
|
case "3.3.3":
|
||||||
|
log.info("Upgrading ThingsBoard from version 3.3.3 to 3.4 ...");
|
||||||
|
dataUpdateService.updateData("3.3.3");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//TODO update CacheCleanupService on the next version upgrade
|
//TODO update CacheCleanupService on the next version upgrade
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
|
||||||
import org.thingsboard.rule.engine.flow.TbRuleChainInputNode;
|
import org.thingsboard.rule.engine.flow.TbRuleChainInputNode;
|
||||||
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
|
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
|
||||||
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
|
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
|
||||||
@ -56,12 +55,9 @@ import org.thingsboard.server.common.data.rule.RuleChainType;
|
|||||||
import org.thingsboard.server.common.data.rule.RuleNode;
|
import org.thingsboard.server.common.data.rule.RuleNode;
|
||||||
import org.thingsboard.server.dao.DaoUtil;
|
import org.thingsboard.server.dao.DaoUtil;
|
||||||
import org.thingsboard.server.dao.alarm.AlarmDao;
|
import org.thingsboard.server.dao.alarm.AlarmDao;
|
||||||
import org.thingsboard.server.dao.alarm.AlarmService;
|
|
||||||
import org.thingsboard.server.dao.entity.EntityService;
|
import org.thingsboard.server.dao.entity.EntityService;
|
||||||
import org.thingsboard.server.dao.entityview.EntityViewService;
|
import org.thingsboard.server.dao.entityview.EntityViewService;
|
||||||
import org.thingsboard.server.dao.model.sql.DeviceProfileEntity;
|
import org.thingsboard.server.dao.model.sql.DeviceProfileEntity;
|
||||||
import org.thingsboard.server.dao.model.sql.RelationEntity;
|
|
||||||
import org.thingsboard.server.dao.oauth2.OAuth2Service;
|
|
||||||
import org.thingsboard.server.dao.relation.RelationService;
|
import org.thingsboard.server.dao.relation.RelationService;
|
||||||
import org.thingsboard.server.dao.rule.RuleChainService;
|
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||||
import org.thingsboard.server.dao.sql.device.DeviceProfileRepository;
|
import org.thingsboard.server.dao.sql.device.DeviceProfileRepository;
|
||||||
@ -101,9 +97,6 @@ public class DefaultDataUpdateService implements DataUpdateService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TimeseriesService tsService;
|
private TimeseriesService tsService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AlarmService alarmService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EntityService entityService;
|
private EntityService entityService;
|
||||||
|
|
||||||
@ -113,9 +106,6 @@ public class DefaultDataUpdateService implements DataUpdateService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DeviceProfileRepository deviceProfileRepository;
|
private DeviceProfileRepository deviceProfileRepository;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private OAuth2Service oAuth2Service;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RateLimitsUpdater rateLimitsUpdater;
|
private RateLimitsUpdater rateLimitsUpdater;
|
||||||
|
|
||||||
@ -140,12 +130,15 @@ public class DefaultDataUpdateService implements DataUpdateService {
|
|||||||
tenantsAlarmsCustomerUpdater.updateEntities(null);
|
tenantsAlarmsCustomerUpdater.updateEntities(null);
|
||||||
deviceProfileEntityDynamicConditionsUpdater.updateEntities(null);
|
deviceProfileEntityDynamicConditionsUpdater.updateEntities(null);
|
||||||
updateOAuth2Params();
|
updateOAuth2Params();
|
||||||
rateLimitsUpdater.updateEntities(null);
|
|
||||||
break;
|
break;
|
||||||
case "3.3.2":
|
case "3.3.2":
|
||||||
log.info("Updating data from version 3.3.2 to 3.3.3 ...");
|
log.info("Updating data from version 3.3.2 to 3.3.3 ...");
|
||||||
updateNestedRuleChains();
|
updateNestedRuleChains();
|
||||||
break;
|
break;
|
||||||
|
case "3.3.3":
|
||||||
|
log.info("Updating data from version 3.3.3 to 3.4 ...");
|
||||||
|
rateLimitsUpdater.updateEntities();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
|
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,10 @@ public abstract class PaginatedUpdater<I, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateEntities() {
|
||||||
|
updateEntities(null);
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean forceReportTotal() {
|
protected boolean forceReportTotal() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,53 +15,53 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.install.update;
|
package org.thingsboard.server.service.install.update;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
|
||||||
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.page.PageData;
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
import org.thingsboard.server.common.data.page.PageLink;
|
import org.thingsboard.server.common.data.page.PageLink;
|
||||||
import org.thingsboard.server.dao.tenant.TenantProfileService;
|
import org.thingsboard.server.dao.tenant.TenantProfileService;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class RateLimitsUpdater extends PaginatedUpdater<String, TenantProfile> {
|
class RateLimitsUpdater extends PaginatedUpdater<String, TenantProfile> {
|
||||||
|
|
||||||
@Value("${server.rest.limits.tenant.enabled}")
|
@Value("${server.rest.limits.tenant.enabled}")
|
||||||
boolean tenantServerRateLimitsEnabled;
|
boolean tenantServerRestLimitsEnabled;
|
||||||
@Value("${server.rest.limits.customer.enabled}")
|
|
||||||
boolean customerServerRateLimitsEnabled;
|
|
||||||
@Value("${server.rest.limits.tenant.configuration}")
|
@Value("${server.rest.limits.tenant.configuration}")
|
||||||
String tenantServerRateLimitsConfiguration;
|
String tenantServerRestLimitsConfiguration;
|
||||||
|
@Value("${server.rest.limits.customer.enabled}")
|
||||||
|
boolean customerServerRestLimitsEnabled;
|
||||||
@Value("${server.rest.limits.customer.configuration}")
|
@Value("${server.rest.limits.customer.configuration}")
|
||||||
String customerServerRateLimitsConfiguration;
|
String customerServerRestLimitsConfiguration;
|
||||||
|
|
||||||
@Value("${server.ws.limits.max_sessions_per_tenant}")
|
@Value("${server.ws.limits.max_sessions_per_tenant}")
|
||||||
private int wsLimitMaxSessionsPerTenant;
|
private int maxWsSessionsPerTenant;
|
||||||
@Value("${server.ws.limits.max_sessions_per_customer}")
|
@Value("${server.ws.limits.max_sessions_per_customer}")
|
||||||
private int wsLimitMaxSessionsPerCustomer;
|
private int maxWsSessionsPerCustomer;
|
||||||
@Value("${server.ws.limits.max_sessions_per_regular_user}")
|
@Value("${server.ws.limits.max_sessions_per_regular_user}")
|
||||||
private int wsLimitMaxSessionsPerRegularUser;
|
private int maxWsSessionsPerRegularUser;
|
||||||
@Value("${server.ws.limits.max_sessions_per_public_user}")
|
@Value("${server.ws.limits.max_sessions_per_public_user}")
|
||||||
private int wsLimitMaxSessionsPerPublicUser;
|
private int maxWsSessionsPerPublicUser;
|
||||||
@Value("${server.ws.limits.max_queue_per_ws_session}")
|
@Value("${server.ws.limits.max_queue_per_ws_session}")
|
||||||
private int wsLimitQueuePerWsSession;
|
private int wsMsgQueueLimitPerSession;
|
||||||
@Value("${server.ws.limits.max_subscriptions_per_tenant}")
|
@Value("${server.ws.limits.max_subscriptions_per_tenant}")
|
||||||
private long wsLimitMaxSubscriptionsPerTenant;
|
private long maxWsSubscriptionsPerTenant;
|
||||||
@Value("${server.ws.limits.max_subscriptions_per_customer}")
|
@Value("${server.ws.limits.max_subscriptions_per_customer}")
|
||||||
private long wsLimitMaxSubscriptionsPerCustomer;
|
private long maxWsSubscriptionsPerCustomer;
|
||||||
@Value("${server.ws.limits.max_subscriptions_per_regular_user}")
|
@Value("${server.ws.limits.max_subscriptions_per_regular_user}")
|
||||||
private long wsLimitMaxSubscriptionsPerRegularUser;
|
private long maxWsSubscriptionsPerRegularUser;
|
||||||
@Value("${server.ws.limits.max_subscriptions_per_public_user}")
|
@Value("${server.ws.limits.max_subscriptions_per_public_user}")
|
||||||
private long wsLimitMaxSubscriptionsPerPublicUser;
|
private long maxWsSubscriptionsPerPublicUser;
|
||||||
@Value("${server.ws.limits.max_updates_per_session}")
|
@Value("${server.ws.limits.max_updates_per_session}")
|
||||||
private String wsLimitUpdatesPerSession;
|
private String wsUpdatesPerSessionRateLimit;
|
||||||
|
|
||||||
@Value("${cassandra.query.tenant_rate_limits.enabled}")
|
@Value("${cassandra.query.tenant_rate_limits.enabled}")
|
||||||
private boolean cassandraLimitsIsEnabled;
|
private boolean cassandraQueryTenantRateLimitsEnabled;
|
||||||
@Value("${cassandra.query.tenant_rate_limits.configuration}")
|
@Value("${cassandra.query.tenant_rate_limits.configuration}")
|
||||||
private String cassandraTenantLimitsConfiguration;
|
private String cassandraQueryTenantRateLimitsConfiguration;
|
||||||
@Value("${cassandra.query.tenant_rate_limits.print_tenant_names}")
|
|
||||||
private boolean printTenantNames;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TenantProfileService tenantProfileService;
|
private TenantProfileService tenantProfileService;
|
||||||
@ -78,40 +78,38 @@ class RateLimitsUpdater extends PaginatedUpdater<String, TenantProfile> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PageData<TenantProfile> findEntities(String id, PageLink pageLink) {
|
protected PageData<TenantProfile> findEntities(String id, PageLink pageLink) {
|
||||||
return tenantProfileService.findTenantProfiles(null, pageLink);
|
return tenantProfileService.findTenantProfiles(TenantId.SYS_TENANT_ID, pageLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateEntity(TenantProfile entity) {
|
protected void updateEntity(TenantProfile tenantProfile) {
|
||||||
var profileConfiguration = entity.getDefaultTenantProfileConfiguration();
|
var profileConfiguration = tenantProfile.getDefaultTenantProfileConfiguration();
|
||||||
if (profileConfiguration != null) {
|
|
||||||
if (tenantServerRateLimitsEnabled && StringUtils.isNotEmpty(tenantServerRateLimitsConfiguration)) {
|
if (tenantServerRestLimitsEnabled && StringUtils.isNotEmpty(tenantServerRestLimitsConfiguration)) {
|
||||||
profileConfiguration.setRateLimitsTenantConfiguration(tenantServerRateLimitsConfiguration);
|
profileConfiguration.setTenantServerRestLimitsConfiguration(tenantServerRestLimitsConfiguration);
|
||||||
}
|
}
|
||||||
if (customerServerRateLimitsEnabled && StringUtils.isNotEmpty(customerServerRateLimitsConfiguration)) {
|
if (customerServerRestLimitsEnabled && StringUtils.isNotEmpty(customerServerRestLimitsConfiguration)) {
|
||||||
profileConfiguration.setRateLimitsCustomerConfiguration(customerServerRateLimitsConfiguration);
|
profileConfiguration.setCustomerServerRestLimitsConfiguration(customerServerRestLimitsConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
profileConfiguration.setWsLimitMaxSessionsPerTenant(wsLimitMaxSessionsPerTenant);
|
profileConfiguration.setMaxWsSessionsPerTenant(maxWsSessionsPerTenant);
|
||||||
profileConfiguration.setWsLimitMaxSessionsPerCustomer(wsLimitMaxSessionsPerCustomer);
|
profileConfiguration.setMaxWsSessionsPerCustomer(maxWsSessionsPerCustomer);
|
||||||
profileConfiguration.setWsLimitMaxSessionsPerPublicUser(wsLimitMaxSessionsPerPublicUser);
|
profileConfiguration.setMaxWsSessionsPerPublicUser(maxWsSessionsPerPublicUser);
|
||||||
profileConfiguration.setWsLimitMaxSessionsPerRegularUser(wsLimitMaxSessionsPerRegularUser);
|
profileConfiguration.setMaxWsSessionsPerRegularUser(maxWsSessionsPerRegularUser);
|
||||||
profileConfiguration.setWsLimitMaxSubscriptionsPerTenant(wsLimitMaxSubscriptionsPerTenant);
|
profileConfiguration.setMaxWsSubscriptionsPerTenant(maxWsSubscriptionsPerTenant);
|
||||||
profileConfiguration.setWsLimitMaxSubscriptionsPerCustomer(wsLimitMaxSubscriptionsPerCustomer);
|
profileConfiguration.setMaxWsSubscriptionsPerCustomer(maxWsSubscriptionsPerCustomer);
|
||||||
profileConfiguration.setWsLimitMaxSubscriptionsPerPublicUser(wsLimitMaxSubscriptionsPerPublicUser);
|
profileConfiguration.setMaxWsSubscriptionsPerPublicUser(maxWsSubscriptionsPerPublicUser);
|
||||||
profileConfiguration.setWsLimitMaxSubscriptionsPerRegularUser(wsLimitMaxSubscriptionsPerRegularUser);
|
profileConfiguration.setMaxWsSubscriptionsPerRegularUser(maxWsSubscriptionsPerRegularUser);
|
||||||
profileConfiguration.setWsLimitQueuePerWsSession(wsLimitQueuePerWsSession);
|
profileConfiguration.setWsMsgQueueLimitPerSession(wsMsgQueueLimitPerSession);
|
||||||
|
if (StringUtils.isNotEmpty(wsUpdatesPerSessionRateLimit)) {
|
||||||
if (StringUtils.isNotEmpty(wsLimitUpdatesPerSession)) {
|
profileConfiguration.setWsUpdatesPerSessionRateLimit(wsUpdatesPerSessionRateLimit);
|
||||||
profileConfiguration.setWsLimitUpdatesPerSession(wsLimitUpdatesPerSession);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cassandraLimitsIsEnabled) {
|
if (cassandraQueryTenantRateLimitsEnabled && StringUtils.isNotEmpty(cassandraQueryTenantRateLimitsConfiguration)) {
|
||||||
profileConfiguration.setCassandraTenantLimitsConfiguration(cassandraTenantLimitsConfiguration);
|
profileConfiguration.setCassandraQueryTenantRateLimitsConfiguration(cassandraQueryTenantRateLimitsConfiguration);
|
||||||
profileConfiguration.setPrintTenantNames(printTenantNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tenantProfileService.saveTenantProfile(null, entity);
|
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.socket.CloseStatus;
|
import org.springframework.web.socket.CloseStatus;
|
||||||
@ -304,29 +305,29 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
|
|||||||
|
|
||||||
private void processSessionClose(TelemetryWebSocketSessionRef sessionRef) {
|
private void processSessionClose(TelemetryWebSocketSessionRef sessionRef) {
|
||||||
var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantProfileConfiguration();
|
var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantProfileConfiguration();
|
||||||
if(tenantProfileConfiguration != null) {
|
if (tenantProfileConfiguration != null) {
|
||||||
String sessionId = "[" + sessionRef.getSessionId() + "]";
|
String sessionId = "[" + sessionRef.getSessionId() + "]";
|
||||||
|
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant() > 0) {
|
if (tenantProfileConfiguration.getMaxWsSubscriptionsPerTenant() > 0) {
|
||||||
Set<String> tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (tenantSubscriptions) {
|
synchronized (tenantSubscriptions) {
|
||||||
tenantSubscriptions.removeIf(subId -> subId.startsWith(sessionId));
|
tenantSubscriptions.removeIf(subId -> subId.startsWith(sessionId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sessionRef.getSecurityCtx().isCustomerUser()) {
|
if (sessionRef.getSecurityCtx().isCustomerUser()) {
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer() > 0) {
|
if (tenantProfileConfiguration.getMaxWsSubscriptionsPerCustomer() > 0) {
|
||||||
Set<String> customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (customerSessions) {
|
synchronized (customerSessions) {
|
||||||
customerSessions.removeIf(subId -> subId.startsWith(sessionId));
|
customerSessions.removeIf(subId -> subId.startsWith(sessionId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
if (tenantProfileConfiguration.getMaxWsSubscriptionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
||||||
Set<String> regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (regularUserSessions) {
|
synchronized (regularUserSessions) {
|
||||||
regularUserSessions.removeIf(subId -> subId.startsWith(sessionId));
|
regularUserSessions.removeIf(subId -> subId.startsWith(sessionId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
if (tenantProfileConfiguration.getMaxWsSubscriptionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
||||||
Set<String> publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (publicUserSessions) {
|
synchronized (publicUserSessions) {
|
||||||
publicUserSessions.removeIf(subId -> subId.startsWith(sessionId));
|
publicUserSessions.removeIf(subId -> subId.startsWith(sessionId));
|
||||||
@ -341,12 +342,12 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
|
|||||||
|
|
||||||
String subId = "[" + sessionRef.getSessionId() + "]:[" + cmd.getCmdId() + "]";
|
String subId = "[" + sessionRef.getSessionId() + "]:[" + cmd.getCmdId() + "]";
|
||||||
try {
|
try {
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant() > 0) {
|
if (tenantProfileConfiguration.getMaxWsSubscriptionsPerTenant() > 0) {
|
||||||
Set<String> tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (tenantSubscriptions) {
|
synchronized (tenantSubscriptions) {
|
||||||
if (cmd.isUnsubscribe()) {
|
if (cmd.isUnsubscribe()) {
|
||||||
tenantSubscriptions.remove(subId);
|
tenantSubscriptions.remove(subId);
|
||||||
} else if (tenantSubscriptions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant()) {
|
} else if (tenantSubscriptions.size() < tenantProfileConfiguration.getMaxWsSubscriptionsPerTenant()) {
|
||||||
tenantSubscriptions.add(subId);
|
tenantSubscriptions.add(subId);
|
||||||
} else {
|
} else {
|
||||||
log.info("[{}][{}][{}] Failed to start subscription. Max tenant subscriptions limit reached"
|
log.info("[{}][{}][{}] Failed to start subscription. Max tenant subscriptions limit reached"
|
||||||
@ -358,12 +359,12 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sessionRef.getSecurityCtx().isCustomerUser()) {
|
if (sessionRef.getSecurityCtx().isCustomerUser()) {
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer() > 0) {
|
if (tenantProfileConfiguration.getMaxWsSubscriptionsPerCustomer() > 0) {
|
||||||
Set<String> customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (customerSessions) {
|
synchronized (customerSessions) {
|
||||||
if (cmd.isUnsubscribe()) {
|
if (cmd.isUnsubscribe()) {
|
||||||
customerSessions.remove(subId);
|
customerSessions.remove(subId);
|
||||||
} else if (customerSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer()) {
|
} else if (customerSessions.size() < tenantProfileConfiguration.getMaxWsSubscriptionsPerCustomer()) {
|
||||||
customerSessions.add(subId);
|
customerSessions.add(subId);
|
||||||
} else {
|
} else {
|
||||||
log.info("[{}][{}][{}] Failed to start subscription. Max customer subscriptions limit reached"
|
log.info("[{}][{}][{}] Failed to start subscription. Max customer subscriptions limit reached"
|
||||||
@ -373,10 +374,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
if (tenantProfileConfiguration.getMaxWsSubscriptionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
||||||
Set<String> regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (regularUserSessions) {
|
synchronized (regularUserSessions) {
|
||||||
if (regularUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser()) {
|
if (regularUserSessions.size() < tenantProfileConfiguration.getMaxWsSubscriptionsPerRegularUser()) {
|
||||||
regularUserSessions.add(subId);
|
regularUserSessions.add(subId);
|
||||||
} else {
|
} else {
|
||||||
log.info("[{}][{}][{}] Failed to start subscription. Max regular user subscriptions limit reached"
|
log.info("[{}][{}][{}] Failed to start subscription. Max regular user subscriptions limit reached"
|
||||||
@ -386,10 +387,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
if (tenantProfileConfiguration.getMaxWsSubscriptionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) {
|
||||||
Set<String> publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
Set<String> publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet());
|
||||||
synchronized (publicUserSessions) {
|
synchronized (publicUserSessions) {
|
||||||
if (publicUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser()) {
|
if (publicUserSessions.size() < tenantProfileConfiguration.getMaxWsSubscriptionsPerPublicUser()) {
|
||||||
publicUserSessions.add(subId);
|
publicUserSessions.add(subId);
|
||||||
} else {
|
} else {
|
||||||
log.info("[{}][{}][{}] Failed to start subscription. Max public user subscriptions limit reached"
|
log.info("[{}][{}][{}] Failed to start subscription. Max public user subscriptions limit reached"
|
||||||
|
|||||||
@ -83,7 +83,7 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
|
|||||||
@ApiModelProperty(position = 1, value = "JSON object with the tenant profile Id. " +
|
@ApiModelProperty(position = 1, value = "JSON object with the tenant profile Id. " +
|
||||||
"Specify this field to update the tenant profile. " +
|
"Specify this field to update the tenant profile. " +
|
||||||
"Referencing non-existing tenant profile Id will cause error. " +
|
"Referencing non-existing tenant profile Id will cause error. " +
|
||||||
"Omit this field to create new tenant profile." )
|
"Omit this field to create new tenant profile.")
|
||||||
@Override
|
@Override
|
||||||
public TenantProfileId getId() {
|
public TenantProfileId getId() {
|
||||||
return super.getId();
|
return super.getId();
|
||||||
@ -133,6 +133,7 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
|
|||||||
public TenantProfileData createDefaultTenantProfileData() {
|
public TenantProfileData createDefaultTenantProfileData() {
|
||||||
TenantProfileData tpd = new TenantProfileData();
|
TenantProfileData tpd = new TenantProfileData();
|
||||||
tpd.setConfiguration(new DefaultTenantProfileConfiguration());
|
tpd.setConfiguration(new DefaultTenantProfileConfiguration());
|
||||||
|
this.profileData = tpd;
|
||||||
return tpd;
|
return tpd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,11 +148,7 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
|
|||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public DefaultTenantProfileConfiguration getDefaultTenantProfileConfiguration() {
|
public DefaultTenantProfileConfiguration getDefaultTenantProfileConfiguration() {
|
||||||
if(getProfileData().getConfiguration() != null &&
|
return getProfileConfiguration().orElse(null);
|
||||||
getProfileData().getConfiguration().getType().equals(TenantProfileType.DEFAULT)) {
|
|
||||||
return (DefaultTenantProfileConfiguration) this.profileData.getConfiguration();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,22 +46,21 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
|
|||||||
private String transportDeviceTelemetryMsgRateLimit;
|
private String transportDeviceTelemetryMsgRateLimit;
|
||||||
private String transportDeviceTelemetryDataPointsRateLimit;
|
private String transportDeviceTelemetryDataPointsRateLimit;
|
||||||
|
|
||||||
private String rateLimitsTenantConfiguration;
|
private String tenantServerRestLimitsConfiguration;
|
||||||
private String rateLimitsCustomerConfiguration;
|
private String customerServerRestLimitsConfiguration;
|
||||||
|
|
||||||
private int wsLimitMaxSessionsPerTenant;
|
private int maxWsSessionsPerTenant;
|
||||||
private int wsLimitMaxSessionsPerCustomer;
|
private int maxWsSessionsPerCustomer;
|
||||||
private int wsLimitMaxSessionsPerRegularUser;
|
private int maxWsSessionsPerRegularUser;
|
||||||
private int wsLimitMaxSessionsPerPublicUser;
|
private int maxWsSessionsPerPublicUser;
|
||||||
private int wsLimitQueuePerWsSession;
|
private int wsMsgQueueLimitPerSession;
|
||||||
private long wsLimitMaxSubscriptionsPerTenant;
|
private long maxWsSubscriptionsPerTenant;
|
||||||
private long wsLimitMaxSubscriptionsPerCustomer;
|
private long maxWsSubscriptionsPerCustomer;
|
||||||
private long wsLimitMaxSubscriptionsPerRegularUser;
|
private long maxWsSubscriptionsPerRegularUser;
|
||||||
private long wsLimitMaxSubscriptionsPerPublicUser;
|
private long maxWsSubscriptionsPerPublicUser;
|
||||||
private String wsLimitUpdatesPerSession;
|
private String wsUpdatesPerSessionRateLimit;
|
||||||
|
|
||||||
private String cassandraTenantLimitsConfiguration;
|
private String cassandraQueryTenantRateLimitsConfiguration;
|
||||||
private boolean printTenantNames;
|
|
||||||
|
|
||||||
private long maxTransportMessages;
|
private long maxTransportMessages;
|
||||||
private long maxTransportDataPoints;
|
private long maxTransportDataPoints;
|
||||||
|
|||||||
@ -29,11 +29,10 @@ import java.time.Duration;
|
|||||||
public class TbRateLimits {
|
public class TbRateLimits {
|
||||||
private final LocalBucket bucket;
|
private final LocalBucket bucket;
|
||||||
@Getter
|
@Getter
|
||||||
private final String currentConfig;
|
private final String configuration;
|
||||||
|
|
||||||
public TbRateLimits(String limitsConfiguration) {
|
public TbRateLimits(String limitsConfiguration) {
|
||||||
LocalBucketBuilder builder = Bucket4j.builder();
|
LocalBucketBuilder builder = Bucket4j.builder();
|
||||||
currentConfig = limitsConfiguration;
|
|
||||||
boolean initialized = false;
|
boolean initialized = false;
|
||||||
for (String limitSrc : limitsConfiguration.split(",")) {
|
for (String limitSrc : limitsConfiguration.split(",")) {
|
||||||
long capacity = Long.parseLong(limitSrc.split(":")[0]);
|
long capacity = Long.parseLong(limitSrc.split(":")[0]);
|
||||||
@ -46,8 +45,7 @@ public class TbRateLimits {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Failed to parse rate limits configuration: " + limitsConfiguration);
|
throw new IllegalArgumentException("Failed to parse rate limits configuration: " + limitsConfiguration);
|
||||||
}
|
}
|
||||||
|
this.configuration = limitsConfiguration;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean tryConsume() {
|
public boolean tryConsume() {
|
||||||
|
|||||||
@ -22,9 +22,7 @@ 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.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.msg.tools.TbRateLimits;
|
|
||||||
import org.thingsboard.server.common.stats.DefaultCounter;
|
import org.thingsboard.server.common.stats.DefaultCounter;
|
||||||
import org.thingsboard.server.common.stats.StatsCounter;
|
import org.thingsboard.server.common.stats.StatsCounter;
|
||||||
import org.thingsboard.server.common.stats.StatsFactory;
|
import org.thingsboard.server.common.stats.StatsFactory;
|
||||||
@ -33,7 +31,6 @@ import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
|
|||||||
import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor;
|
import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor;
|
||||||
import org.thingsboard.server.dao.util.AsyncTaskContext;
|
import org.thingsboard.server.dao.util.AsyncTaskContext;
|
||||||
import org.thingsboard.server.dao.util.NoSqlAnyDao;
|
import org.thingsboard.server.dao.util.NoSqlAnyDao;
|
||||||
import org.thingsboard.server.dao.util.TenantRateLimitException;
|
|
||||||
|
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
|
|
||||||
@ -45,13 +42,6 @@ import javax.annotation.PreDestroy;
|
|||||||
@NoSqlAnyDao
|
@NoSqlAnyDao
|
||||||
public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecutor<CassandraStatementTask, TbResultSetFuture, TbResultSet> {
|
public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecutor<CassandraStatementTask, TbResultSetFuture, TbResultSet> {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private EntityService entityService;
|
|
||||||
@Autowired
|
|
||||||
private TbTenantProfileCache tenantProfileCache;
|
|
||||||
|
|
||||||
private Map<TenantId, String> tenantNamesCache = new HashMap<>();
|
|
||||||
|
|
||||||
static final String BUFFER_NAME = "Read";
|
static final String BUFFER_NAME = "Read";
|
||||||
|
|
||||||
public CassandraBufferedRateReadExecutor(
|
public CassandraBufferedRateReadExecutor(
|
||||||
@ -64,9 +54,10 @@ public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecu
|
|||||||
@Value("${cassandra.query.tenant_rate_limits.print_tenant_names}") boolean printTenantNames,
|
@Value("${cassandra.query.tenant_rate_limits.print_tenant_names}") boolean printTenantNames,
|
||||||
@Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq,
|
@Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq,
|
||||||
@Autowired StatsFactory statsFactory,
|
@Autowired StatsFactory statsFactory,
|
||||||
@Autowired EntityService entityService) {
|
@Autowired EntityService entityService,
|
||||||
|
@Autowired TbTenantProfileCache tenantProfileCache) {
|
||||||
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory,
|
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory,
|
||||||
entityService, printTenantNames);
|
entityService, tenantProfileCache, printTenantNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}")
|
@Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}")
|
||||||
@ -104,24 +95,4 @@ public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecu
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean checkRateLimits(CassandraStatementTask task, SettableFuture<TbResultSet> future) {
|
|
||||||
var tenantProfileConfiguration = tenantProfileCache.get(task.getTenantId()).getDefaultTenantProfileConfiguration();
|
|
||||||
if (StringUtils.isNotEmpty(tenantProfileConfiguration.getCassandraTenantLimitsConfiguration())) {
|
|
||||||
if (task.getTenantId() == null) {
|
|
||||||
log.info("Invalid task received: {}", task);
|
|
||||||
} else if (!task.getTenantId().isNullUid()) {
|
|
||||||
TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(
|
|
||||||
task.getTenantId(), id -> new TbRateLimits(tenantProfileConfiguration.getCassandraTenantLimitsConfiguration())
|
|
||||||
);
|
|
||||||
if (!rateLimits.tryConsume()) {
|
|
||||||
stats.incrementRateLimitedTenant(task.getTenantId());
|
|
||||||
stats.getTotalRateLimited().increment();
|
|
||||||
future.setException(new TenantRateLimitException());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import org.springframework.scheduling.annotation.Scheduled;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.server.common.stats.StatsFactory;
|
import org.thingsboard.server.common.stats.StatsFactory;
|
||||||
import org.thingsboard.server.dao.entity.EntityService;
|
import org.thingsboard.server.dao.entity.EntityService;
|
||||||
|
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
|
||||||
import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor;
|
import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor;
|
||||||
import org.thingsboard.server.dao.util.AsyncTaskContext;
|
import org.thingsboard.server.dao.util.AsyncTaskContext;
|
||||||
import org.thingsboard.server.dao.util.NoSqlAnyDao;
|
import org.thingsboard.server.dao.util.NoSqlAnyDao;
|
||||||
@ -47,14 +48,13 @@ public class CassandraBufferedRateWriteExecutor extends AbstractBufferedRateExec
|
|||||||
@Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads,
|
@Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads,
|
||||||
@Value("${cassandra.query.callback_threads:4}") int callbackThreads,
|
@Value("${cassandra.query.callback_threads:4}") int callbackThreads,
|
||||||
@Value("${cassandra.query.poll_ms:50}") long pollMs,
|
@Value("${cassandra.query.poll_ms:50}") long pollMs,
|
||||||
@Value("${cassandra.query.tenant_rate_limits.enabled}") boolean tenantRateLimitsEnabled,
|
|
||||||
@Value("${cassandra.query.tenant_rate_limits.configuration}") String tenantRateLimitsConfiguration,
|
|
||||||
@Value("${cassandra.query.tenant_rate_limits.print_tenant_names}") boolean printTenantNames,
|
@Value("${cassandra.query.tenant_rate_limits.print_tenant_names}") boolean printTenantNames,
|
||||||
@Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq,
|
@Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq,
|
||||||
@Autowired StatsFactory statsFactory,
|
@Autowired StatsFactory statsFactory,
|
||||||
@Autowired EntityService entityService) {
|
@Autowired EntityService entityService,
|
||||||
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, tenantRateLimitsEnabled, tenantRateLimitsConfiguration, printQueriesFreq, statsFactory,
|
@Autowired TbTenantProfileCache tenantProfileCache) {
|
||||||
entityService, printTenantNames);
|
super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory,
|
||||||
|
entityService, tenantProfileCache, printTenantNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}")
|
@Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}")
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import com.google.common.util.concurrent.SettableFuture;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.thingsboard.common.util.ThingsBoardExecutors;
|
import org.thingsboard.common.util.ThingsBoardExecutors;
|
||||||
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
||||||
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.msg.tools.TbRateLimits;
|
import org.thingsboard.server.common.msg.tools.TbRateLimits;
|
||||||
import org.thingsboard.server.common.stats.DefaultCounter;
|
import org.thingsboard.server.common.stats.DefaultCounter;
|
||||||
@ -38,6 +39,7 @@ import org.thingsboard.server.common.stats.StatsFactory;
|
|||||||
import org.thingsboard.server.common.stats.StatsType;
|
import org.thingsboard.server.common.stats.StatsType;
|
||||||
import org.thingsboard.server.dao.entity.EntityService;
|
import org.thingsboard.server.dao.entity.EntityService;
|
||||||
import org.thingsboard.server.dao.nosql.CassandraStatementTask;
|
import org.thingsboard.server.dao.nosql.CassandraStatementTask;
|
||||||
|
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -71,22 +73,22 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
|
|||||||
private final ScheduledExecutorService timeoutExecutor;
|
private final ScheduledExecutorService timeoutExecutor;
|
||||||
private final int concurrencyLimit;
|
private final int concurrencyLimit;
|
||||||
private final int printQueriesFreq;
|
private final int printQueriesFreq;
|
||||||
protected final ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
|
private final ConcurrentMap<TenantId, TbRateLimits> perTenantLimits = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final AtomicInteger printQueriesIdx = new AtomicInteger(0);
|
private final AtomicInteger printQueriesIdx = new AtomicInteger(0);
|
||||||
|
|
||||||
protected final AtomicInteger concurrencyLevel;
|
protected final AtomicInteger concurrencyLevel;
|
||||||
protected final BufferedRateExecutorStats stats;
|
protected final BufferedRateExecutorStats stats;
|
||||||
|
|
||||||
|
|
||||||
private final EntityService entityService;
|
private final EntityService entityService;
|
||||||
private final Map<TenantId, String> tenantNamesCache = new HashMap<>();
|
private final TbTenantProfileCache tenantProfileCache;
|
||||||
|
|
||||||
private final boolean printTenantNames;
|
private final boolean printTenantNames;
|
||||||
|
private final Map<TenantId, String> tenantNamesCache = new HashMap<>();
|
||||||
|
|
||||||
public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads,
|
public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads,
|
||||||
int callbackThreads, long pollMs, int printQueriesFreq, StatsFactory statsFactory,
|
int callbackThreads, long pollMs, int printQueriesFreq, StatsFactory statsFactory,
|
||||||
EntityService entityService, boolean printTenantNames) {
|
EntityService entityService, TbTenantProfileCache tenantProfileCache, boolean printTenantNames) {
|
||||||
this.maxWaitTime = maxWaitTime;
|
this.maxWaitTime = maxWaitTime;
|
||||||
this.pollMs = pollMs;
|
this.pollMs = pollMs;
|
||||||
this.concurrencyLimit = concurrencyLimit;
|
this.concurrencyLimit = concurrencyLimit;
|
||||||
@ -100,6 +102,7 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
|
|||||||
this.concurrencyLevel = statsFactory.createGauge(concurrencyLevelKey, new AtomicInteger(0));
|
this.concurrencyLevel = statsFactory.createGauge(concurrencyLevelKey, new AtomicInteger(0));
|
||||||
|
|
||||||
this.entityService = entityService;
|
this.entityService = entityService;
|
||||||
|
this.tenantProfileCache = tenantProfileCache;
|
||||||
this.printTenantNames = printTenantNames;
|
this.printTenantNames = printTenantNames;
|
||||||
|
|
||||||
for (int i = 0; i < dispatcherThreads; i++) {
|
for (int i = 0; i < dispatcherThreads; i++) {
|
||||||
@ -107,13 +110,28 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean checkRateLimits(T task, SettableFuture<V> future);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public F submit(T task) {
|
public F submit(T task) {
|
||||||
SettableFuture<V> settableFuture = create();
|
SettableFuture<V> settableFuture = create();
|
||||||
F result = wrap(task, settableFuture);
|
F result = wrap(task, settableFuture);
|
||||||
boolean perTenantLimitReached = checkRateLimits(task, settableFuture);
|
|
||||||
|
boolean perTenantLimitReached = false;
|
||||||
|
var tenantProfileConfiguration = tenantProfileCache.get(task.getTenantId()).getDefaultTenantProfileConfiguration();
|
||||||
|
if (StringUtils.isNotEmpty(tenantProfileConfiguration.getCassandraQueryTenantRateLimitsConfiguration())) {
|
||||||
|
if (task.getTenantId() == null) {
|
||||||
|
log.info("Invalid task received: {}", task);
|
||||||
|
} else if (!task.getTenantId().isNullUid()) {
|
||||||
|
TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(
|
||||||
|
task.getTenantId(), id -> new TbRateLimits(tenantProfileConfiguration.getCassandraQueryTenantRateLimitsConfiguration())
|
||||||
|
);
|
||||||
|
if (!rateLimits.tryConsume()) {
|
||||||
|
stats.incrementRateLimitedTenant(task.getTenantId());
|
||||||
|
stats.getTotalRateLimited().increment();
|
||||||
|
settableFuture.setException(new TenantRateLimitException());
|
||||||
|
perTenantLimitReached = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!perTenantLimitReached) {
|
if (!perTenantLimitReached) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user