Browse Source

Tenant Profile Cache

pull/6536/head
Andrii Shvaika 4 years ago
parent
commit
08ae3e9b83
  1. 38
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileCacheKey.java
  2. 35
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileCaffeineCache.java
  3. 10
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileEvictEvent.java
  4. 51
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileRedisCache.java
  5. 80
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java

38
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileCacheKey.java

@ -0,0 +1,38 @@
package org.thingsboard.server.dao.tenant;
import lombok.Data;
import org.thingsboard.server.common.data.id.TenantProfileId;
import java.io.Serializable;
@Data
public class TenantProfileCacheKey implements Serializable {
private static final long serialVersionUID = 8220455917177676472L;
private final TenantProfileId tenantProfileId;
private final boolean defaultProfile;
private TenantProfileCacheKey(TenantProfileId tenantProfileId, boolean defaultProfile) {
this.tenantProfileId = tenantProfileId;
this.defaultProfile = defaultProfile;
}
public static TenantProfileCacheKey fromId(TenantProfileId id) {
return new TenantProfileCacheKey(id, false);
}
public static TenantProfileCacheKey defaultProfile() {
return new TenantProfileCacheKey(null, true);
}
@Override
public String toString() {
if (defaultProfile) {
return "default";
} else {
return tenantProfileId.getId().toString();
}
}
}

35
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileCaffeineCache.java

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2022 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.tenant;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.dao.asset.AssetCacheKey;
import org.thingsboard.server.dao.cache.CaffeineTbTransactionalCache;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("TenantProfileCache")
public class TenantProfileCaffeineCache extends CaffeineTbTransactionalCache<TenantProfileCacheKey, TenantProfile> {
public TenantProfileCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.TENANT_PROFILE_CACHE);
}
}

10
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileEvictEvent.java

@ -0,0 +1,10 @@
package org.thingsboard.server.dao.tenant;
import lombok.Data;
import org.thingsboard.server.common.data.id.TenantProfileId;
@Data
public class TenantProfileEvictEvent {
private final TenantProfileId tenantProfileId;
private final boolean defaultProfile;
}

51
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileRedisCache.java

