thingsboard-aplcache

Were fixed

9/25/2018 12:57:04 PM

Details

diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index af62e04..84a8151 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -318,7 +318,7 @@ public abstract class BaseController {
                     checkUserId(new UserId(entityId.getId()));
                     return;
                 case ENTITY_VIEW:
-                    checkEntityView(entityViewService.findEntityViewById(new EntityViewId(entityId.getId())));
+                    checkEntityViewId(entityViewService.findEntityViewById(new EntityViewId(entityId.getId())));
                     return;
                 default:
                     throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
@@ -351,14 +351,14 @@ public abstract class BaseController {
         try {
             validateId(entityViewId, "Incorrect entityViewId " + entityViewId);
             EntityView entityView = entityViewService.findEntityViewById(entityViewId);
-            checkEntityView(entityView);
+            checkEntityViewId(entityView);
             return entityView;
         } catch (Exception e) {
             throw handleException(e, false);
         }
     }
 
-    protected void checkEntityView(EntityView entityView) throws ThingsboardException {
+    protected void checkEntityViewId(EntityView entityView) throws ThingsboardException {
         checkNotNull(entityView);
         checkTenantId(entityView.getTenantId());
         if (entityView.getCustomerId() != null && !entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
index 26a48da..e6f149e 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
@@ -17,7 +17,14 @@ package org.thingsboard.server.controller;
 
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.EntityView;
@@ -49,13 +56,10 @@ public class EntityViewController extends BaseController {
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.GET)
     @ResponseBody
-    public EntityView getEntityViewById(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId)
-            throws ThingsboardException {
-
+    public EntityView getEntityViewById(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
         checkParameter(ENTITY_VIEW_ID, strEntityViewId);
         try {
-            EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
-            return checkEntityViewId(entityViewId);
+            return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId)));
         } catch (Exception e) {
             throw handleException(e);
         }
@@ -70,13 +74,10 @@ public class EntityViewController extends BaseController {
             EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
             logEntityAction(savedEntityView.getId(), savedEntityView, null,
                     entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
-
             return savedEntityView;
-
         } catch (Exception e) {
             logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null,
                     entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
-
             throw handleException(e);
         }
     }
@@ -90,7 +91,6 @@ public class EntityViewController extends BaseController {
             EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
             EntityView entityView = checkEntityViewId(entityViewId);
             entityViewService.deleteEntityView(entityViewId);
-
             logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
                     ActionType.DELETED,null, strEntityViewId);
         } catch (Exception e) {
@@ -117,11 +117,9 @@ public class EntityViewController extends BaseController {
             checkEntityViewId(entityViewId);
 
             EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(entityViewId, customerId));
-
             logEntityAction(entityViewId, savedEntityView,
                     savedEntityView.getCustomerId(),
                     ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName());
-
             return savedEntityView;
         } catch (Exception e) {
             logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@@ -143,9 +141,7 @@ public class EntityViewController extends BaseController {
                 throw new IncorrectParameterException("Entity View isn't assigned to any customer!");
             }
             Customer customer = checkCustomerId(entityView.getCustomerId());
-
             EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(entityViewId));
-
             logEntityAction(entityViewId, entityView,
                     entityView.getCustomerId(),
                     ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName());
@@ -208,7 +204,7 @@ public class EntityViewController extends BaseController {
             List<EntityView> entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(query).get());
             entityViews = entityViews.stream().filter(entityView -> {
                 try {
-                    checkEntityView(entityView);
+                    checkEntityViewId(entityView);
                     return true;
                 } catch (ThingsboardException e) {
                     return false;
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
index e866bc8..395f902 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
@@ -78,7 +78,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
     }
 
     @Override
-    public List<EntityView> findEntityViewByTenantId(UUID tenantId, TextPageLink pageLink) {
+    public List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) {
         log.debug("Try to find entity views by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
         List<EntityViewEntity> entityViewEntities =
                 findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
index 742cb92..ba43385 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
@@ -45,7 +45,7 @@ public interface EntityViewDao extends Dao<EntityView> {
      * @param pageLink the page link
      * @return the list of entity view objects
      */
-    List<EntityView> findEntityViewByTenantId(UUID tenantId, TextPageLink pageLink);
+    List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink);
 
     /**
      * Find entity views by tenantId and entity view name.
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
index 89b7e2f..19f326c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
@@ -32,28 +32,27 @@ import java.util.List;
  */
 public interface EntityViewService {
 
-    EntityView findEntityViewById(EntityViewId entityViewId);
-
     EntityView saveEntityView(EntityView entityView);
 
     EntityView assignEntityViewToCustomer(EntityViewId entityViewId, CustomerId customerId);
 
     EntityView unassignEntityViewFromCustomer(EntityViewId entityViewId);
 
-    void deleteEntityView(EntityViewId entityViewId);
+    void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId);
 
-    TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink);
+    EntityView findEntityViewById(EntityViewId entityViewId);
 
-    void deleteEntityViewsByTenantId(TenantId tenantId);
+    TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink);
 
-    TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId,
-                                                                    TextPageLink pageLink);
+    TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
 
-    void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId);
+    ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query);
 
     ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId);
 
