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

EHN: ETag property for the Query

parent 2674aa54
No related branches found
No related tags found
2 merge requests!21Release v0.4.0,!1F etag
......@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### 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
controlled by the usual "cache" flag.
......
......@@ -39,6 +39,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.apache.commons.jcs.access.behavior.ICacheAccess;
......@@ -238,6 +239,14 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
*/
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() {
return this.type;
}
......@@ -668,6 +677,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
/** Remove all cached queries from the cache. */
public static void clearCache() {
cacheETag = UUID.randomUUID().toString();
cache.clear();
}
......@@ -678,10 +688,12 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
* @param resultSet
*/
private void setCache(String key, List<IdVersionPair> resultSet) {
if (resultSet instanceof Serializable) {
cache.put(key, (Serializable) resultSet);
} else {
cache.put(key, new ArrayList<>(resultSet));
synchronized (cache) {
if (resultSet instanceof Serializable) {
cache.put(key, (Serializable) resultSet);
} else {
cache.put(key, new ArrayList<>(resultSet));
}
}
}
......@@ -859,6 +871,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
ret.setAttribute("results", "0");
}
ret.setAttribute("cached", Boolean.toString(this.cached));
ret.setAttribute("etag", cacheETag);
final Element parseTreeElem = new Element("ParseTree");
if (this.el.hasErrors()) {
......@@ -972,4 +985,16 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
public Role getRole() {
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;
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.fail;
import java.io.IOException;
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.Test;
......@@ -61,4 +66,40 @@ public class QueryTest {
assertNull(q.getEntity());
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