@ -0,0 +1,51 @@
/**
* Copyright © 2016-2022 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.tenant;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.dao.asset.AssetCacheKey;
import org.thingsboard.server.dao.cache.RedisTbTransactionalCache;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("TenantProfileCache")
public class TenantProfileRedisCache extends RedisTbTransactionalCache<TenantProfileCacheKey, TenantProfile> {
public TenantProfileRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.TENANT_PROFILE_CACHE, cacheSpecsMap, connectionFactory, configuration, new RedisSerializer<>() {
private final RedisSerializer<Object> java = RedisSerializer.java();
@Override
public byte[] serialize(TenantProfile attributeKvEntry) throws SerializationException {
return java.serialize(attributeKvEntry);
}
@Override
public TenantProfile deserialize(byte[] bytes) throws SerializationException {
return (TenantProfile) java.deserialize(bytes);
}
});
}
}

80
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java

@ -5,7 +5,7 @@
* 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
* 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,
@ -18,10 +18,11 @@ package org.thingsboard.server.dao.tenant;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.server.common.data.EntityInfo;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.TenantId;
@ -30,49 +31,57 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import java.util.Arrays;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import static org.thingsboard.server.common.data.CacheConstants.TENANT_PROFILE_CACHE;
import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
public class TenantProfileServiceImpl extends AbstractEntityService implements TenantProfileService {
public class TenantProfileServiceImpl extends AbstractCachedEntityService<TenantProfileCacheKey, TenantProfile, TenantProfileEvictEvent> implements TenantProfileService {
private static final String INCORRECT_TENANT_PROFILE_ID = "Incorrect tenantProfileId ";
@Autowired
private TenantProfileDao tenantProfileDao;
@Autowired
private CacheManager cacheManager;
@Autowired
private DataValidator<TenantProfile> tenantProfileValidator;
@Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{#tenantProfileId.id}")
@TransactionalEventListener(classes = TenantProfileEvictEvent.class)
@Override
public void handleEvictEvent(TenantProfileEvictEvent event) {
List<TenantProfileCacheKey> keys = new ArrayList<>(2);
keys.add(TenantProfileCacheKey.fromId(event.getTenantProfileId()));
if (event.isDefaultProfile()) {
keys.add(TenantProfileCacheKey.defaultProfile());
}
cache.evict(keys);
}
@Override
public TenantProfile findTenantProfileById(TenantId tenantId, TenantProfileId tenantProfileId) {
log.trace("Executing findTenantProfileById [{}]", tenantProfileId);
Validator.validateId(tenantProfileId, INCORRECT_TENANT_PROFILE_ID + tenantProfileId);
return tenantProfileDao.findById(tenantId, tenantProfileId.getId());
return cache.getAndPutInTransaction(TenantProfileCacheKey.fromId(tenantProfileId),
() -> tenantProfileDao.findById(tenantId, tenantProfileId.getId()), true);
}
@Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{'info', #tenantProfileId.id}")
@Override
public EntityInfo findTenantProfileInfoById(TenantId tenantId, TenantProfileId tenantProfileId) {
log.trace("Executing findTenantProfileInfoById [{}]", tenantProfileId);
Validator.validateId(tenantProfileId, INCORRECT_TENANT_PROFILE_ID + tenantProfileId);
return tenantProfileDao.findTenantProfileInfoById(tenantId, tenantProfileId.getId());
TenantProfile profile = findTenantProfileById(tenantId, tenantProfileId);
return profile == null ? null : new EntityInfo(profile.getId(), profile.getName());
}
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public TenantProfile saveTenantProfile(TenantId tenantId, TenantProfile tenantProfile) {
log.trace("Executing saveTenantProfile [{}]", tenantProfile);
@ -80,6 +89,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T
TenantProfile savedTenantProfile;
try {
savedTenantProfile = tenantProfileDao.save(tenantId, tenantProfile);
publishEvictEvent(new TenantProfileEvictEvent(savedTenantProfile.getId(), savedTenantProfile.isDefault()));
} catch (Exception t) {
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("tenant_profile_name_unq_key")) {
@ -88,16 +98,10 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T
throw t;
}
}
Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE);
cache.evict(Collections.singletonList(savedTenantProfile.getId().getId()));
cache.evict(Arrays.asList("info", savedTenantProfile.getId().getId()));
if (savedTenantProfile.isDefault()) {
cache.evict(Collections.singletonList("default"));
cache.evict(Arrays.asList("default", "info"));
}
return savedTenantProfile;
}
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void deleteTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) {
log.trace("Executing deleteTenantProfile [{}]", tenantProfileId);
@ -121,13 +125,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T
}
}
deleteEntityRelations(tenantId, tenantProfileId);
Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE);
cache.evict(Collections.singletonList(tenantProfileId.getId()));
cache.evict(Arrays.asList("info", tenantProfileId.getId()));
if (isDefault) {
cache.evict(Collections.singletonList("default"));
cache.evict(Arrays.asList("default", "info"));
}
publishEvictEvent(new TenantProfileEvictEvent(tenantProfileId, isDefault));
}
@Override
@ -163,47 +161,43 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T
return defaultTenantProfile;
}
@Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{'default'}")
@Override
public TenantProfile findDefaultTenantProfile(TenantId tenantId) {
log.trace("Executing findDefaultTenantProfile");
return tenantProfileDao.findDefaultTenantProfile(tenantId);
return cache.getAndPutInTransaction(TenantProfileCacheKey.defaultProfile(),
() -> tenantProfileDao.findDefaultTenantProfile(tenantId), true);
}
@Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{'default', 'info'}")
@Override
public EntityInfo findDefaultTenantProfileInfo(TenantId tenantId) {
log.trace("Executing findDefaultTenantProfileInfo");
return tenantProfileDao.findDefaultTenantProfileInfo(tenantId);
var tenantProfile = findDefaultTenantProfile(tenantId);
return tenantProfile == null ? null : new EntityInfo(tenantProfile.getId(), tenantProfile.getName());
}
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public boolean setDefaultTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) {
log.trace("Executing setDefaultTenantProfile [{}]", tenantProfileId);
validateId(tenantId, INCORRECT_TENANT_PROFILE_ID + tenantProfileId);
TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId());
if (!tenantProfile.isDefault()) {
Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE);
tenantProfile.setDefault(true);
TenantProfile previousDefaultTenantProfile = findDefaultTenantProfile(tenantId);
boolean changed = false;
if (previousDefaultTenantProfile == null) {
tenantProfileDao.save(tenantId, tenantProfile);
publishEvictEvent(new TenantProfileEvictEvent(tenantProfileId, true));
changed = true;
} else if (!previousDefaultTenantProfile.getId().equals(tenantProfile.getId())) {
previousDefaultTenantProfile.setDefault(false);
tenantProfileDao.save(tenantId, previousDefaultTenantProfile);
tenantProfileDao.save(tenantId, tenantProfile);
cache.evict(Collections.singletonList(previousDefaultTenantProfile.getId().getId()));
cache.evict(Arrays.asList("info", previousDefaultTenantProfile.getId().getId()));
publishEvictEvent(new TenantProfileEvictEvent(previousDefaultTenantProfile.getId(), false));
publishEvictEvent(new TenantProfileEvictEvent(tenantProfileId, true));
changed = true;
}
if (changed) {
cache.evict(Collections.singletonList(tenantProfile.getId().getId()));
cache.evict(Arrays.asList("info", tenantProfile.getId().getId()));
cache.evict(Collections.singletonList("default"));
cache.evict(Arrays.asList("default", "info"));
}
return changed;
}
return false;
@ -216,7 +210,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T
}
private final PaginatedRemover<String, TenantProfile> tenantProfilesRemover =
new PaginatedRemover<String, TenantProfile>() {
new PaginatedRemover<>() {
@Override
protected PageData<TenantProfile> findEntities(TenantId tenantId, String id, PageLink pageLink) {

Loading…
Cancel
Save