We're building a Stardog-backed application with the Spring Framework. We've included Stardog Spring via Gradle in our project.
At the lowest level, we have an applicationContext.xml file using which we defined our Stardog-related beans like datasource and template. These beans help us manage Stardog connections SnarlTemplate for transaction- and connection-pool safe Stardog programming. For instance our dataSource is like this
As can be seen, we've somehow hardcoded our database name here. We've now decided to support multi tenancy in our application. It means queries related to each user should be executed against his corresponding database. We're not sure what's the best way to implement it. For example, do we need to add/remove dataSource bean at runtime? or anything else. In this regard, it would be great if you could let us know what's the best way to build a multi-tenant Stardog-backed application.
Hi @pavel
We found an interesting topic here: Query through many databases
We think it might be related to our question as well. Could you please read our above-mentioned question and let us if we could benefit from this answer too. If yes, are the documents ready now?
The DataSource bean is a wrapper around the StardogConnectionPool, so best to think of it as the connection pool for that database. If you want to have a pool per database, then yes you may have a bunch of these -- but then I would consider another bean (that you would have to build) that would wire together dataSource and template based on which user/database it was. This could be dynamically in Java, as opposed to the traditional spring applicationContext.xml file.
Note that Pavel's suggestion is where all users talk to one Stardog database, and then you query the other databases.
I'd have to know more about your use case to advise on further direction.
We thought that we don't have a very specific need to use the SnarlTemplate from stardog-spring. So, we refactored our code to use the SNARL protocol in java directly by just instantiating the Connection (versus having Spring do it for us) like the example given here:
In other words, we previously had something like this to execute our queries:
//...
@Qualifier("templateXX")
@Autowired
private SnarlTemplate snarlTemplate;
//....
snarlTemplate.execute(
connection -> {
SelectQueryResult result = null;
try {
//doing usual stuff here like preparing my query, execute it and getting the return the return it
} catch (StardogException e) {
log.error("Error sending SELECT query to Stardog", e);
throw new RuntimeException(e);
} catch (QueryExecutionFailure e) {
log.error("Error evaluating SELECT SPARQL query", e);
throw new RuntimeException(e);
} finally {
if (result != null) {
try {
result.close();
} catch (QueryExecutionFailure e) {
log.error(e.toString());
}
}
}
});
now what we're doing is like this:
//to get information of the tenant that we're going to execute the query against
TenantBean tenantBean = new TenantBean(userDTO.getTenantName());
// creates the Stardog connection pool
ConnectionPool connectionPool =
createConnectionPool(
getConnectionConfig(
tenantBean.getUrl(),
tenantBean.getTenantName(),
tenantBean.getUsername(),
tenantBean.getPassword(),
tenantBean.isReasoningType()),
tenantBean.getMinPool(),
tenantBean.getMaxPool(),
tenantBean.getBlockCapacityTime(),
tenantBean.getExpirationTime());
try (Connection connection = getConnection(connectionPool)) {
SelectQueryResult result = null;
try {
connection.begin();
//doing usual stuff here like preparing my query, execute it and getting the return the return it
connection.commit();
return result;
} catch (StardogException e) {
log.error("Error sending SELECT query to Stardog", e);
throw new RuntimeException(e);
} catch (QueryExecutionFailure e) {
log.error("Error evaluating SELECT SPARQL query", e);
throw new RuntimeException(e);
} finally {
if (result != null) {
try {
result.close();
} catch (QueryExecutionFailure e) {
log.error(e.toString());
}
}
}
});
With regard to the above sample approach, we have these questions:
What's your thoughts?
Are we missing something or making any mistake here?
Is there any cost or potential overhead if we run SELECT queries in a transactional way? For example, like this:
connection.begin();
execute SELECT query here
connection.commit();
We create a connection pool first and then get a connection. Are we imposing any major overhead or security risk here instead of creating only a single connection like this:
ry (Connection c = ConnectionConfiguration.to("myDb").server("http://localhost:5820").credentials("admin", "admin").connect();
SelectQueryResult result = c.select(sparql).execute()) {
...
}