# HG changeset patch # User casties # Date 1485543333 -3600 # Node ID 74cd973f6ecefc7783a1513a36fc8a869e122544 # Parent 83b209aa42267851d1fb7769ac57141945d070fe more work on stale entity update problem. cache compare function works now. diff -r 83b209aa4226 -r 74cd973f6ece .hgignore --- a/.hgignore Thu Jan 26 19:37:25 2017 +0100 +++ b/.hgignore Fri Jan 27 19:55:33 2017 +0100 @@ -18,4 +18,6 @@ syntax: regexp ^src/main/resources/log4j\.properties$ syntax: regexp -^src/main/resources/openmind\.properties$ \ No newline at end of file +^src/main/resources/openmind\.properties$ +syntax: regexp +^bin$ \ No newline at end of file diff -r 83b209aa4226 -r 74cd973f6ece src/main/java/org/mpi/openmind/cache/AbstractCacheService.java --- a/src/main/java/org/mpi/openmind/cache/AbstractCacheService.java Thu Jan 26 19:37:25 2017 +0100 +++ b/src/main/java/org/mpi/openmind/cache/AbstractCacheService.java Fri Jan 27 19:55:33 2017 +0100 @@ -27,7 +27,7 @@ private PersistenceService persistenceService; - private DuplexMap entMap; + protected DuplexMap entMap; private Map entLoadedByOC; public AbstractCacheService(){ @@ -178,7 +178,15 @@ } - public boolean isEntityCurrent(Entity ent) { + /** + * Check if the given Entity is up-to-date with the cache. + * + * Compares accessTime only. + * + * @param ent + * @return + */ + public boolean isLWEntityCurrent(Entity ent) { Entity cacheEnt = this.entMap.getValuesByOwnKey(ent.getId()); if (cacheEnt == null) { // not in cache @@ -197,7 +205,7 @@ //****************************************** //AttKey(OC, attName), SrcId - private TripleMap attMap; + protected TripleMap attMap; private Map attLoadedBySrcId; private Map attLoadedByOCAndName; @@ -243,7 +251,7 @@ } /** - * Return a list of (cloned) Attributes of the Entity. + * Return a list of (cloned) Attributes with the given source id. * * Loads Attributes from the database into the cache. * @@ -258,25 +266,32 @@ return list; } - public List getAttsBySrcIdReadOnly(Long srcId){ - //logger.info("getAttsBySrcIdReadOnly srcId=" + srcId); + /** + * Return a list of (non-cloned) Attributes with the given source id from the cache. + * + * Loads Attributes from the database into the cache. + * + * @param srcId + * @return + */ + public List getAttsBySrcIdReadOnly(Long srcId) { if(attLoadedBySrcId.get(srcId) == null || attLoadedBySrcId.get(srcId) == false){ synchronized (attMap) { - if(attLoadedBySrcId.get(srcId) == null){ - //logger.info("getAttsBySrcIdReadOnly fromDB entId=" + srcId); + if(attLoadedBySrcId.get(srcId) == null) { attLoadedBySrcId.put(srcId, false); - List list = - getPs().getAllAttributes(srcId, -1); - for(Attribute att : list){ + // load from db + List list = getPs().getAllAttributes(srcId, -1); + for (Attribute att : list) { // update access timestamp att.setAccessTime(System.currentTimeMillis()); + // put in cache this.attMap.put(att.getKey(), att); } attLoadedBySrcId.put(srcId, true); - //logger.info("STARTING getAttsBySrcIdReadOnly srcId=" + srcId + " - size=" + list.size()); } } } + // return attributes from cache return this.attMap.getValuesByBKey(srcId); } @@ -304,11 +319,36 @@ return this.attMap.getValuesByAKey(ocNameKey); } + + /** + * Check if the given Attribute is up-to-date with the cache. + * + * Compares accessTime only. + * + * @param att + * @return + */ + public boolean isAttCurrent(Attribute att) { + Attribute cacheAtt = this.attMap.get(att.getKey()); + if (cacheAtt == null) { + // not in cache + return true; + } + long ct = cacheAtt.getAccessTime(); + long et = att.getAccessTime(); + if (ct == 0 || et == 0 || et > ct) { + logger.error("Weird cache access times! old="+Long.toString(ct)+" new="+Long.toString(et)); + } + return (ct == et); + } + + + //****************************************** //************Relations********************* //****************************************** //RelKey<>, src, tar, relId - private PentaMap relMap; + protected PentaMap relMap; private Map relLoadedBySrcId; private Map relLoadedByTarId; private Map relLoadedByName; @@ -384,6 +424,14 @@ return relMap.getValuesByBKey(srcId); } + /** + * Return Relations with matching target id from cache. + * + * Loads Relations from db and stores in cache if not in cache. + * + * @param tarId + * @return + */ public List getRelsByTarId(Long tarId){ if(relLoadedByTarId.get(tarId) == null || relLoadedByTarId.get(tarId) == false){ synchronized (relMap) { @@ -441,6 +489,29 @@ return relMap.getValuesByDKey(name); } + /** + * Check if the given Relation is up-to-date with the cache. + * + * Compares accessTime only. + * + * @param rel + * @return + */ + public boolean isRelCurrent(Relation rel) { + Relation cacheRel = this.relMap.get(rel.getKey()); + if (cacheRel == null) { + // not in cache + return true; + } + long ct = cacheRel.getAccessTime(); + long et = rel.getAccessTime(); + if (ct == 0 || et == 0 || et > ct) { + logger.error("Weird cache access times! old="+Long.toString(ct)+" new="+Long.toString(et)); + } + return (ct == et); + } + + public PersistenceService getPs() { return persistenceService; } diff -r 83b209aa4226 -r 74cd973f6ece src/main/java/org/mpi/openmind/cache/CacheService.java --- a/src/main/java/org/mpi/openmind/cache/CacheService.java Thu Jan 26 19:37:25 2017 +0100 +++ b/src/main/java/org/mpi/openmind/cache/CacheService.java Fri Jan 27 19:55:33 2017 +0100 @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.mpi.openmind.repository.bo.Attribute; import org.mpi.openmind.repository.bo.Entity; @@ -372,6 +373,233 @@ } } + + /** + * Returns if this Entity and its Attributes and Relations are up-to-date with the cache. + * + * Checks accessTimes only. + * + * @param ent + * @return + */ + public boolean isEntityCurrent(Entity ent) { + // check entity + if (!isLWEntityCurrent(ent)) { + return false; + } + + if (ent.isLightweight()) { + logger.error("Can not check attributes and relations of lightweight entity!"); + return false; + } + Long entId = ent.getId(); + + // check attached attributes + List entAtts = ent.getAttributes(); + List cacheAtts = attMap.getValuesByBKey(entId); + for (Attribute att : entAtts) { + if (!isAttCurrent(att)) { + return false; + } else { + if (!cacheAtts.remove(att)) { + return false; + } + } + } + // check additional attributes in cache + if (!cacheAtts.isEmpty()) { + for (Attribute ca : cacheAtts) { + // ignore empty attributes + if (!StringUtils.isEmpty(ca.getOwnValue())) { + return false; + } + } + } + + // check attached source relations + List entSrcRels = ent.getSourceRelations(); + List cacheSrcRels = relMap.getValuesByBKey(entId); + for (Relation rel: entSrcRels) { + if (!isRelCurrent(rel)) { + return false; + } else { + if (!cacheSrcRels.remove(rel)) { + return false; + } + } + } + if (!cacheSrcRels.isEmpty()) { + return false; + } + // check attached target relations + List entTarRels = ent.getTargetRelations(); + List cacheTarRels = relMap.getValuesByCKey(entId); + for (Relation rel: entTarRels) { + if (!isRelCurrent(rel)) { + return false; + } else { + if (!cacheTarRels.remove(rel)) { + return false; + } + } + } + if (!cacheTarRels.isEmpty()) { + return false; + } + + return true; + } + + + public class EntityDiff { + public List addedAttributes = new ArrayList(); + public List removedAttributes = new ArrayList(); + public List modifiedAttributes = new ArrayList(); + public List addedSrcRels = new ArrayList(); + public List removedSrcRels = new ArrayList(); + public List modifiedSrcRels = new ArrayList(); + public List addedTarRels = new ArrayList(); + public List removedTarRels = new ArrayList(); + public List modifiedTarRels = new ArrayList(); + } + + /** + * Returns an EntityDiff with the differences between the given Entity and the current cache. + * + * Ignores empty Attributes. Compares only content if acceptNewIDs. + * + * @param ent + * @return + * @throws Exception + */ + public EntityDiff diffEntityCache(Entity ent, boolean acceptNewIDs) throws Exception { + if (ent.isLightweight()) { + throw new Exception("Can not diff attributes and relations of lightweight entity!"); + } + Long entId = ent.getId(); + EntityDiff diff = new EntityDiff(); + + /* + * check attributes + */ + List entAtts = ent.getAttributes(); + List cacheAtts = attMap.getValuesByBKey(entId); + // check attached attributes and compare to cached + for (Attribute att : entAtts) { + if (!isAttCurrent(att)) { + diff.modifiedAttributes.add(att); + } + if (!cacheAtts.remove(att)) { + boolean found = false; + for (Attribute ca : cacheAtts) { + if (ca.getId() == att.getId()) { + cacheAtts.remove(ca); + found = true; + break; + } else if (acceptNewIDs && ca.equalsContent(att)) { + // same content is good enough + cacheAtts.remove(ca); + found = true; + break; + } + } + if (!found) { + diff.addedAttributes.add(att); + } + } + } + if (!cacheAtts.isEmpty()) { + // more cached attributes + for (Attribute ca : cacheAtts) { + // ignore empty attributes + if (!StringUtils.isEmpty(ca.getOwnValue())) { + diff.removedAttributes.add(ca); + } + } + } + + /* + * source relations + */ + List entSrcRels = ent.getSourceRelations(); + List cacheSrcRels = relMap.getValuesByBKey(entId); + // check attached source relations and compare to cached + for (Relation rel : entSrcRels) { + if (!isRelCurrent(rel)) { + diff.modifiedSrcRels.add(rel); + } + if (!cacheSrcRels.remove(rel)) { + boolean found = false; + for (Relation cr : cacheSrcRels) { + if (cr.getId() == rel.getId()) { + cacheSrcRels.remove(cr); + found = true; + break; + } else if (acceptNewIDs && cr.equalsContent(rel)) { + // same content is good enough + cacheSrcRels.remove(cr); + found = true; + break; + } + } + if (!found) { + diff.addedSrcRels.add(rel); + } + } + } + if (!cacheSrcRels.isEmpty()) { + for (Relation rel : cacheSrcRels) { + diff.removedSrcRels.add(rel); + } + } + + /* + * target relations + */ + List entTarRels = ent.getTargetRelations(); + List cacheTarRels = relMap.getValuesByCKey(entId); + // check attached target relations and compare to cached + for (Relation rel : entTarRels) { + if (!isRelCurrent(rel)) { + diff.modifiedTarRels.add(rel); + } + if (!cacheTarRels.remove(rel)) { + boolean found = false; + for (Relation cr : cacheTarRels) { + if (cr.getId() == rel.getId()) { + cacheTarRels.remove(cr); + found = true; + break; + } else if (acceptNewIDs && cr.equalsContent(rel)) { + // same content is good enough + cacheTarRels.remove(cr); + found = true; + break; + } + } + if (!found) { + diff.addedTarRels.add(rel); + } + } + } + if (!cacheTarRels.isEmpty()) { + for (Relation rel : cacheTarRels) { + diff.removedTarRels.add(rel); + } + } + + // return null if diff is empty + if (diff.addedAttributes.isEmpty() && diff.removedAttributes.isEmpty() && diff.modifiedAttributes.isEmpty() + && diff.addedSrcRels.isEmpty() && diff.removedSrcRels.isEmpty() && diff.modifiedSrcRels.isEmpty() + && diff.addedTarRels.isEmpty() && diff.removedTarRels.isEmpty() && diff.modifiedTarRels.isEmpty()) { + return null; + } + return diff; + } + + + + public List getViewerAttributes(Long page){ return getViewerAttMap().getValuesByAKey(page); diff -r 83b209aa4226 -r 74cd973f6ece src/main/java/org/mpi/openmind/cache/WrapperService.java --- a/src/main/java/org/mpi/openmind/cache/WrapperService.java Thu Jan 26 19:37:25 2017 +0100 +++ b/src/main/java/org/mpi/openmind/cache/WrapperService.java Fri Jan 27 19:55:33 2017 +0100 @@ -11,6 +11,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; +import org.mpi.openmind.cache.CacheService.EntityDiff; import org.mpi.openmind.repository.bo.Attribute; import org.mpi.openmind.repository.bo.Entity; import org.mpi.openmind.repository.bo.Node; @@ -666,6 +667,8 @@ // check if entity is up to date with cache if (!cache.isEntityCurrent(entity)) { logger.error("Entity to save not up to date with cache!"); + EntityDiff diff = cache.diffEntityCache(entity, true); + logger.debug(diff); } // save in database diff -r 83b209aa4226 -r 74cd973f6ece src/main/java/org/mpi/openmind/repository/bo/Attribute.java --- a/src/main/java/org/mpi/openmind/repository/bo/Attribute.java Thu Jan 26 19:37:25 2017 +0100 +++ b/src/main/java/org/mpi/openmind/repository/bo/Attribute.java Fri Jan 27 19:55:33 2017 +0100 @@ -178,6 +178,12 @@ this.possibleValues = possibleValues; } + /** + * Returns if the other Attribute has the same ownValue and name. + * + * @param other + * @return + */ public boolean equalsContent(Attribute other){ if(other == null) return false;