Permalink
Browse files

bug19569: java client now helps delete orphaned saved queries

Preparing a Sesame AGQuery currently has the side effect of saving the
prepared query on the server in memory for the duration of the session.
A client app that prepares many queries during a session thus uses up
server memory.  If an AGQuery instance goes out of scope, the saved
query is no longer needed on the server and is currently only deleted
when the session expires.  With this change, the client library no
longer uses the saved query service during a prepare query, pending
further cost-benefit analysis of the saved query service.  Client code
can explicitly request that a query be saved using an experimental
AGQuery#setSaveName method.  The client library also tries to help
delete orphaned saved queries sooner by tracking when AGQuery instances
are garbage collected on the client and issuing delete requests for the
orphaned saved queries in subsequent query requests.

Test: see AGPrepareQuery.java
make prepush run
ant tutorial run
ant jenatutorial run

<release-note>
bug19569: java client now helps delete orphaned saved queries

Preparing a Sesame AGQuery currently has the side effect of saving the
prepared query on the server in memory for the duration of the session.
A client app that prepares many queries during a session thus uses up
server memory.  If an AGQuery instance goes out of scope, the saved
query is no longer needed on the server and is currently only deleted
when the session expires.  With this change, the client library no
longer uses the saved query service during a prepare query, pending
further cost-benefit analysis of the saved query service.  Client code
can explicitly request that a query be saved using an experimental
AGQuery#setSaveName method.  The client library also tries to help
delete orphaned saved queries sooner by tracking when AGQuery instances
are garbage collected on the client and issuing delete requests for the
orphaned saved queries in subsequent query requests.
</release-note>

Change-Id: Id3ff3ab679561d35ff78717ca1001cc4cbe29f0a
Reviewed-on: https://gerrit.franz.com:9080/605
Reviewed-by: Kevin Layer <layer@franz.com>
Tested-by: Kevin Layer <layer@franz.com>
  • Loading branch information...
