Skip to content
Snippets Groups Projects
Commit 0a141fc0 authored by Henrik tom Wörden's avatar Henrik tom Wörden
Browse files

Merge branch 'f-etag' into 'dev'

EHN: ETag property for the Query

See merge request caosdb/caosdb-server!80
parents 2674aa54 16fbd6db
Branches
Tags
2 merge requests!21Release v0.4.0,!1F etag
Pipeline #5233 failed
...@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ...@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
* `ETag` property for the query. The `ETag` is assigned to the query cache
each time the cache is cleared (currently whenever the server state is being
updated, i.e. the stored entities change).
This can be used to debug the query cache and also allows a client
to determine whether the server's state has changed between queries.
* Basic caching for queries. The caching is enabled by default and can be * Basic caching for queries. The caching is enabled by default and can be
controlled by the usual "cache" flag. controlled by the usual "cache" flag.
......
...@@ -39,6 +39,7 @@ import java.util.LinkedList; ...@@ -39,6 +39,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.UUID;
import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.CommonTokenStream;
import org.apache.commons.jcs.access.behavior.ICacheAccess; import org.apache.commons.jcs.access.behavior.ICacheAccess;
...@@ -238,6 +239,14 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac ...@@ -238,6 +239,14 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
*/ */
private boolean cachable = true; private boolean cachable = true;
/**
* Tags the query cache and is renewed each time the cache is being cleared, i.e. each time the
* database is being updated.
*
* <p>As the name suggests, the idea is similar to the ETag header of the HTTP protocol.
*/
private static String cacheETag = UUID.randomUUID().toString();
public Type getType() { public Type getType() {
return this.type; return this.type;
} }
...@@ -668,6 +677,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac ...@@ -668,6 +677,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
/** Remove all cached queries from the cache. */ /** Remove all cached queries from the cache. */
public static void clearCache() { public static void clearCache() {
cacheETag = UUID.randomUUID().toString();
cache.clear(); cache.clear();
} }
...@@ -678,10 +688,12 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac ...@@ -678,10 +688,12 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
* @param resultSet * @param resultSet
*/ */
private void setCache(String key, List<IdVersionPair> resultSet) { private void setCache(String key, List<IdVersionPair> resultSet) {
if (resultSet instanceof Serializable) { synchronized (cache) {
cache.put(key, (Serializable) resultSet); if (resultSet instanceof Serializable) {
} else { cache.put(key, (Serializable) resultSet);
cache.put(key, new ArrayList<>(resultSet)); } else {
cache.put(key, new ArrayList<>(resultSet));
}
} }
} }
...@@ -859,6 +871,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac ...@@ -859,6 +871,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
ret.setAttribute("results", "0"); ret.setAttribute("results", "0");
} }
ret.setAttribute("cached", Boolean.toString(this.cached)); ret.setAttribute("cached", Boolean.toString(this.cached));
ret.setAttribute("etag", cacheETag);
final Element parseTreeElem = new Element("ParseTree"); final Element parseTreeElem = new Element("ParseTree");
if (this.el.hasErrors()) { if (this.el.hasErrors()) {
...@@ -972,4 +985,16 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac ...@@ -972,4 +985,16 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
public Role getRole() { public Role getRole() {
return this.role; return this.role;
} }
/**
* Return the ETag.
*
* <p>The ETag tags the query cache and is renewed each time the cache is being cleared, i.e. each
* time the database is being updated.
*
* @return The ETag
*/
public static String getETag() {
return cacheETag;
}
} }
package org.caosdb.server.query; package org.caosdb.server.query;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.io.IOException; import java.io.IOException;
import org.caosdb.server.CaosDBServer; import org.caosdb.server.CaosDBServer;
import org.caosdb.server.database.access.InitAccess;
import org.caosdb.server.transaction.WriteTransaction;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
...@@ -61,4 +66,40 @@ public class QueryTest { ...@@ -61,4 +66,40 @@ public class QueryTest {
assertNull(q.getEntity()); assertNull(q.getEntity());
assertEquals(Query.Role.ENTITY, q.getRole()); assertEquals(Query.Role.ENTITY, q.getRole());
} }
/**
* Assure that {@link WriteTransaction#commit()} calls {@link Query#clearCache()}.
*
* Since currently the cache shall be cleared whenever there is a commit.
* */
@Test
public void testEtagChangesAfterWrite() {
String old = Query.getETag();
assertNotNull(old);
WriteTransaction w =
new WriteTransaction(null) {
@Override
public boolean useCache() {
// this function is being overriden purely for the purpose of calling
// commit() (which is protected)
try {
// otherwise the test fails because getAccess() return null;
setAccess(new InitAccess(null));
commit();
} catch (Exception e) {
fail("this should not happen");
}
return false;
}
};
// trigger commit();
w.useCache();
String neu = Query.getETag();
assertNotEquals(old, neu, "old and new tag should not be equal");
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment