diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index d96e50ca7f..742642d8f6 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -474,6 +474,9 @@ cache: userSettings: timeToLiveInMinutes: "${CACHE_SPECS_USER_SETTINGS_TTL:1440}" maxSize: "${CACHE_SPECS_USER_SETTINGS_MAX_SIZE:100000}" + entityCount: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_COUNT_TTL:1440}" + maxSize: "${CACHE_SPECS_ENTITY_COUNT_MAX_SIZE:100000}" #Disable this because it is not required. spring.data.redis.repositories.enabled: false diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 451efafe84..4383ccc0a7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -17,11 +17,11 @@ package org.thingsboard.server.dao.device; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; -import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; @@ -118,5 +118,4 @@ public interface DeviceService extends EntityDaoService { PageData findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink); - long countByTenantId(TenantId tenantId); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityCountService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityCountService.java new file mode 100644 index 0000000000..955f658a8d --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityCountService.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.entity; + +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; + +public interface EntityCountService { + + long countByTenantIdAndEntityType(TenantId tenantId, EntityType entityType); + + void publishCountEntityEvictEvent(TenantId tenantId, EntityType entityType); +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityDaoService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityDaoService.java index 23b7e8b91c..e1b8bec633 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityDaoService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityDaoService.java @@ -26,6 +26,10 @@ public interface EntityDaoService { Optional> findEntity(TenantId tenantId, EntityId entityId); + default long countByTenantId(TenantId tenantId) { + throw new IllegalArgumentException("Not implemented for " + getEntityType()); + } + EntityType getEntityType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index eedf925d61..056e0afca5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -42,4 +42,5 @@ public class CacheConstants { public static final String TWO_FA_VERIFICATION_CODES_CACHE = "twoFaVerificationCodes"; public static final String VERSION_CONTROL_TASK_CACHE = "versionControlTask"; public static final String USER_SETTINGS_CACHE = "userSettings"; + public static final String ENTITY_COUNT_CACHE = "entityCount"; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 97dedb717b..38f030e9ec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.entity.EntityCountService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -83,6 +84,9 @@ public class BaseAssetService extends AbstractCachedEntityService assetValidator; + @Autowired + private EntityCountService countService; + @TransactionalEventListener(classes = AssetCacheEvictEvent.class) @Override public void handleEvictEvent(AssetCacheEvictEvent event) { @@ -151,6 +155,9 @@ public class BaseAssetService extends AbstractCachedEntityService customerValidator; + @Autowired + private EntityCountService countService; + @Override public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerById [{}]", customerId); @@ -105,6 +109,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom try { Customer savedCustomer = customerDao.save(customer.getTenantId(), customer); dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId()); + if (customer.getId() == null) { + countService.publishCountEntityEvictEvent(savedCustomer.getTenantId(), EntityType.CUSTOMER); + } return savedCustomer; } catch (Exception e) { checkConstraintViolation(e, "customer_external_id_unq_key", "Customer with such external id already exists!"); @@ -131,6 +138,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom deleteEntityRelations(tenantId, customerId); apiUsageStateService.deleteApiUsageStateByEntityId(customerId); customerDao.removeById(tenantId, customerId.getId()); + countService.publishCountEntityEvictEvent(tenantId, EntityType.CUSTOMER); } @Override @@ -149,7 +157,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom } catch (IOException e) { throw new IncorrectParameterException("Unable to create public customer.", e); } - return customerDao.save(tenantId, publicCustomer); + Customer savedCustomer = customerDao.save(tenantId, publicCustomer); + countService.publishCountEntityEvictEvent(tenantId, EntityType.CUSTOMER); + return savedCustomer; } } @@ -166,6 +176,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId); Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); customersByTenantRemover.removeEntities(tenantId, tenantId); + countService.publishCountEntityEvictEvent(tenantId, EntityType.CUSTOMER); } private PaginatedRemover customersByTenantRemover = @@ -187,6 +198,11 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom return Optional.ofNullable(findCustomerById(tenantId, new CustomerId(entityId.getId()))); } + @Override + public long countByTenantId(TenantId tenantId) { + return customerDao.countByTenantId(tenantId); + } + @Override public EntityType getEntityType() { return EntityType.CUSTOMER; diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 96d56148eb..37d23eca7e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.dashboard; import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; @@ -39,6 +40,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.EntityCountService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -51,6 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service("DashboardDaoService") @Slf4j +@RequiredArgsConstructor public class DashboardServiceImpl extends AbstractEntityService implements DashboardService { public static final String INCORRECT_DASHBOARD_ID = "Incorrect dashboardId "; @@ -70,6 +73,9 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Autowired private DataValidator dashboardValidator; + @Autowired + private EntityCountService countService; + @Override public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardById [{}]", dashboardId); @@ -103,7 +109,11 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb log.trace("Executing saveDashboard [{}]", dashboard); dashboardValidator.validate(dashboard, DashboardInfo::getTenantId); try { - return dashboardDao.save(dashboard.getTenantId(), dashboard); + Dashboard savedDashboard = dashboardDao.save(dashboard.getTenantId(), dashboard); + if (dashboard.getId() == null) { + countService.publishCountEntityEvictEvent(savedDashboard.getTenantId(), EntityType.DASHBOARD); + } + return dashboard; } catch (Exception e) { checkConstraintViolation(e, "dashboard_external_id_unq_key", "Dashboard with such external id already exists!"); throw e; @@ -170,6 +180,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb deleteEntityRelations(tenantId, dashboardId); try { dashboardDao.removeById(tenantId, dashboardId.getId()); + countService.publishCountEntityEvictEvent(tenantId, EntityType.DASHBOARD); } catch (Exception t) { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_default_dashboard_device_profile")) { @@ -316,6 +327,11 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb return Optional.ofNullable(findDashboardById(tenantId, new DashboardId(entityId.getId()))); } + @Override + public long countByTenantId(TenantId tenantId) { + return dashboardDao.countByTenantId(tenantId); + } + @Override public EntityType getEntityType() { return EntityType.DASHBOARD; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index e5dcc810a6..a32f93d801 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -67,6 +67,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.entity.EntityCountService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @@ -113,6 +114,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService deviceValidator; + @Autowired + private EntityCountService countService; + @Override public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) { log.trace("Executing findDeviceInfoById [{}]", deviceId); @@ -235,6 +239,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findDevicesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); @@ -611,6 +618,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService implements EntityCountService { + + @Lazy + @Autowired + private EntityServiceRegistry entityServiceRegistry; + + @Override + public long countByTenantIdAndEntityType(TenantId tenantId, EntityType entityType) { + return cache.getAndPutInTransaction(new EntityCountCacheKey(tenantId, entityType), + () -> entityServiceRegistry.getServiceByEntityType(entityType).countByTenantId(tenantId), false); + } + + @Override + public void publishCountEntityEvictEvent(TenantId tenantId, EntityType entityType) { + publishEvictEvent(new EntityCountCacheEvictEvent(tenantId, entityType)); + } + + @TransactionalEventListener(classes = EntityCountCacheEvictEvent.class) + @Override + public void handleEvictEvent(EntityCountCacheEvictEvent event) { + cache.evict(new EntityCountCacheKey(event.getTenantId(), event.getEntityType())); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/EntityCountCacheEvictEvent.java b/dao/src/main/java/org/thingsboard/server/dao/entity/EntityCountCacheEvictEvent.java new file mode 100644 index 0000000000..4d743500e2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/EntityCountCacheEvictEvent.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.entity; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@RequiredArgsConstructor +class EntityCountCacheEvictEvent { + private final TenantId tenantId; + private final EntityType entityType; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/EntityCountCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/entity/EntityCountCacheKey.java new file mode 100644 index 0000000000..aac00b3cb8 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/EntityCountCacheKey.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.entity; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; + +import java.io.Serializable; + +@Getter +@EqualsAndHashCode +@RequiredArgsConstructor +public class EntityCountCacheKey implements Serializable { + + private static final long serialVersionUID = -1992105662738434178L; + + private final TenantId tenantId; + private final EntityType entityType; + + @Override + public String toString() { + return tenantId + "_" + entityType; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/count/EntityCountCaffeineCache.java b/dao/src/main/java/org/thingsboard/server/dao/entity/count/EntityCountCaffeineCache.java new file mode 100644 index 0000000000..1d91af4e98 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/count/EntityCountCaffeineCache.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.entity.count; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CaffeineTbTransactionalCache; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.dao.entity.EntityCountCacheKey; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service("EntityCountCache") +public class EntityCountCaffeineCache extends CaffeineTbTransactionalCache { + + public EntityCountCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.ENTITY_COUNT_CACHE); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/count/EntityCountRedisCache.java b/dao/src/main/java/org/thingsboard/server/dao/entity/count/EntityCountRedisCache.java new file mode 100644 index 0000000000..af431f3085 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/count/EntityCountRedisCache.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.entity.count; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CacheSpecsMap; +import org.thingsboard.server.cache.RedisTbTransactionalCache; +import org.thingsboard.server.cache.TBRedisCacheConfiguration; +import org.thingsboard.server.cache.TbFSTRedisSerializer; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.dao.entity.EntityCountCacheKey; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service("EntityCountCache") +public class EntityCountRedisCache extends RedisTbTransactionalCache { + + public EntityCountRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { + super(CacheConstants.ENTITY_COUNT_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/usage/BasicUsageInfoService.java b/dao/src/main/java/org/thingsboard/server/dao/usage/BasicUsageInfoService.java index 52228d1a3e..cf540a205d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usage/BasicUsageInfoService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usage/BasicUsageInfoService.java @@ -21,18 +21,15 @@ import org.springframework.context.annotation.Lazy; 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.UsageInfo; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.asset.AssetDao; -import org.thingsboard.server.dao.customer.CustomerDao; -import org.thingsboard.server.dao.dashboard.DashboardDao; -import org.thingsboard.server.dao.device.DeviceDao; +import org.thingsboard.server.dao.entity.EntityCountService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; -import org.thingsboard.server.dao.user.UserDao; import java.util.Arrays; import java.util.Collection; @@ -45,11 +42,7 @@ import java.util.concurrent.ExecutionException; @RequiredArgsConstructor public class BasicUsageInfoService implements UsageInfoService { - private final DeviceDao deviceDao; - private final AssetDao assetDao; - private final CustomerDao customerDao; - private final UserDao userDao; - private final DashboardDao dashboardDao; + private final EntityCountService countService; private final ApiUsageStateService apiUsageStateService; private final TimeseriesService tsService; @Lazy @@ -60,15 +53,15 @@ public class BasicUsageInfoService implements UsageInfoService { DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); UsageInfo usageInfo = new UsageInfo(); - usageInfo.setDevices(deviceDao.countByTenantId(tenantId)); + usageInfo.setDevices(countService.countByTenantIdAndEntityType(tenantId, EntityType.DEVICE)); usageInfo.setMaxDevices(profileConfiguration.getMaxDevices()); - usageInfo.setAssets(assetDao.countByTenantId(tenantId)); + usageInfo.setAssets(countService.countByTenantIdAndEntityType(tenantId, EntityType.ASSET)); usageInfo.setMaxAssets(profileConfiguration.getMaxAssets()); - usageInfo.setCustomers(customerDao.countByTenantId(tenantId)); + usageInfo.setCustomers(countService.countByTenantIdAndEntityType(tenantId, EntityType.CUSTOMER)); usageInfo.setMaxCustomers(profileConfiguration.getMaxCustomers()); - usageInfo.setUsers(userDao.countByTenantId(tenantId)); + usageInfo.setUsers(countService.countByTenantIdAndEntityType(tenantId, EntityType.USER)); usageInfo.setMaxUsers(profileConfiguration.getMaxUsers()); - usageInfo.setDashboards(dashboardDao.countByTenantId(tenantId)); + usageInfo.setDashboards(countService.countByTenantIdAndEntityType(tenantId, EntityType.DASHBOARD)); usageInfo.setMaxDashboards(profileConfiguration.getMaxDashboards()); usageInfo.setMaxAlarms(profileConfiguration.getMaxCreatedAlarms()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index b9d7422ef0..e3d30c8b01 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent; import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.EntityCountService; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -79,10 +80,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic private final UserDao userDao; private final UserCredentialsDao userCredentialsDao; private final UserAuthSettingsDao userAuthSettingsDao; - private final UserSettingsDao userSettingsDao; private final DataValidator userValidator; private final DataValidator userCredentialsValidator; private final ApplicationEventPublisher eventPublisher; + private final EntityCountService countService; @Override public User findUserByEmail(TenantId tenantId, String email) { @@ -126,6 +127,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic } User savedUser = userDao.save(user.getTenantId(), user); if (user.getId() == null) { + countService.publishCountEntityEvictEvent(savedUser.getTenantId(), EntityType.USER); UserCredentials userCredentials = new UserCredentials(); userCredentials.setEnabled(false); userCredentials.setActivateToken(generateSafeToken(DEFAULT_TOKEN_LENGTH)); @@ -234,6 +236,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic deleteEntityRelations(tenantId, userId); userDao.removeById(tenantId, userId.getId()); eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(userId)); + countService.publishCountEntityEvictEvent(tenantId, EntityType.USER); } @Override @@ -431,6 +434,11 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic return Optional.ofNullable(findUserById(tenantId, new UserId(entityId.getId()))); } + @Override + public long countByTenantId(TenantId tenantId) { + return userDao.countByTenantId(tenantId); + } + @Override public EntityType getEntityType() { return EntityType.USER;