-    ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query);
-
     ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId);
+
+    void deleteEntityView(EntityViewId entityViewId);
+
+    void deleteEntityViewsByTenantId(TenantId tenantId);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
index 8647af7..79ceff2 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
@@ -24,7 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.Cache;
 import org.springframework.cache.CacheManager;
 import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.DataConstants;
@@ -41,6 +40,7 @@ import org.thingsboard.server.common.data.page.TextPageData;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.common.data.relation.EntityRelation;
 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
+import org.thingsboard.server.dao.DaoUtil;
 import org.thingsboard.server.dao.attributes.AttributesService;
 import org.thingsboard.server.dao.customer.CustomerDao;
 import org.thingsboard.server.dao.entity.AbstractEntityService;
@@ -54,13 +54,14 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 
 import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE;
 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
 import static org.thingsboard.server.dao.service.Validator.validateId;
 import static org.thingsboard.server.dao.service.Validator.validatePageLink;
-import static org.thingsboard.server.dao.service.Validator.validateString;
 
 /**
  * Created by Victor Basanets on 8/28/2017.
@@ -73,6 +74,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
     public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
     public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
     public static final String INCORRECT_ENTITY_VIEW_ID = "Incorrect entityViewId ";
+    private static final int DEFAULT_LIMIT = 100;
 
     @Autowired
     private EntityViewDao entityViewDao;
@@ -89,13 +91,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
     @Autowired
     private CacheManager cacheManager;
 
-    @Override
-    public EntityView findEntityViewById(EntityViewId entityViewId) {
-        log.trace("Executing findEntityViewById [{}]", entityViewId);
-        validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
-        return entityViewDao.findById(entityViewId.getId());
-    }
-
     @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.name}")
     @Override
     public EntityView saveEntityView(EntityView entityView) {
@@ -110,41 +105,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
         return savedEntityView;
     }
 
-    private void copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys) {
-        if (keys != null && !keys.isEmpty()) {
-            ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys);
-            Futures.addCallback(getAttrFuture, new FutureCallback<List<AttributeKvEntry>>() {
-                @Override
-                public void onSuccess(@Nullable List<AttributeKvEntry> attributeKvEntries) {
-                    if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) {
-                        List<AttributeKvEntry> filteredAttributes =
-                                attributeKvEntries.stream()
-                                        .filter(attributeKvEntry -> {
-                                            if (entityView.getStartTimeMs() == 0 && entityView.getEndTimeMs() == 0) {
-                                                return true;
-                                            }
-                                            if (entityView.getEndTimeMs() == 0 && entityView.getStartTimeMs() < attributeKvEntry.getLastUpdateTs()) {
-                                                return true;
-                                            }
-                                            if (entityView.getStartTimeMs() == 0 && entityView.getEndTimeMs() > attributeKvEntry.getLastUpdateTs()) {
-                                                return true;
-                                            }
-                                            return entityView.getStartTimeMs() < attributeKvEntry.getLastUpdateTs()
-                                                    && entityView.getEndTimeMs() > attributeKvEntry.getLastUpdateTs();
-                                        }).collect(Collectors.toList());
-                        attributesService.save(entityView.getId(), scope, filteredAttributes);
-                    }
-                }
-
-                @Override
-                public void onFailure(Throwable throwable) {
-                    log.error("Failed to fetch [{}] attributes [{}] for [{}] entity [{}]",
-                            scope, keys, entityView.getEntityId().getEntityType().name(), entityView.getEntityId().getId().toString(), throwable);
-                }
-            });
-        }
-    }
-
     @Override
     public EntityView assignEntityViewToCustomer(EntityViewId entityViewId, CustomerId customerId) {
         EntityView entityView = findEntityViewById(entityViewId);
@@ -160,64 +120,67 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
     }
 
     @Override
-    public void deleteEntityView(EntityViewId entityViewId) {
-        log.trace("Executing deleteEntityView [{}]", entityViewId);
-        Cache cache = cacheManager.getCache(ENTITY_VIEW_CACHE);
+    public void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId) {
+        log.trace("Executing unassignCustomerEntityViews, tenantId [{}], customerId [{}]", tenantId, customerId);
+        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
+        validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
+        new CustomerEntityViewsUnAssigner(tenantId).removeEntities(customerId);
+    }
+
+
+    @Override
+    public EntityView findEntityViewById(EntityViewId entityViewId) {
+        log.trace("Executing findEntityViewById [{}]", entityViewId);
         validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
-        deleteEntityRelations(entityViewId);
-        EntityView entityView = entityViewDao.findById(entityViewId.getId());
-        cache.evict(Arrays.asList(entityView.getTenantId(), entityView.getName()));
-        entityViewDao.removeById(entityViewId.getId());
+        List<Object> ids = Arrays.asList(entityViewId.getId());
+        Cache cache = cacheManager.getCache(ENTITY_VIEW_CACHE);
+        EntityView fromCache = cache.get(ids, EntityView.class);
+        if (fromCache != null) {
+            return fromCache;
+        } else {
+            ListenableFuture<EntityView> entityViewFuture
+                    = Futures.immediateFuture(entityViewDao.findById(entityViewId.getId()));
+            Futures.addCallback(entityViewFuture,
+                    new FutureCallback<EntityView>() {
+                        @Override
+                        public void onSuccess(@Nullable EntityView result) {
+                            cache.putIfAbsent(ids, result);
+                        }
+                        @Override
+                        public void onFailure(Throwable t) {}
+                    });
+            try {
+                return entityViewFuture.get();
+            } catch (Exception e) {
+                log.error(e.getMessage());
+            }
+        }
+        return entityViewDao.findById(entityViewId.getId());
     }
 
     @Override
     public TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink) {
-        log.trace("Executing findEntityViewByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
+        log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
         validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
         validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
-        List<EntityView> entityViews = entityViewDao.findEntityViewByTenantId(tenantId.getId(), pageLink);
+        List<EntityView> entityViews = entityViewDao.findEntityViewsByTenantId(tenantId.getId(), pageLink);
         return new TextPageData<>(entityViews, pageLink);
     }
 
     @Override
-    public void deleteEntityViewsByTenantId(TenantId tenantId) {
-        log.trace("Executing deleteEntityViewsByTenantId, tenantId [{}]", tenantId);
-        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
-        tenantEntityViewRemover.removeEntities(tenantId);
-    }
-
-    @Override
     public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId,
                                                                           TextPageLink pageLink) {
-
         log.trace("Executing findEntityViewByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," +
                         " pageLink [{}]", tenantId, customerId, pageLink);
-
         validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
         validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
         validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
         List<EntityView> entityViews = entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(),
                 customerId.getId(), pageLink);
-
         return new TextPageData<>(entityViews, pageLink);
     }
 
     @Override
-    public void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId) {
-        log.trace("Executing unassignCustomerEntityViews, tenantId [{}], customerId [{}]", tenantId, customerId);
-        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
-        validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
-        new CustomerEntityViewsUnAssigner(tenantId).removeEntities(customerId);
-    }
-
-    @Override
-    public ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId) {
-        log.trace("Executing findEntityViewById [{}]", entityViewId);
-        validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
-        return entityViewDao.findByIdAsync(entityViewId.getId());
-    }
-
-    @Override
     public ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query) {
         ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
         ListenableFuture<List<EntityView>> entityViews = Futures.transformAsync(relations, r -> {
@@ -236,11 +199,98 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
     }
 
     @Override
+    public ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId) {
+        log.trace("Executing findEntityViewById [{}]", entityViewId);
+        validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
+        return entityViewDao.findByIdAsync(entityViewId.getId());
+    }
+
+    @Override
     public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId) {
         log.trace("Executing findEntityViewsByTenantIdAndEntityIdAsync, tenantId [{}], entityId [{}]", tenantId, entityId);
         validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
         validateId(entityId.getId(), "Incorrect entityId" + entityId);
-        return entityViewDao.findEntityViewsByTenantIdAndEntityIdAsync(tenantId.getId(), entityId.getId());
+
+        List<Object> tenantAndEntityIds = Arrays.asList(tenantId, entityId);
+        Cache cache = cacheManager.getCache(ENTITY_VIEW_CACHE);
+        List<EntityView> fromCache = cache.get(tenantAndEntityIds, List.class);
+        if (fromCache != null) {
+            return Futures.immediateFuture(fromCache);
+        } else {
+            ListenableFuture<List<EntityView>> entityViewsFuture =
+                    entityViewDao.findEntityViewsByTenantIdAndEntityIdAsync(tenantId.getId(), entityId.getId());
+            Futures.addCallback(entityViewsFuture,
+                    new FutureCallback<List<EntityView>>() {
+                        @Override
+                        public void onSuccess(@Nullable List<EntityView> result) {
+                            cache.putIfAbsent(tenantAndEntityIds, result);
+                        }
+                        @Override
+                        public void onFailure(Throwable t) {}
+                    });
+            return entityViewsFuture;
+        }
+    }
+
+    @Override
+    public void deleteEntityView(EntityViewId entityViewId) {
+        log.trace("Executing deleteEntityView [{}]", entityViewId);
+        validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
+        deleteEntityRelations(entityViewId);
+        cacheEvict(entityViewId, cacheManager.getCache(ENTITY_VIEW_CACHE));
+        entityViewDao.removeById(entityViewId.getId());
+    }
+
+    @Override
+    public void deleteEntityViewsByTenantId(TenantId tenantId) {
+        log.trace("Executing deleteEntityViewsByTenantId, tenantId [{}]", tenantId);
+        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
+        entityViewDao.findEntityViewsByTenantId(tenantId.getId(), new TextPageLink(DEFAULT_LIMIT)).stream()
+                .map(view ->  view.getId())
+                .collect(Collectors.toList())
+                .forEach(id -> cacheEvict(id, cacheManager.getCache(ENTITY_VIEW_CACHE)));
+        tenantEntityViewRemover.removeEntities(tenantId);
+    }
+
+    private void cacheEvict(EntityViewId entityViewId, Cache cache) {
+        EntityView entityView = entityViewDao.findById(entityViewId.getId());
+        cache.evict(Arrays.asList(entityView.getTenantId(), entityView.getName()));
+        cache.evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId()));
+    }
+
+    private void copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys) {
+        if (keys != null && !keys.isEmpty()) {
+            ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys);
+            Futures.addCallback(getAttrFuture, new FutureCallback<List<AttributeKvEntry>>() {
+                @Override
+                public void onSuccess(@Nullable List<AttributeKvEntry> attributeKvEntries) {
+                    if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) {
+                        List<AttributeKvEntry> filteredAttributes =
+                                attributeKvEntries.stream()
+                                        .filter(attributeKvEntry -> {
+                                            if (entityView.getStartTimeMs() == 0 && entityView.getEndTimeMs() == 0) {
+                                                return true;
+                                            }
+                                            if (entityView.getEndTimeMs() == 0 && entityView.getStartTimeMs() < attributeKvEntry.getLastUpdateTs()) {
+                                                return true;
+                                            }
+                                            if (entityView.getStartTimeMs() == 0 && entityView.getEndTimeMs() > attributeKvEntry.getLastUpdateTs()) {
+                                                return true;
+                                            }
+                                            return entityView.getStartTimeMs() < attributeKvEntry.getLastUpdateTs()
+                                                    && entityView.getEndTimeMs() > attributeKvEntry.getLastUpdateTs();
+                                        }).collect(Collectors.toList());
+                        attributesService.save(entityView.getId(), scope, filteredAttributes);
+                    }
+                }
+
+                @Override
+                public void onFailure(Throwable throwable) {
+                    log.error("Failed to fetch [{}] attributes [{}] for [{}] entity [{}]",
+                            scope, keys, entityView.getEntityId().getEntityType().name(), entityView.getEntityId().getId().toString(), throwable);
+                }
+            });
+        }
     }
 
     private DataValidator<EntityView> entityViewValidator =
@@ -296,7 +346,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
 
                 @Override
                 protected List<EntityView> findEntities(TenantId id, TextPageLink pageLink) {
-                    return entityViewDao.findEntityViewByTenantId(id.getId(), pageLink);
+                    return entityViewDao.findEntityViewsByTenantId(id.getId(), pageLink);
                 }
 
                 @Override
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
index 0cd1b2b..912c9d5 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
@@ -66,7 +66,7 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity,
     }
 
     @Override
-    public List<EntityView> findEntityViewByTenantId(UUID tenantId, TextPageLink pageLink) {
+    public List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) {
         return DaoUtil.convertDataList(
                 entityViewRepository.findByTenantId(
                         fromTimeUUID(tenantId),
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
index d609620..c5dcc0f 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
@@ -15,9 +15,7 @@
  */
 package org.thingsboard.rule.engine.action;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Function;
