diff --git a/CHANGELOG.md b/CHANGELOG.md index bca214af664914277377d179c61ce2716f066e12..564fefa5f71c6596928cbc1df48eecf708e58b3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 by setting the server property `EXT_ENTITY_STATE=ENABLED`. See [!62](https://gitlab.com/caosdb/caosdb-server/-/merge_requests/62) for more information. +* `ETag` property for the query. The `ETag` tags a server state and is being + updated each time 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. diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java index deee34bfea87f15167689c602ab4a238cea547fe..cd323bd972b21a261c84bfa6c16c08ea54558fd0 100644 --- a/src/main/java/org/caosdb/server/query/Query.java +++ b/src/main/java/org/caosdb/server/query/Query.java @@ -239,6 +239,12 @@ 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() { @@ -979,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; + } } diff --git a/src/test/java/org/caosdb/server/query/QueryTest.java b/src/test/java/org/caosdb/server/query/QueryTest.java index edbefab78fdfb5846c59a058fdfb67b55a6707cb..9ca813701ada8e5dd7f311ece5cf9f423974e79f 100644 --- a/src/test/java/org/caosdb/server/query/QueryTest.java +++ b/src/test/java/org/caosdb/server/query/QueryTest.java @@ -1,10 +1,15 @@ 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,36 @@ public class QueryTest { assertNull(q.getEntity()); assertEquals(Query.Role.ENTITY, q.getRole()); } + + /** Assure that {@link WriteTransaction#commit()} calls {@link Query#clearCache()}. */ + @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"); + } }