1 parent bee4f49 commit a886a6aff576a3027f3634389012419afeba0552 Bill Millar committed with dklayer Aug 6, 2010
@@ -23,6 +23,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpException;
@@ -82,11 +83,14 @@
private AGHTTPClient client;
private Repository repo;
+ public ConcurrentLinkedQueue<String> savedQueryDeleteQueue;
+
public AGHttpRepoClient(Repository repo, AGHTTPClient client, String repoRoot, String sessionRoot) {
this.repo = repo;
this.sessionRoot = sessionRoot;
this.repoRoot = repoRoot;
this.client = client;
+ savedQueryDeleteQueue = new ConcurrentLinkedQueue<String>();
}
public String getRoot() throws RepositoryException {
@@ -613,6 +617,7 @@ public void query(AGQuery q, AGResponseHandler handler) throws HttpException,
String url = getRoot();
if (q.isPrepared()) {
+ processSavedQueryDeleteQueue();
url = AGProtocol.getSavedQueryLocation(url,q.getName());
}
List<Header> headers = new ArrayList<Header>(5);
@@ -689,6 +694,32 @@ public void query(AGQuery q, AGResponseHandler handler) throws HttpException,
return queryParams;
}
+ /**
+ * Free up any no-longer-needed saved queries as reported
+ * by AGQuery finalizer.
+ *
+ * @throws RepositoryException
+ */
+ private void processSavedQueryDeleteQueue() throws RepositoryException {
+ String queryName;
+ while((queryName=savedQueryDeleteQueue.poll())!=null) {
+ deleteSavedQuery(queryName);
+ }
+ }
+
+ public void deleteSavedQuery(String queryName) throws RepositoryException {
+ String url = AGProtocol.getSavedQueryLocation(getRoot(), queryName);
+ Header[] headers = {};
+ NameValuePair[] params = {};
+ try {
+ getHTTPClient().delete(url, headers, params);
+ } catch (HttpException e) {
+ throw new RepositoryException(e);
+ } catch (IOException e) {
+ throw new RepositoryException(e);
+ }
+ }
+
public void close() throws RepositoryException {
if (sessionRoot != null) {
try {
@@ -44,7 +44,7 @@
*/
public static final String RESTRICTION = "restriction";
- private static long prepareId = 0L;
+ //private static long prepareId = 0L;
protected AGRepositoryConnection httpCon;
@@ -148,17 +148,21 @@ public void setPlanner(String planner) {
/**
* Schedules the query to be prepared.
+ *
+ * Note: this is a no-op pending further cost-benefit analysis of
+ * the server's saved query service.
*/
- synchronized public void prepare() {
- setSaveName(String.valueOf(prepareId++));
+ synchronized void prepare() {
+ //setSaveName(String.valueOf(prepareId++));
}
/**
- * Sets the savedName for the prepared query.
+ * Sets the name to use when saving this query with the
+ * server's saved query service.
*
- * @return the saved name.
+ * @param name the saved name.
*/
- private void setSaveName(String name) {
+ public void setSaveName(String name) {
saveName = name;
}
@@ -207,4 +211,13 @@ public String toString()
{
return queryString;
}
+
+
+ @Override
+ protected void finalize() {
+ if (saveName!=null) {
+ httpCon.getHttpRepoClient().savedQueryDeleteQueue.add(saveName);
+ }
+ }
+
}
@@ -0,0 +1,76 @@
+/******************************************************************************
+** Copyright (c) 2008-2010 Franz Inc.
+** All rights reserved. This program and the accompanying materials
+** are made available under the terms of the Eclipse Public License v1.0
+** which accompanies this distribution, and is available at
+** http://www.eclipse.org/legal/epl-v10.html
+******************************************************************************/
+
+package test;
+
+import org.openrdf.model.URI;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.query.QueryLanguage;
+import org.openrdf.query.TupleQuery;
+import org.openrdf.query.TupleQueryResult;
+
+import tutorial.TutorialExamples;
+
+import com.franz.agraph.repository.AGRepositoryConnection;
+import com.franz.agraph.repository.AGTupleQuery;
+
+public class AGPrepareQuery extends TutorialExamples {
+
+ /**
+ * Exercises prepared and unprepared queries, and occasionally
+ * forces gc of old AGQuery instances so that orphaned saved
+ * queries can be deleted during subsequent prepare requests;
+ * verify by watching the http traffic to the server.
+ *
+ * @throws Exception
+ */
+ public static void prepareQueryExample() throws Exception {
+ AGRepositoryConnection conn = example2(false);
+ ValueFactory f = conn.getValueFactory();
+ conn.setAutoCommit(false);
+ URI alice = f.createURI("http://example.org/people/alice");
+ URI bob = f.createURI("http://example.org/people/bob");
+ String queryString = "select ?s ?p ?o where { ?s ?p ?o} ";
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 10; j++) {
+ AGTupleQuery tupleQuery = conn.prepareTupleQuery(
+ QueryLanguage.SPARQL, queryString);
+ tupleQuery.setSaveName(String.valueOf(10*i+j));
+ tupleQuery.setBinding("s", alice);
+ TupleQueryResult result = tupleQuery.evaluate();
+ println("\nFacts about Alice:");
+ while (result.hasNext()) {
+ println(result.next());
+ }
+ result.close();
+ tupleQuery.setBinding("s", bob);
+ println("\nFacts about Bob:");
+ result = tupleQuery.evaluate();
+ while (result.hasNext()) {
+ println(result.next());
+ }
+ result.close();
+ TupleQuery tupleQuery2 = new AGTupleQuery(conn, QueryLanguage.SPARQL, queryString, null);
+ result = tupleQuery2.evaluate();
+ println("\nAll Facts:");
+ while (result.hasNext()) {
+ println(result.next());
+ }
+ result.close();
+ }
+ System.gc();
+ }
+ conn.close();
+ System.gc();
+ }
+
+ public static void main(String[] args) throws Exception {
+ prepareQueryExample();
+ }
+
+}

0 comments on commit a886a6a

Please sign in to comment.