-import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.gson.JsonParser;
 import lombok.extern.slf4j.Slf4j;
@@ -28,24 +26,23 @@ import org.thingsboard.rule.engine.api.TbNode;
 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 import org.thingsboard.rule.engine.api.TbNodeException;
 import org.thingsboard.rule.engine.api.TbRelationTypes;
+import org.thingsboard.rule.engine.api.util.DonAsynchron;
 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 import org.thingsboard.server.common.data.DataConstants;
 import org.thingsboard.server.common.data.EntityView;
 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.TsKvEntry;
 import org.thingsboard.server.common.data.plugin.ComponentType;
 import org.thingsboard.server.common.msg.TbMsg;
 import org.thingsboard.server.common.msg.session.SessionMsgType;
 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
 
 import javax.annotation.Nullable;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 
-import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback;
+import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
 
 @Slf4j
 @RuleNode(
@@ -70,25 +67,22 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
 
     @Override
     public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
-        if (msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) ||
-                msg.getType().equals(DataConstants.ATTRIBUTES_DELETED) ||
-                msg.getType().equals(DataConstants.ATTRIBUTES_UPDATED)) {
+        if (!msg.getMetaData().getData().isEmpty()) {
             long now = System.currentTimeMillis();
-            String scope;
-            if (msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name())) {
-                scope = DataConstants.CLIENT_SCOPE;
-            } else {
-                scope = msg.getMetaData().getValue("scope");
-            }
+            String scope = msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) ?
+                    DataConstants.CLIENT_SCOPE : msg.getMetaData().getValue("scope");
+
             ListenableFuture<List<EntityView>> entityViewsFuture =
                     ctx.getEntityViewService().findEntityViewsByTenantIdAndEntityIdAsync(ctx.getTenantId(), msg.getOriginator());
