Skip to content

Commit 0004f28

Browse files
authored
Merge pull request #379 from LiUSemWeb/378-persisted-disk-cache-for-cardinality-queries
378 persisted disk cache for cardinality queries
2 parents 2784cb4 + 20bd44e commit 0004f28

File tree

5 files changed

+1312
-0
lines changed

5 files changed

+1312
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package se.liu.ida.hefquin.base.datastructures;
2+
3+
/**
4+
* A generic interface for data structures that can be used as a persisted cache
5+
* for objects of a specific type. Implementations of this interface may employ
6+
* their particular policies for cache replacement and for cache invalidation.
7+
*
8+
* @param <IdType> the type of the values by which the cached objects can be identified
9+
* @param <ObjectType> the type of the objects to be maintained in this cache
10+
*/
11+
public interface PersistableCache<IdType, ObjectType> extends Cache<IdType, ObjectType> {
12+
13+
/**
14+
* Saves the current state of the cache to persistent storage.
15+
* Implementations may choose different mechanisms for persistence,
16+
* such as writing to a file or a database.
17+
*
18+
* This method should ensure that all cached data is synchronized with
19+
* persistent storage. Depending on the implementation, synchronization
20+
* may be automatic or explicitly controlled.
21+
*/
22+
void save();
23+
24+
/**
25+
* Loads the cache state from persistent storage.
26+
* If persistent data exists, it should be restored into the cache.
27+
* Implementations should handle cases where no prior state exists
28+
* gracefully.
29+
*/
30+
void load();
31+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package se.liu.ida.hefquin.engine.federation.access.impl;
2+
3+
import java.io.Serializable;
4+
import java.util.Objects;
5+
import se.liu.ida.hefquin.engine.federation.BRTPFServer;
6+
import se.liu.ida.hefquin.engine.federation.FederationMember;
7+
import se.liu.ida.hefquin.engine.federation.SPARQLEndpoint;
8+
import se.liu.ida.hefquin.engine.federation.TPFServer;
9+
import se.liu.ida.hefquin.engine.federation.access.BRTPFRequest;
10+
import se.liu.ida.hefquin.engine.federation.access.DataRetrievalRequest;
11+
import se.liu.ida.hefquin.engine.federation.access.SPARQLRequest;
12+
import se.liu.ida.hefquin.engine.federation.access.TPFRequest;
13+
14+
/**
15+
* A key for caching cardinality requests, uniquely identified by a {@link DataRetrievalRequest} and a {@link FederationMember}.
16+
*/
17+
public class CardinalityCacheKey implements Serializable {
18+
private static final long serialVersionUID = 1L;
19+
20+
protected final String query;
21+
protected final String url;
22+
protected final String bindings;
23+
24+
public CardinalityCacheKey( final DataRetrievalRequest req, final FederationMember fm ) {
25+
if ( req instanceof SPARQLRequest sparqlRequest && fm instanceof SPARQLEndpoint sparqlEndpoint ) {
26+
query = sparqlRequest.toString();
27+
url = sparqlEndpoint.getInterface().getURL();
28+
bindings = "";
29+
}
30+
else if ( req instanceof TPFRequest tpfRequest ) {
31+
query = tpfRequest.toString();
32+
bindings = "";
33+
if ( fm instanceof TPFServer tpfServer ) {
34+
url = tpfServer.getInterface().createRequestURL( tpfRequest );
35+
}
36+
else if ( fm instanceof BRTPFServer brtpfServer ) {
37+
url = brtpfServer.getInterface().createRequestURL( tpfRequest );
38+
}
39+
else {
40+
throw new IllegalArgumentException( "Unexpected type of server: " + fm.getClass().getName() );
41+
}
42+
}
43+
else if ( req instanceof BRTPFRequest brtpfRequest && fm instanceof BRTPFServer brtpfServer ) {
44+
query = brtpfRequest.getTriplePattern().toString();
45+
url = brtpfServer.getInterface().createRequestURL( brtpfRequest );
46+
bindings = brtpfRequest.getSolutionMappings().toString();
47+
}
48+
else {
49+
throw new IllegalArgumentException( "Unexpected request type: " + req.getClass().getName() + "(server type: " + fm.getClass().getName() + ")" );
50+
}
51+
}
52+
53+
@Override
54+
public boolean equals( Object obj ) {
55+
if ( this == obj )
56+
return true;
57+
if ( obj == null || getClass() != obj.getClass() )
58+
return false;
59+
CardinalityCacheKey other = (CardinalityCacheKey) obj;
60+
return query.equals( other.query ) && url.equals( other.url );
61+
}
62+
63+
@Override
64+
public int hashCode() {
65+
return Objects.hash( query, url );
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return "CardinalityCacheKey{query='" + query + "', url='" + url + "', bindings='" + bindings + "'}";
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package se.liu.ida.hefquin.engine.federation.access.impl;
2+
3+
import java.util.concurrent.CompletableFuture;
4+
import java.util.concurrent.ExecutorService;
5+
6+
import se.liu.ida.hefquin.base.datastructures.impl.cache.CacheEntry;
7+
import se.liu.ida.hefquin.base.datastructures.impl.cache.CachePolicies;
8+
import se.liu.ida.hefquin.engine.federation.BRTPFServer;
9+
import se.liu.ida.hefquin.engine.federation.SPARQLEndpoint;
10+
import se.liu.ida.hefquin.engine.federation.TPFServer;
11+
import se.liu.ida.hefquin.engine.federation.access.BRTPFRequest;
12+
import se.liu.ida.hefquin.engine.federation.access.CardinalityResponse;
13+
import se.liu.ida.hefquin.engine.federation.access.DataRetrievalResponse;
14+
import se.liu.ida.hefquin.engine.federation.access.FederationAccessException;
15+
import se.liu.ida.hefquin.engine.federation.access.FederationAccessManager;
16+
import se.liu.ida.hefquin.engine.federation.access.SPARQLRequest;
17+
import se.liu.ida.hefquin.engine.federation.access.TPFRequest;
18+
19+
/**
20+
* A FederationAccessManager implementation that incorporates persistent disk
21+
* caching of SPARQL cardinality requests.
22+
*
23+
* TODO: The implementation uses a simple serialization/deserialization
24+
* strategy, where the file is stored to disk by writing the full map to disk
25+
* for each update. This approach is not optimized for this task but is simply
26+
* intended as a proof of concept. A future implementation should support
27+
* standard cache configuration policys, such as time-based eviction (time to
28+
* live), but should leverage an optimized persistence strategy, preferably
29+
* leveraging a libary.
30+
*
31+
* Note: Most of the classes/interfaces involved (e.g., DataRetrievalResponse,
32+
* CardinalityResponse etc.) do not support serialization.
33+
*/
34+
public class FederationAccessManagerWithPersistedDiskCache extends FederationAccessManagerWithCache
35+
{
36+
protected final PersistableCardinalityCacheImpl<CardinalityCacheKey> cardinalityCache;
37+
38+
public FederationAccessManagerWithPersistedDiskCache( final FederationAccessManager fedAccMan,
39+
final int cacheCapacity,
40+
final CachePolicies<Key, CompletableFuture<? extends DataRetrievalResponse>, ? extends CacheEntry<CompletableFuture<? extends DataRetrievalResponse>>> cachePolicies )
41+
{
42+
super( fedAccMan, cacheCapacity, cachePolicies );
43+
cardinalityCache = new PersistableCardinalityCacheImpl<>();
44+
}
45+
46+
public FederationAccessManagerWithPersistedDiskCache( final FederationAccessManager fedAccMan,
47+
final int cacheCapacity )
48+
{
49+
this( fedAccMan, cacheCapacity, new MyDefaultCachePolicies() );
50+
}
51+
52+
/**
53+
* Creates a {@link FederationAccessManagerWithPersistedDiskCache} with a default configuration.
54+
*/
55+
public FederationAccessManagerWithPersistedDiskCache( final ExecutorService execService )
56+
{
57+
this( new AsyncFederationAccessManagerImpl( execService ), 100, new MyDefaultCachePolicies() );
58+
}
59+
60+
@Override
61+
public CompletableFuture<CardinalityResponse> issueCardinalityRequest( final SPARQLRequest req,
62+
final SPARQLEndpoint fm )
63+
throws FederationAccessException
64+
{
65+
final CardinalityCacheKey key = new CardinalityCacheKey( req, fm );
66+
final CompletableFuture<CardinalityResponse> cachedResponse = cardinalityCache.get( key );
67+
if ( cachedResponse != null ) {
68+
cacheHitsSPARQLCardinality++;
69+
return cachedResponse;
70+
}
71+
72+
final CompletableFuture<CardinalityResponse> newResponse = fedAccMan.issueCardinalityRequest( req, fm );
73+
cardinalityCache.put( key, newResponse );
74+
newResponse.thenRun( () -> cardinalityCache.save() );
75+
return newResponse;
76+
}
77+
78+
@Override
79+
public CompletableFuture<CardinalityResponse> issueCardinalityRequest( final TPFRequest req,
80+
final TPFServer fm )
81+
throws FederationAccessException
82+
{
83+
final CardinalityCacheKey key = new CardinalityCacheKey( req, fm );
84+
final CompletableFuture<CardinalityResponse> cachedResponse = cardinalityCache.get( key );
85+
if ( cachedResponse != null ) {
86+
cacheHitsTPFCardinality++;
87+
return cachedResponse;
88+
}
89+
90+
final CompletableFuture<CardinalityResponse> newResponse = fedAccMan.issueCardinalityRequest( req, fm );
91+
cardinalityCache.put( key, newResponse );
92+
newResponse.thenRun( () -> cardinalityCache.save() );
93+
return newResponse;
94+
}
95+
96+
@Override
97+
public CompletableFuture<CardinalityResponse> issueCardinalityRequest( final TPFRequest req,
98+
final BRTPFServer fm )
99+
throws FederationAccessException
100+
{
101+
final CardinalityCacheKey key = new CardinalityCacheKey( req, fm );
102+
final CompletableFuture<CardinalityResponse> cachedResponse = cardinalityCache.get( key );
103+
if ( cachedResponse != null ) {
104+
cacheHitsTPFCardinality++;
105+
return cachedResponse;
106+
}
107+
108+
final CompletableFuture<CardinalityResponse> newResponse = fedAccMan.issueCardinalityRequest( req, fm );
109+
cardinalityCache.put( key, newResponse );
110+
newResponse.thenRun( () -> cardinalityCache.save() );
111+
return newResponse;
112+
}
113+
114+
@Override
115+
public CompletableFuture<CardinalityResponse> issueCardinalityRequest( final BRTPFRequest req,
116+
final BRTPFServer fm )
117+
throws FederationAccessException
118+
{
119+
final CardinalityCacheKey key = new CardinalityCacheKey( req, fm );
120+
final CompletableFuture<CardinalityResponse> cachedResponse = cardinalityCache.get( key );
121+
if ( cachedResponse != null ) {
122+
cacheHitsBRTPFCardinality++;
123+
return cachedResponse;
124+
}
125+
final CompletableFuture<CardinalityResponse> newResponse = fedAccMan.issueCardinalityRequest( req, fm) ;
126+
cardinalityCache.put( key, newResponse );
127+
newResponse.thenRun( () -> cardinalityCache.save() );
128+
return newResponse;
129+
}
130+
131+
/**
132+
* Clears the persisted cardinality cache map.
133+
*/
134+
public void clearCardinalityCache(){
135+
cardinalityCache.clear();
136+
cardinalityCache.save();
137+
}
138+
}

0 commit comments

Comments
 (0)