Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-5231: fix poor query performance for hasStatements() in FedX #5232

Merged
merged 2 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,27 @@ protected SailException convert(RuntimeException e) {
}
}

@Override
protected boolean hasStatementInternal(Resource subj, IRI pred, Value obj, boolean includeInferred,
Resource[] contexts) {
try {
Dataset dataset = new SimpleDataset();
FederationEvalStrategy strategy = federationContext.createStrategy(dataset);
QueryInfo queryInfo = new QueryInfo(subj, pred, obj, 0, includeInferred, federationContext, strategy,
dataset);
federationContext.getMonitoringService().monitorQuery(queryInfo);
return strategy.hasStatements(queryInfo, subj, pred, obj, contexts);

} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
throw new SailException(e);
}
}

@Override
protected void addStatementInternal(Resource subj, IRI pred, Value obj, Resource... contexts) throws SailException {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import org.eclipse.rdf4j.common.annotation.Experimental;
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.EmptyIteration;
import org.eclipse.rdf4j.common.iteration.SingletonIteration;
Expand Down Expand Up @@ -562,7 +563,7 @@ public CloseableIteration<Statement> getStatements(QueryInfo queryInfo, Resource
IRI pred, Value obj, Resource... contexts)
throws RepositoryException, MalformedQueryException, QueryEvaluationException {

List<Endpoint> members = federationContext.getFederation().getMembers();
List<Endpoint> members = getAccessibleFederationMembers(queryInfo);

// a bound query: if at least one fed member provides results
// return the statement, otherwise empty result
Expand Down Expand Up @@ -605,6 +606,53 @@ public CloseableIteration<Statement> getStatements(QueryInfo queryInfo, Resource
return union;
}

/**
* Returns true if the federation has statements
*
* @param queryInfo information about the query
* @param subj the subject or <code>null</code>
* @param pred the predicate or <code>null</code>
* @param obj the object or <code>null</code>
* @param contexts optional list of contexts
* @return the statement iteration
*
* @throws RepositoryException
* @throws MalformedQueryException
* @throws QueryEvaluationException
*/
public boolean hasStatements(QueryInfo queryInfo, Resource subj,
IRI pred, Value obj, Resource... contexts)
throws RepositoryException, MalformedQueryException, QueryEvaluationException {

List<Endpoint> members = getAccessibleFederationMembers(queryInfo);

// form the union of results from relevant endpoints
List<StatementSource> sources = CacheUtils.checkCacheForStatementSourcesUpdateCache(cache, members, subj, pred,
obj, queryInfo, contexts);

if (sources.isEmpty()) {
return false;
}

return true;
}

/**
* Returns the accessible federation members in the context of the query. By default this is all federation members.
* <p>
* Specialized implementations of the {@link FederationEvalStrategy} may override and define custom behavior (e.g.,
* to support resilience).
* </p>
*
*
* @param queryInfo
* @return
*/
@Experimental
protected List<Endpoint> getAccessibleFederationMembers(QueryInfo queryInfo) {
return federationContext.getFederation().getMembers();
}

public CloseableIteration<BindingSet> evaluateService(FedXService service,
BindingSet bindings) throws QueryEvaluationException {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,61 @@ public void testBindClause() throws Exception {
execute("/tests/basic/query_bind.rq", "/tests/basic/query_bind.srx", false, true);
}

@Test
public void testRepositoryConnectionApi() throws Exception {

prepareTest(
Arrays.asList("/tests/basic/data_emptyStore.ttl", "/tests/basic/data_emptyStore.ttl"));

Repository repo1 = getRepository(1);
Repository repo2 = getRepository(2);

IRI bob = Values.iri("http://example.org/bob");
IRI alice = Values.iri("http://example.org/alice");
IRI graph1 = Values.iri("http://example.org/graph1");
IRI graph2 = Values.iri("http://example.org/graph2");

try (RepositoryConnection conn = repo1.getConnection()) {
conn.add(bob, RDF.TYPE, FOAF.PERSON, graph1);
conn.add(bob, FOAF.NAME, Values.literal("Bob"), graph1);
}

try (RepositoryConnection conn = repo2.getConnection()) {
conn.add(alice, RDF.TYPE, FOAF.PERSON, graph2);
conn.add(alice, FOAF.NAME, Values.literal("Alice"), graph2);
}

var fedxRepo = fedxRule.getRepository();

try (var conn = fedxRepo.getConnection()) {

// hasStatement which exist
Assertions.assertTrue(conn.hasStatement(bob, RDF.TYPE, FOAF.PERSON, false));
Assertions.assertTrue(conn.hasStatement(bob, RDF.TYPE, FOAF.PERSON, false, graph1));
Assertions.assertTrue(conn.hasStatement(null, RDF.TYPE, FOAF.PERSON, false));
Assertions.assertTrue(conn.hasStatement(null, RDF.TYPE, FOAF.PERSON, false, graph1));
Assertions.assertTrue(conn.hasStatement(null, RDF.TYPE, null, false));
Assertions.assertTrue(conn.hasStatement(null, RDF.TYPE, null, false, graph1));
Assertions.assertTrue(conn.hasStatement(null, RDF.TYPE, null, false, graph2));
Assertions.assertTrue(conn.hasStatement(null, null, null, false));
Assertions.assertTrue(conn.hasStatement(null, null, null, false, graph1));

// hasStatement which do not exist
Assertions.assertFalse(conn.hasStatement(bob, RDF.TYPE, FOAF.ORGANIZATION, false));
Assertions.assertFalse(conn.hasStatement(bob, RDF.TYPE, FOAF.PERSON, false, graph2));

// getStatements
Assertions.assertEquals(Set.of(bob, alice),
QueryResults.asModel(conn.getStatements(null, RDF.TYPE, FOAF.PERSON, false)).subjects());
Assertions.assertEquals(Set.of(bob),
QueryResults.asModel(conn.getStatements(null, RDF.TYPE, FOAF.PERSON, false, graph1)).subjects());
Assertions.assertEquals(Set.of(bob, alice),
QueryResults.asModel(conn.getStatements(null, null, null, false)).subjects());
Assertions.assertEquals(Set.of(bob),
QueryResults.asModel(conn.getStatements(null, null, null, false, graph1)).subjects());
}
}

@Test
public void testFederationSubSetQuery() throws Exception {
String ns1 = "http://namespace1.org/";
Expand Down
Loading