-            withCallback(entityViewsFuture,
+
+            DonAsynchron.withCallback(entityViewsFuture,
                     entityViews -> {
-                        List<ListenableFuture<List<Void>>> saveFutures = new ArrayList<>();
                         for (EntityView entityView : entityViews) {
-                            if ((entityView.getEndTimeMs() != 0  && entityView.getEndTimeMs() > now && entityView.getStartTimeMs() < now) ||
-                                    (entityView.getEndTimeMs() == 0 && entityView.getStartTimeMs() < now)) {
-                                Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())).getAttributes();
+                            long startTime = entityView.getStartTimeMs();
+                            long endTime = entityView.getEndTimeMs();
+                            if ((endTime != 0  && endTime > now && startTime < now) || (endTime == 0 && startTime < now)) {
+                                Set<AttributeKvEntry> attributes =
+                                        JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())).getAttributes();
                                 List<AttributeKvEntry> filteredAttributes =
                                         attributes.stream()
                                                 .filter(attr -> {
@@ -110,19 +104,22 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
                                                             return entityView.getKeys().getAttributes().getSh().contains(attr.getKey());
                                                     }
                                                     return false;
-                                                })
-                                                .collect(Collectors.toList());
-                                saveFutures.add(ctx.getAttributesService().save(entityView.getId(), scope, new ArrayList<>(filteredAttributes)));
+                                                }).collect(Collectors.toList());
+
+                                ctx.getTelemetryService().saveAndNotify(entityView.getId(), scope, filteredAttributes,
+                                        new FutureCallback<Void>() {
+                                            @Override
+                                            public void onSuccess(@Nullable Void result) {
+                                                ctx.tellNext(msg, SUCCESS);
+                                            }
+
+                                            @Override
+                                            public void onFailure(Throwable t) {
+                                                ctx.tellFailure(msg, t);
+                                            }
+                                        });
                             }
                         }
-                        Futures.transform(Futures.allAsList(saveFutures), new Function<List<List<Void>>, Object>() {
-                            @Nullable
-                            @Override
-                            public Object apply(@Nullable List<List<Void>> lists) {
-                                ctx.tellNext(msg, TbRelationTypes.SUCCESS);
-                                return null;
-                            }
-                        });
                     },
                     t -> ctx.tellFailure(msg, t));
         } else {