From: <tho...@us...> - 2012-09-13 20:43:19
|
Revision: 6571 http://bigdata.svn.sourceforge.net/bigdata/?rev=6571&view=rev Author: thompsonbry Date: 2012-09-13 20:43:12 +0000 (Thu, 13 Sep 2012) Log Message: ----------- I have gone ahead and implemented a "monitor" option for SPARQL UPDATE. This option is enabled by the URL Query parameter named "monitor." When specified, the HTTP status code will be 200 and the response will be an XHTML document, even if there the UPDATE request fails. If the UPDATE request fails, then the fact that it was failed is written into the XHTML document. This option is designed for human monitoring of long-running UPDATE requests. When this option is NOT specified, the HTTP status code indicates whether or not the UPDATE request was successful. @see https://sourceforge.net/apps/trac/bigdata/ticket/597 (SPARQL UPDATE LISTENER) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpUpdate.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/SPARQLUpdateEvent.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/QueryServlet.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/UpdateServlet.java branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/index.html Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpUpdate.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpUpdate.java 2012-09-13 18:27:31 UTC (rev 6570) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpUpdate.java 2012-09-13 20:43:12 UTC (rev 6571) @@ -272,11 +272,17 @@ final long begin = System.nanoTime(); + Throwable cause = null; try { // convert/run the update operation. left = convertUpdateSwitch(left, op, context); } catch (Throwable t) { + cause = t; log.error("SPARQL UPDATE failure: op=" + op + ", ex=" + t, t); + // notify listener(s) + final long elapsed = System.nanoTime() - begin; + context.conn.getSailConnection().fireEvent( + new SPARQLUpdateEvent(op, elapsed, cause)); if (t instanceof Exception) throw (Exception) t; if (t instanceof RuntimeException) @@ -284,11 +290,11 @@ throw new RuntimeException(t); } - final long elapsed = begin - System.nanoTime(); + final long elapsed = System.nanoTime() - begin; // notify listener(s) context.conn.getSailConnection().fireEvent( - new SPARQLUpdateEvent(op, elapsed)); + new SPARQLUpdateEvent(op, elapsed, cause)); updateIndex++; @@ -1558,7 +1564,7 @@ final long nparsed = nmodified.incrementAndGet(); - if (nparsed % 10000 == 0L) { + if ((nparsed % 10000) == 0L) { final long elapsed = System.nanoTime() - beginNanos; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/SPARQLUpdateEvent.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/SPARQLUpdateEvent.java 2012-09-13 18:27:31 UTC (rev 6570) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/SPARQLUpdateEvent.java 2012-09-13 20:43:12 UTC (rev 6571) @@ -29,20 +29,35 @@ * An event reflecting progress for some sequence of SPARQL UPDATE operations. * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> - * - * TODO Support incremental reporting on long running operations, - * especially LOAD. */ public class SPARQLUpdateEvent { private final Update op; private final long elapsed; + private final Throwable cause; - public SPARQLUpdateEvent(final Update op, final long elapsed) { + /** + * + * @param op + * The {@link Update} operation. + * @param elapsed + * The elapsed time. + * @param cause + * The cause iff an error occurred and otherwise + * <code>null</code>. + */ + public SPARQLUpdateEvent(final Update op, final long elapsed, + final Throwable cause) { + if (op == null) + throw new IllegalArgumentException(); + this.op = op; + this.elapsed = elapsed; + this.cause = cause; + } /** @@ -60,6 +75,13 @@ } /** + * The cause iff an error occurred and otherwise <code>null</code>. + */ + public Throwable getCause() { + return cause; + } + + /** * Incremental progress report during <code>LOAD</code>. */ public static class LoadProgress extends SPARQLUpdateEvent { @@ -69,7 +91,7 @@ public LoadProgress(final Update op, final long elapsed, final long nparsed) { - super(op, elapsed); + super(op, elapsed, null/* cause */); this.nparsed = nparsed; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-13 18:27:31 UTC (rev 6570) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-13 20:43:12 UTC (rev 6571) @@ -22,9 +22,12 @@ */ package com.bigdata.rdf.sail.webapp; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.Iterator; @@ -86,6 +89,8 @@ import com.bigdata.rdf.sail.BigdataSailRepositoryConnection; import com.bigdata.rdf.sail.BigdataSailTupleQuery; import com.bigdata.rdf.sail.BigdataSailUpdate; +import com.bigdata.rdf.sail.ISPARQLUpdateListener; +import com.bigdata.rdf.sail.SPARQLUpdateEvent; import com.bigdata.rdf.sail.sparql.Bigdata2ASTSPARQLParser; import com.bigdata.rdf.sparql.ast.ASTContainer; import com.bigdata.rdf.sparql.ast.QueryHints; @@ -117,11 +122,31 @@ protected static final String EXPLAIN = "explain"; /** - * URL Query parameter used to request the "analytic" query hints. + * URL Query parameter used to request the "analytic" query hints. MAY be + * <code>null</code>, in which case we do not set + * {@link QueryHints#ANALYTIC} query hint. */ protected static final String ANALYTIC = "analytic"; /** + * URL Query parameter used to request an XHTML response for SPARQL + * QUERY or SPARQL UPDATE. For SPARQL QUERY, this provides an XHTML + * table view of the solutions. For SPARQL UPDATE, this provides an + * incremental progress report on the UPDATE request. + */ + protected static final String XHTML = "xhtml"; + + /** + * URL Query parameter used to request an incremental XHTML representation + * reporting on the progress of a SPARQL UPDATE. + * <p> + * Note: When this option is specified, the SPARQL UPDATE will use an HTTP + * status code of 200 (Ok) even if the UPDATE fails. In the event of an + * UPDATE failure, the stack trace will be formatted into the response. + */ + protected static final String MONITOR = "monitor"; + + /** * URL query parameter used to specify a URI in the default graph for SPARQL * query (but not for SPARQL update). */ @@ -383,7 +408,10 @@ /** The request. */ protected final HttpServletRequest req; - + + /** The response. */ + protected final HttpServletResponse resp; + /** Where to write the response. */ protected final OutputStream os; @@ -419,19 +447,39 @@ protected AbstractOperation sailQueryOrUpdate; /** - * When true, provide an "explanation" for the query (query plan, query - * evaluation statistics) rather than the results of the query. + * When <code>true</code>, provide an "explanation" for the query (query + * plan, query evaluation statistics) rather than the results of the + * query. */ final boolean explain; - + /** - * When true, enable the "analytic" query hints. MAY be - * <code>null</code>, in which case we do not set the query hint. + * When <code>true</code>, enable the "analytic" query hints. */ final Boolean analytic; + + /** + * When <code>true</code>, provide an view of the XHTML representation + * of the solutions or graph result (SPARQL QUERY) + * + * @see BigdataRDFContext#XHTML + */ + final boolean xhtml; /** + * When <code>true</code>, provide an incremental XHTML representation + * reporting on the progress of a SPARQL UPDATE. + * <p> + * Note: When <code>true</code>, the SPARQL UPDATE will use an HTTP + * status code of 200 (Ok) even if the UPDATE fails. In the event of an + * UPDATE failure, the stack trace will be formatted into the response. * + * @see BigdataRDFContext#MONITOR + */ + final boolean monitor; + + /** + * * @param namespace * The namespace against which the query will be run. * @param timestamp @@ -470,6 +518,7 @@ final Charset charset,// final String fileExt,// final HttpServletRequest req,// + final HttpServletResponse resp,// final OutputStream os// ) { @@ -487,6 +536,8 @@ throw new IllegalArgumentException(); if (req == null) throw new IllegalArgumentException(); + if (resp == null) + throw new IllegalArgumentException(); if (os == null) throw new IllegalArgumentException(); @@ -500,9 +551,14 @@ this.charset = charset; this.fileExt = fileExt; this.req = req; + this.resp = resp; this.explain = req.getParameter(EXPLAIN) != null; this.analytic = getEffectiveBooleanValue( req.getParameter(ANALYTIC), QueryHints.DEFAULT_ANALYTIC); + this.xhtml = getEffectiveBooleanValue(req.getParameter(XHTML), + false); + this.monitor = getEffectiveBooleanValue(req.getParameter(MONITOR), + false); this.os = os; this.queryId = Long.valueOf(m_queryIdFactory.incrementAndGet()); @@ -523,6 +579,8 @@ * tree, etc. * @param req * The request. + * @param resp + * The response. * @param os * Where to write the data for the query result. */ @@ -536,6 +594,7 @@ // final Charset charset,// // final String fileExt,// final HttpServletRequest req,// + final HttpServletResponse resp,// final OutputStream os// ) { @@ -547,6 +606,8 @@ throw new IllegalArgumentException(); if (req == null) throw new IllegalArgumentException(); + if (resp == null) + throw new IllegalArgumentException(); if (os == null) throw new IllegalArgumentException(); @@ -560,9 +621,14 @@ this.charset = Charset.forName("UTF-8"); this.fileExt = null; this.req = req; + this.resp = resp; this.explain = req.getParameter(EXPLAIN) != null; this.analytic = getEffectiveBooleanValue( req.getParameter(ANALYTIC), QueryHints.DEFAULT_ANALYTIC); + this.xhtml = getEffectiveBooleanValue(req.getParameter(XHTML), + false); + this.monitor = getEffectiveBooleanValue(req.getParameter(MONITOR), + false); this.os = os; this.queryId = Long.valueOf(m_queryIdFactory.incrementAndGet()); @@ -858,12 +924,12 @@ final String baseURI, final ASTContainer astContainer, final QueryType queryType, final BooleanQueryResultFormat format, - final HttpServletRequest req, final OutputStream os) { + final HttpServletRequest req, final HttpServletResponse resp, + final OutputStream os) { - super(namespace, timestamp, baseURI, astContainer, - queryType, format.getDefaultMIMEType(), - format.getCharset(), format.getDefaultFileExtension(), req, - os); + super(namespace, timestamp, baseURI, astContainer, queryType, + format.getDefaultMIMEType(), format.getCharset(), format + .getDefaultFileExtension(), req, resp, os); } @@ -871,38 +937,36 @@ final OutputStream os) throws Exception { final BigdataSailBooleanQuery query = (BigdataSailBooleanQuery) setupQuery(cxn); - + // Note: getQueryTask() verifies that format will be non-null. final BooleanQueryResultFormat format = BooleanQueryResultWriterRegistry .getInstance().getFileFormatForMIMEType(mimeType); final BooleanQueryResultWriter w = BooleanQueryResultWriterRegistry .getInstance().get(format).getWriter(os); - + final boolean result = query.evaluate(); - + w.write(result); } } - /** - * Executes a tuple query. - */ - private class TupleQueryTask extends AbstractQueryTask { + /** + * Executes a tuple query. + */ + private class TupleQueryTask extends AbstractQueryTask { public TupleQueryTask(final String namespace, final long timestamp, - final String baseURI, - final ASTContainer astContainer, final QueryType queryType, - final TupleQueryResultFormat format, - final HttpServletRequest req, + final String baseURI, final ASTContainer astContainer, + final QueryType queryType, final TupleQueryResultFormat format, + final HttpServletRequest req, final HttpServletResponse resp, final OutputStream os) { - super(namespace, timestamp, baseURI, astContainer, - queryType, format.getDefaultMIMEType(), - format.getCharset(), format.getDefaultFileExtension(), req, - os); + super(namespace, timestamp, baseURI, astContainer, queryType, + format.getDefaultMIMEType(), format.getCharset(), format + .getDefaultFileExtension(), req, resp, os); } @@ -932,11 +996,12 @@ public GraphQueryTask(final String namespace, final long timestamp, final String baseURI, final ASTContainer astContainer, final QueryType queryType, final RDFFormat format, - final HttpServletRequest req, final OutputStream os) { + final HttpServletRequest req, final HttpServletResponse resp, + final OutputStream os) { super(namespace, timestamp, baseURI, astContainer, queryType, format.getDefaultMIMEType(), format.getCharset(), format - .getDefaultFileExtension(), req, os); + .getDefaultFileExtension(), req, resp, os); } @@ -994,7 +1059,8 @@ public UpdateTask(final String namespace, final long timestamp, final String baseURI, final ASTContainer astContainer, - final HttpServletRequest req, final OutputStream os) { + final HttpServletRequest req, final HttpServletResponse resp, + final OutputStream os) { super(namespace, timestamp, baseURI, astContainer, // null,//queryType @@ -1002,6 +1068,7 @@ // null,//format.getCharset(), // null,//format.getDefaultFileExtension(), req,// + resp,// os// ); @@ -1018,167 +1085,309 @@ // Prepare the UPDATE request. final BigdataSailUpdate update = setupUpdate(cxn); - /* - * Execute first. This let's us know whether or not the entire - * UPDATE request (one or more operations) was successful and that - * let's us communicate the outcome of the request via an HTTP - * status code. - * - * If you write on the response before doing this step, you will be - * unable to send back an HTTP status code indicating a failure. - * - * If we want to provide incremental status reports on running - * UPDATE requests, then we need to change the API contract and make - * sure that our client (RemoteRepository) parses the response - * document to figure out whether or not the UPDATE request was - * successful instead of just looking at the status code. - * - * @see https://sourceforge.net/apps/trac/bigdata/ticket/597 - */ + final SparqlUpdateResponseWriter listener; + final ByteArrayOutputStream baos; + if(monitor) { - /* - * Note: The SPARQL UPDATE listener can not provide incremental - * reporting due to problem with the timing of when the http - * response is committed. See comments immediately above. - */ - // cxn.getSailConnection().addListener(listener); + /* + * Establish a listener that will log the process onto an XHTML + * document that will be delivered (flushed) incrementally to + * the client. The status code for the response will always be + * 200 (Ok). If there is an error, then that error will be + * reported in the XHTML response but NOT in the status code. + */ + + // Do not buffer the response. + baos = null; + + // Always sending an OK with a response entity. + resp.setStatus(BigdataServlet.HTTP_OK); - this.commitTime.set(update.execute2()); + // This will write the response entity. + listener = new SparqlUpdateResponseWriter(resp, os, charset, + true /* reportLoadProgress */, true/* flushEachEvent */); - /* - * Write out the response. - */ + } else { - final Writer w = new OutputStreamWriter(os, charset); + /* + * The listener logs the progress report (with the incremental + * load events) onto an xml document model but DOES NOT write + * anything on the servlet response. If there is an error, the + * HTTP status code will reflect that error. Otherwise we send + * back the XML document with a 200 (Ok) status code. + * + * Note: This code path supports REST clients that expect the + * status code to reflect the *outcome* of the SPARQL UPDATE + * request. We MUST NOT write the XML document onto the response + * incrementally since the servlet response can become committed + * and we will be unable to change the status code from Ok (200) + * if an error occurs. + */ - final HTMLBuilder doc = new HTMLBuilder(charset.name(), w); + // buffer the response here. + baos = new ByteArrayOutputStream(); - final XMLBuilder.Node body = writeSparqlUpdateResponseHeader(doc, - charset); + listener = new SparqlUpdateResponseWriter(resp, os, charset, + false/* reportLoadProgress */, false/* flushEachEvent */); + } + try { + + // Setup the SPARQL UPDATE listener. + cxn.getSailConnection().addListener(listener); - body.node("p")// - .text("commitTime=" + commitTime.get())// - .close(); + // Execute the SPARQL UPDATE. + this.commitTime.set(update.execute2()); - doc.closeAll(body); + // Write out the response. + listener.commit(this.commitTime.get()); - w.flush(); - } finally { + + if (monitor) + listener.flush(); - w.close(); - // os.flush(); - // os.close(); + } + if (baos != null) { + + /* + * Since we buffered the response, we have to send it out now. + */ + + os.write(baos.toByteArray()); + + os.flush(); + } } } - + /** - * Write the header of the SPARQL UPDATE response. + * Writes the SPARQL UPDATE response document onto the caller's + * {@link OutputStream}. Depending on the use case, the stream will either + * write directly onto the servlet response or it will be buffered until the + * UPDATE request is finished. * - * @param doc - * The document. - * @param charset - * The character set. - * - * @return The body. + * @see https://sourceforge.net/apps/trac/bigdata/ticket/597 * - * @throws IOException + * TODO Add total elapsed time (in addition to elapsed per operation). */ - private static XMLBuilder.Node writeSparqlUpdateResponseHeader( - final HTMLBuilder doc, final Charset charset) throws IOException { + private static class SparqlUpdateResponseWriter implements + ISPARQLUpdateListener { - XMLBuilder.Node current = doc.root("html"); - { - current = current.node("head"); - current.node("meta") - .attr("http-equiv", "Content-Type") - .attr("content", - "text/html;charset=" + charset.name()) + private final HttpServletResponse resp; + private final OutputStream os; + private final Writer w; + private final HTMLBuilder doc; + private final Charset charset; + private final XMLBuilder.Node body; + private final boolean reportLoadProgress; + private final boolean flushEachEvent; + + /** + * + * + * @param os + * The {@link OutputStream}. + * @param charset + * The character set. + * @param reportLoadProgress + * When <code>true</code>, the incremental load progress will + * be included in the document. Note that this only makes + * sense when the document will be delivered incrementally to + * the client rather than "at-once" after the completion of + * the UPDATE operation. + * @param flushEachEvent + * When <code>true</code>, each the {@link Writer} will be + * flushed after each logged event in order to ensure timely + * delivery to the client. + * + * @throws IOException + */ + public SparqlUpdateResponseWriter(final HttpServletResponse resp, + final OutputStream os, final Charset charset, + final boolean reportLoadProgress, final boolean flushEachEvent) + throws IOException { + + if (resp == null) + throw new IllegalArgumentException(); + + if (os == null) + throw new IllegalArgumentException(); + + this.resp = resp; + + this.os = os; + + this.charset = charset; + + this.w = new OutputStreamWriter(os, charset); + + this.doc = new HTMLBuilder(charset.name(), w); + + this.reportLoadProgress = reportLoadProgress; + + this.flushEachEvent = flushEachEvent; + + this.body = writeSparqlUpdateResponseHeader(); + + } + + /** + * Write the header of the SPARQL UPDATE response. + * + * @return The body. + * + * @throws IOException + */ + private XMLBuilder.Node writeSparqlUpdateResponseHeader() + throws IOException { + + XMLBuilder.Node current = doc.root("html"); + { + current = current.node("head"); + current.node("meta").attr("http-equiv", "Content-Type") + .attr("content", "text/html;charset=" + charset.name()) + .close(); + current.node("title").textNoEncode("bigdata®").close(); + current = current.close();// close the head. + } + + // open the body + current = current.node("body"); + + return current; + + } + + @Override + public void updateEvent(final SPARQLUpdateEvent e) { + + try { + + final long ms = TimeUnit.NANOSECONDS.toMillis(e + .getElapsedNanos()); + + if (e instanceof SPARQLUpdateEvent.LoadProgress) { + + if (reportLoadProgress) { + + /* + * Incremental progress on LOAD. + * + * TODO Write out the LOAD operation on the first + * incremental update rather than at the end, but only + * if we are doing incremental updates. + */ + + final SPARQLUpdateEvent.LoadProgress tmp = (SPARQLUpdateEvent.LoadProgress) e; + + final long parsed = tmp.getParsedCount(); + + body.node("br") + .text("elapsed=" + ms + "ms, parsed=" + parsed) + .close(); + + } + + } else if (e.getCause() != null) { + + /* + * An exception occurred when processing some update + * operation. + */ + + final Throwable t = e.getCause(); + + final StringWriter w = new StringWriter(); + + final PrintWriter pw = new PrintWriter(w); + + t.printStackTrace(pw); + + pw.flush(); + pw.close(); + + body.node("p").text("ABORT")// + .node("pre").text(e.getUpdate().toString()).close()// + .node("pre").text(w.toString()).close()// + .text("elapsed=" + ms + "ms").close(); + + // horizontal line after each operation. + body.node("hr").close(); + + } else { + + /* + * End of some UPDATE operation. + */ + + body.node("p")// + .node("pre").text(e.getUpdate().toString()).close()// + .text("elapsed=" + ms + "ms")// + .close(); + + // horizontal line after each operation. + body.node("hr").close(); + + } + + if (flushEachEvent) { + + /* + * Flush the response for each event so the client + * (presumably a human operator) can see the progress log + * update "live". + */ + + w.flush(); + + os.flush(); + + /* + * Note: appears to be necessary for incremental writes. + */ + resp.flushBuffer(); + + } + + } catch (IOException e1) { + + throw new RuntimeException(e1); + + } + + } + + /** + * Write the commit time into the document. + * + * @param commitTime + * The commit time. + */ + public void commit(final long commitTime) throws IOException { + + body.node("p").text("COMMIT: commitTime=" + commitTime)// .close(); - current.node("title").textNoEncode("bigdata®") - .close(); - current = current.close();// close the head. + } - // open the body - current = current.node("body"); + public void flush() throws IOException { + + doc.closeAll(body); + + w.flush(); + + w.close(); + + } - return current; - } - /* - * Dead code. In order for this approach to work, the client - * (RemoteRepository) needs to parse the response document rather than - * checking the status code of the response. - * - * @see https://sourceforge.net/apps/trac/bigdata/ticket/597 - */ -// /** -// * Writes the SPARQL UPDATE response document. -// */ -// private static class SparqlUpdateResponseWriter implements -// ISPARQLUpdateListener { -// -// private final AtomicReference<HTMLBuilder> docRef; -// private final Charset charset; -// private final AtomicReference<XMLBuilder.Node> bodyRef; -// private final Writer w; -// -// public SparqlUpdateResponseWriter( -// final AtomicReference<HTMLBuilder> docRef, -// final Charset charset, -// final AtomicReference<XMLBuilder.Node> bodyRef, -// final Writer w) { -// -// this.docRef = docRef; -// this.charset = charset; -// this.bodyRef = bodyRef; -// this.w = w; -// -// } -// -// @Override -// public void updateEvent(final SPARQLUpdateEvent e) { -// try { -// if (bodyRef.get() == null) { -// if (docRef.get() == null) { -// docRef.set(); -// } -// bodyRef.set(writeSparqlUpdateResponseHeader(docRef.get(), -// charset)); -// } -// final long ms = TimeUnit.NANOSECONDS.toMillis(e -// .getElapsedNanos()); -// if (e instanceof SPARQLUpdateEvent.LoadProgress) { -// /* -// * Incremental progress on LOAD. -// */ -// final SPARQLUpdateEvent.LoadProgress tmp = (SPARQLUpdateEvent.LoadProgress) e; -// final long parsed = tmp.getParsedCount(); -// bodyRef.get().node("p") -// .text("elapsed=" + ms + "ms, parsed=" + parsed); -// } else { -// /* -// * End of some UPDATE operation. -// */ -// bodyRef.get().node("p").text("elapsed=" + ms + "ms") -// .node("pre").text(e.getUpdate().toString()).close() -// .close(); -// } -// } catch (IOException e1) { -// throw new RuntimeException(e1); -// } -// } -// -// } - /** * Return the task which will execute the SPARQL Query -or- SPARQL UPDATE. * <p> @@ -1255,7 +1464,7 @@ log.debug(astContainer.toString()); return new UpdateTask(namespace, timestamp, baseURI, astContainer, - req, os); + req, resp, os); } @@ -1300,7 +1509,7 @@ .getBooleanQueryResultFormat(BooleanQueryResultFormat.SPARQL); return new AskQueryTask(namespace, timestamp, baseURI, - astContainer, queryType, format, req, os); + astContainer, queryType, format, req, resp, os); } case DESCRIBE: @@ -1309,7 +1518,7 @@ final RDFFormat format = util.getRDFFormat(RDFFormat.RDFXML); return new GraphQueryTask(namespace, timestamp, baseURI, - astContainer, queryType, format, req, os); + astContainer, queryType, format, req, resp, os); } case SELECT: { @@ -1318,7 +1527,7 @@ .getTupleQueryResultFormat(TupleQueryResultFormat.SPARQL); return new TupleQueryTask(namespace, timestamp, baseURI, - astContainer, queryType, format, req, os); + astContainer, queryType, format, req, resp, os); } } // switch(queryType) Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/QueryServlet.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/QueryServlet.java 2012-09-13 18:27:31 UTC (rev 6570) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/QueryServlet.java 2012-09-13 20:43:12 UTC (rev 6571) @@ -99,7 +99,31 @@ * a SPARQL DESCRIBE query. */ static final transient String ATTR_QUERY = "query"; + + /** + * The name of the URL query parameter that contains the SPARQL UPDATE + * request. + */ + static final transient String ATTR_UPDATE = "update"; + + /** + * The name of the URL query parameter that indicates an ESTCARD request + * (fast range count). + */ + static final transient String ATTR_ESTCARD = "ESTCARD"; + /** + * The name of the URL query parameter that indicates a shards report + * request (scale-out only). + */ + static final transient String ATTR_SHARDS = "SHARDS"; + + /** + * The name of the URL query parameter that indicates a request for a random + * {@link UUID}. + */ + static final transient String ATTR_UUID = "uuid"; + // /** // * The name of the request attribute for the {@link AbstractQueryTask}. // */ @@ -113,7 +137,7 @@ protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { - if (req.getParameter("update") != null) { + if (req.getParameter(ATTR_UPDATE) != null) { // SPARQL 1.1 UPDATE. doUpdate(req, resp); @@ -134,19 +158,19 @@ protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws IOException { - if (req.getParameter("query") != null) { + if (req.getParameter(ATTR_QUERY) != null) { doQuery(req, resp); - } else if (req.getParameter("uuid") != null) { + } else if (req.getParameter(ATTR_UUID) != null) { doUUID(req, resp); - } else if (req.getParameter("ESTCARD") != null) { + } else if (req.getParameter(ATTR_ESTCARD) != null) { doEstCard(req, resp); - } else if (req.getParameter("SHARDS") != null) { + } else if (req.getParameter(ATTR_SHARDS) != null) { doShardReport(req, resp); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/UpdateServlet.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/UpdateServlet.java 2012-09-13 18:27:31 UTC (rev 6570) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/UpdateServlet.java 2012-09-13 20:43:12 UTC (rev 6571) @@ -80,7 +80,7 @@ return; } - final String queryStr = req.getParameter("query"); + final String queryStr = req.getParameter(QueryServlet.ATTR_QUERY); final String contentType = req.getContentType(); @@ -119,7 +119,7 @@ final String namespace = getNamespace(req); - final String queryStr = req.getParameter("query"); + final String queryStr = req.getParameter(QueryServlet.ATTR_QUERY); if (queryStr == null) throw new UnsupportedOperationException(); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/index.html =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/index.html 2012-09-13 18:27:31 UTC (rev 6570) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/index.html 2012-09-13 20:43:12 UTC (rev 6571) @@ -50,6 +50,11 @@ <INPUT type="checkbox" name="analytic" value="true" title="Enable the analytic query package." > Analytic +<!-- TODO XSTL styling of result set or graph result. + <INPUT type="checkbox" name="xhtml" value="true" + title="Request XHTML response (results formatted as table)." + > XHTML +--> </P> </FORM> <h2><a href="http://www.w3.org/TR/sparql11-update/">SPARQL Update</a></h2> @@ -71,6 +76,9 @@ <INPUT type="checkbox" name="analytic" value="true" title="Enable the analytic query package." > Analytic + <INPUT type="checkbox" name="monitor" value="true" + title="Monitor the execution of the UPDATE request." + > Monitor </P> </FORM> <p> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-14 12:18:10
|
Revision: 6572 http://bigdata.svn.sourceforge.net/bigdata/?rev=6572&view=rev Author: thompsonbry Date: 2012-09-14 12:17:59 +0000 (Fri, 14 Sep 2012) Log Message: ----------- Bug fix for [1]. Also includes total elapsed milliseconds for the SPARQL UPDATE XHTML response. [1] https://sourceforge.net/apps/trac/bigdata/ticket/599 (BlobIV for blank node : NotMaterializedException) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/internal/impl/AbstractNonInlineIV.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/internal/impl/BlobIV.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/lexicon/LexiconRelation.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/store/BigdataBindingSetResolverator.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/internal/impl/AbstractNonInlineIV.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/internal/impl/AbstractNonInlineIV.java 2012-09-13 20:43:12 UTC (rev 6571) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/internal/impl/AbstractNonInlineIV.java 2012-09-14 12:17:59 UTC (rev 6572) @@ -177,10 +177,10 @@ * Implements {@link URI#getNamespace()}. */ public String getNamespace() { - - if (!isURI()) - throw new ClassCastException(); - + + if (!isURI()) + throw new ClassCastException(); + return ((URI) getValue()).getNamespace(); } @@ -189,12 +189,12 @@ * Implements {@link BNode#getID()}. */ public String getID() { + + if (!isBNode()) + throw new ClassCastException(); + + return ((BNode) getValue()).getID(); - if (!isBNode()) - throw new ClassCastException(); - - return ((BNode) getValue()).getID(); - } /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/internal/impl/BlobIV.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/internal/impl/BlobIV.java 2012-09-13 20:43:12 UTC (rev 6571) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/internal/impl/BlobIV.java 2012-09-14 12:17:59 UTC (rev 6572) @@ -383,23 +383,27 @@ // // } -// /** -// * {@inheritDoc} -// * <p> -// * Creates a unique blank node ID based on the {@link BlobIV}'s internal -// * data. -// */ -// public String getID() { -// -// final long id = ((long) flags()) << 56/* hash:int + counter:short */ -// | ((long) hash) << 16/* short */| counter; -// -// final String idStr = Long.toString(id); -// -// return idStr; -// -// } + /** + * {@inheritDoc} + * <p> + * Creates a unique blank node ID based on the {@link BlobIV}'s internal + * data. + */ + @Override + public String getID() { + if (!isBNode()) + throw new ClassCastException(); + + final long id = ((long) flags()) << 56/* hash:int + counter:short */ + | ((long) hash) << 16/* short */| counter; + + final String idStr = Long.toString(id); + + return idStr; + + } + @Override final public byte getExtensionByte() { Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/lexicon/LexiconRelation.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/lexicon/LexiconRelation.java 2012-09-13 20:43:12 UTC (rev 6571) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/lexicon/LexiconRelation.java 2012-09-14 12:17:59 UTC (rev 6572) @@ -2468,24 +2468,24 @@ if (iv == null) throw new AssertionError(); - - if (iv.hasValue()) { - - if (isDebugEnabled) - log.debug("already materialized: " + iv.getValue()); - - // already materialized - ret.put(iv, iv.getValue()); - + + if (iv.hasValue()) { + + if (isDebugEnabled) + log.debug("already materialized: " + iv.getValue()); + + // already materialized + ret.put(iv, iv.getValue()); + } else if (iv.isInline()) { - + // translate it into a value directly ret.put(iv, iv.asValue(this)); } else { final BigdataValue value = _getTermId(iv); - + if (value != null) { assert value.getValueFactory() == valueFactory; @@ -2515,16 +2515,16 @@ } } - + } if (numNotFound == 0) { // Done. return ret; - + } - + /* * Setup and run task(s) to resolve IV(s). */ @@ -2684,8 +2684,8 @@ * identifier. */ - final String id = 't' + ((BNode) iv).getID(); - + final String id = 't' + ((BNode) iv).getID(); + final BigdataBNode bnode = valueFactory.createBNode(id); // set the term identifier on the object. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/store/BigdataBindingSetResolverator.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/store/BigdataBindingSetResolverator.java 2012-09-13 20:43:12 UTC (rev 6571) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/store/BigdataBindingSetResolverator.java 2012-09-14 12:17:59 UTC (rev 6572) @@ -201,8 +201,8 @@ if (required == null) { @SuppressWarnings("rawtypes") - final Iterator<Map.Entry<IVariable, IConstant>> itr = - bindingSet.iterator(); + final Iterator<Map.Entry<IVariable, IConstant>> itr = bindingSet + .iterator(); while (itr.hasNext()) { Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-13 20:43:12 UTC (rev 6571) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-14 12:17:59 UTC (rev 6572) @@ -1179,6 +1179,7 @@ private static class SparqlUpdateResponseWriter implements ISPARQLUpdateListener { + private final long begin; private final HttpServletResponse resp; private final OutputStream os; private final Writer w; @@ -1233,6 +1234,8 @@ this.flushEachEvent = flushEachEvent; + this.begin = System.nanoTime(); + this.body = writeSparqlUpdateResponseHeader(); } @@ -1269,7 +1272,12 @@ try { - final long ms = TimeUnit.NANOSECONDS.toMillis(e + // Total elapsed milliseconds from start of update request. + final long totalElapsedMillis = TimeUnit.NANOSECONDS + .toMillis(System.nanoTime() - begin); + + // Elapsed milliseconds for this update operation. + final long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(e .getElapsedNanos()); if (e instanceof SPARQLUpdateEvent.LoadProgress) { @@ -1289,8 +1297,9 @@ final long parsed = tmp.getParsedCount(); body.node("br") - .text("elapsed=" + ms + "ms, parsed=" + parsed) - .close(); + .text("totalElapsed=" + totalElapsedMillis + + "ms, elapsed=" + elapsedMillis + + "ms, parsed=" + parsed).close(); } @@ -1315,7 +1324,9 @@ body.node("p").text("ABORT")// .node("pre").text(e.getUpdate().toString()).close()// .node("pre").text(w.toString()).close()// - .text("elapsed=" + ms + "ms").close(); + .text("totalElapsed=" + totalElapsedMillis + + "ms, elapsed=" + elapsedMillis + "ms") + .close(); // horizontal line after each operation. body.node("hr").close(); @@ -1325,10 +1336,11 @@ /* * End of some UPDATE operation. */ - + body.node("p")// .node("pre").text(e.getUpdate().toString()).close()// - .text("elapsed=" + ms + "ms")// + .text("totalElapsed=" + totalElapsedMillis + + "ms, elapsed=" + elapsedMillis + "ms")// .close(); // horizontal line after each operation. @@ -1371,7 +1383,13 @@ */ public void commit(final long commitTime) throws IOException { - body.node("p").text("COMMIT: commitTime=" + commitTime)// + // Total elapsed milliseconds from start of update request. + final long totalElapsedMillis = TimeUnit.NANOSECONDS + .toMillis(System.nanoTime() - begin); + + body.node("p") + .text("COMMIT: totalElapsed=" + totalElapsedMillis + + "ms, commitTime=" + commitTime)// .close(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-14 13:02:09
|
Revision: 6573 http://bigdata.svn.sourceforge.net/bigdata/?rev=6573&view=rev Author: thompsonbry Date: 2012-09-14 13:01:59 +0000 (Fri, 14 Sep 2012) Log Message: ----------- Bug fix for [1]. This also includes triples per second reporting for SPARQL UPDATE. This also includes a bug fix to the NQuads parser, which was failing to report the startRDF() and endRDF() events. [1] https://sourceforge.net/apps/trac/bigdata/ticket/600 (BlobIV collision counter hits false limit) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/lexicon/BlobsIndexHelper.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/rio/LoadStats.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/rio/nquads/NQuadsParser.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpUpdate.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/SPARQLUpdateEvent.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/lexicon/BlobsIndexHelper.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/lexicon/BlobsIndexHelper.java 2012-09-14 12:17:59 UTC (rev 6572) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/lexicon/BlobsIndexHelper.java 2012-09-14 13:01:59 UTC (rev 6573) @@ -69,7 +69,7 @@ public static final transient int SIZEOF_COUNTER = Bytes.SIZEOF_SHORT; /** The maximum value of the hash collision counter (unsigned short). */ - public static final transient int MAX_COUNTER = (2 ^ 16) - 1; + public static final transient int MAX_COUNTER = ((1 << 16) - 1); /** The offset at which the counter occurs in the key. */ public static final transient int OFFSET_COUNTER = 1/* flags */+ 1/* extension */+ SIZEOF_HASH /* hashCode */; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/rio/LoadStats.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/rio/LoadStats.java 2012-09-14 12:17:59 UTC (rev 6572) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/rio/LoadStats.java 2012-09-14 13:01:59 UTC (rev 6573) @@ -44,7 +44,7 @@ /** * The internal with which this class will log on {@link System#out} in - * milliseconds (it is se to every 10 minutes). This helps to track progress + * milliseconds (it is set to every 10 minutes). This helps to track progress * on very large data loads. */ protected static transient long REPORT_INTERVAL = 10 * 60 * 1000; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/rio/nquads/NQuadsParser.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/rio/nquads/NQuadsParser.java 2012-09-14 12:17:59 UTC (rev 6572) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/rio/nquads/NQuadsParser.java 2012-09-14 13:01:59 UTC (rev 6573) @@ -312,6 +312,8 @@ final NxParser parser = new NxParser(r, strict, parseDTs); + handler.startRDF(); + while (parser.hasNext()) { final Node[] nodes = parser.next(); @@ -368,6 +370,8 @@ } + handler.endRDF(); + } } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpUpdate.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpUpdate.java 2012-09-14 12:17:59 UTC (rev 6572) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpUpdate.java 2012-09-14 13:01:59 UTC (rev 6573) @@ -41,6 +41,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.zip.GZIPInputStream; @@ -93,8 +94,8 @@ import com.bigdata.rdf.model.BigdataURI; import com.bigdata.rdf.rio.IRDFParserOptions; import com.bigdata.rdf.sail.BigdataSail; +import com.bigdata.rdf.sail.BigdataSail.BigdataSailConnection; import com.bigdata.rdf.sail.SPARQLUpdateEvent; -import com.bigdata.rdf.sail.BigdataSail.BigdataSailConnection; import com.bigdata.rdf.sail.Sesame2BigdataIterator; import com.bigdata.rdf.sparql.ast.ASTContainer; import com.bigdata.rdf.sparql.ast.AbstractGraphDataUpdate; @@ -1496,7 +1497,7 @@ */ rdfParser.parse(is, baseURL); - + } finally { if (hconn instanceof HttpURLConnection) { @@ -1570,12 +1571,29 @@ // notify listener(s) conn.fireEvent(new SPARQLUpdateEvent.LoadProgress(op, elapsed, - nparsed)); + nparsed, false/* done */)); } } + /** + * Overridden to send out an incremental progress report for the end of + * the LOAD operation. + */ + @Override + public void endRDF() throws RDFHandlerException { + + final long nparsed = nmodified.get(); + + final long elapsed = System.nanoTime() - beginNanos; + + // notify listener(s) + conn.fireEvent(new SPARQLUpdateEvent.LoadProgress(op, elapsed, + nparsed, true/* done */)); + + } + } /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/SPARQLUpdateEvent.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/SPARQLUpdateEvent.java 2012-09-14 12:17:59 UTC (rev 6572) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/SPARQLUpdateEvent.java 2012-09-14 13:01:59 UTC (rev 6573) @@ -23,6 +23,8 @@ */ package com.bigdata.rdf.sail; +import java.util.concurrent.TimeUnit; + import com.bigdata.rdf.sparql.ast.Update; /** @@ -87,13 +89,16 @@ public static class LoadProgress extends SPARQLUpdateEvent { private final long nparsed; + private final boolean done; public LoadProgress(final Update op, final long elapsed, - final long nparsed) { + final long nparsed, final boolean done) { super(op, elapsed, null/* cause */); this.nparsed = nparsed; + + this.done = done; } @@ -120,6 +125,38 @@ } + /** + * Return <code>true</code> iff the LOAD operation has finished parsing + * the document. + * <p> + * Note: This does not mean that the statements have been written + * through to the disk, just that the parsed is done running. + */ + public boolean isDone() { + + return done; + + } + + /** + * Report the parser rate in triples per second. + */ + public long triplesPerSecond() { + + long elapsedMillis = TimeUnit.NANOSECONDS + .toMillis(getElapsedNanos()); + + if (elapsedMillis == 0) { + + // Note: Avoid divide by zero error. + elapsedMillis = 1; + + } + + return ((long) (((double) nparsed) / ((double) elapsedMillis) * 1000d)); + + } + } - + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-14 12:17:59 UTC (rev 6572) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-14 13:01:59 UTC (rev 6573) @@ -95,6 +95,7 @@ import com.bigdata.rdf.sparql.ast.ASTContainer; import com.bigdata.rdf.sparql.ast.QueryHints; import com.bigdata.rdf.sparql.ast.QueryType; +import com.bigdata.rdf.sparql.ast.Update; import com.bigdata.rdf.store.AbstractTripleStore; import com.bigdata.relation.AbstractResource; import com.bigdata.relation.RelationSchema; @@ -1172,9 +1173,8 @@ * write directly onto the servlet response or it will be buffered until the * UPDATE request is finished. * - * @see https://sourceforge.net/apps/trac/bigdata/ticket/597 - * - * TODO Add total elapsed time (in addition to elapsed per operation). + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/597"> + * SPARQL UPDATE Listener </a> */ private static class SparqlUpdateResponseWriter implements ISPARQLUpdateListener { @@ -1188,6 +1188,10 @@ private final XMLBuilder.Node body; private final boolean reportLoadProgress; private final boolean flushEachEvent; + /** + * Used to correlate incremental LOAD progress messages. + */ + private volatile Update lastOp = null; /** * @@ -1286,20 +1290,35 @@ /* * Incremental progress on LOAD. - * - * TODO Write out the LOAD operation on the first - * incremental update rather than at the end, but only - * if we are doing incremental updates. */ final SPARQLUpdateEvent.LoadProgress tmp = (SPARQLUpdateEvent.LoadProgress) e; final long parsed = tmp.getParsedCount(); + final Update thisOp = e.getUpdate(); + + if (thisOp != lastOp) { + + /* + * This is the first incremental load progress + * report for this LOAD operation. + */ + lastOp = thisOp; + + // Write out the LOAD operation. + body.node("p")// + .node("pre").text(e.getUpdate().toString()).close()// + .close(); + + } + body.node("br") .text("totalElapsed=" + totalElapsedMillis + "ms, elapsed=" + elapsedMillis - + "ms, parsed=" + parsed).close(); + + "ms, parsed=" + parsed + ", tps=" + + tmp.triplesPerSecond() + ", done=" + + tmp.isDone()).close(); } @@ -1337,12 +1356,38 @@ * End of some UPDATE operation. */ - body.node("p")// - .node("pre").text(e.getUpdate().toString()).close()// - .text("totalElapsed=" + totalElapsedMillis - + "ms, elapsed=" + elapsedMillis + "ms")// - .close(); - + if (lastOp == e.getUpdate()) { + /* + * The end of a LOAD operation for which we reported the + * incremental progress. In this case, the LOAD + * operation was already written onto the response + * document, including the final report from the end of + * the parser run. So, all we have to do here is clear + * the reference. + */ + lastOp = null; +// body.node("p") +// // +//// .node("pre") +//// .text(e.getUpdate().toString()) +//// .close() +// // +// .text("totalElapsed=" + totalElapsedMillis +// + "ms, elapsed=" + elapsedMillis + "ms")// +// .close(); + } else { + + body.node("p") + // + .node("pre") + .text(e.getUpdate().toString()) + .close() + // + .text("totalElapsed=" + totalElapsedMillis + + "ms, elapsed=" + elapsedMillis + "ms")// + .close(); + } + // horizontal line after each operation. body.node("hr").close(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-14 19:59:50
|
Revision: 6574 http://bigdata.svn.sourceforge.net/bigdata/?rev=6574&view=rev Author: thompsonbry Date: 2012-09-14 19:59:44 +0000 (Fri, 14 Sep 2012) Log Message: ----------- An UncaughtExceptionHandler has been added in com.bigdata.Banner. It is possible to *disable* this handler using the System property: {{{ com.bigdata.Banner.nocatch }}} by setting that property to 'true'. The AbstractServer class was modified to NOT install this handled since it will be installed by the Banner class. @see https://sourceforge.net/apps/trac/bigdata/ticket/601 (UncaughtExceptionHandler) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/Banner.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/service/jini/AbstractServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/Banner.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/Banner.java 2012-09-14 13:01:59 UTC (rev 6573) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/Banner.java 2012-09-14 19:59:44 UTC (rev 6574) @@ -28,6 +28,7 @@ package com.bigdata; +import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -78,6 +79,12 @@ String QUIET = "com.bigdata.Banner.quiet"; /** + * Suppress the installation of a default + * {@link UncaughtExceptionHandler}. + */ + String NOCATCH = "com.bigdata.Banner.nocatch"; + + /** * This may be used to disable JMX MBeans which self-report on the log4j * properties. */ @@ -88,9 +95,23 @@ static public void banner() { if(didBanner.compareAndSet(false/*expect*/, true/*update*/)) { - + final boolean quiet = Boolean.getBoolean(Options.QUIET); + final boolean nocatch = Boolean.getBoolean(Options.NOCATCH); + + if (!nocatch) { + /* + * Set a logger for any uncaught exceptions. + */ + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { + public void uncaughtException(final Thread t, + final Throwable e) { + log.error("Uncaught exception in thread", e); + } + }); + } + if (!quiet) { final StringBuilder sb = new StringBuilder(banner); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/service/jini/AbstractServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/service/jini/AbstractServer.java 2012-09-14 13:01:59 UTC (rev 6573) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/service/jini/AbstractServer.java 2012-09-14 19:59:44 UTC (rev 6574) @@ -477,12 +477,20 @@ setSecurityManager(); - Thread.setDefaultUncaughtExceptionHandler( - new Thread.UncaughtExceptionHandler() { - public void uncaughtException(Thread t, Throwable e) { - log.warn("Uncaught exception in thread", e); - } - }); + /* + * Display the banner. + * + * Note: This also installs the UncaughtExceptionHandler. + * + * @see https://sourceforge.net/apps/trac/bigdata/ticket/601 + */ + Banner.banner(); +// Thread.setDefaultUncaughtExceptionHandler( +// new Thread.UncaughtExceptionHandler() { +// public void uncaughtException(Thread t, Throwable e) { +// log.warn("Uncaught exception in thread", e); +// } +// }); /* * Read jini configuration & service properties This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-22 15:13:17
|
Revision: 6589 http://bigdata.svn.sourceforge.net/bigdata/?rev=6589&view=rev Author: thompsonbry Date: 2012-09-22 15:13:06 +0000 (Sat, 22 Sep 2012) Log Message: ----------- Added optional query hints that permit the override of the maximum #of iterations and the maximum #of statements for iterative DESCRIBE queries. These hints have defaults that are declared in QueryHints, may be overridden within a query using the named query hint, and may be overridden when the KB is configured using BigdataSail.Options. The two query hints may be set to ZERO to effectively disable the corresponding limit. When both limits are specified, the limits must BOTH be met before the query will be cutoff. The current defaults are a maximum of 5 iterations AND a maximum of 5000 statements. This AND logic means that you may have more iterations as long as there are not that many statements and more statements as long as there are not that many iterations. I have not added unit tests for these new query hints. @see http://sourceforge.net/apps/trac/bigdata/ticket/578 (Concise Bounded Descaription (CBD)) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/ProjectionNode.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/QueryHints.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpContext.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/ASTEvalHelper.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/CBD.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeModeHint.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/QueryHintRegistry.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/BigdataSail.java Added Paths: ----------- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeIterationLimitHint.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeStatementLimitHint.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/ProjectionNode.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/ProjectionNode.java 2012-09-19 10:38:19 UTC (rev 6588) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/ProjectionNode.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -74,9 +74,27 @@ * Optional annotation specifies the {@link DescribeModeEnum} that will * be used to evaluate a DESCRIBE query. The default is controlled by * {@value QueryHints#DEFAULT_DESCRIBE_MODE}. + * + * @see QueryHints#DESCRIBE_MODE */ String DESCRIBE_MODE = "describeMode"; - + + /** + * Optional annotation specifies the limit on the #of iterations for an + * iterative DESCRIBE algorithm. + * + * @see QueryHints#DESCRIBE_ITERATION_LIMIT + */ + String DESCRIBE_ITERATION_LIMIT = "describeIterationLimit"; + + /** + * Optional annotation specifies the limit on the #of statements for an + * iterative DESCRIBE algorithm. + * + * @see QueryHints#DESCRIBE_STATEMENT_LIMIT + */ + String DESCRIBE_STATEMENT_LIMIT = "describeStatementLimit"; + } public ProjectionNode() { @@ -160,6 +178,8 @@ * @param describeMode * The {@link DescribeModeEnum} or <code>null</code> to use the * default. + * + * @see Annotations#DESCRIBE_MODE */ public void setDescribeMode(final DescribeModeEnum describeMode) { @@ -168,6 +188,50 @@ } /** + * Return the optional limit on the #of iterations for a DESCRIBE query. + * + * @return The limit -or- <code>null</code>. + * + * @see Annotations#DESCRIBE_ITERATION_LIMIT + */ + public Integer getDescribeIterationLimit() { + + return (Integer) getProperty(Annotations.DESCRIBE_ITERATION_LIMIT); + + } + + /** + * Return the optional limit on the #of statements for a DESCRIBE query. + * + * @return The limit -or- <code>null</code>. + * + * @see Annotations#DESCRIBE_STATEMENT_LIMIT + */ + public Integer getDescribeStatementLimit() { + + return (Integer) getProperty(Annotations.DESCRIBE_STATEMENT_LIMIT); + + } + + /** + * Set the optional limit on the #of iterations for a DESCRIBE query. + */ + public void setDescribeIterationLimit(final int newValue) { + + setProperty(Annotations.DESCRIBE_ITERATION_LIMIT, newValue); + + } + + /** + * Set the optional limit on the #of statements for a DESCRIBE query. + */ + public void setDescribeStatementLimit(final int newValue) { + + setProperty(Annotations.DESCRIBE_STATEMENT_LIMIT, newValue); + + } + + /** * Adds a variable to be projected. The variable is modeled as an assignment * of itself to itself, so everything in the projection node winds up * looking like an assignment. @@ -314,12 +378,28 @@ final DescribeModeEnum describeMode = getDescribeMode(); + final Integer describeIterationLimit = getDescribeIterationLimit(); + + final Integer describeStatementLimit = getDescribeStatementLimit(); + if (describeMode != null) { sb.append("[describeMode=" + describeMode + "]"); - + } - + + if (describeIterationLimit != null) { + + sb.append("[describeIterationLimit=" + describeIterationLimit + "]"); + + } + + if (describeStatementLimit != null) { + + sb.append("[describeStatement=" + describeStatementLimit + "]"); + + } + if (isWildcard()) { sb.append("* "); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/QueryHints.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/QueryHints.java 2012-09-19 10:38:19 UTC (rev 6588) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/QueryHints.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -437,6 +437,34 @@ DescribeModeEnum DEFAULT_DESCRIBE_MODE = DescribeModeEnum.SymmetricOneStep; /** + * For iterative {@link DescribeModeEnum}s, this property places a limit on + * the number of iterative expansions that will be performed before the + * DESCRIBE query is cut off, providing that the limit on the maximum #of + * statements in the description is also satisfied (the cut off requires + * that both limits are reached). May be ZERO (0) for NO limit. + * + * @see #DESCRIBE_MODE + * @see #DESCRIBE_STATEMENT_LIMIT + */ + String DESCRIBE_ITERATION_LIMIT = "describeIterationLimit"; + + int DEFAULT_DESCRIBE_ITERATION_LIMIT = 5; + + /** + * For iterative {@link DescribeModeEnum}s, this property places a limit on + * the number of statements that will be accumulated before the DESCRIBE + * query is cut off, providing that the limit on the maximum #of iterations + * in the description is also satisfied (the cut off requires that both + * limits are reached). May be ZERO (0) for NO limit. + * + * @see #DESCRIBE_MODE + * @see #DESCRIBE_ITERATION_LIMIT + */ + String DESCRIBE_STATEMENT_LIMIT = "describeStatementLimit"; + + int DEFAULT_DESCRIBE_STATEMENT_LIMIT = 5000; + + /** * Option controls whether or not the proposed SPARQL extension for * reification done right is enabled. * Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpContext.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpContext.java 2012-09-19 10:38:19 UTC (rev 6588) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/AST2BOpContext.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -540,6 +540,16 @@ } + /** + * Return the effective {@link DescribeModeEnum}. + * + * @param projection + * The query projection. + * + * @return The effective {@link DescribeModeEnum} + * + * @see QueryHints#DESCRIBE_MODE + */ public DescribeModeEnum getDescribeMode(final ProjectionNode projection) { // The effective DescribeMode. @@ -575,6 +585,96 @@ } + /** + * Return the effective iteration limit for a DESCRIBE query. + * + * @param projection + * The query projection. + * + * @return The effective iteration limit. + * + * @see QueryHints#DESCRIBE_ITERATION_LIMIT + */ + public int getDescribeIterationLimit(final ProjectionNode projection) { + + // The effective limit. + Integer limit = projection.getDescribeIterationLimit(); + + if (limit != null) { + /* + * Explicitly specified on the project. E.g., set by a query hint or + * through code. + */ + return limit; + } + + /* + * Consult the KB for a configured default behavior. + */ + final String limitStr = db.getProperties().getProperty( + BigdataSail.Options.DESCRIBE_ITERATION_LIMIT); + + if (limitStr != null) { + + // The KB has specified a default DESCRIBE algorithm. + limit = Integer.valueOf(limitStr); + + } else { + + // Use the default specified on QueryHints. + limit = QueryHints.DEFAULT_DESCRIBE_ITERATION_LIMIT; + + } + + return limit; + + } + + /** + * Return the effective statement limit for a DESCRIBE query. + * + * @param projection + * The query projection. + * + * @return The effective statement limit. + * + * @see QueryHints#DESCRIBE_STATEMENT_LIMIT + */ + public int getDescribeStatementLimit(final ProjectionNode projection) { + + // The effective limit. + Integer limit = projection.getDescribeStatementLimit(); + + if (limit != null) { + /* + * Explicitly specified on the project. E.g., set by a query hint or + * through code. + */ + return limit; + } + + /* + * Consult the KB for a configured default behavior. + */ + final String limitStr = db.getProperties().getProperty( + BigdataSail.Options.DESCRIBE_STATEMENT_LIMIT); + + if (limitStr != null) { + + // The KB has specified a default DESCRIBE algorithm. + limit = Integer.valueOf(limitStr); + + } else { + + // Use the default specified on QueryHints. + limit = QueryHints.DEFAULT_DESCRIBE_STATEMENT_LIMIT; + + } + + return limit; + + } + @Override public ISolutionSetStats getSolutionSetStats(final String localName) { Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/ASTEvalHelper.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/ASTEvalHelper.java 2012-09-19 10:38:19 UTC (rev 6588) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/ASTEvalHelper.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -478,6 +478,12 @@ final DescribeModeEnum describeMode = context .getDescribeMode(optimizedQuery.getProjection()); + final int describeIterationLimit = context + .getDescribeIterationLimit(optimizedQuery.getProjection()); + + final int describeStatementlimit = context + .getDescribeStatementLimit(optimizedQuery.getProjection()); + final CloseableIteration<BindingSet, QueryEvaluationException> solutions2; final ConcurrentHashSet<BigdataValue> describedResources; if (describeCache != null) { @@ -553,7 +559,8 @@ * client, so there is no opportunity to cancel a running CBD * DESCRIBE. */ - src2 = new CBD(store, describeMode, bnodes).computeClosure(src); + src2 = new CBD(store, describeMode, describeIterationLimit, + describeStatementlimit, bnodes).computeClosure(src); break; } default: Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/CBD.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/CBD.java 2012-09-19 10:38:19 UTC (rev 6588) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/eval/CBD.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -64,22 +64,13 @@ * @see ASTConstructIterator * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + * + * FIXME Watch a timeout on the top-level query (if present) */ public class CBD { private static final Logger log = Logger.getLogger(CBD.class); - - /** - * Maximum #of expansions for recursive {@link DescribeModeEnum}s. - * - * TODO Configure. - * - * TODO Limit #of resources that are being described. - * - * FIXME Watch a timeout on the top-level query (if present) - */ - private static final int MAX_ROUNDS = 5; - + /** The {@link AbstractTripleStore}. */ private final AbstractTripleStore store; @@ -88,8 +79,20 @@ * DESCRIBE query. */ private final DescribeModeEnum describeMode; + + /** + * The limit on the #of iterations (iff the statement limit is also + * reached) -or- ZERO (0) for no limit. + */ + private final int describeIterationLimit; /** + * The limit on the #of statements (iff the iteration limit is also + * reached) -or- ZERO (0) for no limit. + */ + private final int describeStatementLimit; + + /** * The {@link DescribeModeEnum} specifying how to evaluate each expansion * round of the DESCRIBE query. */ @@ -108,6 +111,12 @@ * @param describeMode * The {@link DescribeModeEnum} specifying how to evaluate the * DESCRIBE query. + * @param describeIterationLimit + * The limit on the #of iterations (iff the statement limit is + * also reached) -or- ZERO (0) for no limit. + * @param describeStatementLimit + * The limit on the #of statements (iff the iteration limit is + * also reached) -or- ZERO (0) for no limit.. * @param bnodes * A mapping that is used to preserve a consistent assignment * from blank node IDs to {@link BigdataBNode}s scoped to the @@ -115,6 +124,8 @@ */ public CBD(final AbstractTripleStore store, final DescribeModeEnum describeMode, + final int describeIterationLimit, + final int describeStatementLimit, final Map<String, BigdataBNode> bnodes) { if (store == null) @@ -123,6 +134,12 @@ if (describeMode == null) throw new IllegalArgumentException(); + if (describeIterationLimit < 0) + throw new IllegalArgumentException(); + + if (describeStatementLimit < 0) + throw new IllegalArgumentException(); + if (bnodes == null) throw new IllegalArgumentException(); @@ -130,6 +147,10 @@ this.describeMode = describeMode; + this.describeIterationLimit = describeIterationLimit; + + this.describeStatementLimit = describeStatementLimit; + this.bnodes = bnodes; switch(describeMode) { @@ -188,10 +209,13 @@ // CBD expansion begins at round ONE (1). nrounds++; - if (nrounds > MAX_ROUNDS) { + // #of statements on entry to this round. + final int nstmts = stmts.size(); + + if (cutoffQuery(nrounds - 1, nstmts)) { src.close(); - throw new QueryEvaluationException( - "CSB would exceed "+MAX_ROUNDS+" rounds."); + throw new QueryEvaluationException("CBD cutoff: nrounds=" + + nrounds + ", nstatements=" + nstmts + "."); } /* @@ -261,6 +285,35 @@ } /** + * Return <code>true</code> iff the DESCRIBE query should be cutoff because + * the limits have been exceeded. + * + * @param nrounds + * The #of evaluation rounds that have already been computed and + * ZERO (0) if this is the ffirst round. + * @param nstmts + * The #of statements at the start of this round. + * + * @return <code>true</code> iff evaluation should be cutoff. + */ + private boolean cutoffQuery(int nrounds, int nstmts) { + + // ZERO implies MAX_INT + final int describeIterationLimit = this.describeIterationLimit == 0 ? Integer.MAX_VALUE + : this.describeIterationLimit; + + final int describeStatementLimit = this.describeStatementLimit == 0 ? Integer.MAX_VALUE + : this.describeStatementLimit; + + final boolean cutoffRounds = nrounds >= describeIterationLimit; + + final boolean cutoffStatements =nrounds >= describeStatementLimit; + + return cutoffRounds && cutoffStatements; + + } + + /** * Log the statements and bnode {@link IV}s @ DEBUG. * * @param stmts Added: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeIterationLimitHint.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeIterationLimitHint.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeIterationLimitHint.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -0,0 +1,75 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2011. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + * Created on Nov 27, 2011 + */ + +package com.bigdata.rdf.sparql.ast.hints; + +import com.bigdata.rdf.sparql.ast.ASTBase; +import com.bigdata.rdf.sparql.ast.ProjectionNode; +import com.bigdata.rdf.sparql.ast.QueryHints; +import com.bigdata.rdf.sparql.ast.eval.AST2BOpContext; + +/** + * Query hint used to indicate optional iteration limit for a DESCRIBE query. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/578"> Concise + * Bounded Description </a> + * + * @see QueryHints#DESCRIBE_ITERATION_LIMIT + */ +final class DescribeIterationLimitHint extends AbstractIntQueryHint { + + protected DescribeIterationLimitHint() { + super(QueryHints.DESCRIBE_ITERATION_LIMIT, + QueryHints.DEFAULT_DESCRIBE_ITERATION_LIMIT); + } + + @Override + public void handle(final AST2BOpContext context, + final QueryHintScope scope, final ASTBase op, + final Integer value) { + + if (op instanceof ProjectionNode) { + + //_setQueryHint(context, scope, op, getName(), value); + ((ProjectionNode) op).setDescribeIterationLimit(value); + + return; + + } + + // throw new QueryHintException(scope, op, getName(), value); + + } + + @Override + public Integer validate(final String value) { + + return Integer.valueOf(value); + + } + +} \ No newline at end of file Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeModeHint.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeModeHint.java 2012-09-19 10:38:19 UTC (rev 6588) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeModeHint.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -39,6 +39,8 @@ * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/578"> Concise * Bounded Description </a> + * + * @see QueryHints#DESCRIBE_MODE */ final class DescribeModeHint extends AbstractQueryHint<DescribeModeEnum> { Added: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeStatementLimitHint.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeStatementLimitHint.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/DescribeStatementLimitHint.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -0,0 +1,75 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2011. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + * Created on Nov 27, 2011 + */ + +package com.bigdata.rdf.sparql.ast.hints; + +import com.bigdata.rdf.sparql.ast.ASTBase; +import com.bigdata.rdf.sparql.ast.ProjectionNode; +import com.bigdata.rdf.sparql.ast.QueryHints; +import com.bigdata.rdf.sparql.ast.eval.AST2BOpContext; + +/** + * Query hint used to indicate optional statement limit for a DESCRIBE query. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/578"> Concise + * Bounded Description </a> + * + * @see QueryHints#DESCRIBE_STATEMENT_LIMIT + */ +final class DescribeStatementLimitHint extends AbstractIntQueryHint { + + protected DescribeStatementLimitHint() { + super(QueryHints.DESCRIBE_STATEMENT_LIMIT, + QueryHints.DEFAULT_DESCRIBE_STATEMENT_LIMIT); + } + + @Override + public void handle(final AST2BOpContext context, + final QueryHintScope scope, final ASTBase op, + final Integer value) { + + if (op instanceof ProjectionNode) { + + //_setQueryHint(context, scope, op, getName(), value); + ((ProjectionNode) op).setDescribeStatementLimit(value); + + return; + + } + + // throw new QueryHintException(scope, op, getName(), value); + + } + + @Override + public Integer validate(final String value) { + + return Integer.valueOf(value); + + } + +} \ No newline at end of file Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/QueryHintRegistry.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/QueryHintRegistry.java 2012-09-19 10:38:19 UTC (rev 6588) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/hints/QueryHintRegistry.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -114,6 +114,8 @@ add(new AccessPathSampleLimitHint()); add(new AccessPathScanAndFilterHint()); add(new DescribeModeHint()); + add(new DescribeIterationLimitHint()); + add(new DescribeStatementLimitHint()); /* * BufferAnnotations Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/BigdataSail.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/BigdataSail.java 2012-09-19 10:38:19 UTC (rev 6588) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/BigdataSail.java 2012-09-22 15:13:06 UTC (rev 6589) @@ -357,6 +357,26 @@ public static final String DESCRIBE_MODE = BigdataSail.class .getPackage().getName() + ".describeMode"; + /** + * Option specifies the iteration limit for the algorithm used to + * compute DESCRIBE responses (optional). + * + * @see QueryHints#DESCRIBE_ITERATION_LIMIT + * @see QueryHints#DEFAULT_DESCRIBE_ITERATION_LIMIT + */ + public static final String DESCRIBE_ITERATION_LIMIT = BigdataSail.class + .getPackage().getName() + ".describeIterationLimit"; + + /** + * Option specifies the statement limit for the algorithm used to + * compute DESCRIBE responses (optional). + * + * @see QueryHints#DESCRIBE_STATEMENT_LIMIT + * @see QueryHints#DEFAULT_DESCRIBE_STATEMENT_LIMIT + */ + public static final String DESCRIBE_STATEMENT_LIMIT = BigdataSail.class + .getPackage().getName() + ".describeIterationStatementLimit"; + } /** This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-25 13:47:34
|
Revision: 6612 http://bigdata.svn.sourceforge.net/bigdata/?rev=6612&view=rev Author: thompsonbry Date: 2012-09-25 13:47:24 +0000 (Tue, 25 Sep 2012) Log Message: ----------- Integrated ganglia self reporting into the Journal. This is off by default, but can be enabled in the configuration properties. It will be enabled by default in the HAJournalServer configuration examples. IIndexManager now implements ICounterSetAccess in support of the ganglia integration. QueryEngineMetricsCollector was also modified to work with the Journal as well as the IBigdataFederation. Ganglia reporting (but not listening) is now enabled in src/resources/HAJournal/HAJournal.config by default. Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/bop/fed/DelegateIndexManager.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/counters/ganglia/QueryEngineMetricsCollector.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractTask.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IIndexManager.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/rule/eval/pipeline/JoinTaskFactoryTask.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/bop/fed/DelegateIndexManager.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/bop/fed/DelegateIndexManager.java 2012-09-25 11:52:12 UTC (rev 6611) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/bop/fed/DelegateIndexManager.java 2012-09-25 13:47:24 UTC (rev 6612) @@ -9,6 +9,7 @@ import com.bigdata.btree.BTree; import com.bigdata.btree.IIndex; import com.bigdata.btree.IndexMetadata; +import com.bigdata.counters.CounterSet; import com.bigdata.journal.IIndexManager; import com.bigdata.journal.IIndexStore; import com.bigdata.journal.IResourceLockService; @@ -165,10 +166,15 @@ // // } + @Override + public CounterSet getCounters() { + return dataService.getFederation().getCounters(); + } + public String toString() { - return super.toString() + "{dataServiceUUID=" - + dataService.getServiceUUID() + "}"; + return super.toString() + "{dataServiceUUID=" + + dataService.getServiceUUID() + "}"; } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/counters/ganglia/QueryEngineMetricsCollector.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/counters/ganglia/QueryEngineMetricsCollector.java 2012-09-25 11:52:12 UTC (rev 6611) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/counters/ganglia/QueryEngineMetricsCollector.java 2012-09-25 13:47:24 UTC (rev 6612) @@ -11,7 +11,9 @@ import com.bigdata.ganglia.GangliaMunge; import com.bigdata.ganglia.IGangliaMetricsCollector; import com.bigdata.ganglia.IGangliaMetricsReporter; +import com.bigdata.journal.IIndexManager; import com.bigdata.service.IBigdataFederation; +import com.bigdata.service.IFederationDelegate; /** * Reflects query engine metrics from the data server nodes to ganglia. @@ -41,19 +43,20 @@ // // } - private final IBigdataFederation<?> fed; +// private final IBigdataFederation<?> fed; + private final IIndexManager indexManager; private final AbstractStatisticsCollector statisticsCollector; - public QueryEngineMetricsCollector(final IBigdataFederation<?> fed, + public QueryEngineMetricsCollector(final IIndexManager indexManager, final AbstractStatisticsCollector statisticsCollector) { - if (fed == null) + if (indexManager == null) throw new IllegalArgumentException(); if (statisticsCollector == null) throw new IllegalArgumentException(); - this.fed = fed; + this.indexManager = indexManager; this.statisticsCollector = statisticsCollector; @@ -79,10 +82,14 @@ */ final String pathPrefix = basePrefix + "Query Engine"; - // Note: Necessary for some kinds of things (lazily created). - fed.reattachDynamicCounters(); + if (indexManager instanceof IBigdataFederation) { + + // Note: Necessary for some kinds of things (lazily created). + ((IFederationDelegate<?>) indexManager).reattachDynamicCounters(); + + } - final CounterSet counters = (CounterSet) fed.getCounters().getPath( + final CounterSet counters = (CounterSet) indexManager.getCounters().getPath( pathPrefix); if (counters == null) { Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractTask.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractTask.java 2012-09-25 11:52:12 UTC (rev 6611) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractTask.java 2012-09-25 13:47:24 UTC (rev 6612) @@ -3239,6 +3239,11 @@ public Iterator<String> indexNameScan(String prefix, long timestamp) { throw new UnsupportedOperationException(); } + + @Override + public CounterSet getCounters() { + return delegate.getCounters(); + } } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IIndexManager.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IIndexManager.java 2012-09-25 11:52:12 UTC (rev 6611) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IIndexManager.java 2012-09-25 13:47:24 UTC (rev 6612) @@ -28,6 +28,7 @@ package com.bigdata.journal; import com.bigdata.btree.IndexMetadata; +import com.bigdata.counters.ICounterSetAccess; /** * Interface for managing named indices. @@ -35,7 +36,7 @@ * @author <a href="mailto:tho...@us...">Bryan Thompson</a> * @version $Id$ */ -public interface IIndexManager extends IIndexStore { +public interface IIndexManager extends IIndexStore, ICounterSetAccess { /** * Register a named index. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java 2012-09-25 11:52:12 UTC (rev 6611) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java 2012-09-25 13:47:24 UTC (rev 6612) @@ -26,6 +26,8 @@ import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.URLEncoder; import java.nio.ByteBuffer; import java.util.Collection; @@ -36,6 +38,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.Semaphore; @@ -62,7 +66,17 @@ import com.bigdata.counters.AbstractStatisticsCollector; import com.bigdata.counters.CounterSet; import com.bigdata.counters.ICounterSet; +import com.bigdata.counters.ganglia.BigdataGangliaService; +import com.bigdata.counters.ganglia.BigdataMetadataFactory; +import com.bigdata.counters.ganglia.HostMetricsCollector; +import com.bigdata.counters.ganglia.QueryEngineMetricsCollector; import com.bigdata.counters.httpd.CounterSetHTTPD; +import com.bigdata.ganglia.DefaultMetadataFactory; +import com.bigdata.ganglia.GangliaMetadataFactory; +import com.bigdata.ganglia.GangliaService; +import com.bigdata.ganglia.GangliaSlopeEnum; +import com.bigdata.ganglia.IGangliaDefaults; +import com.bigdata.ganglia.util.GangliaUtil; import com.bigdata.ha.HAGlue; import com.bigdata.ha.QuorumService; import com.bigdata.journal.jini.ha.HAJournal; @@ -78,7 +92,6 @@ import com.bigdata.rwstore.IRawTx; import com.bigdata.service.AbstractTransactionService; import com.bigdata.service.DataService; -import com.bigdata.service.IBigdataClient; import com.bigdata.service.IBigdataFederation; import com.bigdata.sparse.GlobalRowStoreHelper; import com.bigdata.sparse.SparseRowStore; @@ -217,6 +230,87 @@ */ String DEFAULT_HTTPD_PORT = "-1"; + /** + * The delay between reports of performance counters in milliseconds ( + * {@value #DEFAULT_REPORT_DELAY}). When ZERO (0L), performance counter + * reporting will be disabled. + * + * @see #DEFAULT_REPORT_DELAY + */ + String REPORT_DELAY = Journal.class.getName() + ".reportDelay"; + + /** + * The default {@link #REPORT_DELAY}. + */ + String DEFAULT_REPORT_DELAY = ""+(60*1000); + + /* + * Ganglia + */ + + // Listen + + /** + * The multicast group used to join the ganglia performance monitoring + * network. + */ + String GANGLIA_LISTEN_GROUP = Journal.class.getName() + + ".ganglia.listenGroup"; + + String DEFAULT_GANGLIA_LISTEN_GROUP = IGangliaDefaults.DEFAULT_GROUP; + + /** + * The port for the multicast group used to join the ganglia performance + * monitoring network. + */ + String GANGLIA_LISTEN_PORT = Journal.class.getName() + + ".ganglia.listenPort"; + + String DEFAULT_GANGLIA_LISTEN_PORT = Integer + .toString(IGangliaDefaults.DEFAULT_PORT); + + /** + * When <code>true</code>, the embedded {@link GangliaService} will + * listen on to the specified multicast group and build up an internal + * model of the metrics in the ganglia network. + * <p> + * Note: If both {@link #GANGLIA_LISTEN} and {@link #GANGLIA_REPORT} are + * <code>false</code> then the embedded {@link GangliaService} will not + * be started. + */ + String GANGLIA_LISTEN = Journal.class.getName() + + ".ganglia.listen"; + + String DEFAULT_GANGLIA_LISTEN = "false"; + + // Report + + /** + * When <code>true</code>, the embedded {@link GangliaService} will + * report performance metrics to the specified gmetad server(s). + * <p> + * Note: If both {@link #GANGLIA_LISTEN} and {@link #GANGLIA_REPORT} are + * <code>false</code> then the embedded {@link GangliaService} will not + * be started. + */ + String GANGLIA_REPORT = Journal.class.getName() + + ".ganglia.report"; + + String DEFAULT_GANGLIA_REPORT = "false"; + + /** + * An list of the metric servers (<code>gmetad</code> instances) to + * which metrics will be sent. The default is to send metrics to the + * well known multicast group for ganglia. Zero or more hosts may be + * specified, separated by whitespace or commas. The port for each host + * is optional and defaults to the well known port for ganglia. Each + * host may be either a unicast address or a multicast group. + */ + String GANGLIA_SERVERS = Journal.class.getName() + + ".ganglia.servers"; + + String DEFAULT_GANGLIA_SERVERS = IGangliaDefaults.DEFAULT_GROUP; + } /** @@ -1611,6 +1705,24 @@ */ localTransactionManager.shutdown(); + /* + * Note: The embedded GangliaService is executed on the main thread + * pool. We need to terminate the GangliaService in order for the thread + * pool to shutdown. + */ + { + final FutureTask<Void> ft = gangliaFuture.getAndSet(null); + + if (ft != null) { + + ft.cancel(true/* mayInterruptIfRunning */); + + } + + // Clear the state reference. + gangliaService.set(null); + } + if (platformStatisticsCollector != null) { platformStatisticsCollector.stop(); @@ -1689,6 +1801,24 @@ if (!isOpen()) return; + /* + * Note: The embedded GangliaService is executed on the main thread + * pool. We need to terminate the GangliaService in order for the thread + * pool to shutdown. + */ + { + final FutureTask<Void> ft = gangliaFuture.getAndSet(null); + + if (ft != null) { + + ft.cancel(true/* mayInterruptIfRunning */); + + } + + // Clear the state reference. + gangliaService.set(null); + } + if (platformStatisticsCollector != null) { platformStatisticsCollector.stop(); @@ -2109,6 +2239,18 @@ } /** + * Future for an embedded {@link GangliaService} which listens to + * <code>gmond</code> instances and other {@link GangliaService}s and + * reports out metrics from {@link #getCounters()} to the ganglia network. + */ + private final AtomicReference<FutureTask<Void>> gangliaFuture = new AtomicReference<FutureTask<Void>>(); + + /** + * The embedded ganglia peer. + */ + private final AtomicReference<BigdataGangliaService> gangliaService = new AtomicReference<BigdataGangliaService>(); + + /** * An executor service used to read on the local disk. * * @todo This is currently used by prefetch. We should generalize this @@ -2140,8 +2282,11 @@ * @author <a href="mailto:tho...@us...">Bryan * Thompson</a> * - * FIXME Make sure that we disable this by default for the unit - * tests or we will have a bunch of sampling processes running! + * FIXME Make sure that we disable this by default (including the + * http reporting, the process and host monitoring, and the ganglia + * monitoring) for the unit tests or we will have a bunch of + * sampling processes running! (Right now, these things are disabled + * by default in {@link Journal.Options}.) */ private class StartDeferredTasksTask implements Runnable { @@ -2185,6 +2330,29 @@ // start the local httpd service reporting on this service. startHttpdService(); + /* + * Start embedded Ganglia peer. It will develop a snapshot of the + * metrics in memory for all nodes reporting in the ganglia network + * and will self-report metrics from the performance counter + * hierarchy to the ganglia network. + */ + { + + final Properties properties = getProperties(); + + final boolean listen = Boolean + .valueOf(properties.getProperty(Options.GANGLIA_LISTEN, + Options.DEFAULT_GANGLIA_LISTEN)); + + final boolean report = Boolean + .valueOf(properties.getProperty(Options.GANGLIA_REPORT, + Options.DEFAULT_GANGLIA_REPORT)); + + if (listen || report) + startGangliaService(Journal.this.platformStatisticsCollector); + + } + } /** @@ -2277,7 +2445,7 @@ /** * Start the local httpd service (if enabled). The service is started on - * the {@link IBigdataClient#getHttpdPort()}, on a randomly assigned + * the {@link Journal#getHttpdPort()}, on a randomly assigned * port if the port is <code>0</code>, or NOT started if the port is * <code>-1</code>. If the service is started, then the URL for the * service is reported to the load balancer and also written into the @@ -2329,7 +2497,7 @@ + URLEncoder.encode("", "UTF-8"); if(log.isInfoEnabled()) - log.info("Performance counters: " + httpdURL); + log.info("Performance counters: " + httpdURL); } @@ -2337,6 +2505,197 @@ } + /** + * Start embedded Ganglia peer. It will develop a snapshot of the + * cluster metrics in memory and will self-report metrics from the + * performance counter hierarchy to the ganglia network. + * + * @param statisticsCollector + * Performance counters will be harvested from here. + * + * @see https://sourceforge.net/apps/trac/bigdata/ticket/441 (Ganglia + * Integration). + */ + protected void startGangliaService( + final AbstractStatisticsCollector statisticsCollector) { + + if(statisticsCollector == null) + return; + + try { + + final Properties properties = Journal.this.getProperties(); + + final String hostName = AbstractStatisticsCollector.fullyQualifiedHostName; + + /* + * Note: This needs to be the value reported by the statistics + * collector since that it what makes it into the counter set + * path prefix for this service. + * + * TODO This implies that we can not enable the embedded ganglia + * peer unless platform level statistics collection is enabled. + * We should be able to separate out the collection of host + * metrics from whether or not we are collecting metrics from + * the bigdata service. Do this when moving the host and process + * (pidstat) collectors into the bigdata-ganglia module. + */ + final String serviceName = statisticsCollector.getProcessName(); + + final InetAddress listenGroup = InetAddress + .getByName(properties.getProperty( + Options.GANGLIA_LISTEN_GROUP, + Options.DEFAULT_GANGLIA_LISTEN_GROUP)); + + final int listenPort = Integer.valueOf(properties.getProperty( + Options.GANGLIA_LISTEN_PORT, + Options.DEFAULT_GANGLIA_LISTEN_PORT)); + + final boolean listen = Boolean.valueOf(properties.getProperty( + Options.GANGLIA_LISTEN, + Options.DEFAULT_GANGLIA_LISTEN)); + + final boolean report = Boolean.valueOf(properties.getProperty( + Options.GANGLIA_REPORT, + Options.DEFAULT_GANGLIA_REPORT)); + + // Note: defaults to the listenGroup and port if nothing given. + final InetSocketAddress[] metricsServers = GangliaUtil.parse( + // server(s) + properties.getProperty( + Options.GANGLIA_SERVERS, + Options.DEFAULT_GANGLIA_SERVERS), + // default host (same as listenGroup) + listenGroup.getHostName(), + // default port (same as listenGroup) + listenPort + ); + + final int quietPeriod = IGangliaDefaults.QUIET_PERIOD; + + final int initialDelay = IGangliaDefaults.INITIAL_DELAY; + + /* + * Note: Use ZERO (0) if you are running gmond on the same host. + * That will prevent the GangliaService from transmitting a + * different heartbeat, which would confuse gmond and gmetad. + */ + final int heartbeatInterval = 0; // IFF using gmond. + // final int heartbeatInterval = + // IGangliaDefaults.HEARTBEAT_INTERVAL; + + // Use the report delay for the interval in which we scan the + // performance counters. + final int monitoringInterval = (int) TimeUnit.MILLISECONDS + .toSeconds(Long.parseLong(properties.getProperty( + Options.REPORT_DELAY, + Options.DEFAULT_REPORT_DELAY))); + + final String defaultUnits = IGangliaDefaults.DEFAULT_UNITS; + + final GangliaSlopeEnum defaultSlope = IGangliaDefaults.DEFAULT_SLOPE; + + final int defaultTMax = IGangliaDefaults.DEFAULT_TMAX; + + final int defaultDMax = IGangliaDefaults.DEFAULT_DMAX; + + // Note: Factory is extensible (application can add its own + // delegates). + final GangliaMetadataFactory metadataFactory = new GangliaMetadataFactory( + new DefaultMetadataFactory(// + defaultUnits,// + defaultSlope,// + defaultTMax,// + defaultDMax// + )); + + /* + * Layer on the ability to (a) recognize and align host + * bigdata's performance counters hierarchy with those declared + * by ganglia and; (b) provide nice declarations for various + * application counters of interest. + */ + metadataFactory.add(new BigdataMetadataFactory(hostName, + serviceName, defaultSlope, defaultTMax, defaultDMax, + heartbeatInterval)); + + // The embedded ganglia peer. + final BigdataGangliaService gangliaService = new BigdataGangliaService( + hostName, // + serviceName, // + metricsServers,// + listenGroup,// + listenPort, // + listen,// listen + report,// report + false,// mock, + quietPeriod, // + initialDelay, // + heartbeatInterval,// + monitoringInterval, // + defaultDMax,// globalDMax + metadataFactory); + + // Collect and report host metrics. + gangliaService.addMetricCollector(new HostMetricsCollector( + statisticsCollector)); + + // Collect and report QueryEngine metrics. + gangliaService + .addMetricCollector(new QueryEngineMetricsCollector( + Journal.this, statisticsCollector)); + + /* + * TODO The problem with reporting per-service statistics is + * that ganglia lacks a facility to readily aggregate statistics + * across services on a host (SMS + anything). The only way this + * can readily be made to work is if each service has a distinct + * metric for the same value (e.g., Mark and Sweep GC). However, + * that causes a very large number of distinct metrics. I have + * commented this out for now while I think it through some + * more. Maybe we will wind up only reporting the per-host + * counters to ganglia? + * + * Maybe the right way to handle this is to just filter by the + * service type? Basically, that is what we are doing for the + * QueryEngine metrics. + */ + // Collect and report service metrics. +// gangliaService.addMetricCollector(new ServiceMetricsCollector( +// statisticsCollector, null/* filter */)); + + // Wrap as Future. + final FutureTask<Void> ft = new FutureTask<Void>( + gangliaService, (Void) null); + + // Save reference to future. + gangliaFuture.set(ft); + + // Set the state reference. + Journal.this.gangliaService.set(gangliaService); + + // Start the embedded ganglia service. + getExecutorService().submit(ft); + + } catch (RejectedExecutionException t) { + + /* + * Ignore. + * + * Note: This occurs if the federation shutdown() before we + * start the embedded ganglia peer. For example, it is common + * when running a short lived utility service such as + * ListServices. + */ + + } catch (Throwable t) { + + log.error(t, t); + + } + + } + } // class StartDeferredTasks public ScheduledFuture<?> addScheduledTask(final Runnable task, Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/rule/eval/pipeline/JoinTaskFactoryTask.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/rule/eval/pipeline/JoinTaskFactoryTask.java 2012-09-25 11:52:12 UTC (rev 6611) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/rule/eval/pipeline/JoinTaskFactoryTask.java 2012-09-25 13:47:24 UTC (rev 6612) @@ -15,6 +15,7 @@ import com.bigdata.btree.BTree; import com.bigdata.btree.IIndex; import com.bigdata.btree.IndexMetadata; +import com.bigdata.counters.CounterSet; import com.bigdata.journal.IIndexManager; import com.bigdata.journal.IIndexStore; import com.bigdata.journal.IResourceLockService; @@ -531,6 +532,11 @@ return dataService.getFederation().getHttpdPort(); } + @Override + public CounterSet getCounters() { + return dataService.getFederation().getCounters(); + } + /** * {@inheritDoc} * Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-09-25 11:52:12 UTC (rev 6611) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-09-25 13:47:24 UTC (rev 6612) @@ -62,8 +62,6 @@ * @author <a href="mailto:tho...@us...">Bryan Thompson</a> * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/530"> Journal * HA </a> - * - * TODO Make sure that ganglia reporting can be enabled. */ public class HAJournalServer extends AbstractServer { @@ -257,6 +255,9 @@ // Jini/River ServiceID. final ServiceID serviceID = getServiceID(); + if (serviceID == null) + throw new AssertionError("ServiceID not assigned?"); + // UUID variant of that ServiceID. serviceUUID = JiniUtil.serviceID2UUID(serviceID); @@ -899,7 +900,7 @@ @Override synchronized public void shutdownNow() { - + // // immediate service shutdown (blocks). // super.shutdownNow(); Modified: branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config 2012-09-25 11:52:12 UTC (rev 6611) +++ branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config 2012-09-25 13:47:24 UTC (rev 6612) @@ -283,6 +283,10 @@ new NV(AbstractTransactionService.Options.MIN_RELEASE_AGE,"1"), + // Enable ganglia reporting (listener is not required). + //new NV(Journal.Options.GANGLIA_LISTEN,"true"), + new NV(Journal.Options.GANGLIA_REPORT,"true"), + }, bigdata.kb); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-25 16:10:27
|
Revision: 6616 http://bigdata.svn.sourceforge.net/bigdata/?rev=6616&view=rev Author: thompsonbry Date: 2012-09-25 16:10:16 +0000 (Tue, 25 Sep 2012) Log Message: ----------- Dropped the log level for the haLog for write cache replication down to DEBUG since there is so much volume associated with that operation. Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-09-25 16:03:57 UTC (rev 6615) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-09-25 16:10:16 UTC (rev 6616) @@ -5141,8 +5141,8 @@ public Future<Void> receiveAndReplicate(final HAWriteMessage msg) throws IOException { - if (haLog.isInfoEnabled()) - haLog.info("msg=" + msg); + if (haLog.isDebugEnabled()) + haLog.debug("msg=" + msg); /* * Adjust the size on the disk of the local store to that given in Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-09-25 16:03:57 UTC (rev 6615) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-09-25 16:10:16 UTC (rev 6616) @@ -570,8 +570,8 @@ protected void handleReplicatedWrite(final HAWriteMessage msg, final ByteBuffer data) throws Exception { - if (haLog.isInfoEnabled()) - haLog.info("msg=" + msg + ", buf=" + data); + if (haLog.isDebugEnabled()) + haLog.debug("msg=" + msg + ", buf=" + data); /* * Note: the ByteBuffer is owned by the HAReceiveService. This This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-26 19:03:39
|
Revision: 6631 http://bigdata.svn.sourceforge.net/bigdata/?rev=6631&view=rev Author: thompsonbry Date: 2012-09-26 19:03:29 +0000 (Wed, 26 Sep 2012) Log Message: ----------- Added support for XHTML responses for SELECT queries to the NSS. It will currently deliver ASK queries as plain text when this option is specified, which should be changed. It will not do anything yet for graph results - it should probably do RDFa. Added more unit tests for the solution set cache. Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/QueryServlet.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/tck/BigdataSPARQLUpdateTest2.java branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/index.html Added Paths: ----------- branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/result-to-html.xsl Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-26 15:03:53 UTC (rev 6630) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-26 19:03:29 UTC (rev 6631) @@ -22,6 +22,8 @@ */ package com.bigdata.rdf.sail.webapp; +import info.aduna.xml.XMLWriter; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -61,6 +63,7 @@ import org.openrdf.query.resultio.TupleQueryResultFormat; import org.openrdf.query.resultio.TupleQueryResultWriter; import org.openrdf.query.resultio.TupleQueryResultWriterRegistry; +import org.openrdf.query.resultio.sparqlxml.SPARQLResultsXMLWriter; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.sail.SailQuery; import org.openrdf.rio.RDFFormat; @@ -138,6 +141,20 @@ protected static final String XHTML = "xhtml"; /** + * URL Query parameter used to specify an XSL style sheet to be associated + * with the response in combination with the {@link #XHTML} URL query + * parameter. + */ + protected static final String XSL_STYLESHEET = "xsl-stylesheet"; + + /** + * The default XSL style sheet. + * + * @see #XSL_STYLESHEET + */ + protected static final String DEFAULT_XSL_STYLESHEET = "result-to-html.xsl"; + + /** * URL Query parameter used to request an incremental XHTML representation * reporting on the progress of a SPARQL UPDATE. * <p> @@ -346,6 +363,37 @@ } + /** + * Return the effective string value of a URL query parameter. If the URL + * query parameter was not given, or if it was given without an explicit + * value, then the effective string value is the <i>defaultValue</o>. + * + * @param s + * The value of the URL query parameter. + * @param defaultValue + * The default value to return if the parameter was not given or + * given without an explicit value. + * + * @return The effective value. + */ + protected static String getEffectiveStringValue(String s, + final String defaultValue) { + + if (s == null) + return defaultValue; + + s = s.trim(); + + if (s.length() == 0) { + + return defaultValue; + + } + + return s; + + } + /** * Abstract base class for running queries handles the timing, pipe, * reporting, obtains the connection, and provides the finally {} semantics @@ -961,13 +1009,13 @@ public TupleQueryTask(final String namespace, final long timestamp, final String baseURI, final ASTContainer astContainer, - final QueryType queryType, final TupleQueryResultFormat format, + final QueryType queryType, final String mimeType, + final Charset charset, final String fileExt, final HttpServletRequest req, final HttpServletResponse resp, final OutputStream os) { super(namespace, timestamp, baseURI, astContainer, queryType, - format.getDefaultMIMEType(), format.getCharset(), format - .getDefaultFileExtension(), req, resp, os); + mimeType, charset, fileExt, req, resp, os); } @@ -976,19 +1024,69 @@ final BigdataSailTupleQuery query = (BigdataSailTupleQuery) setupQuery(cxn); - // Note: getQueryTask() verifies that format will be non-null. - final TupleQueryResultFormat format = TupleQueryResultWriterRegistry - .getInstance().getFileFormatForMIMEType(mimeType); + final TupleQueryResultWriter w; - final TupleQueryResultWriter w = TupleQueryResultWriterRegistry - .getInstance().get(format).getWriter(os); + if (xhtml) { + + /* + * Override the XMLWriter to ensure that the XSL style sheet is + * declared in the generated XML document. This will tell the + * browser that it should style the result. + * + * Note: The Content-Type header also needs to be overridden in + * order to have the browser apply the style sheet. + */ + + final String stylesheet = getEffectiveStringValue( + req.getParameter(XSL_STYLESHEET), + DEFAULT_XSL_STYLESHEET); + + final XMLWriter xmlWriter = new MyXMLWriter(os, stylesheet); + + w = new SPARQLResultsXMLWriter(xmlWriter); + + } else { - query.evaluate(w); + // Note: getQueryTask() verifies that format will be non-null. + final TupleQueryResultFormat format = TupleQueryResultWriterRegistry + .getInstance().getFileFormatForMIMEType(mimeType); + w = TupleQueryResultWriterRegistry.getInstance().get(format) + .getWriter(os); + + } + + query.evaluate(w); + } } + + private static class MyXMLWriter extends XMLWriter { + final private String stylesheet; + + public MyXMLWriter(final OutputStream outputStream, + final String stylesheet) { + + super(outputStream); + + this.stylesheet = stylesheet; + + } + + @Override + public void startDocument() throws IOException { + + super.startDocument(); + + _writeLn("<?xml-stylesheet type=\"text/xsl\" href=\"" + stylesheet + + "\" ?>"); + + } + + } + /** * Executes a graph query. */ @@ -1560,6 +1658,8 @@ */ final boolean explain = req.getParameter(EXPLAIN) != null; + final boolean xhtml = req.getParameter(XHTML) != null; + /* * CONNEG for the MIME type. * @@ -1567,11 +1667,43 @@ * a given type of query will result in a response using a default MIME * Type for that query. */ - final String acceptStr = explain ? "text/html" - : acceptOverride != null ? acceptOverride : req.getHeader("Accept"); + final String acceptStr; + if (explain) { + acceptStr = BigdataServlet.MIME_TEXT_HTML; + } else if (acceptOverride != null) { + acceptStr = acceptOverride; + } else if (xhtml) { + switch (queryType) { + case ASK: + // Should be all we need to do. + acceptStr = BooleanQueryResultFormat.TEXT.getDefaultMIMEType(); + break; + case SELECT: + /* + * We will send back an XML document with an XSLT style sheet + * declaration. The Content-Type needs to be application/xml in + * order for the browser to interpret the style sheet + * declaration. + */ + // Generate XML solutions so we can apply XSLT transform. + acceptStr = TupleQueryResultFormat.SPARQL.getDefaultMIMEType(); + break; + case DESCRIBE: + case CONSTRUCT: + // Generate RDF/XML so we can apply XSLT transform. + acceptStr = RDFFormat.RDFXML.getDefaultMIMEType(); + break; + default: + throw new AssertionError("QueryType=" + queryType); + } + } else { + // Use whatever was specified by the client. + acceptStr = req.getHeader("Accept"); + } + // Do conneg. final ConnegUtil util = new ConnegUtil(acceptStr); - + switch (queryType) { case ASK: { @@ -1596,8 +1728,25 @@ final TupleQueryResultFormat format = util .getTupleQueryResultFormat(TupleQueryResultFormat.SPARQL); + final String mimeType; + final Charset charset; + final String fileExt; + if(xhtml) { + /* + * Override as application/xml so the browser will interpret the + * XSL style sheet directive. + */ + mimeType = BigdataServlet.MIME_APPLICATION_XML; + charset = Charset.forName("UTF-8"); + fileExt = "xml"; + } else { + mimeType = format.getDefaultMIMEType(); + charset = format.getCharset(); + fileExt = format.getDefaultFileExtension(); + } return new TupleQueryTask(namespace, timestamp, baseURI, - astContainer, queryType, format, req, resp, os); + astContainer, queryType, mimeType, charset, fileExt, req, + resp, os); } } // switch(queryType) Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/QueryServlet.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/QueryServlet.java 2012-09-26 15:03:53 UTC (rev 6630) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/QueryServlet.java 2012-09-26 19:03:29 UTC (rev 6631) @@ -503,8 +503,8 @@ queryTask = context.getQueryTask(namespace, timestamp, queryStr, null/* acceptOverride */, req, resp, os, false/* update */); - - if(queryTask == null) { + + if (queryTask == null) { // KB not found. Response already committed. return; } @@ -534,9 +534,53 @@ // } // // } - - final FutureTask<Void> ft = new FutureTask<Void>(queryTask); + final FutureTask<Void> ft; + + if (!queryTask.explain && queryTask.xhtml) { + + /* + * Wrap the query result with an XSL transform that will paint + * the page. + */ + + switch(queryTask.queryType) { + case ASK: + // For ASK, just delivery the text/plain response. + ft = new FutureTask<Void>(queryTask); + break; + case SELECT: + /* + * XSLT from XML representation of the solutions to XHTML. + * + * Note: This is handled by attaching a processor + * declaration when we generate the RDF/XML. + */ + ft = new FutureTask<Void>(queryTask); + break; + case DESCRIBE: + case CONSTRUCT: + /* + * FIXME : XSLT from RDF/XML => RDFa. Maybe using a Fresnel + * lens. + */ + ft = new FutureTask<Void>(queryTask); + break; + default: + throw new AssertionError("QueryType=" + queryTask.queryType); + } + + } else { + + /* + * Send back the data using whatever was the negotiated content + * type. + */ + + ft = new FutureTask<Void>(queryTask); + + } + if (log.isTraceEnabled()) log.trace("Will run query: " + queryStr); @@ -547,6 +591,12 @@ resp.setStatus(HTTP_OK); if (queryTask.explain) { + + /* + * Send back an explanation of the query execution, not the + * query results. + */ + resp.setContentType(BigdataServlet.MIME_TEXT_HTML); final Writer w = new OutputStreamWriter(os, queryTask.charset); try { @@ -562,6 +612,11 @@ } } else { + + /* + * Send back the query results. + */ + resp.setContentType(queryTask.mimeType); if (queryTask.charset != null) { Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/tck/BigdataSPARQLUpdateTest2.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/tck/BigdataSPARQLUpdateTest2.java 2012-09-26 15:03:53 UTC (rev 6630) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/tck/BigdataSPARQLUpdateTest2.java 2012-09-26 19:03:29 UTC (rev 6631) @@ -694,26 +694,6 @@ } /** - * Unit test where we INSERT some solutions into the same named solution set - * on which we are reading. This is done using an INCLUDE to join against - * the named solution set within an UPDATE operation which writes on that - * named solution set. The isolation semantics should provide one view for - * the reader and a different view for the writer. - * <p> - * Note: In order to setup this test, we will have to pre-populate the - * solution set. E.g., first load the data into graphs, then INSERT INTO - * SOLUTIONS. At that point we can do the INSERT which is also doing the - * "self-join" against the named solution set. - * - * TODO DO a variant test where the operation is a DELETE. - */ - public void test_isolation_insertIntoSolutionsWithIncludeFromSolutions() { - - fail("write test"); - - } - - /** * Unit test where we are deleting from one solution set and inserting into * another. * @@ -772,7 +752,7 @@ } - // Query the solution set from which the solution were removed. + // Query the solution set from which the solutions were removed. { final StringBuilder sb = new StringBuilder(); @@ -795,7 +775,7 @@ } - // Query the solution set into which the solution were inserted. + // Query the solution set into which the solutions were inserted. { final StringBuilder sb = new StringBuilder(); @@ -821,6 +801,26 @@ } /** + * Unit test where we INSERT some solutions into the same named solution set + * on which we are reading. This is done using an INCLUDE to join against + * the named solution set within an UPDATE operation which writes on that + * named solution set. The isolation semantics should provide one view for + * the reader and a different view for the writer. + * <p> + * Note: In order to setup this test, we will have to pre-populate the + * solution set. E.g., first load the data into graphs, then INSERT INTO + * SOLUTIONS. At that point we can do the INSERT which is also doing the + * "self-join" against the named solution set. + * + * TODO DO a variant test where the operation is a DELETE. + */ + public void test_isolation_insertIntoSolutionsWithIncludeFromSolutions() { + + fail("write test"); + + } + + /** * Unit test of CREATE SOLUTIONS. * <p> * Note: If named solution sets which do not exist are equivalent to empty @@ -909,15 +909,40 @@ } + /** + * A correct rejection test which verifies that an exception is thrown if + * the named solution set does not exist. + */ + public void test_clearSolutionSet_01() throws UpdateExecutionException, + RepositoryException, MalformedQueryException { + + try { + con.prepareUpdate(QueryLanguage.SPARQL, "clear solutions %namedSet1") + .execute(); + fail("Excepting: " + UpdateExecutionException.class); + } catch(UpdateExecutionException ex) { + if(log.isInfoEnabled()) + log.info("Ignoring expected exception: "+ex); + } + + } + + /** + * A test which verifies that an exception is NOT thrown if + * the named solution set does not exist and SILENT is specified. + */ + public void test_clearSolutionSet_02() throws UpdateExecutionException, + RepositoryException, MalformedQueryException { + + con.prepareUpdate(QueryLanguage.SPARQL, + "clear silent solutions %namedSet1").execute(); + + } + /* - * TODO Write tests for CLEAR exactly as per DROP. - * * TODO Write tests for DROP/CLEAR GRAPHS, DROP/CLEAR ALL, and DROP/CLEAR * SOLUTIONS to verify that the effect the correct resources (graphs, * graphs+solutions, and solutions respectively). */ -// public void test_clearSolutionSet_01() { -// -// } } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/index.html =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/index.html 2012-09-26 15:03:53 UTC (rev 6630) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/index.html 2012-09-26 19:03:29 UTC (rev 6631) @@ -50,11 +50,9 @@ <INPUT type="checkbox" name="analytic" value="true" title="Enable the analytic query package." > Analytic -<!-- TODO XSTL styling of result set or graph result. <INPUT type="checkbox" name="xhtml" value="true" title="Request XHTML response (results formatted as table)." > XHTML ---> </P> </FORM> <h2><a href="http://www.w3.org/TR/sparql11-update/">SPARQL Update</a></h2> Added: branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/result-to-html.xsl =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/result-to-html.xsl (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-war/src/html/result-to-html.xsl 2012-09-26 19:03:29 UTC (rev 6631) @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + +XSLT script to format SPARQL Query Results XML Format into xhtml + +Copyright \xA9 2004, 2005 World Wide Web Consortium, (Massachusetts +Institute of Technology, European Research Consortium for +Informatics and Mathematics, Keio University). All Rights +Reserved. This work is distributed under the W3C\xAE Software +License [1] in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. + +[1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + +$Id: result-to-html.xsl,v 1.1 2008/01/15 02:25:58 eric Exp $ + +--> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:res="http://www.w3.org/2005/sparql-results#" exclude-result-prefixes="res xsl"> + + <!-- + <xsl:output + method="html" + media-type="text/html" + doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" + indent="yes" + encoding="UTF-8"/> + --> + + <!-- or this? --> + + <xsl:output method="xml" indent="yes" encoding="UTF-8" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" omit-xml-declaration="no"/> + + + <xsl:template name="header"> + <div> + <h2>Header</h2> + <xsl:for-each select="res:head/res:link"> + <p>Link to <xsl:value-of select="@href"/></p> + </xsl:for-each> + </div> + </xsl:template> + + <xsl:template name="boolean-result"> + <div> + <h2>Boolean Result</h2> + <p>Value <xsl:value-of select="res:boolean"/></p> + </div> + </xsl:template> + + + <xsl:template name="vb-result"> + <div> + <h2>Variable Bindings Result</h2> + + <table border="1"> + <xsl:text> + </xsl:text> + <tr> + <xsl:for-each select="res:head/res:variable"> + <th><xsl:value-of select="@name"/></th> + </xsl:for-each> + </tr> + <xsl:text> + </xsl:text> + <xsl:for-each select="res:results/res:result"> + <tr> + <xsl:apply-templates select="."/> + </tr> + </xsl:for-each> + </table> + </div> + </xsl:template> + + <xsl:template match="res:result"> + <xsl:variable name="current" select="."/> + <xsl:for-each select="//res:head/res:variable"> + <xsl:variable name="name" select="@name"/> + <td> + <xsl:choose> + <xsl:when test="$current/res:binding[@name=$name]"> + <!-- apply template for the correct value type (bnode, uri, + literal) --> + <xsl:apply-templates select="$current/res:binding[@name=$name]"/> + </xsl:when> + <xsl:otherwise> + <!-- no binding available for this variable in this solution --> + [unbound] + </xsl:otherwise> + </xsl:choose> + </td> + </xsl:for-each> + </xsl:template> + + <xsl:template match="res:bnode"> + <xsl:text>nodeID </xsl:text> + <xsl:value-of select="text()"/> + </xsl:template> + + <xsl:template match="res:uri"> + <xsl:variable name="uri" select="text()"/> + <xsl:text>URI </xsl:text> + <xsl:value-of select="$uri"/> + </xsl:template> + + <xsl:template match="res:literal"> + <xsl:choose> + <xsl:when test="@datatype"> + <!-- datatyped literal value --> + <xsl:value-of select="text()"/> (datatype <xsl:value-of select="@datatype"/> ) + </xsl:when> + <xsl:when test="@xml:lang"> + <!-- lang-string --> + <xsl:value-of select="text()"/> @ <xsl:value-of select="@xml:lang"/> + </xsl:when> + <xsl:when test="string-length(text()) != 0"> + <!-- present and not empty --> + <xsl:value-of select="text()"/> + </xsl:when> + <xsl:when test="string-length(text()) = 0"> + <!-- present and empty --> + [empty literal] + </xsl:when> + </xsl:choose> + </xsl:template> + + <xsl:template match="res:sparql"> + <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> + <head> + <title>SPARQL Query Results to XHTML (XSLT)</title> + </head> + <body> + + + <h1>SPARQL Query Results to XHTML (XSLT)</h1> + + <xsl:if test="res:head/res:link"> + <xsl:call-template name="header"/> + </xsl:if> + + <xsl:choose> + <xsl:when test="res:boolean"> + <xsl:call-template name="boolean-result"/> + </xsl:when> + + <xsl:when test="res:results"> + <xsl:call-template name="vb-result"/> + </xsl:when> + + </xsl:choose> + + + </body> + </html> + </xsl:template> +</xsl:stylesheet> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-27 12:16:42
|
Revision: 6632 http://bigdata.svn.sourceforge.net/bigdata/?rev=6632&view=rev Author: thompsonbry Date: 2012-09-27 12:16:36 +0000 (Thu, 27 Sep 2012) Log Message: ----------- I have committed a change to AccessPath and SPOArrayIterator as a workaround for this issue. It raises the limit to 10,000,000 in both classes. The javadoc has been annotated to link to this issue. There may well be other limits that will impact truth maintenance. This all needs to be reviewed in depth. @see https://sourceforge.net/apps/trac/bigdata/ticket/606 (Array limits in truth maintenance code). Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/accesspath/AccessPath.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/spo/SPOArrayIterator.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/accesspath/AccessPath.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/accesspath/AccessPath.java 2012-09-26 19:03:29 UTC (rev 6631) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/accesspath/AccessPath.java 2012-09-27 12:16:36 UTC (rev 6632) @@ -188,14 +188,17 @@ } /** - * The maximum <em>limit</em> that is allowed for a fully-buffered read. - * The {@link #asynchronousIterator(Iterator)} will always be used above - * this limit. + * The maximum <em>limit</em> that is allowed for a fully-buffered read. The + * {@link #asynchronousIterator(Iterator)} will always be used above this + * limit. * - * @todo This should probably be close to the branching factor or chunk - * capacity. + * FIXME Array limits in truth maintenance code. This should probably be + * close to the branching factor or chunk capacity. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/606"> + * Array limits in truth maintenance code. </a> */ - protected static final int MAX_FULLY_BUFFERED_READ_LIMIT = 250000; + protected static final int MAX_FULLY_BUFFERED_READ_LIMIT = 10000000; /** * We cache some stuff for historical reads. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/spo/SPOArrayIterator.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/spo/SPOArrayIterator.java 2012-09-26 19:03:29 UTC (rev 6631) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/spo/SPOArrayIterator.java 2012-09-27 12:16:36 UTC (rev 6632) @@ -30,7 +30,6 @@ import java.util.Arrays; import java.util.NoSuchElementException; -import com.bigdata.btree.ITupleIterator; import com.bigdata.rdf.inf.TruthMaintenance; import com.bigdata.rdf.store.AbstractTripleStore; import com.bigdata.relation.accesspath.IAccessPath; @@ -52,6 +51,17 @@ */ public class SPOArrayIterator implements IChunkedOrderedIterator<ISPO> { + /** + * The maximum capacity for the backing array. + * + * FIXME Array limits in truth maintenance code. This should probably be + * close to the branching factor or chunk capacity. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/606"> + * Array limits in truth maintenance code. </a> + */ + private static final long MAX_CAPACITY = 10000000; + private boolean open = true; /** @@ -197,14 +207,16 @@ final long rangeCount = accessPath.rangeCount(false/*exact*/); - if (rangeCount > 10000000) { + if (rangeCount >= MAX_CAPACITY) { /* * Note: This is a relatively high limit (10M statements). You are * much better off processing smaller chunks! */ - throw new RuntimeException("Too many statements to read into memory: "+rangeCount); + throw new RuntimeException( + "Too many statements to read into memory: rangeCount=" + + rangeCount + ", maxCapacity=" + MAX_CAPACITY); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-27 16:27:57
|
Revision: 6633 http://bigdata.svn.sourceforge.net/bigdata/?rev=6633&view=rev Author: thompsonbry Date: 2012-09-27 16:27:50 +0000 (Thu, 27 Sep 2012) Log Message: ----------- Various changes to support the solution set cache integrated with the backing Journal. Tests and SPARQL UPDATE/QUERY through the NSS are now running. tx based tests still fail as I have not yet addressed tx isolation for solution set access and update. Still turned off by default. Added ability to invoke DumpJournal from the NSS status page using a URL query parameter. This capability is not advertised on the page. @see https://sourceforge.net/apps/trac/bigdata/ticket/531 (Solution Set Cache) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/bop/solutions/SolutionSetStream.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/btree/Checkpoint.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/DumpJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/stream/Stream.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/CacheConnectionImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/SolutionSetCache.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/StatusServlet.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/bop/solutions/SolutionSetStream.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/bop/solutions/SolutionSetStream.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/bop/solutions/SolutionSetStream.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -47,6 +47,7 @@ import com.bigdata.rdf.internal.encoder.SolutionSetStreamDecoder; import com.bigdata.rdf.internal.encoder.SolutionSetStreamEncoder; import com.bigdata.rdf.sparql.ast.ISolutionSetStats; +import com.bigdata.rdf.sparql.ast.SolutionSetStats; import com.bigdata.rwstore.IPSOutputStream; import com.bigdata.stream.Stream; import com.bigdata.striterator.Chunkerator; @@ -190,11 +191,34 @@ * @return The {@link ISolutionSetStats}. */ public ISolutionSetStats getStats() { - + + /* + * Note: This field is set by setCheckpoint(). + */ + return solutionSetStats; } + /** + * Return the address of the {@link SolutionSetStats} to be written into the + * next {@link Checkpoint} record. The caller must have {@link #flush()} the + * {@link SolutionSetStream} as a pre-condition (to ensure that the stats + * have been written out). If the {@link SolutionSetStats} are not loaded, + * then the address from the last {@link Checkpoint} record is returned. + */ + public long getStatsAddr() { + + if (solutionSetStats != null) { + + return solutionSetStats.addr; + + } + + return getCheckpoint().getBloomFilterAddr(); + + } + public ICloseableIterator<IBindingSet[]> get() { if (rootAddr == IRawStore.NULL) @@ -375,9 +399,10 @@ */ if (solutionSetStats != null - && solutionSetStats.addr != getCheckpoint() - .getBloomFilterAddr()) { - + && (solutionSetStats.addr == IRawStore.NULL // + || solutionSetStats.addr != getCheckpoint().getBloomFilterAddr())// + ) { + solutionSetStats.addr = getStore() .write(ByteBuffer.wrap(SerializerUtil .serialize(solutionSetStats.delegate))); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/btree/Checkpoint.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/btree/Checkpoint.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/btree/Checkpoint.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -29,6 +29,7 @@ import java.io.ObjectOutput; import java.nio.ByteBuffer; +import com.bigdata.bop.solutions.SolutionSetStream; import com.bigdata.htree.HTree; import com.bigdata.io.SerializerUtil; import com.bigdata.journal.AbstractJournal; @@ -489,12 +490,12 @@ * is defined but the bloom filter has been disabled, then we * also write a 0L so that the bloom filter is no longer * reachable from the new checkpoint. + * + * FIXME GIST : The SolutionSetStats are hacked into the + * bloom filter addr for the Stream. */ -// (htree.bloomFilter == null ? htree.getCheckpoint() -// .getBloomFilterAddr() -// : htree.bloomFilter.isEnabled() ? htree.bloomFilter -// .getAddr() : 0L),// - 0L, // TODO No bloom filter yet. Do we want to support this? + ((SolutionSetStream)stream).getStatsAddr(),// + // 0, // htree.height,// Note: HTree is not balanced (height not uniform) 0L,//stream.getNodeCount(),// 0L,//stream.getLeafCount(),// @@ -831,6 +832,9 @@ case HTree: ndx = HTree.load(store, checkpointAddr, readOnly); break; + case Stream: + ndx = Stream.load(store, checkpointAddr, readOnly); + break; default: throw new AssertionError("Unknown: " + checkpoint.getIndexType()); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -3776,7 +3776,7 @@ } // Resolve the index against that commit record. - ndx = (BTree) getIndexWithCommitRecord(name, commitRecord); + ndx = (ICheckpointProtocol) getIndexWithCommitRecord(name, commitRecord); if (ndx == null) { Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/DumpJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/DumpJournal.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/DumpJournal.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -30,6 +30,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.PrintStream; import java.nio.ByteBuffer; import java.util.Date; import java.util.Iterator; @@ -71,9 +72,6 @@ * * TODO add an option to dump only as of a specified commitTime? * - * TODO add an option to restrict the names of the indices to be dumped - * (-name=<regex>). - * * TODO GIST : Support all types of indices. * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/585"> GIST @@ -107,22 +105,29 @@ // } /** - * Dump one or more journal files. + * Dump one or more journal files: * - * @param args - * The name(s) of the journal file to open. - * <dl> - * <dt>-history </dt> - * <dd>Dump metadata for indices in all commit records (default - * only dumps the metadata for the indices as of the most current - * committed state).</dd> - * <dt>-indices</dt> - * <dd>Dump the indices (does not show the tuples by default).</dd> - * <dt>-pages</dt> - * <dd>Dump the pages of the indices and reports some information on the page size.</dd> - * <dt>-tuples</dt> - * <dd>Dump the records in the indices.</dd> - * </dl> + * <pre> + * usage: (option*) filename+ + * </pre> + * + * where <i>option</i> is any of: + * <dl> + * <dt>-namespace</dt> + * <dd>Dump only those indices having the specified namespace prefix.</dd> + * <dt>-history</dt> + * <dd>Dump metadata for indices in all commit records (default only dumps + * the metadata for the indices as of the most current committed state).</dd> + * <dt>-indices</dt> + * <dd>Dump the indices (does not show the tuples by default).</dd> + * <dt>-pages</dt> + * <dd>Dump the pages of the indices and reports some information on the + * page size.</dd> + * <dt>-tuples</dt> + * <dd>Dump the records in the indices.</dd> + * </dl> + * + * where <i>filename</i> is one or more journal file names. */ // FIXME feature is not finished. Must differentiate different address types. // * <dt>-addr ADDR</dt> @@ -139,6 +144,10 @@ int i = 0; + // Zero or more namespaces to be dumped. All are dumped if none are + // specified. + final List<String> namespaces = new LinkedList<String>(); + boolean dumpHistory = false; boolean dumpIndices = false; @@ -166,6 +175,12 @@ } + else if(arg.equals("-namespace")) { + + namespaces.add(args[i + 1]); + + } + else if(arg.equals("-indices")) { dumpIndices = true; @@ -196,9 +211,9 @@ throw new RuntimeException("Unknown argument: " + arg); } - - for(; i<args.length; i++) { - + + for (; i < args.length; i++) { + final File file = new File(args[i]); try { @@ -253,8 +268,8 @@ final DumpJournal dumpJournal = new DumpJournal(journal); - dumpJournal.dumpJournal(dumpHistory, dumpPages, - dumpIndices, showTuples); + dumpJournal.dumpJournal(System.out, namespaces, + dumpHistory, dumpPages, dumpIndices, showTuples); for(Long addr : addrs) { @@ -304,6 +319,33 @@ public void dumpJournal(final boolean dumpHistory, final boolean dumpPages, final boolean dumpIndices, final boolean showTuples) { + dumpJournal(System.out, null/* namespaces */, dumpHistory, dumpPages, + dumpIndices, showTuples); + + } + + /** + * @param out + * Where to write the output. + * @param namespaces + * When non-empty and non-<code>null</code>, dump only those + * indices having any of the specified namespaces. + * @param dumpHistory + * Dump metadata for indices in all commit records (default only + * dumps the metadata for the indices as of the most current + * committed state). + * @param dumpPages + * Dump the pages of the indices and reports some information on + * the page size. + * @param dumpIndices + * Dump the indices (does not show the tuples by default). + * @param showTuples + * Dump the records in the indices. + */ + public void dumpJournal(final PrintStream out, final List<String> namespaces, + final boolean dumpHistory, final boolean dumpPages, + final boolean dumpIndices, final boolean showTuples) { + final FileMetadata fmd = journal.getFileMetadata(); if (fmd != null) { @@ -314,8 +356,8 @@ */ // dump the MAGIC and VERSION. - System.out.println("magic=" + Integer.toHexString(fmd.magic)); - System.out.println("version=" + out.println("magic=" + Integer.toHexString(fmd.magic)); + out.println("version=" + Integer.toHexString(fmd.version)); /* @@ -329,7 +371,7 @@ final long bytesAvailable = (fmd.userExtent - fmd.nextOffset); - System.out.println("extent=" + fmd.extent + "(" + fmd.extent + out.println("extent=" + fmd.extent + "(" + fmd.extent / Bytes.megabyte + "M)" + ", userExtent=" + fmd.userExtent + "(" + fmd.userExtent / Bytes.megabyte + "M)" + ", bytesAvailable=" @@ -356,7 +398,7 @@ if (rootBlock0 != null) { - System.out.println(new RootBlockView( + out.println(new RootBlockView( true/* rootBlock0 */, rootBlock0, new ChecksumUtility()).toString()); @@ -371,18 +413,18 @@ if (rootBlock1 != null) { - System.out.println(new RootBlockView( + out.println(new RootBlockView( false/* rootBlock0 */, rootBlock1, new ChecksumUtility()).toString()); } } - // System.out.println(fmd.rootBlock0.toString()); - // System.out.println(fmd.rootBlock1.toString()); + // out.println(fmd.rootBlock0.toString()); + // out.println(fmd.rootBlock1.toString()); // report on which root block is the current root block. - System.out.println("The current root block is #" + out.println("The current root block is #" + (journal.getRootBlockView().isRootBlock0() ? 0 : 1)); } @@ -398,7 +440,7 @@ store.showAllocators(sb); - System.out.println(sb); + out.println(sb); } @@ -407,7 +449,7 @@ final DeleteBlockStats stats = store.checkDeleteBlocks(journal); - System.out.println(stats.toString(store)); + out.println(stats.toString(store)); final Set<Integer> duplicateAddrs = stats .getDuplicateAddresses(); @@ -453,18 +495,18 @@ final CommitRecordIndex commitRecordIndex = journal .getCommitRecordIndex(); - System.out.println("There are " + commitRecordIndex.getEntryCount() + out.println("There are " + commitRecordIndex.getEntryCount() + " commit points."); if (dumpGRS) { - dumpGlobalRowStore(); + dumpGlobalRowStore(out); } if (dumpHistory) { - System.out.println("Historical commit points follow in temporal sequence (first to last):"); + out.println("Historical commit points follow in temporal sequence (first to last):"); // final IKeyBuilder keyBuilder = KeyBuilder.newInstance(Bytes.SIZEOF_LONG); // @@ -488,20 +530,20 @@ while(itr.hasNext()) { - System.out.println("----"); + out.println("----"); final CommitRecordIndex.Entry entry = itr.next().getObject(); - System.out.print("Commit Record: " + entry.commitTime + out.print("Commit Record: " + entry.commitTime + ", addr=" + journal.toString(entry.addr)+", "); final ICommitRecord commitRecord = journal .getCommitRecord(entry.commitTime); - System.out.println(commitRecord.toString()); + out.println(commitRecord.toString()); - dumpNamedIndicesMetadata(commitRecord, dumpPages, - dumpIndices, showTuples); + dumpNamedIndicesMetadata(out, namespaces, commitRecord, + dumpPages, dumpIndices, showTuples); } @@ -513,11 +555,11 @@ final ICommitRecord commitRecord = journal.getCommitRecord(); - System.out.println(commitRecord.toString()); + out.println(commitRecord.toString()); - dumpNamedIndicesMetadata(commitRecord, dumpPages, dumpIndices, - showTuples); - + dumpNamedIndicesMetadata(out, namespaces, commitRecord, + dumpPages, dumpIndices, showTuples); + } } @@ -533,7 +575,7 @@ } - public void dumpGlobalRowStore() { + public void dumpGlobalRowStore(final PrintStream out) { final SparseRowStore grs = journal.getGlobalRowStore(journal .getLastCommitTime()); @@ -546,7 +588,7 @@ final ITPS tps = itr.next(); - System.out.println(tps.toString()); + out.println(tps.toString()); } @@ -562,7 +604,7 @@ final ITPS tps = itr.next(); - System.out.println(tps.toString()); + out.println(tps.toString()); } @@ -576,7 +618,8 @@ * @param journal * @param commitRecord */ - private void dumpNamedIndicesMetadata(final ICommitRecord commitRecord, + private void dumpNamedIndicesMetadata(final PrintStream out, + final List<String> namespaces, final ICommitRecord commitRecord, final boolean dumpPages, final boolean dumpIndices, final boolean showTuples) { @@ -590,8 +633,33 @@ // a registered index. final String name = nitr.next(); + + if (namespaces != null && !namespaces.isEmpty()) { + + boolean found = false; + + for(String namespace : namespaces) { + + if (name.startsWith(namespace)) { + + found = true; + + break; + + } + + } + + if (!found) { + + // Skip this index. Not a desired namespace. + continue; + + } + + } - System.out.println("name=" + name); + out.println("name=" + name); // load index from its checkpoint record. final ICheckpointProtocol ndx; @@ -622,10 +690,10 @@ } // show checkpoint record. - System.out.println("\t" + ndx.getCheckpoint()); + out.println("\t" + ndx.getCheckpoint()); // show metadata record. - System.out.println("\t" + ndx.getIndexMetadata()); + out.println("\t" + ndx.getIndexMetadata()); /* * Collect statistics on the page usage for the index. @@ -642,7 +710,7 @@ final PageStats stats = ((ISimpleTreeIndexAccess) ndx) .dumpPages(); - System.out.println("\t" + stats); + out.println("\t" + stats); pageStats.put(name, stats); @@ -671,7 +739,7 @@ /* * Write out the header. */ - System.out.println(PageStats.getHeaderRow()); + out.println(PageStats.getHeaderRow()); for (Map.Entry<String, PageStats> e : pageStats.entrySet()) { @@ -688,7 +756,7 @@ final ICheckpointProtocol tmp = journal .getIndexWithCommitRecord(name, commitRecord); - System.out.println("name: " + name + ", class=" + out.println("name: " + name + ", class=" + tmp.getClass() + ", checkpoint=" + tmp.getCheckpoint()); @@ -700,7 +768,7 @@ * Write out the stats for this index. */ - System.out.println(stats.getDataRow()); + out.println(stats.getDataRow()); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/stream/Stream.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/stream/Stream.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/stream/Stream.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -229,6 +229,8 @@ // this.counter = new AtomicLong( checkpoint.getCounter() ); this.recordVersion = checkpoint.getRecordVersion(); + + this.rootAddr = checkpoint.getRootAddr(); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/CacheConnectionImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/CacheConnectionImpl.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/CacheConnectionImpl.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -133,7 +133,7 @@ * journal instance, then the commit protocol should be taking care of * things for us. */ - private static final boolean useMainDatabaseForCache = false; + private static final boolean useMainDatabaseForCache = true; private IIndexManager getLocalIndexManager() { Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/SolutionSetCache.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/SolutionSetCache.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/SolutionSetCache.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -332,6 +332,9 @@ timestamp); } + + // Note: Forces all access to be unisolated. +// return (SolutionSetStream) cache.getStore().getUnisolatedIndex(fqn); } @@ -479,8 +482,14 @@ if (sset != null) { - return sset.getStats(); + final ISolutionSetStats stats = sset.getStats(); + if (stats == null) + throw new RuntimeException("No statistics? solutionSet=" + + solutionSet); + + return stats; + } return null; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -1675,7 +1675,11 @@ } else if (xhtml) { switch (queryType) { case ASK: - // Should be all we need to do. + /* + * TODO This is just sending back text/plain. If we want to keep + * to the XHTML semantics, then we should send back XML with an + * XSL style sheet. + */ acceptStr = BooleanQueryResultFormat.TEXT.getDefaultMIMEType(); break; case SELECT: @@ -1690,7 +1694,10 @@ break; case DESCRIBE: case CONSTRUCT: - // Generate RDF/XML so we can apply XSLT transform. + /* Generate RDF/XML so we can apply XSLT transform. + * + * FIXME This should be sending back RDFs or using a lens. + */ acceptStr = RDFFormat.RDFXML.getDefaultMIMEType(); break; default: Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/StatusServlet.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/StatusServlet.java 2012-09-27 12:16:36 UTC (rev 6632) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/StatusServlet.java 2012-09-27 16:27:50 UTC (rev 6633) @@ -24,7 +24,9 @@ import java.io.IOException; import java.io.OutputStreamWriter; +import java.io.PrintStream; import java.io.Writer; +import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; @@ -50,7 +52,10 @@ import com.bigdata.bop.engine.QueryLog; import com.bigdata.bop.fed.QueryEngineFactory; import com.bigdata.counters.CounterSet; +import com.bigdata.journal.AbstractJournal; +import com.bigdata.journal.DumpJournal; import com.bigdata.journal.IIndexManager; +import com.bigdata.journal.Journal; import com.bigdata.rdf.sail.sparql.ast.SimpleNode; import com.bigdata.rdf.sail.webapp.BigdataRDFContext.RunningQuery; import com.bigdata.rdf.sparql.ast.ASTContainer; @@ -85,8 +90,23 @@ * which could be served. */ private static final String SHOW_NAMESPACES = "showNamespaces"; - + /** + * Request a low-level dump of the journal. + * + * @see DumpJournal + */ + private static final String DUMP_JOURNAL = "dumpJournal"; + + /** + * Request a low-level dump of the pages in the indices for the journal. The + * {@link #DUMP_JOURNAL} option MUST also be specified. + * + * @see DumpJournal + */ + private static final String DUMP_PAGES = "dumpPages"; + + /** * The name of a request parameter used to request a display of the * currently running queries. Legal values for this request parameter are * either {@value #DETAILS} or no value. @@ -234,6 +254,10 @@ * <dt>showNamespaces</dt> * <dd>List the namespaces for the registered {@link AbstractTripleStore}s.</dd> * </dl> + * <dt>dumpJournal</dt> + * <dd>Provides low-level information about the backing {@link Journal} (if + * any).</dd> + * </dl> * </p> * * @todo This status page combines information about the addressed KB and @@ -290,8 +314,47 @@ } // open the body - current = current.node("body"); + current = current.node("body",""); + // Dump Journal? + final boolean dumpJournal = req.getParameter(DUMP_JOURNAL) != null; + + if(dumpJournal && getIndexManager() instanceof AbstractJournal) { + + current.node("h1", "Dump Journal").node("p", "Running..."); + +// final XMLBuilder.Node section = current.node("pre"); + // flush writer before writing on PrintStream. + w.flush(); + + // dump onto the response. + final PrintStream out = new PrintStream(resp.getOutputStream()); + + out.print("<pre>\n"); + + final DumpJournal dump = new DumpJournal((Journal) getIndexManager()); + + final List<String> namespaces = Collections.emptyList(); + + final boolean dumpHistory = false; + + final boolean dumpPages = req.getParameter(DUMP_PAGES) != null; + + final boolean dumpIndices = false; + + final boolean dumpTuples = false; + + dump.dumpJournal(out, namespaces, dumpHistory, dumpPages, dumpIndices, dumpTuples); + + // flush PrintStream before resuming writes on Writer. + out.flush(); + + // close section. +// section.close(); + out.print("\n</pre>"); + + } + current.node("br", "Accepted query count=" + getBigdataRDFContext().getQueryIdFactory().get()); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-09-28 20:10:13
|
Revision: 6635 http://bigdata.svn.sourceforge.net/bigdata/?rev=6635&view=rev Author: thompsonbry Date: 2012-09-28 20:10:07 +0000 (Fri, 28 Sep 2012) Log Message: ----------- - Found problem where BigdataSailConnection.rollback() was not being invoked for a SPARQL UPDATE request through the NanoSparqlServer. - Removed _abort() invocation in AbstractJournal.setQuorumToken() when the quorum breaks. Modified abort() to invoke a local _abort() if not HA or if the 2-phase HA abort() fails. - Fixed some bugs in awaitBreak(), awaitQuorum(), and awaitEnoughServicesJoined() where the method would fail to throw a TimeoutException if the timeout expired without the condition being satisfied. @see https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA). Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-09-27 16:36:24 UTC (rev 6634) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-09-28 20:10:07 UTC (rev 6635) @@ -2218,9 +2218,22 @@ if (quorum != null) { - // HA mode - quorum.getClient().abort2Phase(quorumToken); + try { + // HA mode + quorum.getClient().abort2Phase(quorumToken); + + } catch (Throwable t) { + + haLog.error( + "2-Phase abort failure. Will do local abort. cause=" + + t, t); + + // Low-level abort. + _abort(); + + } + } else { // Non-HA mode. @@ -4644,7 +4657,7 @@ */ // local abort (no quorum, so we can do 2-phase abort). - _abort(); +// _abort(); // getLocalTransactionManager(). /* Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java 2012-09-27 16:36:24 UTC (rev 6634) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java 2012-09-28 20:10:07 UTC (rev 6635) @@ -1058,13 +1058,15 @@ try { // remaining = nanos - (now - begin) [aka elapsed] remaining = nanos - (System.nanoTime() - begin); - while (token == NO_QUORUM && client != null) { - if(!quorumChange.await(remaining,TimeUnit.NANOSECONDS)) + while (token == NO_QUORUM && client != null && remaining > 0) { + if (!quorumChange.await(remaining, TimeUnit.NANOSECONDS)) throw new TimeoutException(); remaining = nanos - (System.nanoTime() - begin); } if (client == null) throw new AsynchronousQuorumCloseException(); + if (remaining <= 0) + throw new TimeoutException(); return token; } finally { lock.unlock(); @@ -1097,13 +1099,15 @@ try { // remaining = nanos - (now - begin) [aka elapsed] remaining = nanos - (System.nanoTime() - begin); - while (token != NO_QUORUM && client != null) { + while (token != NO_QUORUM && client != null && remaining > 0) { if (!quorumChange.await(remaining, TimeUnit.NANOSECONDS)) throw new TimeoutException(); remaining = nanos - (System.nanoTime() - begin); } if (client == null) throw new AsynchronousQuorumCloseException(); + if (remaining <= 0) + throw new TimeoutException(); return; } finally { lock.unlock(); @@ -1115,17 +1119,18 @@ * <p> * Note: This is used in certain places to work around concurrent * indeterminism. + * + * @throws TimeoutException */ - private void awaitEnoughJoinedToMeet() throws QuorumException { + private void awaitEnoughJoinedToMeet() throws QuorumException, + TimeoutException { if (!lock.isHeldByCurrentThread()) throw new IllegalMonitorStateException(); try { final long begin = System.nanoTime(); final long nanos = timeout; long remaining = nanos; - while (!isQuorum(joined.size())) { - if (client == null) - throw new AsynchronousQuorumCloseException(); + while (!isQuorum(joined.size()) && client != null && remaining > 0) { if (!joinedChange.await(remaining, TimeUnit.NANOSECONDS)) throw new QuorumException( "Not enough joined services: njoined=" @@ -1134,6 +1139,10 @@ // remaining = nanos - (now - begin) [aka elapsed] remaining = nanos - (System.nanoTime() - begin); } + if (client == null) + throw new AsynchronousQuorumCloseException(); + if (remaining <= 0) + throw new TimeoutException(); return; } catch (InterruptedException e) { // propagate interrupt. @@ -2492,7 +2501,11 @@ + lastValidToken + ", newToken=" + newToken); return; } - awaitEnoughJoinedToMeet(); + try { + awaitEnoughJoinedToMeet(); + } catch (TimeoutException e1) { + throw new QuorumException(e1); + } token = lastValidToken = newToken; quorumChange.signalAll(); if (log.isInfoEnabled()) Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-27 16:36:24 UTC (rev 6634) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-09-28 20:10:07 UTC (rev 6635) @@ -938,9 +938,14 @@ // log.error(t.getLocalizedMessage() + ":\n" + queryStr, t); // } return null; -// } catch (Throwable t) { -// // launder and rethrow the exception. -// throw BigdataRDFServlet.launderThrowable(t, resp, queryStr); + } catch (Throwable t) { + if (cxn != null && !cxn.isReadOnly()) { + /* + * Force rollback of the connection. + */ + cxn.rollback(); + } + throw new Exception(t); } finally { m_queries.remove(queryId); // if (os != null) { @@ -952,6 +957,7 @@ // } if (cxn != null) { try { + // Force close of the connection. cxn.close(); if(log.isTraceEnabled()) log.trace("Connection closed."); @@ -1810,8 +1816,10 @@ } /** - * Return a read-only transaction which will read from the commit point - * associated with the given timestamp. + * Return a connection transaction. When the timestamp is associated with a + * historical commit point, this will be a read-only connection. When it is + * associated with the {@link ITx#UNISOLATED} view or a read-write + * transaction, this will be a mutable connection. * * @param namespace * The namespace. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-02 15:30:58
|
Revision: 6640 http://bigdata.svn.sourceforge.net/bigdata/?rev=6640&view=rev Author: thompsonbry Date: 2012-10-02 15:30:48 +0000 (Tue, 02 Oct 2012) Log Message: ----------- I have implemented a history service. It can be enabled through AbstractTripleStore.Options.HISTORY_SERVICE. There is also an option to prune the head of the history index maintained by that service. It indexes a revision time, the (s,p,o[,c]), and the associated ChangeAction (INSERTED, REMOVED, or UPDATED), and the various metadata bits that are associated with an ISPO in the indices (statement type, and some flags). I have not done an integration with SPARQL yet through the SERVICE keyword. I am not convinced yet whether this facility would be used from SPARQL or from code. We should talk about this. You can use code similar to that found in the HistoryServiceFactory class to access the data in the history index from code. Integrating this into SPARQL is more like making it a feature rather than a demonstration concept / prototype. The revision time for the entries in the history index is currently lastCommitTime+1 (for an unisolated connection). The issue with revision time is that we have to record things in the history index incrementally, so it can not be a commit time because we do not have that yet. lastCommitTime+1 will always be strictly greater than the previous commit point. When you scan the history index, you can then use fromKey=firstCommitTime to visit (or null for the head of the index). toKey=firstCommitTime to be excluded (or null for the tail of the index). That all has semantics that are pretty much what people would expect for the scan. However, the reported revision times are not going to correspond to the commit points. When reporting this data, we could resolve (and cache) the first commit time greater than that commit point, but only if we still have access to that commit point (the resolution would be against the commit record index, and commit points are pruned from that index when they are recycled). The revision time for a full read/write tx is less well defined. We can use the same value (lastCommitTime+1). However, it is a little more ambiguous in this case because you can have concurrent transactions so there could be multiple transactions that wind up with the same revisionTime in the history index. We could also use the actual timestamp when we touched the index for a given ISPO, but there are issues with that as well. For example, on a long running data load we would have a bunch of different revision times for the same commit point and the actual order of those revision times within the index is not material since all changes are associated with the same commit point. - Refactored IChangeRecord.ChangeAction into its own file. - Added transactionBegin() and transactionPrepare() methods to IChangeLog. - Integrated a history index into SPORelation. - Added a HistoryServiceFactory that will index the data reported by the IChangeLog. - Added option to enable the history service to AbstractTripleStore. - Added option to specify the min release time for the history index (defaults to infinite). - Added SPOKeyOrder.appendKey(...) to append the key component for an ISPO without invoking reset() on the IKeyBuilder or extracting and returning the byte[] key. This makes it possible to reuse the SPOKeyOrder for the history index. - Added hashCode() to ChangeRecord (based on the ISPO hashCode). - Wrote unit tests of the history index. - TODO This feature is just a prototype right now. We have to work through the use cases (including the SPARQL SERVICE use cases) and read/write tx support before we can support this. Once supported, document this on wiki (HISTORY_SERVICE, HISTORY_SERVICE_MIN_RELEASE_TIME, how to access from code, how to access from query). - TODO Unit tests of the history index at the SPARQL layer. This requires a SERVICE translation. The <<>> syntax might be a nice way to express access to the statements in the history index. See http://sourceforge.net/apps/trac/bigdata/ticket/607 (History Service) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/AbstractRelation.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/AbstractResource.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/axioms/BaseAxioms.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/ChangeRecord.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/DelegatingChangeLog.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/IChangeLog.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/IChangeRecord.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/InMemChangeLog.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/StatementWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/DescribeServiceFactory.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/ServiceRegistry.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/spo/SPOKeyOrder.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/spo/SPORelation.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/store/AbstractTripleStore.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/BigdataSail.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/TestBigdataSailWithQuads.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/TestBigdataSailWithSids.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/TestBigdataSailWithoutSids.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/TestChangeSets.java Added Paths: ----------- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/ChangeAction.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryChangeRecord.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryIndexTupleSerializer.java branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryServiceFactory.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/test/com/bigdata/rdf/sail/TestHistoryIndex.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/AbstractRelation.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/AbstractRelation.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/AbstractRelation.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -95,23 +95,40 @@ * @param keyOrder * The natural index order. * - * @return The index name. + * @return The fully qualified index name. */ static public <E> String getFQN(final IRelation<E> relation, final IKeyOrder<? extends E> keyOrder) { + + return getFQN(relation, keyOrder.getIndexName()); + + } - /* - * TODO We wind up calling this a lot. intern() might help reduce the - * heap requirements while the returned value is being held, but it is - * not reducing the heap pressure caused by this string concatenation. - * To do that we would have to search a cache using the component - * elements [namespace] and [keyOrder]. - */ - return (relation.getNamespace() + "." + keyOrder.getIndexName()) - .intern(); + /** + * The fully qualified name of the index. + * + * @param relation + * The relation. + * @param localName + * The local name of the index. + * + * @return The fully qualified index name. + */ + static public <E> String getFQN(final IRelation<E> relation, + final String localName) { + /* + * TODO We wind up calling this a lot. intern() might help reduce the + * heap requirements while the returned value is being held, but it is + * not reducing the heap pressure caused by this string concatenation. + * To do that we would have to search a cache using the component + * elements [namespace] and [keyOrder]. + */ + + return (relation.getNamespace() + "." + localName).intern(); + } - + /** * Return the index for the {@link IKeyOrder} the timestamp for this view of * the relation. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/AbstractResource.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/AbstractResource.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/relation/AbstractResource.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -807,7 +807,7 @@ * * @see Configuration */ - protected String getProperty(final String localName, + public String getProperty(final String localName, final String defaultValue) { return Configuration.getProperty(indexManager, properties, namespace, @@ -824,7 +824,7 @@ * The default value. * @return */ - protected <T> T getProperty(final String name, final String defaultValue, + public <T> T getProperty(final String name, final String defaultValue, final IValidator<T> validator) { return Configuration.getProperty(indexManager, properties, namespace, Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/axioms/BaseAxioms.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/axioms/BaseAxioms.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/axioms/BaseAxioms.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -487,7 +487,7 @@ for (SPO spo : axioms) { final byte[] key = SPOKeyOrder.SPO.encodeKey(keyBuilder, spo); - + if (true) { final IV[] ivs = IVUtility.decodeAll(key); Added: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/ChangeAction.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/ChangeAction.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/ChangeAction.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -0,0 +1,79 @@ +package com.bigdata.rdf.changesets; + +import com.bigdata.rdf.model.StatementEnum; + +/** + * Attempting to add or remove statements can have a number of different + * effects. This enum captures the different actions that can take place as + * a result of trying to add or remove a statement from the database. + */ + public enum ChangeAction { + + /** + * The focus statement was not in the database before and will be + * in the database after the commit. This can be the result of either + * explicit addStatement() operations on the SAIL connection, or from + * new inferences being generated via truth maintenance when the + * database has inference enabled. If the focus statement has a + * statement type of explicit then it was added via an addStatement() + * operation. If the focus statement has a statement type of inferred + * then it was added via truth maintenance. + */ + INSERTED, + + /** + * The focus statement was in the database before and will not + * be in the database after the commit. When the database has inference + * and truth maintenance enabled, the statement that is the focus of + * this change record was either an explicit statement that was the + * subject of a removeStatements() operation on the connection, or it + * was an inferred statement that was removed as a result of truth + * maintenance. Either way, the statement is no longer provable as an + * inference using other statements still in the database after the + * commit. If it were still provable, the explicit statement would have + * had its type changed to inferred, and the inferred statement would + * have remained untouched by truth maintenance. If an inferred + * statement was the subject of a removeStatement() operation on the + * connection it would have resulted in a no-op, since inferences can + * only be removed via truth maintenance. + */ + REMOVED, + + /** + * This change action can only occur when inference and truth + * maintenance are enabled on the database. Sometimes an attempt at + * statement addition or removal via an addStatement() or + * removeStatements() operation on the connection will result in a type + * change rather than an actual assertion or deletion. When in + * inference mode, statements can have one of three statement types: + * explicit, inferred, or axiom (see {@link StatementEnum}). There are + * several reasons why a statement will change type rather than be + * asserted or deleted: + * <p> + * <ul> + * <li> A statement is asserted, but already exists in the database as + * an inference or an axiom. The existing statement will have its type + * changed from inference or axiom to explicit. </li> + * <li> An explicit statement is retracted, but is still provable by + * other means. It will have its type changed from explicit to + * inference. </li> + * <li> An explicit statement is retracted, but is one of the axioms + * needed for inference. It will have its type changed from explicit to + * axiom. </li> + * </ul> + */ + UPDATED, + +// /** +// * This change action can occur for one of two reasons: +// * <p> +// * <ul> +// * <li> A statement is asserted, but already exists in the database as +// * an explicit statement. </li> +// * <li> An inferred statement or an axiom is retracted. Only explicit +// * statements can be retracted via removeStatements() operations. </li> +// * </ul> +// */ +// NO_OP + + } \ No newline at end of file Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/ChangeRecord.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/ChangeRecord.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/ChangeRecord.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -1,3 +1,25 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.rdf.changesets; import java.util.Comparator; @@ -10,20 +32,10 @@ private final ChangeAction action; -// private final StatementEnum oldType; - public ChangeRecord(final ISPO stmt, final ChangeAction action) { -// this(stmt, action, null); -// -// } -// -// public ChangeRecord(final BigdataStatement stmt, final ChangeAction action, -// final StatementEnum oldType) { -// this.stmt = stmt; this.action = action; -// this.oldType = oldType; } @@ -33,12 +45,6 @@ } -// public StatementEnum getOldStatementType() { -// -// return oldType; -// -// } - public ISPO getStatement() { return stmt; @@ -46,8 +52,15 @@ } @Override - public boolean equals(Object o) { + public int hashCode() { + + return stmt.hashCode(); + } + + @Override + public boolean equals(final Object o) { + if (o == this) return true; @@ -73,14 +86,17 @@ public String toString() { - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); sb.append(action).append(": ").append(stmt); return sb.toString(); } - + + /** + * Comparator imposes an {@link ISPO} order. + */ public static final Comparator<IChangeRecord> COMPARATOR = new Comparator<IChangeRecord>() { Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/DelegatingChangeLog.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/DelegatingChangeLog.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/DelegatingChangeLog.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -1,7 +1,28 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.rdf.changesets; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import org.apache.log4j.Logger; @@ -10,72 +31,95 @@ * delegates through a listener pattern. * * @author mike - * */ public class DelegatingChangeLog implements IChangeLog { - protected static final Logger log = Logger.getLogger(DelegatingChangeLog.class); + private static transient final Logger log = Logger + .getLogger(DelegatingChangeLog.class); - private Set<IChangeLog> delegates; - + private final CopyOnWriteArraySet<IChangeLog> delegates; + public DelegatingChangeLog() { - this.delegates = new LinkedHashSet<IChangeLog>(); + + this.delegates = new CopyOnWriteArraySet<IChangeLog>(); + } - - public synchronized void addDelegate(final IChangeLog delegate) { - this.delegates.add(delegate); + + public void addDelegate(final IChangeLog delegate) { + + this.delegates.add(delegate); + } - - public synchronized void removeDelegate(final IChangeLog delegate) { - this.delegates.remove(delegate); + + public void removeDelegate(final IChangeLog delegate) { + + this.delegates.remove(delegate); + } - - /** - * See {@link IChangeLog#changeEvent(IChangeRecord)}. - */ - public synchronized void changeEvent(final IChangeRecord record) { - - if (log.isInfoEnabled()) + + public void changeEvent(final IChangeRecord record) { + + if (log.isInfoEnabled()) log.info(record); - + for (IChangeLog delegate : delegates) { delegate.changeEvent(record); - + } - + } + + public void transactionBegin() { + + if (log.isInfoEnabled()) + log.info(""); + + for (IChangeLog delegate : delegates) { + + delegate.transactionBegin(); + + } + + } - /** - * See {@link IChangeLog#transactionCommited()}. - */ - public synchronized void transactionCommited(final long commitTime) { + public void transactionPrepare() { + + if (log.isInfoEnabled()) + log.info(""); + + for (IChangeLog delegate : delegates) { + + delegate.transactionPrepare(); + + } + + } - if (log.isInfoEnabled()) + public void transactionCommited(final long commitTime) { + + if (log.isInfoEnabled()) log.info("transaction committed"); - + for (IChangeLog delegate : delegates) { - + delegate.transactionCommited(commitTime); - + } - + } - - /** - * See {@link IChangeLog#transactionAborted()}. - */ - public synchronized void transactionAborted() { - if (log.isInfoEnabled()) + public void transactionAborted() { + + if (log.isInfoEnabled()) log.info("transaction aborted"); - + for (IChangeLog delegate : delegates) { delegate.transactionAborted(); - + } - + } - + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/IChangeLog.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/IChangeLog.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/IChangeLog.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -1,3 +1,25 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.rdf.changesets; /** @@ -24,8 +46,20 @@ * the {@link IChangeRecord} */ void changeEvent(final IChangeRecord record); + + /** + * Message issued when a new transaction will begin. + */ + void transactionBegin(); /** + * Message issued when preparing for a commit. The next message will be + * either {@link #transactionCommited(long)} or + * {@link #transactionAborted()}. + */ + void transactionPrepare(); + + /** * Occurs when the current SAIL transaction is committed. * * @param commitTime Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/IChangeRecord.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/IChangeRecord.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/IChangeRecord.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -1,7 +1,27 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.rdf.changesets; -import com.bigdata.rdf.model.BigdataStatement; -import com.bigdata.rdf.model.StatementEnum; import com.bigdata.rdf.spo.ISPO; /** @@ -16,82 +36,6 @@ public interface IChangeRecord { /** - * Attempting to add or remove statements can have a number of different - * effects. This enum captures the different actions that can take place as - * a result of trying to add or remove a statement from the database. - */ - public enum ChangeAction { - - /** - * The focus statement was not in the database before and will be - * in the database after the commit. This can be the result of either - * explicit addStatement() operations on the SAIL connection, or from - * new inferences being generated via truth maintenance when the - * database has inference enabled. If the focus statement has a - * statement type of explicit then it was added via an addStatement() - * operation. If the focus statement has a statement type of inferred - * then it was added via truth maintenance. - */ - INSERTED, - - /** - * The focus statement was in the database before and will not - * be in the database after the commit. When the database has inference - * and truth maintenance enabled, the statement that is the focus of - * this change record was either an explicit statement that was the - * subject of a removeStatements() operation on the connection, or it - * was an inferred statement that was removed as a result of truth - * maintenance. Either way, the statement is no longer provable as an - * inference using other statements still in the database after the - * commit. If it were still provable, the explicit statement would have - * had its type changed to inferred, and the inferred statement would - * have remained untouched by truth maintenance. If an inferred - * statement was the subject of a removeStatement() operation on the - * connection it would have resulted in a no-op, since inferences can - * only be removed via truth maintenance. - */ - REMOVED, - - /** - * This change action can only occur when inference and truth - * maintenance are enabled on the database. Sometimes an attempt at - * statement addition or removal via an addStatement() or - * removeStatements() operation on the connection will result in a type - * change rather than an actual assertion or deletion. When in - * inference mode, statements can have one of three statement types: - * explicit, inferred, or axiom (see {@link StatementEnum}). There are - * several reasons why a statement will change type rather than be - * asserted or deleted: - * <p> - * <ul> - * <li> A statement is asserted, but already exists in the database as - * an inference or an axiom. The existing statement will have its type - * changed from inference or axiom to explicit. </li> - * <li> An explicit statement is retracted, but is still provable by - * other means. It will have its type changed from explicit to - * inference. </li> - * <li> An explicit statement is retracted, but is one of the axioms - * needed for inference. It will have its type changed from explicit to - * axiom. </li> - * </ul> - */ - UPDATED, - -// /** -// * This change action can occur for one of two reasons: -// * <p> -// * <ul> -// * <li> A statement is asserted, but already exists in the database as -// * an explicit statement. </li> -// * <li> An inferred statement or an axiom is retracted. Only explicit -// * statements can be retracted via removeStatements() operations. </li> -// * </ul> -// */ -// NO_OP - - } - - /** * Return the ISPO that is the focus of this change record. * * @return Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/InMemChangeLog.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/InMemChangeLog.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/InMemChangeLog.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -1,3 +1,25 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.rdf.changesets; import java.util.Collection; @@ -52,6 +74,16 @@ } + @Override + public void transactionBegin() { + + } + + @Override + public void transactionPrepare() { + + } + /** * See {@link IChangeLog#transactionCommited(long)}. */ @@ -156,7 +188,5 @@ return resolved; } - - - + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/StatementWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/StatementWriter.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/changesets/StatementWriter.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -1,3 +1,25 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.rdf.changesets; import java.util.Iterator; @@ -4,7 +26,6 @@ import org.apache.log4j.Logger; -import com.bigdata.rdf.changesets.IChangeRecord.ChangeAction; import com.bigdata.rdf.spo.ISPO; import com.bigdata.rdf.spo.ModifiedEnum; import com.bigdata.rdf.spo.SPO; @@ -15,7 +36,7 @@ public class StatementWriter { - protected static final Logger log = Logger.getLogger(StatementWriter.class); +// private static final Logger log = Logger.getLogger(StatementWriter.class); public static long addStatements(final AbstractTripleStore database, final AbstractTripleStore statementStore, Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/DescribeServiceFactory.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/DescribeServiceFactory.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/cache/DescribeServiceFactory.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -5,9 +5,9 @@ import org.apache.log4j.Logger; +import com.bigdata.rdf.changesets.ChangeAction; import com.bigdata.rdf.changesets.IChangeLog; import com.bigdata.rdf.changesets.IChangeRecord; -import com.bigdata.rdf.changesets.IChangeRecord.ChangeAction; import com.bigdata.rdf.internal.IV; import com.bigdata.rdf.sail.BigdataSail.BigdataSailConnection; import com.bigdata.rdf.sparql.ast.service.BigdataNativeServiceOptions; @@ -196,13 +196,23 @@ } @Override - public void transactionCommited(long commitTime) { + public void transactionBegin() { + } + + @Override + public void transactionPrepare() { + flush(); } @Override + public void transactionCommited(final long commitTime) { + + } + + @Override public void transactionAborted() { reset(); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/ServiceRegistry.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/ServiceRegistry.java 2012-10-02 15:28:16 UTC (rev 6639) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/ServiceRegistry.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -10,10 +10,11 @@ import org.openrdf.model.URI; import org.openrdf.model.impl.URIImpl; -import com.bigdata.rdf.sparql.ast.eval.SearchInSearchServiceFactory; import com.bigdata.rdf.sparql.ast.QueryHints; import com.bigdata.rdf.sparql.ast.cache.DescribeServiceFactory; +import com.bigdata.rdf.sparql.ast.eval.SearchInSearchServiceFactory; import com.bigdata.rdf.sparql.ast.eval.SearchServiceFactory; +import com.bigdata.rdf.sparql.ast.service.history.HistoryServiceFactory; import com.bigdata.rdf.store.AbstractTripleStore; import com.bigdata.rdf.store.BD; @@ -85,6 +86,19 @@ } + if (true) { + + /** + * @see <a + * href="https://sourceforge.net/apps/trac/bigdata/ticket/607"> + * HISTORY SERVICE </a> + */ + + add(new URIImpl(BD.NAMESPACE + "history"), + new HistoryServiceFactory()); + + } + } /** Added: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryChangeRecord.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryChangeRecord.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryChangeRecord.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -0,0 +1,114 @@ +/* + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +/* + * Created on Oct 1, 2012 + */ +package com.bigdata.rdf.sparql.ast.service.history; + +import com.bigdata.rdf.changesets.ChangeAction; +import com.bigdata.rdf.changesets.ChangeRecord; +import com.bigdata.rdf.changesets.IChangeRecord; +import com.bigdata.rdf.spo.ISPO; + +/** + * Extended to include a revision time for each record. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ +public class HistoryChangeRecord extends ChangeRecord { + + private final long revisionTime; + + public HistoryChangeRecord(final ISPO stmt, final ChangeAction action, + final long revisionTime) { + + super(stmt, action); + + this.revisionTime = revisionTime; + + } + + public HistoryChangeRecord(final IChangeRecord changeRecord, + final long revisionTime) { + + this(changeRecord.getStatement(), changeRecord.getChangeAction(), + revisionTime); + + } + + /** + * The revision time is <code>lastCommitTime+1</code>. + * + * TODO The revision time for the entries in the history index is currently + * <code>lastCommitTime+1</code>, but this needs to be thought through some + * more and various use cases considered. + * <p> + * The issue with revision time is that we have to record things in the + * history index incrementally, so it can not be a commit time because we do + * not have that yet. lastCommitTime+1 will always be strictly greater than + * the previous commit point. When you scan the history index, you can then + * use fromKey=firstCommitTime to visit (or null for the head of the index). + * toKey=firstCommitTime to be excluded (or null for the tail of the index). + * That all has semantics that are pretty much what people would expect for + * the scan. However, the reported revision times are not going to + * correspond directly to the commit points. + * <p> + * When reporting this data, we could resolve (and cache) the first commit + * time greater than that commit point, but only if we still have access to + * that commit point (the resolution would be against the commit record + * index, and commit points are pruned from that index when they are + * recycled). + */ + public long getRevisionTime() { + + return revisionTime; + + } + + public String toString() { + + return "revisionTime=" + revisionTime + " : " + super.toString(); + + } + + @Override + public boolean equals(final Object o) { + + if (o == this) + return true; + + if (o == null || o instanceof HistoryChangeRecord == false) + return false; + + final HistoryChangeRecord rec = (HistoryChangeRecord) o; + + if(revisionTime != rec.getRevisionTime()) + return false; + + return super.equals(o); + + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryIndexTupleSerializer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryIndexTupleSerializer.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryIndexTupleSerializer.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -0,0 +1,298 @@ +/* + +Copyright (C) SYSTAP, LLC 2006-2008. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +/* + * Created on Jun 23, 2008 + */ + +package com.bigdata.rdf.sparql.ast.service.history; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import com.bigdata.btree.DefaultTupleSerializer; +import com.bigdata.btree.IRangeQuery; +import com.bigdata.btree.ITuple; +import com.bigdata.btree.ITupleSerializer; +import com.bigdata.btree.keys.ASCIIKeyBuilderFactory; +import com.bigdata.btree.keys.IKeyBuilder; +import com.bigdata.btree.keys.KeyBuilder; +import com.bigdata.btree.raba.codec.IRabaCoder; +import com.bigdata.rawstore.Bytes; +import com.bigdata.rdf.changesets.ChangeAction; +import com.bigdata.rdf.changesets.IChangeRecord; +import com.bigdata.rdf.model.StatementEnum; +import com.bigdata.rdf.spo.ISPO; +import com.bigdata.rdf.spo.SPO; +import com.bigdata.rdf.spo.SPOKeyOrder; + +/** + * (De-)serializes {@link IChangeRecord}s for the history index. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/607"> History + * Service</a> + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + * @version $Id: SPOTupleSerializer.java 5281 2011-10-03 14:00:39Z thompsonbry $ + */ +public class HistoryIndexTupleSerializer extends + DefaultTupleSerializer<HistoryChangeRecord, HistoryChangeRecord> { + + private static final long serialVersionUID = -1L; + + /** + * The natural order for the {@link ISPO} data in the index (this is not a + * total key order since the key has a revision timestamp prefix). + */ + private SPOKeyOrder keyOrder; + + /** + * If true, explicit SPOs decoded from index tuples will have a sid + * attached. + */ + private boolean sids; + + /** + * De-serialization constructor. + */ + public HistoryIndexTupleSerializer() { + + } + + /** + * Create an {@link ITupleSerializer} for the indicated access path. + * + * @param keyOrder + * The key order for the (s,p,o[,c]) component of the key. + */ + public HistoryIndexTupleSerializer(final SPOKeyOrder keyOrder, final boolean sids) { + + this(keyOrder, sids, getDefaultLeafKeysCoder(), getDefaultValuesCoder()); + + } + + /** + * Create an {@link ITupleSerializer} for the indicated access path. + * + * @param keyOrder + * The access path. + * @param sids + * If true, attach sids to decoded SPOs where appropriate. + * @param leafKeySer + * @param leafValSer + */ + public HistoryIndexTupleSerializer(final SPOKeyOrder keyOrder, + final boolean sids, final IRabaCoder leafKeySer, + final IRabaCoder leafValSer) { + + super(new ASCIIKeyBuilderFactory(), leafKeySer, leafValSer); + + this.keyOrder = keyOrder; + + this.sids = sids; + + } + + @Override + public byte[] serializeKey(final Object obj) { + + if (obj == null) + throw new IllegalArgumentException(); + + if (obj instanceof HistoryChangeRecord) + return serializeKey((HistoryChangeRecord) obj); + + throw new UnsupportedOperationException(); + + } + + public byte[] serializeKey(final HistoryChangeRecord changeRecord) { + + final IKeyBuilder keyBuilder = getKeyBuilder().reset(); + + // append the revision time. + keyBuilder.append(changeRecord.getRevisionTime()); + + // append the statement (encoded IVs). + keyOrder.appendKey(keyBuilder, changeRecord.getStatement()); + + final byte[] key = keyBuilder.getKey(); + + return key; + + } + + @Override + public byte[] serializeVal(final HistoryChangeRecord changeRecord) { + + if (changeRecord == null) + throw new IllegalArgumentException(); + + final ISPO spo = changeRecord.getStatement(); + + final boolean override = spo.isOverride(); + + final boolean userFlag = spo.getUserFlag(); + + final StatementEnum type = spo.getStatementType(); + + // optionally set the override and user flag bits on the value. + final byte lowNibble = (byte) // + (type.code()// statement type + | (override ? StatementEnum.MASK_OVERRIDE : 0x0) // + | (userFlag ? StatementEnum.MASK_USER_FLAG : 0x0)// + ); + + final byte highNibble = (byte) changeRecord.getChangeAction().ordinal(); + + final byte b = (byte) (0xff & (highNibble << 4 | lowNibble)); + + return new byte[] { b }; + + } + + public HistoryChangeRecord deserializeKey(final ITuple tuple) { + + // just de-serialize the whole tuple. + return deserialize(tuple); + + } + + public HistoryChangeRecord deserialize(final ITuple tuple) { + + if (tuple == null) + throw new IllegalArgumentException(); + + // copy of the key in a reused buffer. + final byte[] key = tuple.getKeyBuffer().array(); + + // Decode the revision timestamp. + final long revisionTimestamp = KeyBuilder.decodeLong(key, 0/* off */); + + // Decode statement, starting after the revision timestamp. + final SPO spo = keyOrder.decodeKey(key, Bytes.SIZEOF_LONG/* off */); + + final ChangeAction changeAction; + if ((tuple.flags() & IRangeQuery.VALS) != 0) { + + /* + * Decode the statement type, bit flags, and optional sid based on + * the tuple value. + */ + + final byte b = tuple.getValueBuffer().array()[0]; + + // Just the low nibble. + final byte lowNibble = (byte) (0xff & (b & 0x0f)); + + // Just the high nibble. + final byte highNibble = (byte) (0xff & (b >> 4)); + + { + + final byte code = lowNibble; + + final StatementEnum type = StatementEnum.decode(code); + + spo.setStatementType(type); + + spo.setOverride(StatementEnum.isOverride(code)); + + spo.setUserFlag(StatementEnum.isUserFlag(code)); + + if (sids) { + + // SIDs only valid for triples. + assert keyOrder.getKeyArity() == 3; + + if (spo.isExplicit()) { + + spo.setStatementIdentifier(true); + + } + + } + + } + + changeAction = ChangeAction.values()[highNibble]; + + } else { + + // Not available unless VALS are read. + changeAction = null; + + } + + final HistoryChangeRecord changeRecord = new HistoryChangeRecord(spo, + changeAction, revisionTimestamp); + + return changeRecord; + + } + + /** + * The initial version. + */ + private final static transient byte VERSION0 = 0; + + /** + * The current version. + */ + private final static transient byte VERSION = VERSION0; + + public void readExternal(final ObjectInput in) throws IOException, + ClassNotFoundException { + + super.readExternal(in); + + final byte version = in.readByte(); + + switch (version) { + case VERSION0: + keyOrder = SPOKeyOrder.valueOf(in.readByte()); + sids = in.readByte() > 0; + break; + default: + throw new UnsupportedOperationException("Unknown version: " + + version); + } + + + } + + public void writeExternal(final ObjectOutput out) throws IOException { + + super.writeExternal(out); + + out.writeByte(VERSION); + + out.writeByte(keyOrder.index()); + + out.writeByte(sids ? 1 : 0); + + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryServiceFactory.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryServiceFactory.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-rdf/src/java/com/bigdata/rdf/sparql/ast/service/history/HistoryServiceFactory.java 2012-10-02 15:30:48 UTC (rev 6640) @@ -0,0 +1,425 @@ +package com.bigdata.rdf.sparql.ast.service.history; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.bigdata.btree.IIndex; +import com.bigdata.btree.IRangeQuery; +import com.bigdata.btree.ITupleIterator; +import com.bigdata.btree.keys.IKeyBuilder; +import com.bigdata.btree.keys.KVO; +import com.bigdata.journal.IIndexManager; +import com.bigdata.rdf.changesets.ChangeAction; +import com.bigdata.rdf.changesets.IChangeLog; +import com.bigdata.rdf.changesets.IChangeRecord; +import com.bigdata.rdf.internal.IV; +import com.bigdata.rdf.sail.BigdataSail; +import com.bigdata.rdf.sail.BigdataSail.BigdataSailConnection; +import com.bigdata.rdf.sparql.ast.service.BigdataNativeServiceOptions; +import com.bigdata.rdf.sparql.ast.service.CustomServiceFactory; +import com.bigdata.rdf.sparql.ast.service.IServiceOptions; +import com.bigdata.rdf.sparql.ast.service.ServiceCall; +import com.bigdata.rdf.sparql.ast.service.ServiceCallCreateParams; +import com.bigdata.rdf.spo.ISPO; +import com.bigdata.rdf.spo.SPORelation; +import com.bigdata.rdf.store.AbstractTripleStore; +import com.bigdata.relation.AbstractRelation; + +/** + * This service tracks KB updates via an {@link IChangeLog} and is responsible + * for maintaining an ordered index over the assertions that have been added to + * or removed from a KB instance. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/607"> History + * Service</a> + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ +public class HistoryServiceFactory implements CustomServiceFactory { + + static private transient final Logger log = Logger + .getLogger(HistoryServiceFactory.class); + + private final BigdataNativeServiceOptions serviceOptions; + + public HistoryServiceFactory() { + + serviceOptions = new BigdataNativeServiceOptions(); + + /* + * TODO Review decision to make this a runFirst service. The rational is + * that this service can only apply a very limited set of restrictions + * during query, therefore it will often make sense to run it first. + * However, the fromTime and toTime could be bound by the query and the + * service can filter some things more efficiently internally than if we + * generated a bunch of intermediate solutions for those things. + */ + serviceOptions.setRunFirst(true); + + } + + @Override + public IServiceOptions getServiceOptions() { + + return serviceOptions; + + } + + /** + * TODO Implement: Query should support an index scan of a date range with + * optional filters on the (s,p,o,c) and add/remove flags. It might make + * more sense to index in (POS) order rather than SPO order so we can more + * efficiently scan a specific predicate within some date range using an + * advancer pattern. + * <p> + * The restrictions that this service COULD apply to the index scan are: + * <dl> + * <dt>fromTime</dt> + * <dd>Inclusive lower bound.</dd> + * <dt>toTime</dt> + * <dd>Exclusive upper bound (e.g., the first commit point NOT to be + * reported).</dd> + * <dt>P</dt> + * <dd>The {@link IV} for the predicate (this is the first statement key + * component in the history index for both triples and quads mode KBs)</dd> + * </dl> + * In addition, it could filter on the remaining fields (that is, skip over + * tuples that fail a filter): + * <dl> + * <dt>S, O [, C]</dt> + * <dd>The {@link IV} for the subject, object, and (for quads mode, the + * context).</dd> + * <dt>action</dt> + * <dd>The {@link ChangeAction}.</dd> + * <dt>type</dt> + * <dd>The {@link StatementTypeEnum}.</dd> + * </dl> + */ + @Override + public ServiceCall<?> create(final ServiceCallCreateParams params) { + + throw new UnsupportedOperationException(); + + } + + /** + * Register an {@link IChangeLog} listener that will manage the maintenance + * of the describe cache. + */ + @Override + public void startConnection(final BigdataSailConnection conn) { + + final AbstractTripleStore tripleStore = conn.getTripleStore(); + + if (Boolean.valueOf(tripleStore.getProperty( + BigdataSail.Options.HISTORY_SERVICE, + BigdataSail.Options.DEFAULT_HISTORY_SERVICE))) { + + conn.addChangeLog(new HistoryChangeLogListener(conn)); + + } + + } + + /** + * Handles maintenance of the history index. + * + * @author <a href="mailto:tho...@us...">Bryan + * Thompson</a> + */ + static private class HistoryChangeLogListener implements IChangeLog { + + /** The vector size for updates. */ + private static final int threshold = 10000; + /** The connection. */ + private final BigdataSailConnection conn; + /** The KB instance. */ + private final AbstractTripleStore tripleStore; + /** + * The head of the index is pruned on update to remove entries that are + * older than this age (milliseconds). + */ + private final long minReleaseAge; + /** + * The first timestamp that WILL NOT be released. + */ + private final long releaseTime; + /** + * The timestamp that will be associated with the {@link IChangeLog} + * events in the index. + */ + private volatile long revisionTimestamp; + /** The set of IVs to be invalidated (lazily instantiated). */ + private Map<ISPO, IChangeRecord> changeSet; + /** The history index. */ + private IIndex ndx = null; + + HistoryChangeLogListener(final BigdataSailConnection conn) { + + this.conn = conn; + + this.tripleStore = conn.getTripleStore(); + + this.revisionTimestamp = getRevisionTimestamp(tripleStore); + + this.minReleaseAge = Long + .valueOf(tripleStore + .getProperty( + BigdataSail.Options.HISTORY_SERVICE_MIN_RELEASE_AGE, + BigdataSail.Options.DEFAULT_HISTORY_SERVICE_MIN_RELEASE_AGE)); + + /* + * TODO We should be able to reach the timestamp service from the + * index manager. We want to use the globally agreed on clock for + * the current time when making the decision to prune the head of + * the index. + */ + + releaseTime = (System.currentTimeMillis() - minReleaseAge) + 1; + + if (log.isInfoEnabled()) { + log.info("minReleaseAge=" + minReleaseAge + ", releaseTime=" + + releaseTime); + } + + } + + /** + * Return the revision time that will be used for all changes written + * onto the history index by this {@link IChangeLog} listener. + * + * @see HistoryChangeRecord#getRevisionTime() + */ + static private long getRevisionTimestamp( + final AbstractTripleStore tripleStore) { + + final long revisionTimestamp; + + final IIndexManager indexManager = tripleStore.getIndexManager(); + + revisionTimestamp = indexManager.getLastCommitTime() + 1; + +// if (indexManager instanceof IJournal) { +// +// revisionTimestamp = indexManager.getLastCommitTime() + 1; +//// ((IJournal) indexManager) +//// .getLocalTransactionManager().nextTimestamp(); +// +// } else if (indexManager instanceof IBigdataFederation) { +// +// try { +// +// revisionTimestamp = ((IBigdataFederation<?>) indexManager) +// .getTransactionService().nextTimestamp(); +// +// } catch (IOException e) { +// +// throw new RuntimeException(e); +// +// } +// +// } else { +// +// throw new AssertionError("indexManager=" +// + indexManager.getClass()); +// +// } + + return revisionTimestamp; + } + + @Override + public void transactionBegin() { + + this.revisionTimestamp = getRevisionTimestamp(tripleStore); + + } + + @Override + public void transactionPrepare() { + + flush(); + + } + + /** + * Vectors updates against the DESCRIBE cache. + */ + @Override + public void changeEvent(final IChangeRecord record) { + + if (changeSet == null) { + + // Lazy instantiation. + changeSet = new HashMap<ISPO, IChangeRecord>(); + + // Get the history index. + ndx = getHistoryIndex(tripleStore); + + if (minReleaseAge > 0) { + + pruneHistory(); + + } + + } + + final ISPO spo = record.getStatement(); + + changeSet.put(spo, record); + + if (changeSet.size() > threshold) { + + flush(); + + } + + } + + /** + * Return the pre-existing history index. + * + * @param tripleStore + * The KB. + * @return The history index and never <code>null</code>. + * + * @throws IllegalStateException + * if the index was not configured / does not exist. + */ + private IIndex getHistoryIndex(final AbstractTripleStore tripleStore) { +... [truncated message content] |
From: <tho...@us...> - 2012-10-03 15:13:55
|
Revision: 6643 http://bigdata.svn.sourceforge.net/bigdata/?rev=6643&view=rev Author: thompsonbry Date: 2012-10-03 15:13:47 +0000 (Wed, 03 Oct 2012) Log Message: ----------- Working through resynchronization protocol with Martyn for the HA Journal. - done. HAWriteMessageBase - added a version code (short). Added equals() and hashCode() methods. Added equals() on HAWriteMessage. Added the commit counter, commit time, and write block sequence fields. - done. HAWriteMessage: add commitCounter, lastCommitTime, writeCacheSequence (starting at zero). this is integrated into the RWStore and WORMStrategy. - done. HAWriteMessage: unit test serialization format. This is integrated into CI. - done. HAJournal : added a required configuration option for the HA_LOG_DIR. This is the directory in which the HA write messages, write cache blocks, and root blocks will be logged. There is one such log file per commit point. The log files are deleted at the commit, unless the quorum is not fully met in which case the log files will be used to resynchronize the other quorum members. - done. QuorumServiceBase : added a logWriteCacheBlock() method. This is where we will log the HAWriteMessage and WriteCache blocks. Nobody is calling this method yet. That is the next step. - done. Modified AbstractJournal to NOT take the internal field lock for getLastCommitTime() and getRootBlockView. This was causing deadlocks based on lock ordering problems. - done. Modified QuorumCommitImpl to use getService() to access the leader rather than getLeader(token). The latter returns the RMI interface. The former is the local object. The 2-phase commit protocol was going through RMI on the leader to reach the leader. @see https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/pipeline/HAWriteMessageBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestAll.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/TestAll.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-B.config branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-C.config branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.config branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config Added Paths: ----------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestHAWriteMessage.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -381,10 +381,12 @@ { /* - * Run the operation on the leader using local method call in the - * caller's thread to avoid deadlock. + * Run the operation on the leader using a local method call + * (non-RMI) in the caller's thread to avoid deadlock. */ - final Future<Void> f = member.getLeader(token).abort2Phase(token); + member.assertLeader(token); + final S leader = member.getService(); + final Future<Void> f = leader.abort2Phase(token); remoteFutures.add(f); // // Note: This runs synchronously (ignores timeout). // f.run(); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -31,6 +31,7 @@ import java.nio.ByteBuffer; import java.util.concurrent.Future; +import com.bigdata.io.writecache.WriteCache; import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.Quorum; @@ -97,4 +98,20 @@ // */ // HAReceiveService<HAWriteMessage> getHAReceiveService(); + /** + * Return the lastCommitTime for this service (based on its current root + * block). This supports the {@link HAWriteMessage} which requires this + * information as part of the metadata about replicated {@link WriteCache} + * blocks. + */ + long getLastCommitTime(); + + /** + * Return the lastCommitCounter for this service (based on its current root + * block). This supports the {@link HAWriteMessage} which requires this + * information as part of the metadata about replicated {@link WriteCache} + * blocks. + */ + long getLastCommitCounter(); + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -24,6 +24,7 @@ import com.bigdata.io.DirectBufferPool; import com.bigdata.io.IBufferAccess; import com.bigdata.journal.ha.HAWriteMessage; +import com.bigdata.quorum.QuorumException; import com.bigdata.quorum.QuorumMember; import com.bigdata.quorum.QuorumStateChangeListener; import com.bigdata.quorum.QuorumStateChangeListenerBase; @@ -155,7 +156,7 @@ * follower. */ private IBufferAccess receiveBuffer; - + /** * Cached metadata about the downstream service. */ @@ -790,6 +791,20 @@ lock.lock(); try { + + if (receiveBuffer == null) { + + /* + * The quorum broke and the receive buffer was cleared or + * possibly we have become a leader. + * + * TODO We should probably pass in the Quorum and then just + * assert that the msg.getQuorumToken() is valid for the quorum. + */ + + throw new QuorumException(); + + } final PipelineState<S> downstream = pipelineStateRef.get(); @@ -797,12 +812,12 @@ log.trace("Will receive " + ((downstream != null) ? " and replicate" : "") + ": msg=" + msg); - + final ByteBuffer b = getReceiveBuffer(); final HAReceiveService<HAWriteMessage> receiveService = getHAReceiveService(); - if (downstream == null) { + if (downstream == null) { /* * This is the last service in the write pipeline, so just receive @@ -921,7 +936,6 @@ this.b = b; this.downstream = downstream; this.receiveService = receiveService; - } public Void call() throws Exception { @@ -1048,6 +1062,5 @@ } } - } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -27,6 +27,8 @@ package com.bigdata.ha; +import java.io.File; + import com.bigdata.quorum.Quorum; import com.bigdata.quorum.QuorumMember; @@ -63,5 +65,16 @@ * block). */ long getLastCommitTime(); + + /** + * Return the lastCommitCounter for this service (based on its current root + * block). + */ + long getLastCommitCounter(); + /** + * Return the directory in which we are logging the write blocks. + */ + File getHALogDir(); + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -27,8 +27,12 @@ package com.bigdata.ha; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.ObjectOutputStream; import java.nio.ByteBuffer; +import java.util.Formatter; import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.Future; @@ -37,8 +41,7 @@ import org.apache.log4j.Logger; -import com.bigdata.ha.pipeline.HAReceiveService; -import com.bigdata.ha.pipeline.HASendService; +import com.bigdata.io.writecache.WriteCacheService; import com.bigdata.journal.AbstractJournal; import com.bigdata.journal.IResourceManager; import com.bigdata.journal.IRootBlockView; @@ -102,13 +105,27 @@ addListener(this.pipelineImpl = new QuorumPipelineImpl<S>(this) { @Override - protected void handleReplicatedWrite(HAWriteMessage msg, - ByteBuffer data) throws Exception { + protected void handleReplicatedWrite(final HAWriteMessage msg, + final ByteBuffer data) throws Exception { - QuorumServiceBase.this.handleReplicatedWrite(msg,data); + QuorumServiceBase.this.handleReplicatedWrite(msg, data); + + } + + @Override + public long getLastCommitTime() { + + return QuorumServiceBase.this.getLastCommitTime(); } - + + @Override + public long getLastCommitCounter() { + + return QuorumServiceBase.this.getLastCommitCounter(); + + } + }); addListener(this.commitImpl = new QuorumCommitImpl<S>(this)); @@ -202,7 +219,99 @@ */ abstract protected void handleReplicatedWrite(HAWriteMessage msg, ByteBuffer data) throws Exception; + + /** + * Log the {@link HAWriteMessage} and the associated data onto the + * appropriate log file. + * <p> + * Note: Logging MUST NOT start in the middle of a write set (all log files + * must be complete). The {@link HAWriteMessage#getSequence()} MUST be ZERO + * (0) when we open a log file. + * + * TODO The WORM should not bother to log the write cache block since it can + * be obtained directly from the backing store. Abstract out an object to + * manage the log file for a commit counter and make it smart about the WORM + * versus the RWStore. The same object will need to handle the read back + * from the log file and in the case of the WORM should get the data block + * from the backing file. However, this object is not responsible for reply + * of log files. We'll have to locate a place for that logic next - probably + * in this class (QuorumServiceBase). + * + * FIXME NOTHING IS CALLING THIS CODE YET! Invoke from + * {@link #handleReplicatedWrite(HAWriteMessage, ByteBuffer)} and from + * {@link WriteCacheService}s WriteTask.call() method (on the leader). + */ + public void logWriteCacheBlock(final HAWriteMessage msg, + final ByteBuffer data) throws IOException { +// final long currentCommitCounter = getLastCommitCounter(); + + getQuorum().assertQuorum(msg.getQuorumToken()); + + if (msg.getSequence() == 0L) { + + if (processLog != null) { + processLog.close(); + processLog = null; + } + + /* + * The commit counter that will be used to identify the file. + * + * Note: We use commitCounter+1 so the file will be labeled by the + * commit point that will be achieved when that log file is applied + * to a journal whose current commit point is [commitCounter]. + */ + final long commitCounter = msg.getCommitCounter() + 1; + + /* + * Format the name of the log file. + * + * Note: The commit counter in the file name should be zero filled + * to 20 digits so we have the files in lexical order in the file + * system (for convenience). + */ + final String logFile; + { + + final StringBuilder sb = new StringBuilder(); + + final Formatter f = new Formatter(sb); + + f.format("%020d.log", commitCounter); + + logFile = sb.toString(); + + } + + // Establish new log file. + processLog = new ObjectOutputStream(new FileOutputStream(new File( + getHALogDir(), logFile))); + + } + + /* + * FIXME We need to track whether or not we began the sequence at ZERO + * (0). If we did, then we can open a log and start writing for the + * current commitCounter. We do need to keep track of the commit counter + * associated with the log file so we can correctly refuse to log blocks + * on the log file that are associated with a different commit counter. + * We also need to manage the abort() and commit() transitions, ensuring + * that we truncate() the log for abort() (assuming it is for the same + * commit counter) and that we append the root block, force() and + * close() the log for commit. + */ + + } + + /** + * Process log to which the receiveService should write the messages to and + * <code>null</code> if we may not write on it. + * + * FIXME We need to clear this any time the quorum breaks. + */ + private ObjectOutputStream processLog = null; + /* * QuorumCommit. */ @@ -234,14 +343,30 @@ } @Override - public long getLastCommitTime() { + final public long getLastCommitTime() { final L localService = getLocalService(); - return localService.getLastCommitTime(); + return localService.getRootBlockView().getLastCommitTime(); } + @Override + final public long getLastCommitCounter() { + + final L localService = getLocalService(); + + return localService.getRootBlockView().getCommitCounter(); + + } + + @Override + final public File getHALogDir() { + + return getLocalService().getHALogDir(); + + } + /* * QuorumRead */ Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/pipeline/HAWriteMessageBase.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/pipeline/HAWriteMessageBase.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/pipeline/HAWriteMessageBase.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -29,6 +29,8 @@ import java.io.ObjectInput; import java.io.ObjectOutput; +import com.bigdata.journal.ha.HAWriteMessage; + /** * Base class for RMI messages used to communicate metadata about a raw data * transfer occurring on a socket channel. @@ -60,10 +62,14 @@ * The Alder32 checksum of the bytes to be transfered. */ public HAWriteMessageBase(final int sze, final int chk) { + if (sze <= 0) throw new IllegalArgumentException(); - this.sze = sze; - this.chk = chk; + + this.sze = sze; + + this.chk = chk; + } /** @@ -71,12 +77,18 @@ */ public HAWriteMessageBase() {} + /** The #of bytes of data to be transfered. */ public int getSize() { - return sze; + + return sze; + } - + + /** The Alder32 checksum of the bytes to be transfered. */ public int getChk() { - return chk; + + return chk; + } public String toString() { @@ -85,9 +97,41 @@ } + @Override + public boolean equals(final Object obj) { + + if (this == obj) + return true; + + if (!(obj instanceof HAWriteMessageBase)) + return false; + + final HAWriteMessageBase t = (HAWriteMessageBase) obj; + + return sze == t.getSize() && chk == t.getChk(); + + } + + @Override + public int hashCode() { + + // checksum is a decent hash code if given otherwise the size. + return chk == 0 ? sze : chk; + + } + + private static final transient short VERSION0 = 0x0; + + private static final transient short currentVersion = VERSION0; + public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { + final short version = in.readShort(); + + if (version != VERSION0) + throw new RuntimeException("Bad version for serialization"); + sze = in.readInt(); chk = in.readInt(); @@ -96,6 +140,8 @@ public void writeExternal(final ObjectOutput out) throws IOException { + out.writeShort(currentVersion); + out.writeInt(sze); out.writeInt(chk); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -371,6 +371,22 @@ */ private boolean m_closedForWrites = false; + /** + * The sequence must be set when the cache is ready to be flushed. In HA this + * is sent down the pipeline to ensure correct synchronization when processing + * logged messages. + */ + private long sequence = -1; + + /** + * The sequence must be set when the cache is ready to be flushed. In HA this + * is sent down the pipeline to ensure correct synchronization when processing + * logged messages. + */ + void setSequence(final long i) { + sequence = i; + } + /** * Create a {@link WriteCache} from either a caller supplied buffer or a * direct {@link ByteBuffer} allocated from the {@link DirectBufferPool}. @@ -1327,9 +1343,17 @@ * * @return cache A {@link WriteCache} to be replicated. */ - public HAWriteMessage newHAWriteMessage(final long quorumToken) { + final public HAWriteMessage newHAWriteMessage(// + final long quorumToken, + final long lastCommitCounter,// + final long lastCommitTime// + ) { - return new HAWriteMessage(bytesWritten(), getWholeBufferChecksum(), + return new HAWriteMessage( + lastCommitCounter,// + lastCommitTime,// + sequence, // + bytesWritten(), getWholeBufferChecksum(), prefixWrites ? StoreTypeEnum.RW : StoreTypeEnum.WORM, quorumToken, fileExtent.get(), firstOffset.get()); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -64,8 +64,10 @@ import com.bigdata.io.IReopenChannel; import com.bigdata.io.writecache.WriteCache.WriteCacheCounters; import com.bigdata.journal.AbstractBufferStrategy; +import com.bigdata.journal.IBufferStrategy; import com.bigdata.journal.RWStrategy; import com.bigdata.journal.WORMStrategy; +import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.Quorum; import com.bigdata.quorum.QuorumMember; import com.bigdata.rawstore.Bytes; @@ -485,6 +487,15 @@ localWriteFuture = localWriteService.submit(newWriteTask()); } + + /** + * Called from {@link IBufferStrategy#commit()} and {@link #reset()} to + * reset WriteCache sequence for HA synchronization. + */ + public void resetSequence() { + cacheSequence = 0; + } + private volatile long cacheSequence = 0; protected Callable<Void> newWriteTask() { @@ -556,6 +567,9 @@ /* * Only process non-empty cache buffers. */ + + // increment writeCache sequence + cache.setSequence(cacheSequence++); if (quorum != null && quorum.isHighlyAvailable()) { @@ -589,14 +603,31 @@ // flip(limit=pos;pos=0) b.flip(); assert b.remaining() > 0 : "Empty cache: " + cache; + // send to 1st follower. @SuppressWarnings("unchecked") final QuorumPipeline<HAPipelineGlue> quorumMember = (QuorumPipeline<HAPipelineGlue>) quorum .getMember(); + assert quorumMember != null : "Not quorum member?"; - remoteWriteFuture = quorumMember.replicate( - cache.newHAWriteMessage(quorumToken), b); + + final HAWriteMessage msg = cache.newHAWriteMessage( + quorumToken,// + quorumMember.getLastCommitCounter(),// + quorumMember.getLastCommitTime()// + ); + + /* + * FIXME The quorum leader must log the write cache + * block. However, it must be logged exactly once + * (if there is a retry, we do not want to re-log + * the block!) + */ + + remoteWriteFuture = quorumMember.replicate(msg, b); + counters.get().nsend++; + } /* @@ -614,8 +645,8 @@ if (remoteWriteFuture != null) { try { remoteWriteFuture.get(); - } catch (ExecutionException ex) { - retrySend(quorum, cache, ex); + } catch (ExecutionException ex) { + retrySend(quorum, cache, ex); } } @@ -799,11 +830,19 @@ b.flip(); assert b.remaining() > 0 : "Empty cache: " + cache; + + @SuppressWarnings("unchecked") + final QuorumPipeline<HAPipelineGlue> quorumMember = (QuorumPipeline<HAPipelineGlue>) quorum + .getMember(); + + final HAWriteMessage msg = cache.newHAWriteMessage(// + quorumToken,// + quorumMember.getLastCommitCounter(),// + quorumMember.getLastCommitTime()// + ); // send to 1st follower. - remoteWriteFuture = ((QuorumPipeline<HAPipelineGlue>) quorum - .getMember()).replicate(cache - .newHAWriteMessage(quorumToken), b); + remoteWriteFuture = quorumMember.replicate(msg, b); counters.get().nsend++; @@ -852,7 +891,7 @@ IReopenChannel<? extends Channel> opener, final long fileExtent) throws InterruptedException; - /** + /** * {@inheritDoc} * <p> * This implementation calls {@link IWriteCache#reset()} on all @@ -942,6 +981,9 @@ c.nclean = buffers.length-1; c.nreset++; } + + // reset cacheSequence for HA + cacheSequence = 0; /* * Restart the WriteTask Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -90,6 +90,7 @@ import com.bigdata.io.SerializerUtil; import com.bigdata.journal.Name2Addr.Entry; import com.bigdata.journal.ha.HAWriteMessage; +import com.bigdata.journal.jini.ha.HAJournal; import com.bigdata.mdi.IResourceMetadata; import com.bigdata.mdi.JournalMetadata; import com.bigdata.quorum.Quorum; @@ -1507,8 +1508,22 @@ return tmp.getFile(); + } + + /** + * The HA log directory. + * + * @see HAJournal.Options#HA_LOG_DIR + * + * @throws UnsupportedOperationException + * always. + */ + public File getHALogDir() { + + throw new UnsupportedOperationException(); + } - + /** * Core implementation of immediate shutdown handles event reporting. */ @@ -2100,14 +2115,26 @@ } + /** + * {@inheritDoc} + * <p> + * Returns the current root block (immediate, non-blocking peek). + * <p> + * Note: The root block reference can be <code>null</code> until the journal + * has been initialized. Once it has been set, the root block will always be + * non-<code>null</code>. Since this method does not obtain the inner lock, + * it is possible for another thread to change the root block reference + * through a concurrent {@link #abort()} or {@link #commitNow(long)}. The + * {@link IRootBlockView} itself is an immutable data structure. + */ final public IRootBlockView getRootBlockView() { - final ReadLock lock = _fieldReadWriteLock.readLock(); +// final ReadLock lock = _fieldReadWriteLock.readLock(); +// +// lock.lock(); +// +// try { - lock.lock(); - - try { - if (_rootBlock == null) { /* @@ -2122,30 +2149,30 @@ return _rootBlock; - } finally { +// } finally { +// +// lock.unlock(); +// +// } - lock.unlock(); - - } - } final public long getLastCommitTime() { - final ReadLock lock = _fieldReadWriteLock.readLock(); +// final ReadLock lock = _fieldReadWriteLock.readLock(); +// +// lock.lock(); +// +// try { - lock.lock(); - - try { - return _rootBlock.getLastCommitTime(); - } finally { +// } finally { +// +// lock.unlock(); +// +// } - lock.unlock(); - - } - } /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -1010,6 +1010,7 @@ * IResourceManager */ + @Override public File getTmpDir() { return tmpDir; @@ -1020,6 +1021,7 @@ * The directory in which the journal's file is located -or- * <code>null</code> if the journal is not backed by a file. */ + @Override public File getDataDir() { final File file = getFile(); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -1090,15 +1090,19 @@ * {@inheritDoc} * <p> * This implementation flushes the write cache (if enabled). - * - * @todo Should be a NOP for the WORM? Check - * {@link AbstractJournal#commitNow(long)} */ @Override public void commit() { flushWriteCache(); + if (writeCacheService != null) { + + // Reset the write cache block counter. + writeCacheService.resetSequence(); + + } + } /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -48,7 +48,16 @@ */ private static final long serialVersionUID = -2673171474897401979L; - /** The type of backing store (RW or WORM). */ + /** The most recent commit counter associated with this message */ + private long commitCounter; + + /** The most recent commit time associated with this message */ + private long commitTime; + + /** The write sequence since last commit beginning at zero */ + private long sequence; + + /** The type of backing store (RW or WORM). */ private StoreTypeEnum storeType; /** The quorum token for which this message is valid. */ @@ -60,10 +69,25 @@ /** The file offset at which the data will be written (WORM only). */ private long firstOffset; - // /** The write cache buffer sequence number (incremented for each buffer - // sent by the master). */ - // private long sequenceId; + /** The commit counter associated with this message */ + public long getCommitCounter() { + return commitCounter; + } + + /** The commit time associated with this message. */ + public long getCommitTime() { + return commitTime; + } + /** + * The write cache buffer sequence number (reset to ZERO (0) for the first + * message after each commit and incremented for each buffer sent by the + * leader). + */ + public long getSequence() { + return sequence; + } + /** The type of backing store (RW or WORM). */ public StoreTypeEnum getStoreType() { return storeType; @@ -86,11 +110,18 @@ public String toString() { - return getClass().getName() + "{size=" + getSize() + ",chksum=" - + getChk() + ",storeType=" + getStoreType() + ",quorumToken=" - + getQuorumToken() + ",fileExtent=" + getFileExtent() - + ",firstOffset=" + getFirstOffset() + "}"; - + return getClass().getName() // + + "{size=" + getSize() // + + ",chksum=" + getChk() // + + ",commitCounter=" + commitCounter // + + ",commitTime=" + commitTime // + + ",sequence=" + sequence // + + ",storeType=" + getStoreType() // + + ",quorumToken=" + getQuorumToken()// + + ",fileExtent=" + getFileExtent() // + + ",firstOffset=" + getFirstOffset() // + + "}"; + } /** @@ -100,6 +131,16 @@ } /** + * @param commitCounter + * The commit counter for the current root block for the write + * set which is being replicated by this message. + * @param commitTime + * The commit time for the current root block for the write set + * which is being replicated by this message. + * @param sequence + * The write cache block sequence number. This is reset to ZERO + * (0) for the first replicated write cache block in each write + * set. * @param sze * The #of bytes in the payload. * @param chk @@ -113,7 +154,8 @@ * @param firstOffset * The file offset at which the data will be written (WORM only). */ - public HAWriteMessage(final int sze, final int chk, + public HAWriteMessage(final long commitCounter, final long commitTime, + final long sequence, final int sze, final int chk, final StoreTypeEnum storeType, final long quorumToken, final long fileExtent, final long firstOffset) { @@ -122,6 +164,12 @@ if (storeType == null) throw new IllegalArgumentException(); + this.commitCounter = commitCounter; + + this.commitTime = commitTime; + + this.sequence = sequence; + this.storeType = storeType; this.quorumToken = quorumToken; @@ -134,30 +182,60 @@ private static final byte VERSION0 = 0x0; - public void readExternal(final ObjectInput in) throws IOException, - ClassNotFoundException { + @Override + public boolean equals(final Object obj) { + + if (this == obj) + return true; - super.readExternal(in); - final byte version = in.readByte(); - switch (version) { - case VERSION0: - break; - default: - throw new IOException("Unknown version: " + version); - } - storeType = StoreTypeEnum.valueOf(in.readByte()); - quorumToken = in.readLong(); - fileExtent = in.readLong(); - firstOffset = in.readLong(); - } + if (!super.equals(obj)) + return false; - public void writeExternal(final ObjectOutput out) throws IOException { - super.writeExternal(out); - out.write(VERSION0); - out.writeByte(storeType.getType()); - out.writeLong(quorumToken); - out.writeLong(fileExtent); - out.writeLong(firstOffset); + if (!(obj instanceof HAWriteMessage)) + return false; + + final HAWriteMessage other = (HAWriteMessage) obj; + + return commitCounter == other.getCommitCounter() + && commitTime == other.getCommitTime() // + && sequence == other.getSequence() + && storeType == other.getStoreType() + && quorumToken == other.getQuorumToken() + && fileExtent == other.getFileExtent() + && firstOffset == other.getFirstOffset(); + } + public void readExternal(final ObjectInput in) throws IOException, + ClassNotFoundException { + + super.readExternal(in); + final byte version = in.readByte(); + switch (version) { + case VERSION0: + break; + default: + throw new IOException("Unknown version: " + version); + } + storeType = StoreTypeEnum.valueOf(in.readByte()); + commitCounter = in.readLong(); + commitTime = in.readLong(); + sequence = in.readLong(); + quorumToken = in.readLong(); + fileExtent = in.readLong(); + firstOffset = in.readLong(); + } + + public void writeExternal(final ObjectOutput out) throws IOException { + super.writeExternal(out); + out.write(VERSION0); + out.writeByte(storeType.getType()); + out.writeLong(commitCounter); + out.writeLong(commitTime); + out.writeLong(sequence); + out.writeLong(quorumToken); + out.writeLong(fileExtent); + out.writeLong(firstOffset); + } + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -2578,6 +2578,7 @@ try { m_writeCache.flush(true); + m_writeCache.resetSequence(); } catch (InterruptedException e) { log.error(e, e); throw new RuntimeException(e); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestAll.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestAll.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestAll.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -62,9 +62,11 @@ final TestSuite suite = new TestSuite("high availability"); + suite.addTestSuite(TestHAWriteMessage.class); + // if (s_includeHA) suite.addTest(com.bigdata.ha.pipeline.TestAll.suite()); - + return suite; } Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestHAWriteMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestHAWriteMessage.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestHAWriteMessage.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -0,0 +1,80 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha; + +import java.io.IOException; + +import junit.framework.TestCase; + +import com.bigdata.btree.BytesUtil; +import com.bigdata.io.SerializerUtil; +import com.bigdata.journal.StoreTypeEnum; +import com.bigdata.journal.ha.HAWriteMessage; + +public class TestHAWriteMessage extends TestCase { + + /** + * Simple test to verify HAWriteMessage serialization + */ + public void testSerialization() throws IOException, ClassNotFoundException { + + final HAWriteMessage msg1 = new HAWriteMessage( + 12L,// commitCounter + 13L,// commitTime + 14L,// sequence + 15,// size + 16,// checksum + StoreTypeEnum.RW,// + 17L,// quorumToken + 18L,// fileExtent + 19L // firstOffset + ); + + final byte[] ser1 = serialized(msg1); + + final HAWriteMessage msg2 = (HAWriteMessage) SerializerUtil + .deserialize(ser1); + + assertTrue(msg1.equals(msg2)); + + // now confirm serialized byte equivalence in case we just messed up + // equals + final byte[] ser2 = serialized(msg2); + + assertTrue(BytesUtil.bytesEqual(ser1, ser2)); + +// System.err.println("msg1: " + msg1); +// System.err.println("msg2: " + msg2); + + } + + /** + * Utility to return byte[] serialization of the HAWriteMessage + */ + private byte[] serialized(final HAWriteMessage msg) { + + return SerializerUtil.serialize(msg); + } + +} Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -53,11 +53,9 @@ import com.bigdata.ha.HAPipelineGlue; import com.bigdata.ha.QuorumPipeline; import com.bigdata.ha.QuorumPipelineImpl; -import com.bigdata.ha.pipeline.HAReceiveService; -import com.bigdata.ha.pipeline.HASendService; import com.bigdata.io.DirectBufferPool; +import com.bigdata.io.FileChannelUtility; import com.bigdata.io.IBufferAccess; -import com.bigdata.io.FileChannelUtility; import com.bigdata.io.IReopenChannel; import com.bigdata.io.TestCase3; import com.bigdata.io.writecache.WriteCache.FileChannelScatteredWriteCache; @@ -67,10 +65,10 @@ import com.bigdata.quorum.AbstractQuorumMember; import com.bigdata.quorum.AbstractQuorumTestCase; import com.bigdata.quorum.MockQuorumFixture; +import com.bigdata.quorum.MockQuorumFixture.MockQuorum; import com.bigdata.quorum.Quorum; import com.bigdata.quorum.QuorumActor; import com.bigdata.quorum.QuorumMember; -import com.bigdata.quorum.MockQuorumFixture.MockQuorum; import com.bigdata.rawstore.Bytes; import com.bigdata.util.ChecksumUtility; @@ -303,6 +301,20 @@ } + @Override + public long getLastCommitTime() { + + return MyMockQuorumMember.this.getLastCommitTime(); + + } + + @Override + public long getLastCommitCounter() { + + return MyMockQuorumMember.this.getLastCommitCounter(); + + } + }); } @@ -358,6 +370,23 @@ } + @Override + public long getLastCommitTime() { + + return lastCommitTime; + + } + + @Override + public long getLastCommitCounter() { + + return lastCommitCounter; + + } + + private long lastCommitCounter = 0; + private long lastCommitTime = 0; + } // MockQuorumMemberImpl /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/TestAll.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/TestAll.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/TestAll.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -27,9 +27,6 @@ package com.bigdata.journal.ha; -import com.bigdata.ha.pipeline.TestHASendAndReceive; -import com.bigdata.ha.pipeline.TestHASendAndReceive3Nodes; - import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-B.config =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-B.config 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-B.config 2012-10-03 15:13:47 UTC (rev 6643) @@ -21,6 +21,7 @@ import com.bigdata.util.config.NicUtil; import com.bigdata.journal.Options; import com.bigdata.journal.BufferMode; +import com.bigdata.journal.jini.ha.HAJournal; import com.bigdata.jini.lookup.entry.*; import com.bigdata.service.IBigdataClient; import com.bigdata.service.AbstractTransactionService; @@ -89,6 +90,9 @@ // journal data directory. private static dataDir = serviceDir; + // HA log directory. + private static logDir = new File(serviceDir,"logs"); + // one federation, multicast discovery. //static private groups = LookupDiscovery.ALL_GROUPS; @@ -278,6 +282,8 @@ new NV(AbstractTransactionService.Options.MIN_RELEASE_AGE,"1"), + new NV(HAJournal.Options.HA_LOG_DIR, ""+bigdata.logDir), + }, bigdata.kb); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-C.config =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-C.config 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-C.config 2012-10-03 15:13:47 UTC (rev 6643) @@ -21,6 +21,7 @@ import com.bigdata.util.config.NicUtil; import com.bigdata.journal.Options; import com.bigdata.journal.BufferMode; +import com.bigdata.journal.jini.ha.HAJournal; import com.bigdata.jini.lookup.entry.*; import com.bigdata.service.IBigdataClient; import com.bigdata.service.AbstractTransactionService; @@ -89,6 +90,9 @@ // journal data directory. private static dataDir = serviceDir; + // HA log directory. + private static logDir = new File(serviceDir,"logs"); + // one federation, multicast discovery. //static private groups = LookupDiscovery.ALL_GROUPS; @@ -278,6 +282,8 @@ new NV(AbstractTransactionService.Options.MIN_RELEASE_AGE,"1"), + new NV(HAJournal.Options.HA_LOG_DIR, ""+bigdata.logDir), + }, bigdata.kb); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.config =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.config 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.config 2012-10-03 15:13:47 UTC (rev 6643) @@ -21,6 +21,7 @@ import com.bigdata.util.config.NicUtil; import com.bigdata.journal.Options; import com.bigdata.journal.BufferMode; +import com.bigdata.journal.jini.ha.HAJournal; import com.bigdata.jini.lookup.entry.*; import com.bigdata.service.IBigdataClient; import com.bigdata.service.AbstractTransactionService; @@ -89,6 +90,9 @@ // journal data directory. private static dataDir = serviceDir; + // HA log directory. + private static logDir = new File(serviceDir,"logs"); + // one federation, multicast discovery. //static private groups = LookupDiscovery.ALL_GROUPS; @@ -279,6 +283,8 @@ new NV(AbstractTransactionService.Options.MIN_RELEASE_AGE,"1"), + new NV(HAJournal.Options.HA_LOG_DIR, ""+bigdata.logDir), + }, bigdata.kb); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -23,6 +23,7 @@ */ package com.bigdata.journal.jini.ha; +import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.InetSocketAddress; @@ -37,11 +38,16 @@ import com.bigdata.concurrent.FutureTaskMon; import com.bigdata.ha.HAGlue; import com.bigdata.ha.QuorumService; +import com.bigdata.io.writecache.WriteCache; import com.bigdata.journal.BufferMode; +import com.bigdata.journal.IRootBlockView; import com.bigdata.journal.Journal; import com.bigdata.journal.ValidationError; +import com.bigdata.journal.WORMStrategy; +import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.Quorum; import com.bigdata.quorum.zk.ZKQuorumImpl; +import com.bigdata.rwstore.RWStore; import com.bigdata.service.AbstractTransactionService; import com.bigdata.service.proxy.ThickFuture; @@ -86,9 +92,70 @@ String WRITE_PIPELINE_ADDR = HAJournal.class.getName() + ".writePipelineAddr"; + /** + * The required property whose value is the name of the directory in + * which write ahead log files will be created to support + * resynchronization services trying to join an HA quorum. + * <p> + * The directory should not contain any other files. It will be + * populated with files whose names correspond to commit counters. The + * commit counter is recorded in the root block at each commit. It is + * used to identify the write set for a given commit. A log file is + * written for each commit point. Each log files is normally deleted at + * the commit. However, if the quorum is not fully met at the commit, + * then the log files not be deleted. Instead, they will be used to + * resynchronize the other quorum members. + * <p> + * The log file name includes the value of the commit counter for the + * commit point that will be achieved when that log file is applied to a + * journal whose current commit point is [commitCounter-1]. The commit + * counter for a new journal (without any commit points) is ZERO (0). + * This the first log file will be labeled with the value ONE (1). The + * commit counter is written out with leading zeros in the log file name + * so the natural sort order of the log files should correspond to the + * ascending commit order. + * <p> + * The log files are a sequence of zero or more {@link HAWriteMessage} + * objects. For the {@link RWStore}, each {@link HAWriteMessage} is + * followed by the data from the corresponding {@link WriteCache} block. + * For the {@link WORMStrategy}, the {@link WriteCache} block is omitted + * since this data can be trivially reconstructed from the backing file. + * When the quorum prepares for a commit, the proposed root block is + * written onto the end of the log file. + * <p> + * The log files are deleted once the quorum is fully met (k out of k + * nodes have met in the quorum). It is possible for a quorum to form + * with only <code>(k+1)/2</code> nodes. When this happens, the nodes in + * the quorum will all write log files into the {@link #HA_LOG_DIR}. + * Those files will remain until the other nodes in the quorum + * synchronize and join the quorum. Once the quorum is fully met, the + * files in the log directory will be deleted. + * <p> + * If some or all log files are not present, then any node that is not + * synchronized with the quorum must be rebuilt from scratch rather than + * by incrementally applying logged write sets until it catches up and + * can join the quorum. + * + * @see IRootBlockView#getCommitCounter() + * + * TODO We may need to also write a marker either on the head or + * tail of the log file to indicate that the commit was applied. + * Work this out when we work through the extension to the 2-phase + * commit protocol that supports resynchronization. + */ + String HA_LOG_DIR = HAJournal.class.getName() + ".haLogDir"; + } + /** + * @see Options#WRITE_PIPELINE_ADDR + */ private final InetSocketAddress writePipelineAddr; + + /** + * @see Options#HA_LOG_DIR + */ + private final File haLogDir; public HAJournal(final Properties properties) { @@ -110,6 +177,17 @@ writePipelineAddr = (InetSocketAddress) properties .get(Options.WRITE_PIPELINE_ADDR); + final String logDirStr = properties.getProperty(Options.HA_LOG_DIR); + + haLogDir = new File(logDirStr); + + if (!haLogDir.exists()) { + + // Create the directory. + haLogDir.mkdirs(); + + } + } /** @@ -172,6 +250,15 @@ } + final String logDirStr = properties.getProperty(Options.HA_LOG_DIR); + + if (logDirStr == null) { + + throw new IllegalArgumentException(Options.HA_LOG_DIR + + " : must be specified"); + + } + return properties; } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/BigdataRDFContext.java 2012-10-03 15:13:47 UTC (rev 6643) @@ -939,9 +939,16 @@ // } return null; } catch (Throwable t) { + log.error(t); if (cxn != null && !cxn.isReadOnly()) { /* * Force rollback of the connection. + * + * Note: It is possible that the commit has already been + * processed, in which case this rollback() will be a NOP. + * This can happen when there is an IO error when + * communicating with the client, but the database has + * already gone through a commit. */ cxn.rollback(); } @@ -1418,9 +1425,8 @@ lastOp = thisOp; // Write out the LOAD operation. - body.node("p")// - .node("pre").text(thisOp.toString()).close()// - .close(); + body.node("pre").text(thisOp.toString())// + .close(); } @@ -1451,10 +1457,10 @@ pw.flush(); pw.close(); - body.node("p").text("ABORT")// + body.node("p").text("ABORT").close()// .node("pre").text(e.getUpdate().toString()).close()// .node("pre").text(w.toString()).close()// - .text("totalElapsed=" + totalElapsedMillis + .node("p").text("totalElapsed=" + totalElapsedMillis + "ms, elapsed=" + elapsedMillis + "ms") .close(); @@ -1488,16 +1494,15 @@ // .close(); } else { - body.node("p") - // - .node("pre") + body.node("pre") .text(e.getUpdate().toString()) .close() // + .node("p") .text("totalElapsed=" + totalElapsedMillis + "ms, elapsed=" + elapsedMillis + "ms")// .close(); - } + } // horizontal line after each operation. body.node("hr").close(); Modified: branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config 2012-10-02 16:32:04 UTC (rev 6642) +++ branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config 2012-10-03 15:13:47 UTC (rev 6643) @@ -22,6 +22,7 @@ import com.bigdata.journal.Options; import com.bigdata.journal.BufferMode; import com.bigdata.journal.Journal; +import com.bigdata.journal.jini.ha.HAJournal; import com.bigdata.jini.lookup.entry.*; import com.bigdata.service.IBigdataClient; import com.bigdata.service.AbstractTransactionService; @@ -91,6 +92,9 @@ // journal data directory. private static dataDir = serviceDir; + // HA log directory. + private static logDir = new File(serviceDir,"logs"); + // one federation, multicast discovery. //static private groups = LookupDiscovery.ALL_GROUPS; @@ -286,6 +290,8 @@ new NV(AbstractTransactionService.Options.MIN_RELEASE_AGE,"1"), + new NV(HAJournal.Options.HA_LOG_DIR, ""+bigdata.logDir), + /* Enable statistics collection and reporting. */ new NV(Journal.Options.COLLECT_QUEUE_STATISTICS,"true"), This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-03 18:01:13
|
Revision: 6649 http://bigdata.svn.sourceforge.net/bigdata/?rev=6649&view=rev Author: thompsonbry Date: 2012-10-03 18:01:03 +0000 (Wed, 03 Oct 2012) Log Message: ----------- Working w/ martyn on the resynchronization protocol. Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumReadImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -1,3 +1,26 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.ha; import java.io.IOException; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -114,4 +114,17 @@ */ long getLastCommitCounter(); + /** + * Log the {@link HAWriteMessage} and the associated data (if necessary). + * The log file for the current write set will be deleted if the quorum is + * fully met at the next 2-phase commit. + * + * @param msg + * The {@link HAWriteMessage}. + * @param data + * The {@link WriteCache} block. + */ + void logWriteCacheBlock(final HAWriteMessage msg, + final ByteBuffer data) throws IOException; + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -1005,6 +1005,10 @@ abstract protected void handleReplicatedWrite(final HAWriteMessage msg, final ByteBuffer data) throws Exception; + @Override + abstract public void logWriteCacheBlock(final HAWriteMessage msg, + final ByteBuffer data) throws IOException; + /** * A utility class that bundles together the Internet address and port at which * the downstream service will accept and relay cache blocks for the write Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumReadImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumReadImpl.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumReadImpl.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -1,3 +1,26 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.ha; import java.io.IOException; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -28,11 +28,8 @@ package com.bigdata.ha; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.ObjectOutputStream; import java.nio.ByteBuffer; -import java.util.Formatter; import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.Future; @@ -41,7 +38,6 @@ import org.apache.log4j.Logger; -import com.bigdata.io.writecache.WriteCacheService; import com.bigdata.journal.AbstractJournal; import com.bigdata.journal.IResourceManager; import com.bigdata.journal.IRootBlockView; @@ -126,6 +122,14 @@ } + @Override + public void logWriteCacheBlock(final HAWriteMessage msg, + final ByteBuffer data) throws IOException { + + QuorumServiceBase.this.logWriteCacheBlock(msg, data); + + } + }); addListener(this.commitImpl = new QuorumCommitImpl<S>(this)); @@ -224,93 +228,14 @@ * Log the {@link HAWriteMessage} and the associated data onto the * appropriate log file. * <p> - * Note: Logging MUST NOT start in the middle of a write set (all log files - * must be complete). The {@link HAWriteMessage#getSequence()} MUST be ZERO - * (0) when we open a log file. - * - * TODO The WORM should not bother to log the write cache block since it can - * be obtained directly from the backing store. Abstract out an object to - * manage the log file for a commit counter and make it smart about the WORM - * versus the RWStore. The same object will need to handle the read back - * from the log file and in the case of the WORM should get the data block - * from the backing file. However, this object is not responsible for reply - * of log files. We'll have to locate a place for that logic next - probably - * in this class (QuorumServiceBase). - * - * FIXME NOTHING IS CALLING THIS CODE YET! Invoke from - * {@link #handleReplicatedWrite(HAWriteMessage, ByteBuffer)} and from - * {@link WriteCacheService}s WriteTask.call() method (on the leader). + * The default implementation is a NOP. */ public void logWriteCacheBlock(final HAWriteMessage msg, final ByteBuffer data) throws IOException { -// final long currentCommitCounter = getLastCommitCounter(); - - getQuorum().assertQuorum(msg.getQuorumToken()); - - if (msg.getSequence() == 0L) { - - if (processLog != null) { - processLog.close(); - processLog = null; - } - - /* - * The commit counter that will be used to identify the file. - * - * Note: We use commitCounter+1 so the file will be labeled by the - * commit point that will be achieved when that log file is applied - * to a journal whose current commit point is [commitCounter]. - */ - final long commitCounter = msg.getCommitCounter() + 1; - - /* - * Format the name of the log file. - * - * Note: The commit counter in the file name should be zero filled - * to 20 digits so we have the files in lexical order in the file - * system (for convenience). - */ - final String logFile; - { - - final StringBuilder sb = new StringBuilder(); - - final Formatter f = new Formatter(sb); - - f.format("%020d.log", commitCounter); - - logFile = sb.toString(); - - } - - // Establish new log file. - processLog = new ObjectOutputStream(new FileOutputStream(new File( - getHALogDir(), logFile))); - - } - - /* - * FIXME We need to track whether or not we began the sequence at ZERO - * (0). If we did, then we can open a log and start writing for the - * current commitCounter. We do need to keep track of the commit counter - * associated with the log file so we can correctly refuse to log blocks - * on the log file that are associated with a different commit counter. - * We also need to manage the abort() and commit() transitions, ensuring - * that we truncate() the log for abort() (assuming it is for the same - * commit counter) and that we append the root block, force() and - * close() the log for commit. - */ + // NOP } - - /** - * Process log to which the receiveService should write the messages to and - * <code>null</code> if we may not write on it. - * - * FIXME We need to clear this any time the quorum breaks. - */ - private ObjectOutputStream processLog = null; /* * QuorumCommit. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -618,11 +618,12 @@ ); /* - * FIXME The quorum leader must log the write cache - * block. However, it must be logged exactly once - * (if there is a retry, we do not want to re-log - * the block!) + * The quorum leader must log the write cache block. + * + * Note: It will be logged exactly once (retry send + * will not re-log the block). */ + quorumMember.logWriteCacheBlock(msg, b.duplicate()); remoteWriteFuture = quorumMember.replicate(msg, b); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -52,7 +52,7 @@ private long commitCounter; /** The most recent commit time associated with this message */ - private long commitTime; + private long lastCommitTime; /** The write sequence since last commit beginning at zero */ private long sequence; @@ -75,8 +75,8 @@ } /** The commit time associated with this message. */ - public long getCommitTime() { - return commitTime; + public long getLastCommitTime() { + return lastCommitTime; } /** @@ -114,7 +114,7 @@ + "{size=" + getSize() // + ",chksum=" + getChk() // + ",commitCounter=" + commitCounter // - + ",commitTime=" + commitTime // + + ",commitTime=" + lastCommitTime // + ",sequence=" + sequence // + ",storeType=" + getStoreType() // + ",quorumToken=" + getQuorumToken()// @@ -166,7 +166,7 @@ this.commitCounter = commitCounter; - this.commitTime = commitTime; + this.lastCommitTime = commitTime; this.sequence = sequence; @@ -197,7 +197,7 @@ final HAWriteMessage other = (HAWriteMessage) obj; return commitCounter == other.getCommitCounter() - && commitTime == other.getCommitTime() // + && lastCommitTime == other.getLastCommitTime() // && sequence == other.getSequence() && storeType == other.getStoreType() && quorumToken == other.getQuorumToken() @@ -219,7 +219,7 @@ } storeType = StoreTypeEnum.valueOf(in.readByte()); commitCounter = in.readLong(); - commitTime = in.readLong(); + lastCommitTime = in.readLong(); sequence = in.readLong(); quorumToken = in.readLong(); fileExtent = in.readLong(); @@ -231,7 +231,7 @@ out.write(VERSION0); out.writeByte(storeType.getType()); out.writeLong(commitCounter); - out.writeLong(commitTime); + out.writeLong(lastCommitTime); out.writeLong(sequence); out.writeLong(quorumToken); out.writeLong(fileExtent); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -314,7 +314,15 @@ return MyMockQuorumMember.this.getLastCommitCounter(); } - + + @Override + public void logWriteCacheBlock(final HAWriteMessage msg, + final ByteBuffer data) throws IOException { + + MyMockQuorumMember.this.logWriteCacheBlock(msg, data); + + } + }); } @@ -387,6 +395,12 @@ private long lastCommitCounter = 0; private long lastCommitTime = 0; + @Override + public void logWriteCacheBlock(final HAWriteMessage msg, + final ByteBuffer data) throws IOException { + // NOP. + } + } // MockQuorumMemberImpl /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-03 17:56:13 UTC (rev 6648) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-03 18:01:03 UTC (rev 6649) @@ -1,5 +1,9 @@ package com.bigdata.journal.jini.ha; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URL; @@ -7,6 +11,7 @@ import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.ServerNotActiveException; +import java.util.Formatter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -31,12 +36,12 @@ import com.bigdata.ha.HAGlue; import com.bigdata.ha.HAGlueDelegate; +import com.bigdata.ha.ProcessLogWriter; import com.bigdata.ha.QuorumService; import com.bigdata.ha.QuorumServiceBase; import com.bigdata.io.IBufferAccess; import com.bigdata.jini.start.config.ZookeeperClientConfig; import com.bigdata.jini.util.JiniUtil; -import com.bigdata.journal.AbstractJournal; import com.bigdata.journal.IHABufferStrategy; import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.Quorum; @@ -133,7 +138,7 @@ * Caching discovery client for the {@link HAGlue} services. */ private HAJournalDiscoveryClient discoveryClient; - + /** * The journal. */ @@ -171,6 +176,15 @@ */ private Server jettyServer; + /** + * Caching discovery client for the {@link HAGlue} services. + */ + public HAJournalDiscoveryClient getDiscoveryClient() { + + return discoveryClient; + + } + public HAJournalServer(final String[] args, final LifeCycle lifeCycle) { super(args, lifeCycle); @@ -503,112 +517,189 @@ /** * Factory for the {@link QuorumService} implementation. * + * @param logicalServiceZPath + * @param serviceId * @param remoteServiceImpl * The object that implements the {@link Remote} interfaces * supporting HA operations. + * @param store + * The {@link HAJournal}. */ - private QuorumServiceBase<HAGlue, AbstractJournal> newQuorumService( + private QuorumServiceBase<HAGlue, HAJournal> newQuorumService( final String logicalServiceZPath, final UUID serviceId, final HAGlue remoteServiceImpl, - final AbstractJournal store) { + final HAJournal store) { - return new QuorumServiceBase<HAGlue, AbstractJournal>( - logicalServiceZPath, serviceId, remoteServiceImpl, store) { + return new HAQuorumService<HAGlue, HAJournal>(logicalServiceZPath, + serviceId, remoteServiceImpl, store, this); - @Override - public void start(final Quorum<?,?> quorum) { - - super.start(quorum); + } - // Inform the Journal about the current token (if any). - journal.setQuorumToken(quorum.token()); - - } + /** + * Concrete {@link QuorumServiceBase} implementation for the + * {@link HAJournal}. + * + * @param logicalServiceZPath + * @param serviceId + * @param remoteServiceImpl + * The object that implements the {@link Remote} interfaces + * supporting HA operations. + * @param store + * The {@link HAJournal}. + */ + static private class HAQuorumService<S extends HAGlue, L extends HAJournal> + extends QuorumServiceBase<S, L> { + + private final L journal; + private final HAJournalServer server; + + public HAQuorumService(final String logicalServiceZPath, + final UUID serviceId, final S remoteServiceImpl, final L store, + final HAJournalServer server) { + + super(logicalServiceZPath, serviceId, remoteServiceImpl, store); + + this.journal = store; - @Override - public void quorumBreak() { + this.server = server; - super.quorumBreak(); - - // Inform the Journal that the quorum token is invalid. - journal.setQuorumToken(Quorum.NO_QUORUM); - - } + } - @Override - public void quorumMeet(final long token, final UUID leaderId) { + @Override + public void start(final Quorum<?,?> quorum) { + + super.start(quorum); - super.quorumMeet(token, leaderId); - - // Inform the journal that there is a new quorum token. - journal.setQuorumToken(token); + // Inform the Journal about the current token (if any). + journal.setQuorumToken(quorum.token()); + + } + + @Override + public void quorumBreak() { - } + super.quorumBreak(); - /** - * Resolve an {@link HAGlue} object from its Service UUID. - */ - @Override - public HAGlue getService(final UUID serviceId) { - - final ServiceItem serviceItem = discoveryClient - .getServiceItem(serviceId); + // Inform the Journal that the quorum token is invalid. + journal.setQuorumToken(Quorum.NO_QUORUM); + + } - if (serviceItem == null) { + @Override + public void quorumMeet(final long token, final UUID leaderId) { - // Not found (per the API). - throw new QuorumException("Service not found: uuid=" - + serviceId); + super.quorumMeet(token, leaderId); + + // Inform the journal that there is a new quorum token. + journal.setQuorumToken(token); - } + } + + /** + * Resolve an {@link HAGlue} object from its Service UUID. + */ + @Override + public S getService(final UUID serviceId) { + + final HAJournalDiscoveryClient discoveryClient = server + .getDiscoveryClient(); - return (HAGlue) serviceItem.service; - + final ServiceItem serviceItem = discoveryClient + .getServiceItem(serviceId); + + if (serviceItem == null) { + + // Not found (per the API). + throw new QuorumException("Service not found: uuid=" + + serviceId); + } - @Override - protected void handleReplicatedWrite(final HAWriteMessage msg, - final ByteBuffer data) throws Exception { + return (S) serviceItem.service; + + } - if (haLog.isDebugEnabled()) - haLog.debug("msg=" + msg + ", buf=" + data); + @Override + protected void handleReplicatedWrite(final HAWriteMessage msg, + final ByteBuffer data) throws Exception { - /* - * Note: the ByteBuffer is owned by the HAReceiveService. This - * just wraps up the reference to the ByteBuffer with an - * interface that is also used by the WriteCache to control - * access to ByteBuffers allocated from the DirectBufferPool. - * However, release() is a NOP on this implementation since the - * ByteBuffer is owner by the HAReceiveService. - */ - final IBufferAccess b = new IBufferAccess() { + if (haLog.isDebugEnabled()) + haLog.debug("msg=" + msg + ", buf=" + data); - @Override - public void release(long timeout, TimeUnit unit) - throws InterruptedException { - // NOP - } + /* + * Log the message and write cache block. + */ + logWriteCacheBlock(msg, data); + + /* + * Note: the ByteBuffer is owned by the HAReceiveService. This + * just wraps up the reference to the ByteBuffer with an + * interface that is also used by the WriteCache to control + * access to ByteBuffers allocated from the DirectBufferPool. + * However, release() is a NOP on this implementation since the + * ByteBuffer is owner by the HAReceiveService. + */ + final IBufferAccess b = new IBufferAccess() { - @Override - public void release() throws InterruptedException { - // NOP - } + @Override + public void release(long timeout, TimeUnit unit) + throws InterruptedException { + // NOP + } - @Override - public ByteBuffer buffer() { - return data; - } - }; + @Override + public void release() throws InterruptedException { + // NOP + } - ((IHABufferStrategy) journal.getBufferStrategy()) - .writeRawBuffer(msg, b); + @Override + public ByteBuffer buffer() { + return data; + } + }; - } + ((IHABufferStrategy) journal.getBufferStrategy()) + .writeRawBuffer(msg, b); - }; + } + /** + * {@inheritDoc} + * <p> + * Note: Logging MUST NOT start in the middle of a write set (all log files + * must be complete). The {@link HAWriteMessage#getSequence()} MUST be ZERO + * (0) when we open a log file. + * + * TODO The WORM should not bother to log the write cache block since it can + * be obtained directly from the backing store. Abstract out an object to + * manage the log file for a commit counter and make it smart about the WORM + * versus the RWStore. The same object will need to handle the read back + * from the log file and in the case of the WORM should get the data block + * from the backing file. However, this object is not responsible for reply + * of log files. We'll have to locate a place for that logic next - probably + * in this class (QuorumServiceBase). + * + * FIXME We need to get the root block on the log and integrate it into the + * commit protocol. Everyone in the pipeline needs to get the root block. + * + * FIXME The class that handles the logging must not write a block twice, + * even if it gets transmitted twice. + */ + public void logWriteCacheBlock(final HAWriteMessage msg, + final ByteBuffer data) throws IOException { + + } + + /** + * Process log to which the receiveService should write the messages to and + * <code>null</code> if we may not write on it. + * + * FIXME We need to clear this any time the quorum breaks. + */ + ProcessLogWriter haLogWriter = null; + } - + /** * Setup and start the {@link NanoSparqlServer}. * <p> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-04 14:17:54
|
Revision: 6653 http://bigdata.svn.sourceforge.net/bigdata/?rev=6653&view=rev Author: thompsonbry Date: 2012-10-04 14:17:47 +0000 (Thu, 04 Oct 2012) Log Message: ----------- Working on the HA resynchronization protocol with Martyn. done. The first root block can be set on the HALogWriter when we vote a lastCommitTime. However, there are two distinct cases. One where the quorum meets on our vote and one where the quorum meets on a different vote (in which case we need to resynchronize). done. When the quorum meets on our vote, we can proceed to log the write cache blocks as they are replicated and then delete on a fully met quorum commit. done. QuorumCommitImpl and must also coordinate with the met services and piplined members and provide a clear command when a commit occurs in a fully met quorum that all HA logs should be deleted. @see https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA). Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-04 14:12:24 UTC (rev 6652) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-04 14:17:47 UTC (rev 6653) @@ -32,6 +32,7 @@ import java.util.concurrent.Future; import com.bigdata.io.writecache.WriteCache; +import com.bigdata.journal.IRootBlockView; import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.Quorum; @@ -127,4 +128,21 @@ void logWriteCacheBlock(final HAWriteMessage msg, final ByteBuffer data) throws IOException; + /** + * Log the root block for the commit point that closes the current write set + * onto the {@link ProcessLogWriter}. + * + * @param rootBlock + * The root block for the commit point that was just achieved. + */ + void logRootBlock(final IRootBlockView rootBlock) throws IOException; + + /** + * Purge the local HA log files. This should be invoked when the service + * goes through a commit point in which the quorum is fully met. At that + * moment, we no longer require these log files to resynchronize any + * service. + */ + void purgeHALogs(); + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-04 14:12:24 UTC (rev 6652) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-04 14:17:47 UTC (rev 6653) @@ -23,6 +23,7 @@ import com.bigdata.ha.pipeline.HASendService; import com.bigdata.io.DirectBufferPool; import com.bigdata.io.IBufferAccess; +import com.bigdata.journal.IRootBlockView; import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.QuorumException; import com.bigdata.quorum.QuorumMember; @@ -1005,9 +1006,13 @@ abstract protected void handleReplicatedWrite(final HAWriteMessage msg, final ByteBuffer data) throws Exception; - @Override - abstract public void logWriteCacheBlock(final HAWriteMessage msg, - final ByteBuffer data) throws IOException; +// @Override +// abstract public void logWriteCacheBlock(final HAWriteMessage msg, +// final ByteBuffer data) throws IOException; +// +// @Override +// abstract public void logRootBlock(final IRootBlockView rootBlock) +// throws IOException; /** * A utility class that bundles together the Internet address and port at which Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-04 14:12:24 UTC (rev 6652) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-04 14:17:47 UTC (rev 6653) @@ -130,6 +130,21 @@ } + @Override + public void logRootBlock(final IRootBlockView rootBlock) + throws IOException { + + QuorumServiceBase.this.logRootBlock(rootBlock); + + } + + @Override + public void purgeHALogs() { + + QuorumServiceBase.this.purgeHALogs(); + + } + }); addListener(this.commitImpl = new QuorumCommitImpl<S>(this)); @@ -225,11 +240,11 @@ ByteBuffer data) throws Exception; /** - * Log the {@link HAWriteMessage} and the associated data onto the - * appropriate log file. + * {@inheritDoc} * <p> - * The default implementation is a NOP. + * Note: The default implementation is a NOP. */ + @Override public void logWriteCacheBlock(final HAWriteMessage msg, final ByteBuffer data) throws IOException { @@ -237,6 +252,30 @@ } + /** + * {@inheritDoc} + * <p> + * Note: The default implementation is a NOP. + */ + @Override + public void purgeHALogs() { + + // NOP + + } + + /** + * {@inheritDoc} + * <p> + * Note: The default implementation is a NOP. + */ + @Override + public void logRootBlock(final IRootBlockView rootBlock) throws IOException { + + // NOP + + } + /* * QuorumCommit. */ Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-04 14:12:24 UTC (rev 6652) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-04 14:17:47 UTC (rev 6653) @@ -4765,19 +4765,34 @@ } - /* - * We need to reset the backing store with the token for the new - * quorum. There should not be any active writers since there - * was no quorum. Thus, this should just cause the backing store - * to become aware of the new quorum and enable writes. - * - * Note: This is done using a local abort, not a 2-phase abort. - * Each node in the quorum should handle this locally when it - * sees the quorum meet event. - */ + if (localService.isJoinedMember(quorumToken)) { - _abort(); + /* + * We need to reset the backing store with the token for the + * new quorum. There should not be any active writers since + * there was no quorum. Thus, this should just cause the + * backing store to become aware of the new quorum and + * enable writes. + * + * Note: This is done using a local abort, not a 2-phase + * abort. Each node in the quorum should handle this locally + * when it sees the quorum meet event. + * + * TODO This assumes that a service that is not joined with + * the quorum will not go through an _abort(). Such a + * service will have to go through the synchronization + * protocol. If the service is in the pipeline when the + * quorum meets, even through it is not joined, and votes + * the same lastCommitTime, then it MIGHT see all necessary + * replicated writes and if it does, then it could + * synchronize immediately. There is basically a data race + * here. + */ + _abort(); + + } + } else { throw new AssertionError(); @@ -5077,13 +5092,60 @@ // reload the commit record from the new root block. _commitRecord = _getCommitRecord(); - prepareRequest.set(null/* discard */); - if (txLog.isInfoEnabled()) txLog.info("COMMIT: commitTime=" + commitTime); + try { + + /* + * Write the root block on the HA log file, closing + * out that file. + */ + + localService.logRootBlock(rootBlock); + + } catch (IOException e) { + /* + * We have already committed. + * + * This HA log file will be unusable if we have to + * replay the write set to resynchronize some other + * service. However, it is still possible to obtain + * the HA log file from some other service in the + * qourum. If all else fails, the hypothetical + * service can be rebuilt from scratch. + */ + haLog.error("UNABLE TO SEAL HA LOG FILE WITH ROOT BLOCK: " + + getServiceId() + + ", rootBlock=" + + rootBlock); + } + + if (quorum.isQuorumFullyMet(rootBlock.getQuorumToken())) { + + /* + * The HA log files are purged on each node any time + * the quorum is fully met and goes through a commit + * point. + */ + + localService.purgeHALogs(); + + } + + } catch(Throwable t) { + + haLog.error("ERROR IN 2-PHASE COMMIT: " + t + + ", rootBlock=" + rootBlock, t); + + quorum.getActor().serviceLeave(); + + throw new RuntimeException(t); + } finally { + prepareRequest.set(null/* discard */); + _fieldReadWriteLock.writeLock().unlock(); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java 2012-10-04 14:12:24 UTC (rev 6652) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java 2012-10-04 14:17:47 UTC (rev 6653) @@ -60,6 +60,7 @@ import com.bigdata.io.TestCase3; import com.bigdata.io.writecache.WriteCache.FileChannelScatteredWriteCache; import com.bigdata.io.writecache.WriteCache.FileChannelWriteCache; +import com.bigdata.journal.IRootBlockView; import com.bigdata.journal.StoreTypeEnum; import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.AbstractQuorumMember; @@ -323,6 +324,21 @@ } + @Override + public void logRootBlock(final IRootBlockView rootBlock) + throws IOException { + + MyMockQuorumMember.this.logRootBlock(rootBlock); + + } + + @Override + public void purgeHALogs() { + + MyMockQuorumMember.this.purgeHALogs(); + + } + }); } @@ -401,6 +417,21 @@ // NOP. } + @Override + public void logRootBlock(final IRootBlockView rootBlock) + throws IOException { + + // NOP + + } + + @Override + public void purgeHALogs() { + + // NOP + + } + } // MockQuorumMemberImpl /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-04 14:12:24 UTC (rev 6652) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-04 14:17:47 UTC (rev 6653) @@ -282,6 +282,13 @@ } + @Override + public final File getHALogDir() { + + return haLogDir; + + } + /** * Extended implementation supports RMI. */ Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-04 14:12:24 UTC (rev 6652) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-04 14:17:47 UTC (rev 6653) @@ -1,9 +1,8 @@ package com.bigdata.journal.jini.ha; import java.io.File; -import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; -import java.io.ObjectOutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URL; @@ -11,7 +10,6 @@ import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.ServerNotActiveException; -import java.util.Formatter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -43,6 +41,7 @@ import com.bigdata.jini.start.config.ZookeeperClientConfig; import com.bigdata.jini.util.JiniUtil; import com.bigdata.journal.IHABufferStrategy; +import com.bigdata.journal.IRootBlockView; import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.Quorum; import com.bigdata.quorum.QuorumActor; @@ -133,6 +132,12 @@ int DEFAULT_PORT = 8080; } + + /** + * FIXME FLAG CONDITIONALLY ENABLES THE HA LOG AND RESYNC PROTOCOL. Disabled + * in committed code until we have this running properly. + */ + private static final boolean HA_LOG_ENABLED = false; /** * Caching discovery client for the {@link HAGlue} services. @@ -144,6 +149,12 @@ */ private HAJournal journal; + /** + * Write ahead log for replicated writes used to resynchronize services that + * are not in the met quorum. + */ + private ProcessLogWriter haLogWriter = null; + private UUID serviceUUID; private HAGlue haGlueService; private ZookeeperClientConfig zkClientConfig; @@ -370,6 +381,8 @@ this.journal = new HAJournal(properties, quorum); + this.haLogWriter = new ProcessLogWriter(journal.getHALogDir()); + } haGlueService = journal.newHAGlue(serviceUUID); @@ -582,6 +595,20 @@ // Inform the Journal that the quorum token is invalid. journal.setQuorumToken(Quorum.NO_QUORUM); + + if(HA_LOG_ENABLED) { + + try { + + server.haLogWriter.disable(); + + } catch (IOException e) { + + haLog.error(e, e); + + } + + } } @@ -593,6 +620,39 @@ // Inform the journal that there is a new quorum token. journal.setQuorumToken(token); + if (HA_LOG_ENABLED) { + + if (isJoinedMember(token)) { + + try { + + server.haLogWriter + .createLog(journal.getRootBlockView()); + + } catch (IOException e) { + + /* + * We can not remain in the quorum if we can not write + * the HA Log file. + */ + haLog.error("CAN NOT OPEN LOG: " + e, e); + + getActor().serviceLeave(); + + } + + } else if(isPipelineMember()) { + + /* + * FIXME RESYNC : Start the resynchronization protocol. + */ + + haLog.info("RESYNCH REQUIRED: " + server.getServiceName()); + + } + + } + } /** @@ -666,38 +726,85 @@ /** * {@inheritDoc} * <p> - * Note: Logging MUST NOT start in the middle of a write set (all log files - * must be complete). The {@link HAWriteMessage#getSequence()} MUST be ZERO - * (0) when we open a log file. - * - * TODO The WORM should not bother to log the write cache block since it can - * be obtained directly from the backing store. Abstract out an object to - * manage the log file for a commit counter and make it smart about the WORM - * versus the RWStore. The same object will need to handle the read back - * from the log file and in the case of the WORM should get the data block - * from the backing file. However, this object is not responsible for reply - * of log files. We'll have to locate a place for that logic next - probably - * in this class (QuorumServiceBase). - * - * FIXME We need to get the root block on the log and integrate it into the - * commit protocol. Everyone in the pipeline needs to get the root block. - * - * FIXME The class that handles the logging must not write a block twice, - * even if it gets transmitted twice. - */ + * Writes the {@link HAWriteMessage} and the data onto the + * {@link ProcessLogWriter} + */ + @Override public void logWriteCacheBlock(final HAWriteMessage msg, final ByteBuffer data) throws IOException { + if (!HA_LOG_ENABLED) + return; + + server.haLogWriter.write(msg, data); + } + + /** + * {@inheritDoc} + * <p> + * Writes the root block onto the {@link ProcessLogWriter} and closes + * the log file. + */ + @Override + public void logRootBlock(final IRootBlockView rootBlock) throws IOException { - /** - * Process log to which the receiveService should write the messages to and - * <code>null</code> if we may not write on it. - * - * FIXME We need to clear this any time the quorum breaks. - */ - ProcessLogWriter haLogWriter = null; + if (!HA_LOG_ENABLED) + return; + server.haLogWriter.closeLog(rootBlock); + + } + + /** + * {@inheritDoc} + * <p> + * Destroys all HA log files in the HA log directory. + */ + @Override + public void purgeHALogs() { + + if (!HA_LOG_ENABLED) + return; + + final File logDir = journal.getHALogDir(); + + final File[] files = logDir.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + + return name.endsWith(ProcessLogWriter.HA_LOG_EXT); + + } + }); + + int ndeleted = 0; + long totalBytes = 0L; + + for (File file : files) { + + final long len = file.length(); + + if (!file.delete()) { + + haLog.warn("COULD NOT DELETE FILE: " + file); + + continue; + + } + + ndeleted++; + + totalBytes += len; + + } + + haLog.info("PURGED LOGS: ndeleted=" + ndeleted + ", totalBytes=" + + totalBytes); + + } + } /** This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-04 14:59:51
|
Revision: 6654 http://bigdata.svn.sourceforge.net/bigdata/?rev=6654&view=rev Author: thompsonbry Date: 2012-10-04 14:59:40 +0000 (Thu, 04 Oct 2012) Log Message: ----------- Working towards a functional integration of the HA Log Writer with the HAJournal. Fixed several bugs in the process log writer related to writing of ByteBuffers and to the consistency checks for the root block. Moved the HALogWriter initialization into the HAJournal constructor. Added getBytes(ByteBuffer) to BytesUtil and removed it from TestCase3. This is now used by the ProcessLogWriter. Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/btree/BytesUtil.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/TestCase3.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/btree/BytesUtil.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/btree/BytesUtil.java 2012-10-04 14:17:47 UTC (rev 6653) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/btree/BytesUtil.java 2012-10-04 14:59:40 UTC (rev 6654) @@ -1579,4 +1579,43 @@ } + /** + * Return the data in the buffer. When possible, the backing array is + * returned. Otherwise, a new byte[] is allocated, the data are copied into + * the array, and the new array is returned. + */ + public static byte[] getBytes(ByteBuffer buf) { + + if (buf.hasArray() && buf.arrayOffset() == 0 && buf.position() == 0 + && buf.limit() == buf.capacity()) { + + /* + * Return the backing array. + */ + + return buf.array(); + + } + + /* + * Copy the expected data into a byte[] using a read-only view on the + * buffer so that we do not mess with its position, mark, or limit. + */ + final byte[] a; + { + + buf = buf.asReadOnlyBuffer(); + + final int len = buf.remaining(); + + a = new byte[len]; + + buf.get(a); + + } + + return a; + + } + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-04 14:17:47 UTC (rev 6653) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-04 14:59:40 UTC (rev 6654) @@ -10,6 +10,7 @@ import org.apache.log4j.Logger; +import com.bigdata.btree.BytesUtil; import com.bigdata.journal.IRootBlockView; import com.bigdata.journal.ha.HAWriteMessage; @@ -26,7 +27,10 @@ */ public class ProcessLogWriter { - private static final Logger log = Logger.getLogger(ProcessLogWriter.class); + /** + * Logger for HA events. + */ + protected static final Logger haLog = Logger.getLogger("com.bigdata.haLog"); /** HA log directory. */ private final File m_dir; @@ -136,23 +140,29 @@ if (rootBlock == null) throw new IllegalArgumentException(); - if (rootBlock.getCommitCounter() != this.m_rootBlock.getCommitCounter()) { + final long expectedCommitCounter = this.m_rootBlock.getCommitCounter() + 1; - throw new IllegalStateException(); + if (expectedCommitCounter != rootBlock.getCommitCounter()) { + throw new IllegalStateException("CommitCounter: expected=" + + expectedCommitCounter + ", actual=" + + rootBlock.getCommitCounter()); + } - if (rootBlock.getLastCommitTime() != this.m_rootBlock - .getLastCommitTime()) { +// if (rootBlock.getLastCommitTime() != this.m_rootBlock +// .getLastCommitTime()) { +// +// throw new IllegalStateException(); +// +// } - throw new IllegalStateException(); + if (!this.m_rootBlock.getUUID().equals(rootBlock.getUUID())) { - } + throw new IllegalStateException("Store UUID: expected=" + + (m_rootBlock.getUUID()) + ", actual=" + + rootBlock.getUUID()); - if (rootBlock.getUUID() != this.m_rootBlock.getUUID()) { - - throw new IllegalStateException(); - } writeRootBlock(rootBlock); @@ -169,10 +179,10 @@ if (rootBlock == null) throw new IllegalArgumentException(); - m_out.write(rootBlock.asReadOnlyBuffer().array()); + m_out.write(BytesUtil.getBytes(rootBlock.asReadOnlyBuffer())); - if (log.isDebugEnabled()) - log.debug("wrote root block: " + rootBlock); + if (haLog.isDebugEnabled()) + haLog.debug("wrote root block: " + rootBlock); } @@ -200,17 +210,23 @@ if (m_sequence != msg.getSequence()) return; - final byte[] array = data.array(); - m_out.writeObject(msg); switch(m_rootBlock.getStoreType()) { case RW: { + /* + * FIXME Efficient channel access and write. I think that we are + * much better off reusing the WORMStategy without the WriteCache + * and pre-serializing the HAWriteMessage as a byte[]. That will + * give us efficient, proven writes and a place to put both root + * blocks. + */ + final byte[] array = BytesUtil.getBytes(data); + assert msg.getSize() == array.length; - // TODO Efficient channel access and write - must flush first? - m_out.write(data.array()); + m_out.write(array); } case WORM: break; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/TestCase3.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/TestCase3.java 2012-10-04 14:17:47 UTC (rev 6653) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/TestCase3.java 2012-10-04 14:59:40 UTC (rev 6654) @@ -29,6 +29,7 @@ import java.nio.ByteBuffer; +import com.bigdata.btree.BytesUtil; import com.bigdata.journal.TestHelper; import junit.framework.TestCase; @@ -168,38 +169,10 @@ /** * Return the data in the buffer. */ - public static byte[] getBytes(ByteBuffer buf) { + public static byte[] getBytes(final ByteBuffer buf) { - if (buf.hasArray() && buf.arrayOffset() == 0 && buf.position() == 0 - && buf.limit() == buf.capacity()) { + return BytesUtil.getBytes(buf); - /* - * Return the backing array. - */ - - return buf.array(); - - } - - /* - * Copy the expected data into a byte[] using a read-only view on the - * buffer so that we do not mess with its position, mark, or limit. - */ - final byte[] a; - { - - buf = buf.asReadOnlyBuffer(); - - final int len = buf.remaining(); - - a = new byte[len]; - - buf.get(a); - - } - - return a; - } } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-04 14:17:47 UTC (rev 6653) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-04 14:59:40 UTC (rev 6654) @@ -37,6 +37,7 @@ import com.bigdata.concurrent.FutureTaskMon; import com.bigdata.ha.HAGlue; +import com.bigdata.ha.ProcessLogWriter; import com.bigdata.ha.QuorumService; import com.bigdata.io.writecache.WriteCache; import com.bigdata.journal.BufferMode; @@ -157,6 +158,25 @@ */ private final File haLogDir; + /** + * Write ahead log for replicated writes used to resynchronize services that + * are not in the met quorum. + * + * @see Options#HA_LOG_DIR + * @see ProcessLogWriter + */ + private final ProcessLogWriter haLogWriter; + + /** + * The {@link ProcessLogWriter} for this {@link HAJournal} and never + * <code>null</code>. + */ + ProcessLogWriter getHALogWriter() { + + return haLogWriter; + + } + public HAJournal(final Properties properties) { this(properties, null); @@ -187,7 +207,10 @@ haLogDir.mkdirs(); } - + + // Set up the HA log writer. + haLogWriter = new ProcessLogWriter(haLogDir); + } /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-04 14:17:47 UTC (rev 6653) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-04 14:59:40 UTC (rev 6654) @@ -149,12 +149,6 @@ */ private HAJournal journal; - /** - * Write ahead log for replicated writes used to resynchronize services that - * are not in the met quorum. - */ - private ProcessLogWriter haLogWriter = null; - private UUID serviceUUID; private HAGlue haGlueService; private ZookeeperClientConfig zkClientConfig; @@ -381,8 +375,6 @@ this.journal = new HAJournal(properties, quorum); - this.haLogWriter = new ProcessLogWriter(journal.getHALogDir()); - } haGlueService = journal.newHAGlue(serviceUUID); @@ -600,7 +592,7 @@ try { - server.haLogWriter.disable(); + journal.getHALogWriter().disable(); } catch (IOException e) { @@ -626,8 +618,8 @@ try { - server.haLogWriter - .createLog(journal.getRootBlockView()); + journal.getHALogWriter().createLog( + journal.getRootBlockView()); } catch (IOException e) { @@ -736,7 +728,7 @@ if (!HA_LOG_ENABLED) return; - server.haLogWriter.write(msg, data); + journal.getHALogWriter().write(msg, data); } @@ -752,7 +744,7 @@ if (!HA_LOG_ENABLED) return; - server.haLogWriter.closeLog(rootBlock); + journal.getHALogWriter().closeLog(rootBlock); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-04 20:31:57
|
Revision: 6655 http://bigdata.svn.sourceforge.net/bigdata/?rev=6655&view=rev Author: thompsonbry Date: 2012-10-04 20:31:50 +0000 (Thu, 04 Oct 2012) Log Message: ----------- The HA write log is now written by the leader and the joined followers. I have verified that it is binary identical on each node in a 2 node cluster using the 3-degrees of separation FOAF data. The main problems were (a) we were failing to open the new log when the old log was closed; and (b) the NSS start was being executed before we opened the first log. I fixed the latter issue by moving the NSS start into the HAQuorumService implementation. The underlying problem is that the QuorumListener in the HAJournalServer class was registered before the HAQuorumService, so it was observing the quorum events before the HAQuorumService and thus starting the NSS before we had opened the HA write log. I made a few changes to the ProcessLogWriter that will need to be reconciled. In particular, I added logging against the haLog and added some state checks in the public methods. The HA_LOG_ENABLED field is false in the committed code. The next step is to bring the third node online and work through the synchronization of that node from the ha log files in the quorum. https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-04 14:59:40 UTC (rev 6654) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-04 20:31:50 UTC (rev 6655) @@ -30,7 +30,7 @@ /** * Logger for HA events. */ - protected static final Logger haLog = Logger.getLogger("com.bigdata.haLog"); + private static final Logger haLog = Logger.getLogger("com.bigdata.haLog"); /** HA log directory. */ private final File m_dir; @@ -71,8 +71,14 @@ if (rootBlock == null) throw new IllegalArgumentException(); - this.m_rootBlock = rootBlock; + if (m_rootBlock != null) // may not be open. + throw new IllegalStateException(); + if (haLog.isInfoEnabled()) + haLog.info("rootBlock=" + rootBlock); + + m_rootBlock = rootBlock; + m_sequence = 0L; /* @@ -140,6 +146,12 @@ if (rootBlock == null) throw new IllegalArgumentException(); + if (m_rootBlock == null) // no root block associated with log. + throw new IllegalStateException(); + + if (haLog.isInfoEnabled()) + haLog.info("rootBlock=" + rootBlock); + final long expectedCommitCounter = this.m_rootBlock.getCommitCounter() + 1; if (expectedCommitCounter != rootBlock.getCommitCounter()) { @@ -210,6 +222,9 @@ if (m_sequence != msg.getSequence()) return; + if (haLog.isInfoEnabled()) + haLog.info("msg=" + msg); + m_out.writeObject(msg); switch(m_rootBlock.getStoreType()) { @@ -320,6 +335,9 @@ * Disable the current log file if one is open. */ public void disable() throws IOException { + + if (haLog.isInfoEnabled()) + haLog.info(""); /* * Remove unless log file was already closed. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-04 14:59:40 UTC (rev 6654) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-04 20:31:50 UTC (rev 6655) @@ -5072,7 +5072,7 @@ + localService.getServiceId()); ((IHABufferStrategy) _bufferStrategy) - .resetFromHARootBlock(_rootBlock); + .resetFromHARootBlock(rootBlock); /* * Clear reference and reload from the store. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-04 14:59:40 UTC (rev 6654) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-04 20:31:50 UTC (rev 6655) @@ -421,56 +421,30 @@ @Override public void notify(final QuorumEvent e) { if (log.isTraceEnabled()) - System.err.print("QuorumEvent: " + e); - switch(e.getEventType()) { - case CAST_VOTE: - break; - case CONSENSUS: - break; - case MEMBER_ADD: - break; - case MEMBER_REMOVE: - break; - case PIPELINE_ADD: - break; - case PIPELINE_REMOVE: - break; - case QUORUM_BROKE: - break; - case QUORUM_MEET: - if (jettyServer == null) { - /* - * The NSS will start on each service in the quorum. - * However, only the leader will create the default KB - * (if that option is configured). - * - * Submit task since we can not do this in the event - * thread. - */ - journal.getExecutorService().submit( - new Callable<Void>() { - @Override - public Void call() throws Exception { - if (jettyServer == null) { - try { - startNSS(); - } catch (Exception e1) { - log.error( - "Could not start NanoSparqlServer: " - + e1, e1); - } - } - return null; - } - }); - } - break; - case SERVICE_JOIN: - break; - case SERVICE_LEAVE: - break; - case WITHDRAW_VOTE: - } + haLog.trace("QuorumEvent: " + e); +// switch(e.getEventType()) { +// case CAST_VOTE: +// break; +// case CONSENSUS: +// break; +// case MEMBER_ADD: +// break; +// case MEMBER_REMOVE: +// break; +// case PIPELINE_ADD: +// break; +// case PIPELINE_REMOVE: +// break; +// case QUORUM_BROKE: +// break; +// case QUORUM_MEET: +// break; +// case SERVICE_JOIN: +// break; +// case SERVICE_LEAVE: +// break; +// case WITHDRAW_VOTE: +// } } }); @@ -608,12 +582,12 @@ public void quorumMeet(final long token, final UUID leaderId) { super.quorumMeet(token, leaderId); - + // Inform the journal that there is a new quorum token. journal.setQuorumToken(token); if (HA_LOG_ENABLED) { - + if (isJoinedMember(token)) { try { @@ -633,18 +607,43 @@ } - } else if(isPipelineMember()) { - + } else if (isPipelineMember()) { + /* * FIXME RESYNC : Start the resynchronization protocol. */ - haLog.info("RESYNCH REQUIRED: " + server.getServiceName()); + haLog.warn("RESYNCH REQUIRED: " + server.getServiceName()); } } + if (isJoinedMember(token) && server.jettyServer == null) { + /* + * The NSS will start on each service in the quorum. However, + * only the leader will create the default KB (if that option is + * configured). + * + * Submit task since we can not do this in the event thread. + */ + journal.getExecutorService().submit(new Callable<Void>() { + @Override + public Void call() throws Exception { + if (server.jettyServer == null) { + try { + server.startNSS(); + } catch (Exception e1) { + log.error("Could not start NanoSparqlServer: " + + e1, e1); + } + } + return null; + } + }); + + } + } /** @@ -736,7 +735,8 @@ * {@inheritDoc} * <p> * Writes the root block onto the {@link ProcessLogWriter} and closes - * the log file. + * the log file. A new log is then opened, using the given root block as + * the starting point for that log file. */ @Override public void logRootBlock(final IRootBlockView rootBlock) throws IOException { @@ -744,8 +744,12 @@ if (!HA_LOG_ENABLED) return; + // Close off the old log file with the root block. journal.getHALogWriter().closeLog(rootBlock); + // Open up a new log file with this root block. + journal.getHALogWriter().createLog(rootBlock); + } /** This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-05 13:16:52
|
Revision: 6657 http://bigdata.svn.sourceforge.net/bigdata/?rev=6657&view=rev Author: thompsonbry Date: 2012-10-05 13:16:38 +0000 (Fri, 05 Oct 2012) Log Message: ----------- Refactored the HA messages to be pure interfaces and moved them into a com.bigdata.ha.msg package, along with the test suite for those messages. I wanted to do this now (the interface stuff) because I believe that the easiest way to handle the non-joined services that are in the pipeline is to send them the 2-phase prepare/commit/abort messages, but to notice and indicate that they are advisory messages rather than required messaged based on whether or not a service in the pipeline is also joined (in the met quorum). That way all pipeline services will see the root blocks and can update their ha log files appropriately. - done. HAWriteMessage: Make interface (globally replace class for both this and HAWriteMessage with IHAWriteMessage interfaces). - done. HACommitGlue: methods should have interface args. - done. HAReadGlue: method should have interface args and interface result. - done. HAGlue.getRootBlock() - argument should be interface. @see https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HACommitGlue.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlue.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAReadGlue.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommit.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumReadImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/pipeline/HAReceiveService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IHABufferStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RWStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RootBlockView.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestAll.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/pipeline/TestHASendAndReceive.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/pipeline/TestHASendAndReceive3Nodes.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/AbstractHAJournalTestCase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/TestHAWritePipeline.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/quorum/MockQuorumFixture.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/test/com/bigdata/quorum/zk/MockQuorumMember.java Added Paths: ----------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseAbortMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseCommitMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhasePrepareMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAReadRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAReadResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HARootBlockRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HARootBlockResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAWriteMessageBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseAbortMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseCommitMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhasePrepareMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAReadRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAReadResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHARootBlockRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHARootBlockResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAWriteMessageBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/package.html branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/msg/ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/msg/TestAll.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/msg/TestHAWriteMessage.java Removed Paths: ------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/pipeline/HAWriteMessageBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/package.html branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/TestHAWriteMessage.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HACommitGlue.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HACommitGlue.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HACommitGlue.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -30,8 +30,10 @@ import java.io.IOException; import java.rmi.Remote; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import com.bigdata.ha.msg.IHA2PhaseAbortMessage; +import com.bigdata.ha.msg.IHA2PhaseCommitMessage; +import com.bigdata.ha.msg.IHA2PhasePrepareMessage; import com.bigdata.journal.AbstractJournal; /** @@ -50,30 +52,25 @@ * channel and acknowledge "yes" if ready to commit. If the node can not * prepare for any reason, then it must return "no". * - * @param isRootBlock0 - * <code>true</code> if this is rootBlock0 for the leader. - * @param rootBlock - * The new root block. - * @param timeout - * How long to wait for the other services to prepare. - * @param unit - * The unit for the timeout. - + * @param prepareMessage + * The message used to prepare for the commit. + * * @return A {@link Future} which evaluates to a yes/no vote on whether the * service is prepared to commit. */ - Future<Boolean> prepare2Phase(boolean isRootBlock0, byte[] rootBlock, - long timeout, TimeUnit unit) throws IOException; + Future<Boolean> prepare2Phase(IHA2PhasePrepareMessage prepareMessage) + throws IOException; /** * Commit using the root block from the corresponding prepare message. It is * an error if a commit message is observed without the corresponding * prepare message. * - * @param commitTime - * The commit time that will be assigned to the new commit point. + * @param commitMessage + * The commit message. */ - Future<Void> commit2Phase(long commitTime) throws IOException; + Future<Void> commit2Phase(IHA2PhaseCommitMessage commitMessage) + throws IOException; /** * Discard the current write set using {@link AbstractJournal#abort()}, @@ -88,6 +85,7 @@ * @param token * The token for the quorum for which this request was made. */ - Future<Void> abort2Phase(long token) throws IOException; + Future<Void> abort2Phase(IHA2PhaseAbortMessage abortMessage) + throws IOException; } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlue.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlue.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlue.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -1,3 +1,26 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.ha; import java.io.IOException; @@ -2,5 +25,6 @@ import java.rmi.Remote; -import java.util.UUID; import java.util.concurrent.Future; +import com.bigdata.ha.msg.IHARootBlockRequest; +import com.bigdata.ha.msg.IHARootBlockResponse; import com.bigdata.journal.AbstractJournal; @@ -59,15 +83,12 @@ * are identical, so this may be used to create a new journal in a quorum by * replicating the root blocks of the quorum leader. * - * @param storeId - * The {@link UUID} of the journal whose root block will be - * returned (optional, defaults to the current Journal). This - * parameter is intended for scale-out if there is a need to - * fetch the root block of a historical journal (versus the live - * journal). + * @param msg + * The message requesting the root block. * * @return The root block. */ - byte[] getRootBlock(UUID storeId) throws IOException; + IHARootBlockResponse getRootBlock(IHARootBlockRequest msg) + throws IOException; } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -28,10 +28,15 @@ import java.rmi.RemoteException; import java.util.UUID; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import com.bigdata.ha.msg.IHA2PhaseAbortMessage; +import com.bigdata.ha.msg.IHA2PhaseCommitMessage; +import com.bigdata.ha.msg.IHA2PhasePrepareMessage; +import com.bigdata.ha.msg.IHAReadRequest; +import com.bigdata.ha.msg.IHAReadResponse; +import com.bigdata.ha.msg.IHARootBlockRequest; +import com.bigdata.ha.msg.IHARootBlockResponse; import com.bigdata.journal.ValidationError; -import com.bigdata.journal.ha.HAWriteMessage; /** * Delegation pattern. @@ -59,37 +64,39 @@ return delegate.getServiceId(); } - public Future<Boolean> prepare2Phase(boolean isRootBlock0, - byte[] rootBlock, long timeout, TimeUnit unit) throws IOException { - return delegate.prepare2Phase(isRootBlock0, rootBlock, timeout, unit); + public Future<Boolean> prepare2Phase(IHA2PhasePrepareMessage msg) + throws IOException { + return delegate.prepare2Phase(msg); } - public Future<byte[]> readFromDisk(long token, UUID storeId, long addr) - throws IOException { - return delegate.readFromDisk(token, storeId, addr); + public Future<IHAReadResponse> readFromDisk( + IHAReadRequest readMessage) throws IOException { + return delegate.readFromDisk(readMessage); } public InetSocketAddress getWritePipelineAddr() throws IOException { return delegate.getWritePipelineAddr(); } - public byte[] getRootBlock(UUID storeId) throws IOException { - return delegate.getRootBlock(storeId); + public IHARootBlockResponse getRootBlock(IHARootBlockRequest msg) throws IOException { + return delegate.getRootBlock(msg); } public Future<Void> moveToEndOfPipeline() throws IOException { return delegate.moveToEndOfPipeline(); } - public Future<Void> commit2Phase(long commitTime) throws IOException { - return delegate.commit2Phase(commitTime); + public Future<Void> commit2Phase(IHA2PhaseCommitMessage commitMessage) + throws IOException { + return delegate.commit2Phase(commitMessage); } - public Future<Void> abort2Phase(long token) throws IOException { - return delegate.abort2Phase(token); + public Future<Void> abort2Phase(IHA2PhaseAbortMessage abortMessage) + throws IOException { + return delegate.abort2Phase(abortMessage); } - public Future<Void> receiveAndReplicate(HAWriteMessage msg) + public Future<Void> receiveAndReplicate(IHAWriteMessage msg) throws IOException { return delegate.receiveAndReplicate(msg); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -33,7 +33,6 @@ import java.util.concurrent.Future; import com.bigdata.journal.WriteExecutorService; -import com.bigdata.journal.ha.HAWriteMessage; /** * A {@link Remote} interface supporting the write replication pipeline. The @@ -119,6 +118,6 @@ * @return The {@link Future} which will become available once the buffer * transfer is complete. */ - Future<Void> receiveAndReplicate(HAWriteMessage msg) throws IOException; + Future<Void> receiveAndReplicate(IHAWriteMessage msg) throws IOException; } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAReadGlue.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAReadGlue.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAReadGlue.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -32,6 +32,8 @@ import java.util.UUID; import java.util.concurrent.Future; +import com.bigdata.ha.msg.IHAReadRequest; +import com.bigdata.ha.msg.IHAReadResponse; import com.bigdata.rawstore.IRawStore; /** @@ -54,18 +56,15 @@ * because there is a checksum error when reading on the {@link IRawStore}, * then that exception should be thrown back to the caller. * - * @param storeId - * The {@link UUID} identifying the {@link IRawStore} for which - * the record was requested. - * @param addr - * The address of the record. + * @param readMessage + * The {@link IHAReadRequest}. * * @return The {@link Future} of an operation which evaluated to the desired * record. * * @see QuorumRead#readFromQuorum(UUID, long) */ - public Future<byte[]> readFromDisk(final long token, UUID storeId, long addr) + public Future<IHAReadResponse> readFromDisk(IHAReadRequest readMessage) throws IOException; } Copied: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java (from rev 6649, branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/ha/HAWriteMessage.java) =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -0,0 +1,259 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + * Created on May 18, 2010 + */ + +package com.bigdata.ha; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import com.bigdata.ha.msg.HAWriteMessageBase; +import com.bigdata.journal.StoreTypeEnum; + +/** + * A message carrying RMI metadata about a payload which will be replicated + * using a socket-level transfer facility. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + * @version $Id$ + */ +public class HAWriteMessage extends HAWriteMessageBase implements + IHAWriteMessage { + + /** + * + */ + private static final long serialVersionUID = -2673171474897401979L; + + /** The most recent commit counter associated with this message */ + private long commitCounter; + + /** The most recent commit time associated with this message */ + private long lastCommitTime; + + /** The write sequence since last commit beginning at zero */ + private long sequence; + + /** The type of backing store (RW or WORM). */ + private StoreTypeEnum storeType; + + /** The quorum token for which this message is valid. */ + private long quorumToken; + + /** The length of the backing file on the disk. */ + private long fileExtent; + + /** The file offset at which the data will be written (WORM only). */ + private long firstOffset; + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getCommitCounter() + */ + @Override + public long getCommitCounter() { + return commitCounter; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getLastCommitTime() + */ + @Override + public long getLastCommitTime() { + return lastCommitTime; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getSequence() + */ + @Override + public long getSequence() { + return sequence; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getStoreType() + */ + @Override + public StoreTypeEnum getStoreType() { + return storeType; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getQuorumToken() + */ + @Override + public long getQuorumToken() { + return quorumToken; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getFileExtent() + */ + @Override + public long getFileExtent() { + return fileExtent; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getFirstOffset() + */ + @Override + public long getFirstOffset() { + return firstOffset; + } + + public String toString() { + + return getClass().getName() // + + "{size=" + getSize() // + + ",chksum=" + getChk() // + + ",commitCounter=" + commitCounter // + + ",commitTime=" + lastCommitTime // + + ",sequence=" + sequence // + + ",storeType=" + getStoreType() // + + ",quorumToken=" + getQuorumToken()// + + ",fileExtent=" + getFileExtent() // + + ",firstOffset=" + getFirstOffset() // + + "}"; + + } + + /** + * De-serialization constructor. + */ + public HAWriteMessage() { + } + + /** + * @param commitCounter + * The commit counter for the current root block for the write + * set which is being replicated by this message. + * @param commitTime + * The commit time for the current root block for the write set + * which is being replicated by this message. + * @param sequence + * The write cache block sequence number. This is reset to ZERO + * (0) for the first replicated write cache block in each write + * set. + * @param sze + * The #of bytes in the payload. + * @param chk + * The checksum of the payload. + * @param storeType + * The type of backing store (RW or WORM). + * @param quorumToken + * The quorum token for which this message is valid. + * @param fileExtent + * The length of the backing file on the disk. + * @param firstOffset + * The file offset at which the data will be written (WORM only). + */ + public HAWriteMessage(final long commitCounter, final long commitTime, + final long sequence, final int sze, final int chk, + final StoreTypeEnum storeType, final long quorumToken, + final long fileExtent, final long firstOffset) { + + super(sze, chk); + + if (storeType == null) + throw new IllegalArgumentException(); + + this.commitCounter = commitCounter; + + this.lastCommitTime = commitTime; + + this.sequence = sequence; + + this.storeType = storeType; + + this.quorumToken = quorumToken; + + this.fileExtent = fileExtent; + + this.firstOffset = firstOffset; + + } + + private static final byte VERSION0 = 0x0; + + @Override + public boolean equals(final Object obj) { + + if (this == obj) + return true; + + if (!super.equals(obj)) + return false; + + if (!(obj instanceof IHAWriteMessage)) + return false; + + final IHAWriteMessage other = (IHAWriteMessage) obj; + + return commitCounter == other.getCommitCounter() + && lastCommitTime == other.getLastCommitTime() // + && sequence == other.getSequence() + && storeType == other.getStoreType() + && quorumToken == other.getQuorumToken() + && fileExtent == other.getFileExtent() + && firstOffset == other.getFirstOffset(); + + } + + public void readExternal(final ObjectInput in) throws IOException, + ClassNotFoundException { + + super.readExternal(in); + final byte version = in.readByte(); + switch (version) { + case VERSION0: + break; + default: + throw new IOException("Unknown version: " + version); + } + storeType = StoreTypeEnum.valueOf(in.readByte()); + commitCounter = in.readLong(); + lastCommitTime = in.readLong(); + sequence = in.readLong(); + quorumToken = in.readLong(); + fileExtent = in.readLong(); + firstOffset = in.readLong(); + } + + public void writeExternal(final ObjectOutput out) throws IOException { + super.writeExternal(out); + out.write(VERSION0); + out.writeByte(storeType.getType()); + out.writeLong(commitCounter); + out.writeLong(lastCommitTime); + out.writeLong(sequence); + out.writeLong(quorumToken); + out.writeLong(fileExtent); + out.writeLong(firstOffset); + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -0,0 +1,62 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package com.bigdata.ha; + +import com.bigdata.ha.msg.IHAWriteMessageBase; +import com.bigdata.journal.StoreTypeEnum; + +/** + * A message carrying RMI metadata about a payload which will be replicated + * using a socket-level transfer facility. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ +public interface IHAWriteMessage extends IHAWriteMessageBase { + + /** The commit counter associated with this message */ + long getCommitCounter(); + + /** The commit time associated with this message. */ + long getLastCommitTime(); + + /** + * The write cache buffer sequence number (reset to ZERO (0) for the first + * message after each commit and incremented for each buffer sent by the + * leader). + */ + long getSequence(); + + /** The type of backing store (RW or WORM). */ + StoreTypeEnum getStoreType(); + + /** The quorum token for which this message is valid. */ + long getQuorumToken(); + + /** The length of the backing file on the disk. */ + long getFileExtent(); + + /** The file offset at which the data will be written (WORM only). */ + long getFirstOffset(); + +} \ No newline at end of file Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -12,7 +12,6 @@ import com.bigdata.btree.BytesUtil; import com.bigdata.journal.IRootBlockView; -import com.bigdata.journal.ha.HAWriteMessage; /** * Wrapper class to handle process log creation and output for HA. @@ -203,7 +202,7 @@ * @param msg * @param data */ - public void write(final HAWriteMessage msg, final ByteBuffer data) + public void write(final IHAWriteMessage msg, final ByteBuffer data) throws IOException { if (m_out == null) Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommit.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommit.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommit.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -68,7 +68,7 @@ * @return A {@link Future} which evaluates to a yes/no vote on whether the * service is prepared to commit. */ - int prepare2Phase(boolean isRootBlock0, IRootBlockView rootBlock, + int prepare2Phase(IRootBlockView rootBlock, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, IOException; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -34,7 +34,12 @@ import org.apache.log4j.Logger; -import com.bigdata.btree.BytesUtil; +import com.bigdata.ha.msg.HA2PhaseAbortMessage; +import com.bigdata.ha.msg.HA2PhaseCommitMessage; +import com.bigdata.ha.msg.HA2PhasePrepareMessage; +import com.bigdata.ha.msg.IHA2PhaseAbortMessage; +import com.bigdata.ha.msg.IHA2PhaseCommitMessage; +import com.bigdata.ha.msg.IHA2PhasePrepareMessage; import com.bigdata.journal.IRootBlockView; import com.bigdata.quorum.Quorum; import com.bigdata.quorum.QuorumMember; @@ -112,7 +117,7 @@ * asynchronously on their side while the leader awaits their future's using * get(). */ - public int prepare2Phase(final boolean isRootBlock0, + public int prepare2Phase(//final boolean isRootBlock0, final IRootBlockView rootBlock, final long timeout, final TimeUnit unit) throws InterruptedException, TimeoutException, IOException { @@ -123,10 +128,12 @@ if (unit == null) throw new IllegalArgumentException(); + final boolean isRootBlock0 = rootBlock.isRootBlock0(); + if (log.isInfoEnabled()) - log.info("isRootBlock0=" + isRootBlock0 + ", rootBlock=" - + rootBlock + ", timeout=" + timeout + ", unit=" + unit); - + log.info("isRootBlock0=" + isRootBlock0 + "rootBlock=" + rootBlock + + ", timeout=" + timeout + ", unit=" + unit); + /* * The token of the quorum for which the leader issued this prepare * message. @@ -164,7 +171,9 @@ // Verify the quorum is valid. member.assertLeader(token); - final byte[] tmp = BytesUtil.toArray(rootBlock.asReadOnlyBuffer()); +// final byte[] tmp = BytesUtil.toArray(rootBlock.asReadOnlyBuffer()); + final IHA2PhasePrepareMessage msg = new HA2PhasePrepareMessage( + rootBlock, timeout, unit); for (int i = 1; i < joinedServiceIds.length; i++) { @@ -173,8 +182,7 @@ /* * Runnable which will execute this message on the remote service. */ - final Future<Boolean> rf = getService(serviceId).prepare2Phase( - isRootBlock0, tmp, timeout, unit); + final Future<Boolean> rf = getService(serviceId).prepare2Phase(msg); // add to list of futures we will check. remoteFutures.add(rf); @@ -190,15 +198,14 @@ { /* - * Run the operation on the leader using local method call in the - * caller's thread to avoid deadlock. + * Run the operation on the leader using local method call (non-RMI) + * in the caller's thread to avoid deadlock. * * Note: Because we are running this in the caller's thread on the * leader the timeout will be ignored for the leader. */ final S leader = member.getService(); - final Future<Boolean> f = leader.prepare2Phase(isRootBlock0, tmp, - timeout, unit); + final Future<Boolean> f = leader.prepare2Phase(msg); remoteFutures.add(f); // /* // * Note: This runs synchronously in the caller's thread (it ignores @@ -275,6 +282,8 @@ member.assertLeader(token); + final IHA2PhaseCommitMessage msg = new HA2PhaseCommitMessage(commitTime); + for (int i = 1; i < joinedServiceIds.length; i++) { final UUID serviceId = joinedServiceIds[i]; @@ -282,8 +291,7 @@ /* * Runnable which will execute this message on the remote service. */ - final Future<Void> rf = getService(serviceId).commit2Phase( - commitTime); + final Future<Void> rf = getService(serviceId).commit2Phase(msg); // add to list of futures we will check. remoteFutures.add(rf); @@ -303,7 +311,7 @@ * caller's thread to avoid deadlock. */ final S leader = member.getService(); - final Future<Void> f = leader.commit2Phase(commitTime); + final Future<Void> f = leader.commit2Phase(msg); remoteFutures.add(f); // // Note: This runs synchronously (ignores timeout). // f.run(); @@ -380,6 +388,8 @@ final UUID[] joinedServiceIds = getQuorum().getJoined(); member.assertLeader(token); + + final IHA2PhaseAbortMessage msg = new HA2PhaseAbortMessage(token); for (int i = 1; i < joinedServiceIds.length; i++) { @@ -388,7 +398,7 @@ /* * Runnable which will execute this message on the remote service. */ - final Future<Void> rf = getService(serviceId).abort2Phase(token); + final Future<Void> rf = getService(serviceId).abort2Phase(msg); // add to list of futures we will check. remoteFutures.add(rf); @@ -409,7 +419,7 @@ */ member.assertLeader(token); final S leader = member.getService(); - final Future<Void> f = leader.abort2Phase(token); + final Future<Void> f = leader.abort2Phase(msg); remoteFutures.add(f); // // Note: This runs synchronously (ignores timeout). // f.run(); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -1,6 +1,6 @@ /** -Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. +wCopyright (C) SYSTAP, LLC 2006-2010. All rights reserved. Contact: SYSTAP, LLC @@ -33,7 +33,6 @@ import com.bigdata.io.writecache.WriteCache; import com.bigdata.journal.IRootBlockView; -import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.Quorum; /** @@ -50,14 +49,14 @@ * along the write pipeline. This method is only invoked by the quorum * leader. The payload is replicated to the first follower in the write * pipeline. That follower will accept the payload (and replicate it if - * necessary) using {@link #receiveAndReplicate(HAWriteMessage)}. + * necessary) using {@link #receiveAndReplicate(IHAWriteMessage)}. * * @param msg * The RMI metadata about the payload. * @param b * The payload. */ - Future<Void> replicate(HAWriteMessage msg, ByteBuffer b) throws IOException; + Future<Void> replicate(IHAWriteMessage msg, ByteBuffer b) throws IOException; /** * Return a {@link Future} for a task which will replicate an NIO buffer @@ -67,7 +66,7 @@ * @param msg * The RMI metadata about the payload. */ - Future<Void> receiveAndReplicate(HAWriteMessage msg) throws IOException; + Future<Void> receiveAndReplicate(IHAWriteMessage msg) throws IOException; /* * Note: Method removed since it does not appear necessary to let this @@ -101,7 +100,7 @@ /** * Return the lastCommitTime for this service (based on its current root - * block). This supports the {@link HAWriteMessage} which requires this + * block). This supports the {@link IHAWriteMessage} which requires this * information as part of the metadata about replicated {@link WriteCache} * blocks. */ @@ -109,23 +108,23 @@ /** * Return the lastCommitCounter for this service (based on its current root - * block). This supports the {@link HAWriteMessage} which requires this + * block). This supports the {@link IHAWriteMessage} which requires this * information as part of the metadata about replicated {@link WriteCache} * blocks. */ long getLastCommitCounter(); /** - * Log the {@link HAWriteMessage} and the associated data (if necessary). + * Log the {@link IHAWriteMessage} and the associated data (if necessary). * The log file for the current write set will be deleted if the quorum is * fully met at the next 2-phase commit. * * @param msg - * The {@link HAWriteMessage}. + * The {@link IHAWriteMessage}. * @param data * The {@link WriteCache} block. */ - void logWriteCacheBlock(final HAWriteMessage msg, + void logWriteCacheBlock(final IHAWriteMessage msg, final ByteBuffer data) throws IOException; /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -1,3 +1,26 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.ha; import java.io.Externalizable; @@ -23,8 +46,6 @@ import com.bigdata.ha.pipeline.HASendService; import com.bigdata.io.DirectBufferPool; import com.bigdata.io.IBufferAccess; -import com.bigdata.journal.IRootBlockView; -import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.QuorumException; import com.bigdata.quorum.QuorumMember; import com.bigdata.quorum.QuorumStateChangeListener; @@ -150,7 +171,7 @@ /** * The receive service (iff this is a follower in a met quorum). */ - private HAReceiveService<HAWriteMessage> receiveService; + private HAReceiveService<IHAWriteMessage> receiveService; /** * The buffer used to relay the data. This is only allocated for a @@ -246,7 +267,7 @@ * @return The buffer -or- <code>null</code> if the pipeline has been torn * down or if this is the leader. */ - private HAReceiveService<HAWriteMessage> getHAReceiveService() { + private HAReceiveService<IHAWriteMessage> getHAReceiveService() { if (!lock.isHeldByCurrentThread()) { @@ -593,9 +614,9 @@ final InetSocketAddress addrNext = downstreamId == null ? null : member.getService(downstreamId).getWritePipelineAddr(); // Setup the receive service. - receiveService = new HAReceiveService<HAWriteMessage>(addrSelf, - addrNext, new IHAReceiveCallback<HAWriteMessage>() { - public void callback(HAWriteMessage msg, ByteBuffer data) + receiveService = new HAReceiveService<IHAWriteMessage>(addrSelf, + addrNext, new IHAReceiveCallback<IHAWriteMessage>() { + public void callback(IHAWriteMessage msg, ByteBuffer data) throws Exception { // delegate handling of write cache blocks. handleReplicatedWrite(msg, data); @@ -625,7 +646,7 @@ /* * This is the leader, so send() the buffer. */ - public Future<Void> replicate(final HAWriteMessage msg, final ByteBuffer b) + public Future<Void> replicate(final IHAWriteMessage msg, final ByteBuffer b) throws IOException { final RunnableFuture<Void> ft; @@ -719,12 +740,12 @@ static private class SendBufferTask<S extends HAPipelineGlue> implements Callable<Void> { - private final HAWriteMessage msg; + private final IHAWriteMessage msg; private final ByteBuffer b; private final PipelineState<S> downstream; private final HASendService sendService; - public SendBufferTask(final HAWriteMessage msg, final ByteBuffer b, + public SendBufferTask(final IHAWriteMessage msg, final ByteBuffer b, final PipelineState<S> downstream, final HASendService sendService) { this.msg = msg; @@ -784,7 +805,7 @@ } - public Future<Void> receiveAndReplicate(final HAWriteMessage msg) + public Future<Void> receiveAndReplicate(final IHAWriteMessage msg) throws IOException { final RunnableFuture<Void> ft; @@ -816,7 +837,7 @@ final ByteBuffer b = getReceiveBuffer(); - final HAReceiveService<HAWriteMessage> receiveService = getHAReceiveService(); + final HAReceiveService<IHAWriteMessage> receiveService = getHAReceiveService(); if (downstream == null) { @@ -924,14 +945,14 @@ private static class ReceiveAndReplicateTask<S extends HAPipelineGlue> implements Callable<Void> { - private final HAWriteMessage msg; + private final IHAWriteMessage msg; private final ByteBuffer b; private final PipelineState<S> downstream; - private final HAReceiveService<HAWriteMessage> receiveService; + private final HAReceiveService<IHAWriteMessage> receiveService; - public ReceiveAndReplicateTask(final HAWriteMessage msg, + public ReceiveAndReplicateTask(final IHAWriteMessage msg, final ByteBuffer b, final PipelineState<S> downstream, - final HAReceiveService<HAWriteMessage> receiveService) { + final HAReceiveService<IHAWriteMessage> receiveService) { this.msg = msg; this.b = b; @@ -1003,7 +1024,7 @@ * * @throws Exception */ - abstract protected void handleReplicatedWrite(final HAWriteMessage msg, + abstract protected void handleReplicatedWrite(final IHAWriteMessage msg, final ByteBuffer data) throws Exception; // @Override Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumReadImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumReadImpl.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumReadImpl.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -31,6 +31,9 @@ import org.apache.log4j.Logger; +import com.bigdata.ha.msg.HAReadRequest; +import com.bigdata.ha.msg.IHAReadRequest; +import com.bigdata.ha.msg.IHAReadResponse; import com.bigdata.quorum.QuorumMember; import com.bigdata.quorum.QuorumStateChangeListenerBase; import com.bigdata.rawstore.IRawStore; @@ -145,15 +148,16 @@ // The RMI interface for the service on which we will read. final S otherService = member.getService(otherId); + final IHAReadRequest msg = new HAReadRequest(token, + storeId, addr); /* * Read from that service. The request runs in the caller's thread. */ try { - final Future<byte[]> rf = otherService.readFromDisk(token, - storeId, addr); + final Future<IHAReadResponse> rf = otherService.readFromDisk(msg); - final byte[] a = rf.get(); + final byte[] a = rf.get().getData(); // Verify quorum is still valid. member.getQuorum().assertQuorum(token); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-05 10:04:16 UTC (rev 6656) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -42,7 +42,6 @@ import com.bigdata.journal.IResourceManager; import com.bigdata.journal.IRootBlockView; import com.bigdata.journal.Journal; -import com.bigdata.journal.ha.HAWriteMessage; import com.bigdata.quorum.AbstractQuorumMember; /** @@ -101,7 +100,7 @@ addListener(this.pipelineImpl = new QuorumPipelineImpl<S>(this) { @Override - protected void handleReplicatedWrite(final HAWriteMessage msg, + protected void handleReplicatedWrite(final IHAWriteMessage msg, final ByteBuffer data) throws Exception { QuorumServiceBase.this.handleReplicatedWrite(msg, data); @@ -123,7 +122,7 @@ } @Override - public void logWriteCacheBlock(final HAWriteMessage msg, + public void logWriteCacheBlock(final IHAWriteMessage msg, final ByteBuffer data) throws IOException { QuorumServiceBase.this.logWriteCacheBlock(msg, data); @@ -207,7 +206,7 @@ // } @Override - public Future<Void> receiveAndReplicate(final HAWriteMessage msg) + public Future<Void> receiveAndReplicate(final IHAWriteMessage msg) throws IOException { return pipelineImpl.receiveAndReplicate(msg); @@ -215,7 +214,7 @@ } @Override - public Future<Void> replicate(final HAWriteMessage msg, final ByteBuffer b) + public Future<Void> replicate(final IHAWriteMessage msg, final ByteBuffer b) throws IOException { return pipelineImpl.replicate(msg, b); @@ -234,9 +233,9 @@ * * @throws Exception * - * @see QuorumPipelineImpl#handleReplicatedWrite(HAWriteMessage, ByteBuffer) + * @see QuorumPipelineImpl#handleReplicatedWrite(IHAWriteMessage, ByteBuffer) */ - abstract protected void handleReplicatedWrite(HAWriteMessage msg, + abstract protected void handleReplicatedWrite(IHAWriteMessage msg, ByteBuffer data) throws Exception; /** @@ -245,7 +244,7 @@ * Note: The default implementation is a NOP. */ @Override - public void logWriteCacheBlock(final HAWriteMessage msg, + public void logWriteCacheBlock(final IHAWriteMessage msg, final ByteBuffer data) throws IOException { // NOP @@ -297,12 +296,13 @@ } @Override - public int prepare2Phase(final boolean isRootBlock0, + public int prepare2Phase(//final boolean isRootBlock0, final IRootBlockView rootBlock, final long timeout, final TimeUnit unit) throws InterruptedException, TimeoutException, IOException { - return commitImpl.prepare2Phase(isRootBlock0, rootBlock, timeout, unit); + return commitImpl.prepare2Phase(/* isRootBlock0, */rootBlock, timeout, + unit); } Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseAbortMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseAbortMessage.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseAbortMessage.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -0,0 +1,46 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha.msg; + +import java.io.Serializable; + + +public class HA2PhaseAbortMessage implements IHA2PhaseAbortMessage, Serializable { + + private static final long serialVersionUID = 1L; + + private final long quorumToken; + + public HA2PhaseAbortMessage(final long quorumToken) { + + this.quorumToken = quorumToken; + + } + + @Override + public long getQuorumToken() { + return quorumToken; + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseCommitMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseCommitMessage.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseCommitMessage.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -0,0 +1,46 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha.msg; + +import java.io.Serializable; + + +public class HA2PhaseCommitMessage implements IHA2PhaseCommitMessage, Serializable { + + private static final long serialVersionUID = 1L; + + private final long commitTime; + + public HA2PhaseCommitMessage(final long commitTime) { + + this.commitTime = commitTime; + + } + + @Override + public long getCommitTime() { + return commitTime; + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhasePrepareMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhasePrepareMessage.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhasePrepareMessage.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -0,0 +1,94 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha.msg; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import com.bigdata.btree.BytesUtil; +import com.bigdata.journal.IRootBlockView; +import com.bigdata.journal.RootBlockView; +import com.bigdata.util.ChecksumUtility; + +public class HA2PhasePrepareMessage implements IHA2PhasePrepareMessage, Serializable { + + private static final long serialVersionUID = 1L; + + private final boolean isRootBlock0; + private final byte[] rootBlock; + private final long timeout; + private final TimeUnit unit; + + public HA2PhasePrepareMessage(final IRootBlockView rootBlock, + final long timeout, final TimeUnit unit) { + + if (rootBlock == null) + throw new IllegalArgumentException(); + + if (timeout < 0L) + throw new IllegalArgumentException(); + + if (unit == null) + throw new IllegalArgumentException(); + + this.isRootBlock0 = rootBlock.isRootBlock0(); + + /* + * Convert to a byte[]. + * + * Note: This does NOT preserve the isRootBlock0 flag! + */ + this.rootBlock = BytesUtil.getBytes(rootBlock.asReadOnlyBuffer()); + + this.timeout = timeout; + + this.unit = unit; + + } + + @Override + public boolean isRootBlock0() { + return isRootBlock0; + } + + @Override + public IRootBlockView getRootBlock() { + + return new RootBlockView(isRootBlock0, ByteBuffer.wrap(rootBlock), + new ChecksumUtility()); + + } + + @Override + public long getTimeout() { + return timeout; + } + + @Override + public TimeUnit getUnit() { + return unit; + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAReadRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAReadRequest.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAReadRequest.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -0,0 +1,76 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha.msg; + +import java.io.Serializable; +import java.util.UUID; + +import com.bigdata.rawstore.IRawStore; + +public class HAReadRequest implements IHAReadRequest, + Serializable { + + private static final long serialVersionUID = 1L; + + private final long quorumToken; + private final UUID storeUUID; + private final long addr; + + public HAReadRequest(final long quorumToken, final UUID storeUUID, final long addr) { + + // Note: Optional. +// if (storeUUID == null) +// throw new IllegalArgumentException(); + + if (addr == IRawStore.NULL) + throw new IllegalArgumentException(); + + this.quorumToken = quorumToken; + this.storeUUID = storeUUID; + this.addr = addr; + + } + + @Override + public long getQuorumToken() { + + return quorumToken; + + } + + @Override + public UUID getStoreUUID() { + + return storeUUID; + + } + + @Override + public long getAddr() { + + return addr; + + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAReadResponse.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAReadResponse.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAReadResponse.java 2012-10-05 13:16:38 UTC (rev 6657) @@ -0,0 +1,50 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha.msg; + +import java.io.Serializable; + +public class HAReadResponse implements IHAReadResponse, Serializable { + + private static final long serialVersionUID = 1L; + + final private byte[] data; + + public HAReadResponse(final byte[] data) { + + if (data == null) + throw new IllegalArgumentException(); + + this.data = data; + + } + + @Override + public byte[] getData() { + + return data; + + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HARootBlockRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HARootBlockRequest.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HARootBlockReque... [truncated message content] |
From: <tho...@us...> - 2012-10-05 14:07:53
|
Revision: 6658 http://bigdata.svn.sourceforge.net/bigdata/?rev=6658&view=rev Author: thompsonbry Date: 2012-10-05 14:07:40 +0000 (Fri, 05 Oct 2012) Log Message: ----------- I missed an interface and a class when moving the ha messages into their own package. This commit moves those two files (IHAWriteMessage and HAWriteMessage). Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IHABufferStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RWStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/msg/TestHAWriteMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/AbstractHAJournalTestCase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/quorum/MockQuorumFixture.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/test/com/bigdata/quorum/zk/MockQuorumMember.java Added Paths: ----------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAWriteMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAWriteMessage.java Removed Paths: ------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -36,6 +36,7 @@ import com.bigdata.ha.msg.IHAReadResponse; import com.bigdata.ha.msg.IHARootBlockRequest; import com.bigdata.ha.msg.IHARootBlockResponse; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.journal.ValidationError; /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -32,6 +32,7 @@ import java.rmi.Remote; import java.util.concurrent.Future; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.journal.WriteExecutorService; /** Deleted: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -1,259 +0,0 @@ -/** - -Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. - -Contact: - SYSTAP, LLC - 4501 Tower Road - Greensboro, NC 27410 - lic...@bi... - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* - * Created on May 18, 2010 - */ - -package com.bigdata.ha; - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -import com.bigdata.ha.msg.HAWriteMessageBase; -import com.bigdata.journal.StoreTypeEnum; - -/** - * A message carrying RMI metadata about a payload which will be replicated - * using a socket-level transfer facility. - * - * @author <a href="mailto:tho...@us...">Bryan Thompson</a> - * @version $Id$ - */ -public class HAWriteMessage extends HAWriteMessageBase implements - IHAWriteMessage { - - /** - * - */ - private static final long serialVersionUID = -2673171474897401979L; - - /** The most recent commit counter associated with this message */ - private long commitCounter; - - /** The most recent commit time associated with this message */ - private long lastCommitTime; - - /** The write sequence since last commit beginning at zero */ - private long sequence; - - /** The type of backing store (RW or WORM). */ - private StoreTypeEnum storeType; - - /** The quorum token for which this message is valid. */ - private long quorumToken; - - /** The length of the backing file on the disk. */ - private long fileExtent; - - /** The file offset at which the data will be written (WORM only). */ - private long firstOffset; - - /* (non-Javadoc) - * @see com.bigdata.journal.ha.IHAWriteMessage#getCommitCounter() - */ - @Override - public long getCommitCounter() { - return commitCounter; - } - - /* (non-Javadoc) - * @see com.bigdata.journal.ha.IHAWriteMessage#getLastCommitTime() - */ - @Override - public long getLastCommitTime() { - return lastCommitTime; - } - - /* (non-Javadoc) - * @see com.bigdata.journal.ha.IHAWriteMessage#getSequence() - */ - @Override - public long getSequence() { - return sequence; - } - - /* (non-Javadoc) - * @see com.bigdata.journal.ha.IHAWriteMessage#getStoreType() - */ - @Override - public StoreTypeEnum getStoreType() { - return storeType; - } - - /* (non-Javadoc) - * @see com.bigdata.journal.ha.IHAWriteMessage#getQuorumToken() - */ - @Override - public long getQuorumToken() { - return quorumToken; - } - - /* (non-Javadoc) - * @see com.bigdata.journal.ha.IHAWriteMessage#getFileExtent() - */ - @Override - public long getFileExtent() { - return fileExtent; - } - - /* (non-Javadoc) - * @see com.bigdata.journal.ha.IHAWriteMessage#getFirstOffset() - */ - @Override - public long getFirstOffset() { - return firstOffset; - } - - public String toString() { - - return getClass().getName() // - + "{size=" + getSize() // - + ",chksum=" + getChk() // - + ",commitCounter=" + commitCounter // - + ",commitTime=" + lastCommitTime // - + ",sequence=" + sequence // - + ",storeType=" + getStoreType() // - + ",quorumToken=" + getQuorumToken()// - + ",fileExtent=" + getFileExtent() // - + ",firstOffset=" + getFirstOffset() // - + "}"; - - } - - /** - * De-serialization constructor. - */ - public HAWriteMessage() { - } - - /** - * @param commitCounter - * The commit counter for the current root block for the write - * set which is being replicated by this message. - * @param commitTime - * The commit time for the current root block for the write set - * which is being replicated by this message. - * @param sequence - * The write cache block sequence number. This is reset to ZERO - * (0) for the first replicated write cache block in each write - * set. - * @param sze - * The #of bytes in the payload. - * @param chk - * The checksum of the payload. - * @param storeType - * The type of backing store (RW or WORM). - * @param quorumToken - * The quorum token for which this message is valid. - * @param fileExtent - * The length of the backing file on the disk. - * @param firstOffset - * The file offset at which the data will be written (WORM only). - */ - public HAWriteMessage(final long commitCounter, final long commitTime, - final long sequence, final int sze, final int chk, - final StoreTypeEnum storeType, final long quorumToken, - final long fileExtent, final long firstOffset) { - - super(sze, chk); - - if (storeType == null) - throw new IllegalArgumentException(); - - this.commitCounter = commitCounter; - - this.lastCommitTime = commitTime; - - this.sequence = sequence; - - this.storeType = storeType; - - this.quorumToken = quorumToken; - - this.fileExtent = fileExtent; - - this.firstOffset = firstOffset; - - } - - private static final byte VERSION0 = 0x0; - - @Override - public boolean equals(final Object obj) { - - if (this == obj) - return true; - - if (!super.equals(obj)) - return false; - - if (!(obj instanceof IHAWriteMessage)) - return false; - - final IHAWriteMessage other = (IHAWriteMessage) obj; - - return commitCounter == other.getCommitCounter() - && lastCommitTime == other.getLastCommitTime() // - && sequence == other.getSequence() - && storeType == other.getStoreType() - && quorumToken == other.getQuorumToken() - && fileExtent == other.getFileExtent() - && firstOffset == other.getFirstOffset(); - - } - - public void readExternal(final ObjectInput in) throws IOException, - ClassNotFoundException { - - super.readExternal(in); - final byte version = in.readByte(); - switch (version) { - case VERSION0: - break; - default: - throw new IOException("Unknown version: " + version); - } - storeType = StoreTypeEnum.valueOf(in.readByte()); - commitCounter = in.readLong(); - lastCommitTime = in.readLong(); - sequence = in.readLong(); - quorumToken = in.readLong(); - fileExtent = in.readLong(); - firstOffset = in.readLong(); - } - - public void writeExternal(final ObjectOutput out) throws IOException { - super.writeExternal(out); - out.write(VERSION0); - out.writeByte(storeType.getType()); - out.writeLong(commitCounter); - out.writeLong(lastCommitTime); - out.writeLong(sequence); - out.writeLong(quorumToken); - out.writeLong(fileExtent); - out.writeLong(firstOffset); - } - -} Deleted: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -1,62 +0,0 @@ -/** - -Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. - -Contact: - SYSTAP, LLC - 4501 Tower Road - Greensboro, NC 27410 - lic...@bi... - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package com.bigdata.ha; - -import com.bigdata.ha.msg.IHAWriteMessageBase; -import com.bigdata.journal.StoreTypeEnum; - -/** - * A message carrying RMI metadata about a payload which will be replicated - * using a socket-level transfer facility. - * - * @author <a href="mailto:tho...@us...">Bryan Thompson</a> - */ -public interface IHAWriteMessage extends IHAWriteMessageBase { - - /** The commit counter associated with this message */ - long getCommitCounter(); - - /** The commit time associated with this message. */ - long getLastCommitTime(); - - /** - * The write cache buffer sequence number (reset to ZERO (0) for the first - * message after each commit and incremented for each buffer sent by the - * leader). - */ - long getSequence(); - - /** The type of backing store (RW or WORM). */ - StoreTypeEnum getStoreType(); - - /** The quorum token for which this message is valid. */ - long getQuorumToken(); - - /** The length of the backing file on the disk. */ - long getFileExtent(); - - /** The file offset at which the data will be written (WORM only). */ - long getFirstOffset(); - -} \ No newline at end of file Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -11,6 +11,7 @@ import org.apache.log4j.Logger; import com.bigdata.btree.BytesUtil; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.journal.IRootBlockView; /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -31,6 +31,7 @@ import java.nio.ByteBuffer; import java.util.concurrent.Future; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.writecache.WriteCache; import com.bigdata.journal.IRootBlockView; import com.bigdata.quorum.Quorum; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -41,6 +41,7 @@ import org.apache.log4j.Logger; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.ha.pipeline.HAReceiveService; import com.bigdata.ha.pipeline.HAReceiveService.IHAReceiveCallback; import com.bigdata.ha.pipeline.HASendService; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -38,6 +38,7 @@ import org.apache.log4j.Logger; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.journal.AbstractJournal; import com.bigdata.journal.IResourceManager; import com.bigdata.journal.IRootBlockView; Copied: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAWriteMessage.java (from rev 6657, branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAWriteMessage.java) =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAWriteMessage.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HAWriteMessage.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -0,0 +1,258 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + * Created on May 18, 2010 + */ + +package com.bigdata.ha.msg; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import com.bigdata.journal.StoreTypeEnum; + +/** + * A message carrying RMI metadata about a payload which will be replicated + * using a socket-level transfer facility. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + * @version $Id$ + */ +public class HAWriteMessage extends HAWriteMessageBase implements + IHAWriteMessage { + + /** + * + */ + private static final long serialVersionUID = -2673171474897401979L; + + /** The most recent commit counter associated with this message */ + private long commitCounter; + + /** The most recent commit time associated with this message */ + private long lastCommitTime; + + /** The write sequence since last commit beginning at zero */ + private long sequence; + + /** The type of backing store (RW or WORM). */ + private StoreTypeEnum storeType; + + /** The quorum token for which this message is valid. */ + private long quorumToken; + + /** The length of the backing file on the disk. */ + private long fileExtent; + + /** The file offset at which the data will be written (WORM only). */ + private long firstOffset; + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getCommitCounter() + */ + @Override + public long getCommitCounter() { + return commitCounter; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getLastCommitTime() + */ + @Override + public long getLastCommitTime() { + return lastCommitTime; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getSequence() + */ + @Override + public long getSequence() { + return sequence; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getStoreType() + */ + @Override + public StoreTypeEnum getStoreType() { + return storeType; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getQuorumToken() + */ + @Override + public long getQuorumToken() { + return quorumToken; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getFileExtent() + */ + @Override + public long getFileExtent() { + return fileExtent; + } + + /* (non-Javadoc) + * @see com.bigdata.journal.ha.IHAWriteMessage#getFirstOffset() + */ + @Override + public long getFirstOffset() { + return firstOffset; + } + + public String toString() { + + return getClass().getName() // + + "{size=" + getSize() // + + ",chksum=" + getChk() // + + ",commitCounter=" + commitCounter // + + ",commitTime=" + lastCommitTime // + + ",sequence=" + sequence // + + ",storeType=" + getStoreType() // + + ",quorumToken=" + getQuorumToken()// + + ",fileExtent=" + getFileExtent() // + + ",firstOffset=" + getFirstOffset() // + + "}"; + + } + + /** + * De-serialization constructor. + */ + public HAWriteMessage() { + } + + /** + * @param commitCounter + * The commit counter for the current root block for the write + * set which is being replicated by this message. + * @param commitTime + * The commit time for the current root block for the write set + * which is being replicated by this message. + * @param sequence + * The write cache block sequence number. This is reset to ZERO + * (0) for the first replicated write cache block in each write + * set. + * @param sze + * The #of bytes in the payload. + * @param chk + * The checksum of the payload. + * @param storeType + * The type of backing store (RW or WORM). + * @param quorumToken + * The quorum token for which this message is valid. + * @param fileExtent + * The length of the backing file on the disk. + * @param firstOffset + * The file offset at which the data will be written (WORM only). + */ + public HAWriteMessage(final long commitCounter, final long commitTime, + final long sequence, final int sze, final int chk, + final StoreTypeEnum storeType, final long quorumToken, + final long fileExtent, final long firstOffset) { + + super(sze, chk); + + if (storeType == null) + throw new IllegalArgumentException(); + + this.commitCounter = commitCounter; + + this.lastCommitTime = commitTime; + + this.sequence = sequence; + + this.storeType = storeType; + + this.quorumToken = quorumToken; + + this.fileExtent = fileExtent; + + this.firstOffset = firstOffset; + + } + + private static final byte VERSION0 = 0x0; + + @Override + public boolean equals(final Object obj) { + + if (this == obj) + return true; + + if (!super.equals(obj)) + return false; + + if (!(obj instanceof IHAWriteMessage)) + return false; + + final IHAWriteMessage other = (IHAWriteMessage) obj; + + return commitCounter == other.getCommitCounter() + && lastCommitTime == other.getLastCommitTime() // + && sequence == other.getSequence() + && storeType == other.getStoreType() + && quorumToken == other.getQuorumToken() + && fileExtent == other.getFileExtent() + && firstOffset == other.getFirstOffset(); + + } + + public void readExternal(final ObjectInput in) throws IOException, + ClassNotFoundException { + + super.readExternal(in); + final byte version = in.readByte(); + switch (version) { + case VERSION0: + break; + default: + throw new IOException("Unknown version: " + version); + } + storeType = StoreTypeEnum.valueOf(in.readByte()); + commitCounter = in.readLong(); + lastCommitTime = in.readLong(); + sequence = in.readLong(); + quorumToken = in.readLong(); + fileExtent = in.readLong(); + firstOffset = in.readLong(); + } + + public void writeExternal(final ObjectOutput out) throws IOException { + super.writeExternal(out); + out.write(VERSION0); + out.writeByte(storeType.getType()); + out.writeLong(commitCounter); + out.writeLong(lastCommitTime); + out.writeLong(sequence); + out.writeLong(quorumToken); + out.writeLong(fileExtent); + out.writeLong(firstOffset); + } + +} Copied: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAWriteMessage.java (from rev 6657, branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/IHAWriteMessage.java) =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAWriteMessage.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAWriteMessage.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -0,0 +1,61 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package com.bigdata.ha.msg; + +import com.bigdata.journal.StoreTypeEnum; + +/** + * A message carrying RMI metadata about a payload which will be replicated + * using a socket-level transfer facility. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ +public interface IHAWriteMessage extends IHAWriteMessageBase { + + /** The commit counter associated with this message */ + long getCommitCounter(); + + /** The commit time associated with this message. */ + long getLastCommitTime(); + + /** + * The write cache buffer sequence number (reset to ZERO (0) for the first + * message after each commit and incremented for each buffer sent by the + * leader). + */ + long getSequence(); + + /** The type of backing store (RW or WORM). */ + StoreTypeEnum getStoreType(); + + /** The quorum token for which this message is valid. */ + long getQuorumToken(); + + /** The length of the backing file on the disk. */ + long getFileExtent(); + + /** The file offset at which the data will be written (WORM only). */ + long getFirstOffset(); + +} \ No newline at end of file Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -51,8 +51,8 @@ import com.bigdata.counters.CAT; import com.bigdata.counters.CounterSet; import com.bigdata.counters.Instrument; -import com.bigdata.ha.HAWriteMessage; -import com.bigdata.ha.IHAWriteMessage; +import com.bigdata.ha.msg.HAWriteMessage; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.DirectBufferPool; import com.bigdata.io.FileChannelUtility; import com.bigdata.io.IBufferAccess; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -58,8 +58,8 @@ import com.bigdata.counters.Instrument; import com.bigdata.counters.OneShotInstrument; import com.bigdata.ha.HAPipelineGlue; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.ha.QuorumPipeline; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.DirectBufferPool; import com.bigdata.io.IBufferAccess; import com.bigdata.io.IReopenChannel; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -82,7 +82,6 @@ import com.bigdata.counters.CounterSet; import com.bigdata.counters.Instrument; import com.bigdata.ha.HAGlue; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.ha.QuorumService; import com.bigdata.ha.msg.HAReadResponse; import com.bigdata.ha.msg.HARootBlockRequest; @@ -94,6 +93,7 @@ import com.bigdata.ha.msg.IHAReadResponse; import com.bigdata.ha.msg.IHARootBlockRequest; import com.bigdata.ha.msg.IHARootBlockResponse; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.htree.HTree; import com.bigdata.io.DirectBufferPool; import com.bigdata.io.IDataRecord; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IHABufferStrategy.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IHABufferStrategy.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IHABufferStrategy.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -30,7 +30,7 @@ import java.io.IOException; import java.nio.ByteBuffer; -import com.bigdata.ha.IHAWriteMessage; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.IBufferAccess; import com.bigdata.quorum.Quorum; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RWStrategy.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RWStrategy.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RWStrategy.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -35,8 +35,8 @@ import com.bigdata.cache.ConcurrentWeakValueCache; import com.bigdata.counters.CounterSet; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.ha.QuorumRead; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.IBufferAccess; import com.bigdata.mdi.IResourceMetadata; import com.bigdata.quorum.Quorum; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -42,8 +42,8 @@ import com.bigdata.counters.CounterSet; import com.bigdata.counters.Instrument; import com.bigdata.counters.striped.StripedCounters; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.ha.QuorumRead; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.FileChannelUtility; import com.bigdata.io.IBufferAccess; import com.bigdata.io.IReopenChannel; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -60,7 +60,7 @@ import com.bigdata.counters.CounterSet; import com.bigdata.counters.Instrument; import com.bigdata.counters.striped.StripedCounters; -import com.bigdata.ha.IHAWriteMessage; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.FileChannelUtility; import com.bigdata.io.IBufferAccess; import com.bigdata.io.IReopenChannel; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/msg/TestHAWriteMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/msg/TestHAWriteMessage.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/ha/msg/TestHAWriteMessage.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -28,8 +28,6 @@ import junit.framework.TestCase; import com.bigdata.btree.BytesUtil; -import com.bigdata.ha.HAWriteMessage; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.io.SerializerUtil; import com.bigdata.journal.StoreTypeEnum; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -51,9 +51,9 @@ import com.bigdata.ha.HAGlueBase; import com.bigdata.ha.HAPipelineGlue; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.ha.QuorumPipeline; import com.bigdata.ha.QuorumPipelineImpl; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.DirectBufferPool; import com.bigdata.io.FileChannelUtility; import com.bigdata.io.IBufferAccess; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/AbstractHAJournalTestCase.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/AbstractHAJournalTestCase.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/AbstractHAJournalTestCase.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -44,9 +44,9 @@ import com.bigdata.LRUNexus; import com.bigdata.ha.HAGlue; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.ha.QuorumService; import com.bigdata.ha.QuorumServiceBase; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.journal.AbstractJournal; import com.bigdata.journal.AbstractJournalTestCase; import com.bigdata.journal.Journal; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/quorum/MockQuorumFixture.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/quorum/MockQuorumFixture.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/quorum/MockQuorumFixture.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -56,7 +56,7 @@ import org.apache.log4j.Logger; import com.bigdata.ha.HAPipelineGlue; -import com.bigdata.ha.IHAWriteMessage; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.quorum.MockQuorumFixture.MockQuorum.MockQuorumWatcher; import com.bigdata.util.InnerCause; import com.bigdata.util.concurrent.DaemonThreadFactory; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -37,9 +37,9 @@ import com.bigdata.concurrent.FutureTaskMon; import com.bigdata.ha.HAGlue; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.ha.ProcessLogWriter; import com.bigdata.ha.QuorumService; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.writecache.WriteCache; import com.bigdata.journal.BufferMode; import com.bigdata.journal.IRootBlockView; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -34,10 +34,10 @@ import com.bigdata.ha.HAGlue; import com.bigdata.ha.HAGlueDelegate; -import com.bigdata.ha.IHAWriteMessage; import com.bigdata.ha.ProcessLogWriter; import com.bigdata.ha.QuorumService; import com.bigdata.ha.QuorumServiceBase; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.IBufferAccess; import com.bigdata.jini.start.config.ZookeeperClientConfig; import com.bigdata.jini.util.JiniUtil; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/test/com/bigdata/quorum/zk/MockQuorumMember.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/test/com/bigdata/quorum/zk/MockQuorumMember.java 2012-10-05 13:16:38 UTC (rev 6657) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/test/com/bigdata/quorum/zk/MockQuorumMember.java 2012-10-05 14:07:40 UTC (rev 6658) @@ -13,7 +13,7 @@ import java.util.concurrent.FutureTask; import com.bigdata.ha.HAPipelineGlue; -import com.bigdata.ha.IHAWriteMessage; +import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.quorum.AbstractQuorumMember; import com.bigdata.quorum.MockQuorumFixture; import com.bigdata.quorum.Quorum; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-05 19:46:36
|
Revision: 6660 http://bigdata.svn.sourceforge.net/bigdata/?rev=6660&view=rev Author: thompsonbry Date: 2012-10-05 19:46:27 +0000 (Fri, 05 Oct 2012) Log Message: ----------- The HALogWriter and HALogReader are now working. The written log file has a header that is similar, but with a different MAGIC, to the header on the Journal. The root blocks are the root blocks from the start/end of a given write set. Each HA log file represents one commit point. The HALogReader can dump log files (or directories containing log files). The WORM will require more work here. It needs to use HA failover reads to get the write cache blocks from the quorum because it does not log them out onto the HA Log file (to avoid the redundancy). I have extended the 2-phase prepare/commit/abort protocol such that services that are not joined with the met quorum but which are in the write pipeline will now receive these events. However, non-joined services do not get a "vote" in the 2-phase commit protocol. This is all done in prepare2Phase(), but the code and logic need some more refinement in the commit2Phase() and abort2Phase() methods. The next things to focus on are: 1. How a non-joined service participates in the 2-phase prepare/commit/abort, specifically verifying that it properly logs things from the write pipeline and the 2-phase commit onto a local HA Log file. 2. Working through the ability of the non-joined services to gather log files from the met quorum and replay them, incrementally advancing to newly committed states. 3. Working through the race conditions involved in atomically joining the met quorum. This should be able to happen with or without concurrent ongoing writes on the quorum. @see https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommit.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhaseCommitMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HA2PhasePrepareMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseCommitMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhasePrepareMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RootBlockView.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Added Paths: ----------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java Removed Paths: ------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java 2012-10-05 16:58:14 UTC (rev 6659) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java 2012-10-05 19:46:27 UTC (rev 6660) @@ -1,7 +1,7 @@ package com.bigdata.ha; import java.io.File; -import java.io.FileNotFoundException; +import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; @@ -9,88 +9,227 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import org.apache.log4j.Logger; + import com.bigdata.ha.msg.IHAWriteMessage; +import com.bigdata.io.DirectBufferPool; +import com.bigdata.io.FileChannelUtility; +import com.bigdata.io.IBufferAccess; +import com.bigdata.io.IReopenChannel; import com.bigdata.journal.IRootBlockView; -import com.bigdata.journal.RootBlockView; +import com.bigdata.journal.RootBlockUtility; +import com.bigdata.journal.StoreTypeEnum; +import com.bigdata.util.ChecksumError; +import com.bigdata.util.ChecksumUtility; /** - * Given an HALog file can be used to replay the file and can provide a readable dump of the content. + * Given an HALog file can be used to replay the file and can provide a readable + * dump of the content. * - * When replaying, the current position is compared to the EOF to determine whether more data can be read. + * When replaying, the current position is compared to the EOF to determine + * whether more data can be read. * - * The called should call hasMoreBuffers() and if so read the next associated buffer and process with the - * returned IHAMessage. + * The called should call hasMoreBuffers() and if so read the next associated + * buffer and process with the returned IHAMessage. * - * If hasMoreBuffers() is false, then the committing rootBlock should be used to commit the replayed - * transaction. + * If hasMoreBuffers() is false, then the committing rootBlock should be used to + * commit the replayed transaction. * * @author Martyn Cutcher - * */ public class HALogReader { - final RandomAccessFile m_raf; - final FileChannel m_channel; - final IRootBlockView m_openRootBlock; - final IRootBlockView m_commitRootBlock; + private static final Logger log = Logger.getLogger(HALogReader.class); + + private final File m_file; + private final RandomAccessFile m_raf; + private final FileChannel m_channel; + private final IRootBlockView m_openRootBlock; + private final IRootBlockView m_closeRootBlock; + private final StoreTypeEnum m_storeType; + private final int magic; + private final int version; - public HALogReader(final File halog) throws IOException { - m_raf = new RandomAccessFile(halog, "r"); + public HALogReader(final File file) throws IOException { + + m_file = file; + + m_raf = new RandomAccessFile(file, "r"); + m_channel = m_raf.getChannel(); - - /** - * Must determine whether the file has consistent open and committed - * rootBlocks, using the commitCounter to determine which rootBlock - * is which. - */ - final IRootBlockView rb0 = readRootBlock(ProcessLogWriter.ROOTBLOCK_0, true); - final long cc0 = rb0.getCommitCounter(); - final IRootBlockView rb1 = readRootBlock(ProcessLogWriter.ROOTBLOCK_1, false); - final long cc1 = rb0.getCommitCounter(); + try { + /** + * Must determine whether the file has consistent open and committed + * rootBlocks, using the commitCounter to determine which rootBlock + * is which. + * + * Note: Both root block should exist (they are both written on + * startup). If they are identical, then the log is empty (the + * closing root block has not been written and the data in the log + * is useless). + * + * The root block is slot 0 is always the root block for the + * previous commit point. + * + * The root block in slot 1 is either identical to the root block in + * slot zero (in which case the log file is logically empty) or it + * is the root block that closes the write set in the HA Log file + * (and its commit counter must be exactly one more than the commit + * counter for the opening root block). + */ + /* + * Read the MAGIC and VERSION. + */ + m_raf.seek(0L); + try { + /* + * Note: this next line will throw IOException if there is a + * file lock contention. + */ + magic = m_raf.readInt(); + } catch (IOException ex) { + throw new RuntimeException( + "Can not read magic. Is file locked by another process?", + ex); + } + if (magic != HALogWriter.MAGIC) + throw new RuntimeException("Bad journal magic: expected=" + + HALogWriter.MAGIC + ", actual=" + magic); + version = m_raf.readInt(); + if (version != HALogWriter.VERSION1) + throw new RuntimeException("Bad journal version: expected=" + + HALogWriter.VERSION1 + ", actual=" + version); + + final RootBlockUtility tmp = new RootBlockUtility(reopener, file, + true/* validateChecksum */, false/* alternateRootBlock */, + false/* ignoreBadRootBlock */); + + m_openRootBlock = tmp.rootBlock0; + + m_closeRootBlock = tmp.rootBlock1; + + final long cc0 = m_openRootBlock.getCommitCounter(); + + final long cc1 = m_closeRootBlock.getCommitCounter(); + + if ((cc0 + 1) != cc1 && (cc0 != cc1)) { + /* + * Counters are inconsistent with either an empty log file or a + * single transaction scope. + */ + throw new IllegalStateException("Incompatible rootblocks: cc0=" + + cc0 + ", cc1=" + cc1); + } + + m_channel.position(HALogWriter.headerSize0); + + m_storeType = m_openRootBlock.getStoreType(); + + } catch (Throwable t) { + + close(); + + throw new RuntimeException(t); + + } - if (cc0 == cc1) { // same rootblock - no committed block written - throw new IllegalArgumentException("Uncommitted log file"); - } else if (cc0 == cc1+1) { // open - 1, commit - 0 - m_openRootBlock = rb1; - m_commitRootBlock = rb0; - } else if (cc1 == cc1+1) { //open - 0, commit - 1 - m_openRootBlock = rb0; - m_commitRootBlock = rb1; - } else { // counters inconsistent with single transaction scope - throw new IllegalStateException("Incompatible rootblocks for single transaction"); - } - m_channel.position(ProcessLogWriter.START_DATA); } - private IRootBlockView readRootBlock(final int rootblockPos, final boolean rootBlock0) throws IOException { - final ByteBuffer dst = ByteBuffer.allocate(RootBlockView.SIZEOF_ROOT_BLOCK); - - m_channel.read(dst); - - return new RootBlockView(rootBlock0, dst, null); + /** + * Hook for {@link FileChannelUtility#readAll(FileChannel, ByteBuffer, long)} + */ + private final IReopenChannel<FileChannel> reopener = new IReopenChannel<FileChannel>() { + + @Override + public FileChannel reopenChannel() throws IOException { + + if (m_channel == null) + throw new IOException("Closed"); + + return m_channel; + + } + }; + + public void close() { + + if (m_channel.isOpen()) { + + try { + m_raf.close(); + } catch (IOException e) { + log.error("Problem closing file: file=" + m_file + " : " + e, e); + } + + } + + } + + /** + * Return <code>true</code> if the root blocks in the log file have the same + * commit counter. Such log files are logically empty regardless of their + * length. + */ + public boolean isEmpty() { + + return m_openRootBlock.getCommitCounter()==m_closeRootBlock.getCommitCounter(); + + } + + private void assertOpen() throws IOException { + + if (!m_channel.isOpen()) + throw new IOException("Closed: " + m_file); + + } + + /** + * The {@link IRootBlockView} for the committed state BEFORE the write set + * contained in the HA log file. + */ + public IRootBlockView getOpeningRootBlock() { + + return m_openRootBlock; + } - - public IRootBlockView getOpeningRootblock() { - return m_openRootBlock; - } - - public IRootBlockView getCommittingRootblock() { - return m_commitRootBlock; - } /** - * Checks whether we have reached the end of the file + * The {@link IRootBlockView} for the committed state AFTER the write + * set contained in the HA log file has been applied. */ + public IRootBlockView getClosingRootBlock() { + + return m_closeRootBlock; + + } + + /** + * Checks whether we have reached the end of the file. + */ public boolean hasMoreBuffers() throws IOException { - return m_channel.position() < m_channel.size(); + + assertOpen(); + + if(isEmpty()) { + + /* + * Ignore the file length if it is logically empty. + */ + + return false; + + } + + return m_channel.position() < m_channel.size(); + } /** * To stream from the Channel, we can use the associated RandomAccessFile * since the FilePointer for one is the same as the other. */ - class RAFInputStream extends InputStream { + private class RAFInputStream extends InputStream { @Override public int read() throws IOException { @@ -103,33 +242,229 @@ } } - - /** - * Attempts to read the next IHAWriteMessage and then the - * expected buffer, that is read into the client buffer. - * The IHAWriteMessage is returned to the caller. - */ - public IHAWriteMessage processNextBuffer(final ByteBuffer clientBuffer) throws IOException { - final ObjectInputStream objinstr = new ObjectInputStream(new RAFInputStream()); - final IHAWriteMessage msg; - try { - msg = (IHAWriteMessage) objinstr.readObject(); - } catch (ClassNotFoundException e) { - throw new IllegalStateException(e); - } + + /** + * Attempts to read the next {@link IHAWriteMessage} and then the expected + * buffer, that is read into the client buffer. The {@link IHAWriteMessage} + * is returned to the caller. + * + * @param clientBuffer + * A buffer from the {@link DirectBufferPool#INSTANCE}. + * + * FIXME We need to pass in the backing store against which the + * log would be resolved. This should be done using the + * {@link HAReadGlue} API (failover reads). We should be able to + * put together the [addr] from the + * {@link IHAWriteMessage#getFirstOffset()} (the offset) plus the + * {@link IHAWriteMessage#getSize()} (the byte length). There + * might also be a checksum in the write cache block, in which + * case we need to back that out of the data that we are trying + * to read, but we still want to verify that checksum once we get + * the data block from the quorum. + * + * FIXME Encapsulate as an iterator pattern where we pass in the + * quorum token (if necessary), and the {@link QuorumServiceBase} + * so we can resolve the nodes on which we can read the raw + * records. + */ + public IHAWriteMessage processNextBuffer(final ByteBuffer clientBuffer) + throws IOException { + + final ObjectInputStream objinstr = new ObjectInputStream( + new RAFInputStream()); + + final IHAWriteMessage msg; + try { + + msg = (IHAWriteMessage) objinstr.readObject(); + + } catch (ClassNotFoundException e) { + + throw new IllegalStateException(e); + + } + + switch (m_storeType) { + case RW: { + + if (msg.getSize() > clientBuffer.capacity()) { + + throw new IllegalStateException( + "Client buffer is not large enough for logged buffer"); + + } + + // Now setup client buffer to receive from the channel + final int nbytes = msg.getSize(); + clientBuffer.position(0); + clientBuffer.limit(nbytes); + + // Current position on channel. + final long pos = m_channel.position(); + + // Robustly read of write cache block at that position into the + // caller's buffer. (pos=limit=nbytes) + FileChannelUtility.readAll(reopener, clientBuffer, pos); + + // Advance the file channel beyond the block we just read. + m_channel.position(pos + msg.getSize()); + + // limit=pos; pos=0; + clientBuffer.flip(); // ready for reading + + final int chksum = new ChecksumUtility().checksum(clientBuffer + .duplicate()); + + if (chksum != msg.getChk()) + throw new ChecksumError("Expected=" + msg.getChk() + + ", actual=" + chksum); + + if (clientBuffer.remaining() != nbytes) + throw new AssertionError(); + + break; + } + case WORM: { + /* + * FIXME The WriteCache block needs to be recovered from the quorum + * using a failover read. + */ + throw new UnsupportedOperationException(); +// break; + } + } + + return msg; - if (msg.getSize() > clientBuffer.capacity()) { - throw new IllegalStateException("Client buffer is not large enough for logged buffer"); - } - - // Now setup client buffer to receive from the channel - clientBuffer.position(0); - clientBuffer.limit(msg.getSize()); - - m_channel.read(clientBuffer); - clientBuffer.flip(); // ready for reading - - return msg; } + + /** + * Utility program will dump log files (or directories containing log files) + * provided as arguments. + * + * @param args + * Zero or more files or directories. + * + * @throws IOException + * @throws InterruptedException + */ + public static void main(final String[] args) throws IOException, + InterruptedException { + + final IBufferAccess buf = DirectBufferPool.INSTANCE.acquire(); + + try { + + for (String arg : args) { + + final File file = new File(arg); + + if (!file.exists()) { + + System.err.println("No such file: " + file); + + continue; + + } + + if (file.isDirectory()) { + + doDirectory(file, buf); + + } else { + + doFile(file, buf); + + } + + } + + } finally { + + buf.release(); + + } + + } + + private static void doDirectory(final File dir, final IBufferAccess buf) + throws IOException { + + final File[] files = dir.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + + if (new File(dir, name).isDirectory()) { + + // Allow recursion through directories. + return true; + + } + + return name.endsWith(HALogWriter.HA_LOG_EXT); + + } + }); + + for (File file : files) { + + if(file.isDirectory()) { + + doDirectory(file, buf); + + } else { + + doFile(file, buf); + + } + + } + + } + + private static void doFile(final File file, final IBufferAccess buf) + throws IOException { + + final HALogReader r = new HALogReader(file); + + try { + + final IRootBlockView openingRootBlock = r + .getOpeningRootBlock(); + + final IRootBlockView closingRootBlock = r + .getClosingRootBlock(); + + if (openingRootBlock.getCommitCounter() == closingRootBlock + .getCommitCounter()) { + + System.err.println("EMPTY LOG: " + file); + + } + + System.out.println("----------begin----------"); + System.out.println("file=" + file); + System.out.println("openingRootBlock=" + openingRootBlock); + System.out.println("closingRootBlock=" + closingRootBlock); + + while (r.hasMoreBuffers()) { + + final IHAWriteMessage msg = r.processNextBuffer(buf + .buffer()); + + System.out.println(msg.toString()); + + } + System.out.println("-----------end-----------"); + + } finally { + + r.close(); + + } + + } + } Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-05 19:46:27 UTC (rev 6660) @@ -0,0 +1,477 @@ +package com.bigdata.ha; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.Formatter; + +import org.apache.log4j.Logger; + +import com.bigdata.ha.msg.IHAWriteMessage; +import com.bigdata.io.FileChannelUtility; +import com.bigdata.io.IReopenChannel; +import com.bigdata.io.SerializerUtil; +import com.bigdata.journal.IRootBlockView; +import com.bigdata.journal.RootBlockView; +import com.bigdata.rawstore.Bytes; + +/** + * Wrapper class to handle process log creation and output for HA. + * + * The process log stores the HAWriteMessages and buffers to support reading and + * reprocessing as part of the HA synchronization protocol. + * + * The writer encapsulates not only the writing of individual messages but also + * the closing and creation of new files. + * + * @author Martyn Cutcher + */ +public class HALogWriter { + + /** + * Logger for HA events. + */ + private static final Logger haLog = Logger.getLogger("com.bigdata.haLog"); + + /* + * Note: All of this stuff is to be more or less compatible with the magic + * and version at the start of a Journal file. We use a different MAGIC + * value for the HA Log, but the same offset to the first and second root + * blocks. The data starts after the 2nd root block. + */ + + static final int SIZE_MAGIC = Bytes.SIZEOF_INT; + static final int SIZE_VERSION = Bytes.SIZEOF_INT; + static final int SIZEOF_ROOT_BLOCK = RootBlockView.SIZEOF_ROOT_BLOCK; + + /** + * Offset of the first root block in the file. + */ + static final int OFFSET_ROOT_BLOCK0 = SIZE_MAGIC + SIZE_VERSION; + + /** + * Offset of the second root block in the file. + */ + static final int OFFSET_ROOT_BLOCK1 = SIZE_MAGIC + SIZE_VERSION + (SIZEOF_ROOT_BLOCK * 1); + + /** + * The size of the file header, including MAGIC, version, and both root + * blocks. The data starts at this offset. + */ + static final int headerSize0 = SIZE_MAGIC + SIZE_VERSION + (SIZEOF_ROOT_BLOCK * 2); + + /** + * Magic value for HA Log (the root blocks have their own magic value). + */ + static final int MAGIC = 0x83d9b735; + + /** + * HA log version number (version 1). + */ + static final int VERSION1 = 0x1; + + /** HA log directory. */ + private final File m_dir; + + /** + * The root block of the leader at the start of the current write set. + */ + private IRootBlockView m_rootBlock; + + /** Current write cache block sequence counter. */ + private long m_sequence = 0; + + /** current log file. */ + private File m_log = null; + + public static final String HA_LOG_EXT = ".ha-log"; + + /** current output file channel. */ + private RandomAccessFile m_raf = null; + private FileChannel m_channel = null; + + /** current write point on the channel. */ + private long m_position = headerSize0; + + public HALogWriter(final File logDir) { + + m_dir = logDir; + + } + + /** + * Open an HA log file for the write set starting with the given root block. + * + * @param rootBlock + * The root block. + * @throws FileNotFoundException + * @throws IOException + */ + public void createLog(final IRootBlockView rootBlock) + throws FileNotFoundException, IOException { + + if (rootBlock == null) + throw new IllegalArgumentException(); + + if (m_rootBlock != null) // may not be open. + throw new IllegalStateException(); + + if (haLog.isInfoEnabled()) + haLog.info("rootBlock=" + rootBlock); + + m_rootBlock = rootBlock; + + m_sequence = 0L; + + /* + * Format the name of the log file. + * + * Note: The commit counter in the file name should be zero filled to 20 + * digits so we have the files in lexical order in the file system (for + * convenience). + */ + final String logFile; + { + + final StringBuilder sb = new StringBuilder(); + + final Formatter f = new Formatter(sb); + + /* + * Note: We use commitCounter+1 so the file will be labeled by the + * commit point that will be achieved when that log file is applied + * to a journal whose current commit point is [commitCounter]. + */ + + final long commitCounter = rootBlock.getCommitCounter(); + + f.format("%020d" + HA_LOG_EXT, (commitCounter + 1)); + + logFile = sb.toString(); + + } + + m_log = new File(m_dir, logFile); + + // Must delete file if it exists. + if (m_log.exists() && !m_log.delete()) { + + /* + * It is a problem if a file exists and we can not delete it. We + * need to be able to remove the file and replace it with a new file + * when we log the write set for this commit point. + */ + + throw new IOException("Could not delete: " + m_log); + + } + + m_raf = new RandomAccessFile(m_log, "rw"); + + m_channel = m_raf.getChannel(); + + /* + * Write the MAGIC and version on the file. + */ + m_raf.seek(0); + m_raf.writeInt(MAGIC); + m_raf.writeInt(VERSION1); + + /* + * Write root block to slots 0 and 1. + * + * Initially, the same root block is in both slots. This is a valid, but + * logically empty, HA Log file. + * + * When the HA Log file is properly sealed with a root block, that root + * block is written onto slot 1. + */ + + writeRootBlock(true/* isRootBlock0 */, rootBlock); + + writeRootBlock(false/* isRootBlock0 */, rootBlock); + + } + + /** + * Hook for {@link FileChannelUtility#writeAll(IReopenChannel, ByteBuffer, long)} + */ + private final IReopenChannel<FileChannel> reopener = new IReopenChannel<FileChannel>() { + + @Override + public FileChannel reopenChannel() throws IOException { + + if (m_channel == null) + throw new IOException("Closed"); + + return m_channel; + + } + }; + + /** + * Write the final root block on the HA log and close the file. This "seals" + * the file, which now represents the entire write set associated with the + * commit point in the given root block. + * + * @param rootBlock + * The final root block for the write set. + * @throws FileNotFoundException + * @throws IOException + */ + public void closeLog(final IRootBlockView rootBlock) + throws FileNotFoundException, IOException { + + if (rootBlock == null) + throw new IllegalArgumentException(); + + if (m_rootBlock == null) // no root block associated with log. + throw new IllegalStateException(); + + if (haLog.isInfoEnabled()) + haLog.info("rootBlock=" + rootBlock); + + final long expectedCommitCounter = this.m_rootBlock.getCommitCounter() + 1; + + if (expectedCommitCounter != rootBlock.getCommitCounter()) { + + throw new IllegalStateException("CommitCounter: expected=" + + expectedCommitCounter + ", actual=" + + rootBlock.getCommitCounter()); + + } + +// if (rootBlock.getLastCommitTime() != this.m_rootBlock +// .getLastCommitTime()) { +// +// throw new IllegalStateException(); +// +// } + + if (!this.m_rootBlock.getUUID().equals(rootBlock.getUUID())) { + + throw new IllegalStateException("Store UUID: expected=" + + (m_rootBlock.getUUID()) + ", actual=" + + rootBlock.getUUID()); + + } + + flush(); // current streamed data + + // The closing root block is always in slot 1. + writeRootBlock(false/* isRootBlock0 */, rootBlock); + + close(); + + } + + /** + * Writes the root block at the given offset. + */ + private void writeRootBlock(final boolean isRootBlock0, + final IRootBlockView rootBlock) throws IOException { + + if (rootBlock == null) + throw new IllegalArgumentException(); + + final long position = isRootBlock0 ? OFFSET_ROOT_BLOCK0 + : OFFSET_ROOT_BLOCK1; + + FileChannelUtility.writeAll(reopener, rootBlock.asReadOnlyBuffer(), + position); + + if (haLog.isDebugEnabled()) + haLog.debug("wrote root block: " + rootBlock); + + } + + /** + * + * @param msg + * @param data + */ + public void write(final IHAWriteMessage msg, final ByteBuffer data) + throws IOException { + + if (m_channel == null) + return; + + /* + * Check if this really is a valid message for this file. If it is not, + * then close the file and return immediately + */ + if (m_rootBlock.getCommitCounter() != msg.getCommitCounter()) + return; + + if (m_rootBlock.getLastCommitTime() != msg.getLastCommitTime()) + return; + + if (m_sequence != msg.getSequence()) + return; + + if (haLog.isInfoEnabled()) + haLog.info("msg=" + msg); + + if (m_position < headerSize0) + throw new AssertionError("position=" + m_position + + ", but headerSize=" + headerSize0); + + /* + * Write the HAWriteMessage onto the channel. + */ + { + // serialized message object (pos=0; limit=nbytes) + final ByteBuffer tmp = bufferObject(msg); + + final int nbytes = tmp.limit(); + + FileChannelUtility.writeAll(reopener, tmp, m_position); + + m_position += nbytes; + } + + switch(m_rootBlock.getStoreType()) { + case RW: { + /* + * Write the WriteCache block on the channel. + */ + final int nbytes = msg.getSize(); + assert data.position() == 0; + assert data.limit() == nbytes; + // Note: duplicate() to avoid side effects on ByteBuffer!!! + FileChannelUtility.writeAll(reopener, data.duplicate(), m_position); + m_position += nbytes; + } + case WORM: { + /* + * We will use the HA failover read API to recover the block from a + * node in the quorum when we need to replay the HA log. + */ + break; + } + default: + throw new AssertionError(); + } + + } + + /** + * Utility to return a ByteBuffer containing the external version of the + * object. + * + * @return The {@link ByteBuffer}. The position will be zero. The limit will + * be the #of bytes in the serialized object. + */ + private ByteBuffer bufferObject(final Object obj) throws IOException { + + // Note: pos=0; limit=capacity=length. + return ByteBuffer.wrap(SerializerUtil.serialize(obj)); + + } + + /** + * Close the file (does not flush). + */ + private void close() throws IOException { + try { + if (m_channel != null) { + m_channel.close(); + } + } finally { + reset(); + } + } + + /** + * Clear internal fields. + */ + private void reset() { + + m_log = null; + + m_raf = null; + + m_channel = null; + + m_position = headerSize0; + + m_rootBlock = null; + + m_sequence = 0L; + + } + + /** + * When the HA leader commits it must flush the log + */ + private void flush() throws IOException { + + if (m_channel != null) { + + m_channel.force(true); + + } + + } + + /** + * On various error conditions we may need to remove the log + * + * @throws IOException + */ + private void remove() throws IOException { + + try { + + if (m_channel != null) { + + /* + * Conditional remove iff file is open. Will not remove + * something that has been closed. + */ + + m_channel.close(); + + if (m_log.exists() && !m_log.delete()) { + + /* + * It is a problem if a file exists and we can not delete + * it. We need to be able to remove the file and replace it + * with a new file when we log the write set for this commit + * point. + */ + + throw new IOException("Could not delete: " + m_log); + + } + + } + + } finally { + + reset(); + + } + + } + + /** + * Disable the current log file if one is open. + */ + public void disable() throws IOException { + + if (haLog.isInfoEnabled()) + haLog.info(""); + + /* + * Remove unless log file was already closed. + */ + + remove(); + + } + +} Deleted: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-05 16:58:14 UTC (rev 6659) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/ProcessLogWriter.java 2012-10-05 19:46:27 UTC (rev 6660) @@ -1,386 +0,0 @@ -package com.bigdata.ha; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Formatter; - -import org.apache.log4j.Logger; - -import com.bigdata.btree.BytesUtil; -import com.bigdata.journal.IRootBlockView; -import com.bigdata.journal.RootBlockView; -import com.bigdata.ha.msg.IHAWriteMessage; - -/** - * Wrapper class to handle process log creation and output for HA. - * - * The process log stores the HAWriteMessages and buffers to support reading and - * reprocessing as part of the HA synchronization protocol. - * - * The writer encapsulates not only the writing of individual messages but also - * the closing and creation of new files. - * - * @author Martyn Cutcher - */ -public class ProcessLogWriter { - - /** - * Logger for HA events. - */ - private static final Logger haLog = Logger.getLogger("com.bigdata.haLog"); - - /** HA log directory. */ - private final File m_dir; - - /** - * The root block of the leader at the start of the current write set. - */ - private IRootBlockView m_rootBlock; - - /** Current write cache block sequence counter. */ - private long m_sequence = 0; - - /** current log file. */ - private File m_log = null; - - public static final String HA_LOG_EXT = ".ha-log"; - - /** current output file channel. */ - private RandomAccessFile m_raf = null; - private FileChannel m_channel = null; - final static int ROOTBLOCK_0 = 0; - final static int ROOTBLOCK_1 = RootBlockView.SIZEOF_ROOT_BLOCK; - final static int START_DATA = ROOTBLOCK_1 + RootBlockView.SIZEOF_ROOT_BLOCK; - - public ProcessLogWriter(final File logDir) { - - m_dir = logDir; - - } - - /** - * Open an HA log file for the write set starting with the given root block. - * - * @param rootBlock - * The root block. - * @throws FileNotFoundException - * @throws IOException - */ - public void createLog(final IRootBlockView rootBlock) - throws FileNotFoundException, IOException { - - if (rootBlock == null) - throw new IllegalArgumentException(); - - if (m_rootBlock != null) // may not be open. - throw new IllegalStateException(); - - if (haLog.isInfoEnabled()) - haLog.info("rootBlock=" + rootBlock); - - m_rootBlock = rootBlock; - - m_sequence = 0L; - - /* - * Format the name of the log file. - * - * Note: The commit counter in the file name should be zero filled to 20 - * digits so we have the files in lexical order in the file system (for - * convenience). - */ - final String logFile; - { - - final StringBuilder sb = new StringBuilder(); - - final Formatter f = new Formatter(sb); - - /* - * Note: We use commitCounter+1 so the file will be labeled by the - * commit point that will be achieved when that log file is applied - * to a journal whose current commit point is [commitCounter]. - */ - - final long commitCounter = rootBlock.getCommitCounter(); - - f.format("%020d" + HA_LOG_EXT, (commitCounter + 1)); - - logFile = sb.toString(); - - } - - m_log = new File(m_dir, logFile); - - // Must delete file if it exists. - if (m_log.exists() && !m_log.delete()) { - - /* - * It is a problem if a file exists and we can not delete it. We - * need to be able to remove the file and replace it with a new file - * when we log the write set for this commit point. - */ - - throw new IOException("Could not delete: " + m_log); - - } - - m_raf = new RandomAccessFile(m_log, "rw"); - - m_channel = m_raf.getChannel(); - m_channel.position(START_DATA); - - // On open write rootblock to 0 and 1 - writeRootBlock(ROOTBLOCK_0, rootBlock); - writeRootBlock(ROOTBLOCK_1, rootBlock); - - } - - /** - * Write the final root block on the HA log and close the file. This "seals" - * the file, which now represents the entire write set associated with the - * commit point in the given root block. - * - * @param rootBlock - * The final root block for the write set. - * @throws FileNotFoundException - * @throws IOException - */ - public void closeLog(final IRootBlockView rootBlock) - throws FileNotFoundException, IOException { - - if (rootBlock == null) - throw new IllegalArgumentException(); - - if (m_rootBlock == null) // no root block associated with log. - throw new IllegalStateException(); - - if (haLog.isInfoEnabled()) - haLog.info("rootBlock=" + rootBlock); - - final long expectedCommitCounter = this.m_rootBlock.getCommitCounter() + 1; - - if (expectedCommitCounter != rootBlock.getCommitCounter()) { - - throw new IllegalStateException("CommitCounter: expected=" - + expectedCommitCounter + ", actual=" - + rootBlock.getCommitCounter()); - - } - -// if (rootBlock.getLastCommitTime() != this.m_rootBlock -// .getLastCommitTime()) { -// -// throw new IllegalStateException(); -// -// } - - if (!this.m_rootBlock.getUUID().equals(rootBlock.getUUID())) { - - throw new IllegalStateException("Store UUID: expected=" - + (m_rootBlock.getUUID()) + ", actual=" - + rootBlock.getUUID()); - - } - - flush(); // current streamed data - - writeRootBlock(rootBlock.isRootBlock0() ? ROOTBLOCK_0 : ROOTBLOCK_1, rootBlock); - - close(); - - } - - /** - * Writes the rootblock at the given offset, but preserves the raf offset - * for streamed output. - */ - private void writeRootBlock(final int writePos, final IRootBlockView rootBlock) - throws IOException { - - if (rootBlock == null) - throw new IllegalArgumentException(); - - final long saveSeek = m_channel.position(); - - try { - m_channel.position(writePos); - - m_channel.write(rootBlock.asReadOnlyBuffer()); - - if (haLog.isDebugEnabled()) - haLog.debug("wrote root block: " + rootBlock); - } finally { - m_channel.position(saveSeek); - } - - } - - /** - * - * @param msg - * @param data - */ - public void write(final IHAWriteMessage msg, final ByteBuffer data) - throws IOException { - - if (m_channel == null) - return; - - /* - * Check if this really is a valid message for this file. If it is not, - * then close the file and return immediately - */ - if (m_rootBlock.getCommitCounter() != msg.getCommitCounter()) - return; - - if (m_rootBlock.getLastCommitTime() != msg.getLastCommitTime()) - return; - - if (m_sequence != msg.getSequence()) - return; - - if (haLog.isInfoEnabled()) - haLog.info("msg=" + msg); - - // write serialized message object - m_channel.write(bufferObject(msg)); - - switch(m_rootBlock.getStoreType()) { - case RW: { - m_channel.write(data); - } - case WORM: - break; - } - - } - - /** - * Utility to return a ByteBuffer containing the external version of the object - */ - private ByteBuffer bufferObject(final Object obj) throws IOException { - final ByteArrayOutputStream baout = new ByteArrayOutputStream(); - final ObjectOutputStream objout = new ObjectOutputStream(baout); - - objout.writeObject(obj); - objout.flush(); - objout.close(); - - return ByteBuffer.wrap(baout.toByteArray()); - } - - /** - * Close the file (does not flush). - */ - private void close() throws IOException { - try { - if (m_channel != null) { - m_channel.close(); - } - } finally { - reset(); - } - } - - /** - * Clear internal fields. - */ - private void reset() { - - m_log = null; - - m_raf = null; - - m_channel = null; - - m_rootBlock = null; - - m_sequence = 0L; - - } - - /** - * When the HA leader commits it must flush the log - */ - private void flush() throws IOException { - - if (m_channel != null) { - - m_channel.force(true); - - } - - } - - /** - * On various error conditions we may need to remove the log - * - * @throws IOException - */ - private void remove() throws IOException { - - try { - - if (m_channel != null) { - - /* - * Conditional remove iff file is open. Will not remove - * something that has been closed. - */ - - m_channel.close(); - - if (m_log.exists() && !m_log.delete()) { - - /* - * It is a problem if a file exists and we can not delete - * it. We need to be able to remove the file and replace it - * with a new file when we log the write set for this commit - * point. - */ - - throw new IOException("Could not delete: " + m_log); - - } - - } - - } finally { - - reset(); - - } - - } - - /** - * Disable the current log file if one is open. - */ - public void disable() throws IOException { - - if (haLog.isInfoEnabled()) - haLog.info(""); - - /* - * Remove unless log file was already closed. - */ - - remove(); - - } - - // FIXME write utility to dump one or more log files. - public static void main(final String[] args) { - - } - -} Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommit.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommit.java 2012-10-05 16:58:14 UTC (rev 6659) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommit.java 2012-10-05 19:46:27 UTC (rev 6660) @@ -28,6 +28,8 @@ package com.bigdata.ha; import java.io.IOException; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -56,6 +58,10 @@ * root block for use with the next {@link #commit2Phase(long, long) commit} * message. * + * @param joinedServiceIds + * The services joined with the met quorum, in their join order. + * @param nonJoinedPipelineServiceIds + * The non-joined services in the write pipeline (in any order). * @param isRootBlock0 * if this is rootBlock0. * @param rootBlock @@ -68,9 +74,11 @@ * @return A {@link Future} which evaluates to a yes/no vote on whether the * service is prepared to commit. */ - int prepare2Phase(IRootBlockView rootBlock, - long timeout, TimeUnit unit) throws InterruptedException, - TimeoutException, IOException; + int prepare2Phase(final UUID[] joinedServiceIds, // + final Set<UUID> nonJoinedPipelineServiceIds,// + final IRootBlockView rootBlock, final long timeout, + final TimeUnit unit) throws InterruptedException, TimeoutException, + IOException; /** * Used by the leader to send a message to each joined service in the quorum @@ -80,13 +88,19 @@ * the lastCommitTime on this message agree with the quorum token and * lastCommitTime in the root block from the last "prepare" message. * + * @param joinedServiceIds + * The services joined with the met quorum, in their join order. + * @param nonJoinedPipelineServiceIds + * The non-joined services in the write pipeline (in any order). * @param token * The quorum token used in the prepare message. * @param commitTime * The commit time that assigned to the new commit point. */ - void commit2Phase(long token, long commitTime) throws IOException, - InterruptedException; + void commit2Phase( + final UUID[] joinedServiceIds, // + final Set<UUID> nonJoinedPipelineServiceIds, long token, + long commitTime) throws IOException, InterruptedException; /** * Used by the leader to send a message to each service joined with the Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java 2012-10-05 16:58:14 UTC (rev 6659) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumCommitImpl.java 2012-10-05 19:46:27 UTC (rev 6660) @@ -24,8 +24,10 @@ package com.bigdata.ha; import java.io.IOException; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -116,11 +118,33 @@ * thread to avoid deadlock. The other services run the operation * asynchronously on their side while the leader awaits their future's using * get(). + * <p> + * Note: The {@link IHA2PhasePrepareMessage} is sent to all services in + * write pipeline. This ensures that services that are not yet joined with + * the met quorum will still observe the root blocks. This is necessary in + * order for them to catch up with the met quorum. The 2-phase commit + * protocol is "aware" that not all messages are being sent to services + * whose votes count. Only services that are actually in the met quorum get + * a vote. + * <p> + * Note: This method is responsible for the atomic decision regarding + * whether a service will be considered to be "joined" with the met quorum + * for the 2-phase commit. A service that is synchronizing will either have + * already voted the appropriate lastCommitTime and entered the met quorum + * or it will not. That decision point is captured atomically when we obtain + * a snapshot of the joined services from the quorum state. The decision is + * propagated through the {@link IHA2PhasePrepareMessage} and that + * information is retained by the service together with the new root block + * from the prepare message. This metadata is used to decide how the service + * will handle the prepare, commit, and abort messages. */ - public int prepare2Phase(//final boolean isRootBlock0, - final IRootBlockView rootBlock, final long timeout, - final TimeUnit unit) throws InterruptedException, TimeoutException, - IOException { + public int prepare2Phase(// + final UUID[] joinedServiceIds, // + final Set<UUID> nonJoinedPipelineServiceIds,// + final IRootBlockView rootBlock,// + final long timeout, final TimeUnit unit// + ) + throws InterruptedException, TimeoutException, IOException { if (rootBlock == null) throw new IllegalArgumentException(); @@ -149,95 +173,171 @@ final long begin = System.nanoTime(); final long nanos = unit.toNanos(timeout); long remaining = nanos; + + /* + * The leader is a local service. The followers and other service in the + * pipeline (but not yet joined) are remote services. + */ - int nyes = 0; + // #of remote followers (joined services, excluding the leader). + final int nfollowers = (joinedServiceIds.length - 1); - // // Copy the root block into a byte[]. - // final byte[] data; - // { - // final ByteBuffer rb = rootBlock.asReadOnlyBuffer(); - // data = new byte[rb.limit()]; - // rb.get(data); - // } + // #of non-joined services in the pipeline. + final int nNonJoinedPipelineServices = nonJoinedPipelineServiceIds + .size(); - final List<Future<Boolean>> remoteFutures = new LinkedList<Future<Boolean>>(); + // #of remote services (followers plus others in the pipeline). + final int remoteServiceCount = nfollowers + nNonJoinedPipelineServices; - /* - * For services (other than the leader) in the quorum, submit the - * RunnableFutures to an Executor. - */ - final UUID[] joinedServiceIds = getQuorum().getJoined(); + // Random access list of futures. + final ArrayList<Future<Boolean>> remoteFutures = new ArrayList<Future<Boolean>>( + remoteServiceCount); + + for (int i = 0; i <= remoteServiceCount; i++) { + + // Pre-size to ensure sufficient room for set(i,foo). + remoteFutures.add(null); - // Verify the quorum is valid. - member.assertLeader(token); + } -// final byte[] tmp = BytesUtil.toArray(rootBlock.asReadOnlyBuffer()); - final IHA2PhasePrepareMessage msg = new HA2PhasePrepareMessage( - rootBlock, timeout, unit); - - for (int i = 1; i < joinedServiceIds.length; i++) { - - final UUID serviceId = joinedServiceIds[i]; + try { + // Verify the quorum is valid. + member.assertLeader(token); + + { + + // The message used for the services that are joined. + final IHA2PhasePrepareMessage msgForJoinedService = new HA2PhasePrepareMessage( + true/* isJoinedService */, rootBlock, timeout, unit); + + // First, message the joined services (met with the quorum). + int i = 1; + { + + for (; i < joinedServiceIds.length; i++) { + + final UUID serviceId = joinedServiceIds[i]; + + /* + * Runnable which will execute this message on the + * remote service. + */ + final Future<Boolean> rf = getService(serviceId) + .prepare2Phase(msgForJoinedService); + + // add to list of futures we will check. + remoteFutures.set(i, rf); + + } + + } + + // Next, message the pipeline services NOT met with the quorum. + { + + // message for non-joined services. + final IHA2PhasePrepareMessage msg = new HA2PhasePrepareMessage( + false/* isJoinedService */, rootBlock, timeout, unit); + + for (UUID serviceId : nonJoinedPipelineServiceIds) { + + /* + * Runnable which will execute this message on the + * remote service. + */ + final Future<Boolean> rf = getService(serviceId) + .prepare2Phase(msg); + + // add to list of futures we will check. + remoteFutures.set(i, rf); + + i++; + + } + } + + /* + * Finally, run the operation on the leader using local method + * call (non-RMI) in the caller's thread to avoid deadlock. + * + * Note: Because we are running this in the caller's thread on + * the leader the timeout will be ignored for the leader. + * + * Note: The leader will do this operation synchronously in this + * thread. This is why we add its future into the collection of + * futures after we have issued the RMI requests to the other + * services. + */ + { + + final S leader = member.getService(); + + final Future<Boolean> f = leader + .prepare2Phase(msgForJoinedService); + + remoteFutures.set(0/* index */, f); + + } + + } + /* - * Runnable which will execute this message on the remote service. + * Check futures for all services that were messaged. */ - final Future<Boolean> rf = getService(serviceId).prepare2Phase(msg); + int nyes = 0; + assert remoteFutures.size() == remoteServiceCount + 1; + for (int i = 0; i <= remoteServiceCount; i++) { + final Future<Boolean> rf = remoteFutures.get(i); + if (rf == null) + throw new AssertionError("null @ index=" + i); + boolean done = false; + try { + remaining = nanos - (begin - System.nanoTime()); + final boolean vote = rf + .get(remaining, TimeUnit.NANOSECONDS); + if (i < joinedServiceIds.length) { + // Only the leader and the followers get a vote. + nyes += vote ? 1 : 0; + } else { + // non-joined pipeline service. vote does not count. + if (!vote) { + log.warn("Non-joined pipeline service will not prepare"); + } + } + done = true; + } catch (ExecutionException ex) { + log.error(ex, ex); + } finally { + if (!done) { + // Cancel the request on the remote service (RMI). + try { + rf.cancel(true/* mayInterruptIfRunning */); + } catch (Throwable t) { + // ignored. + } + } + } + } - // add to list of futures we will check. - remoteFutures.add(rf); + final int k = getQuorum().replicationFactor(); -// /* -// * Submit the runnable for execution by the leader's -// * ExecutorService. When the runnable runs it will execute the -// * message on the remote service using RMI. -// */ -// member.getExecutor().execute(rf); + if (!getQuorum().isQuorum(nyes)) { - } + log.error("prepare rejected: nyes=" + nyes + " out of " + k); - { + } + + return nyes; + + } finally { /* - * Run the operation on the leader using local method call (non-RMI) - * in the caller's thread to avoid deadlock. - * - * Note: Because we are running this in the caller's thread on the - * leader the timeout will be ignored for the leader. + * Ensure that all futures are cancelled. */ - final S leader = member.getService(); - final Future<Boolean> f = leader.prepare2Phase(msg); - remoteFutures.add(f); -// /* -// * Note: This runs synchronously in the caller's thread (it ignores -// * timeout). -// */ -// f.run(); -// try { -// remaining = nanos - (begin - System.nanoTime()); -// nyes += f.get(remaining, TimeUnit.NANOSECONDS) ? 1 : 0; -// } catch (ExecutionException e) { -// // Cancel remote futures. -// cancelRemoteFutures(remoteFutures); -// // Error on the leader. -// ... [truncated message content] |
From: <tho...@us...> - 2012-10-08 14:04:44
|
Revision: 6661 http://bigdata.svn.sourceforge.net/bigdata/?rev=6661&view=rev Author: thompsonbry Date: 2012-10-08 14:04:33 +0000 (Mon, 08 Oct 2012) Log Message: ----------- Found problem where the ZkQuorumWatcher was clearing the token in zk before it had enough information to justify its belief that the quorum was not met (this was happening in getLastValidTokenFromQuorumState(). Modified the ZkQuorumImpl to sort the votes when processing them. Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/quorum/zk/ZKQuorumImpl.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-05 19:46:27 UTC (rev 6660) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-08 14:04:33 UTC (rev 6661) @@ -149,6 +149,8 @@ final long commitCounter = rootBlock.getCommitCounter(); f.format("%020d" + HA_LOG_EXT, (commitCounter + 1)); + f.flush(); + f.close(); logFile = sb.toString(); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java 2012-10-05 19:46:27 UTC (rev 6660) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java 2012-10-08 14:04:33 UTC (rev 6661) @@ -3119,7 +3119,7 @@ + type + ",lastValidToken=" + lastValidToken - + ",token=" + + ",currentToken=" + token + ",serviceId=" + serviceId Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-05 19:46:27 UTC (rev 6660) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-08 14:04:33 UTC (rev 6661) @@ -69,7 +69,7 @@ */ public class HAJournalServer extends AbstractServer { - private static final Logger log = Logger.getLogger(HAJournal.class); + private static final Logger log = Logger.getLogger(HAJournalServer.class); /** * Logger for HA events. @@ -421,7 +421,7 @@ @Override public void notify(final QuorumEvent e) { if (log.isTraceEnabled()) - haLog.trace("QuorumEvent: " + e); + System.err.println("QuorumEvent: " + e); // TODO LOG @ TRACE // switch(e.getEventType()) { // case CAST_VOTE: // break; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/quorum/zk/ZKQuorumImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/quorum/zk/ZKQuorumImpl.java 2012-10-05 19:46:27 UTC (rev 6660) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/quorum/zk/ZKQuorumImpl.java 2012-10-08 14:04:33 UTC (rev 6661) @@ -31,6 +31,7 @@ import java.net.InetSocketAddress; import java.rmi.Remote; import java.util.Arrays; +import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -507,11 +508,55 @@ // return modified; // } + /** + * Return the zpath for the VOTES for the logical service to which this + * service belongs. + */ + private String getVotesZPath() { + + // TODO Extract constant. + return logicalServiceId + "/" + QUORUM + "/" + QUORUM_VOTES; + + } + + /** + * Return the zpath for the given last commit time for the logical + * service to which this service belongs. + * + * @param lastCommitTime + * The last commit time. + * + * @return The zpath. + */ +// * <p> +// * Note: The lastCommitTime znode name is formatted with 20 digits and +// * leading zeros in order to allow any possible last commit time values +// * while preserving the ability to order them by a lexical sort on the +// * zpaths or znode names. + private String getLastCommitTimeZPath(final long lastCommitTime) { + + final StringBuilder sb = new StringBuilder(); + + sb.append(getVotesZPath()); + + sb.append("/"); + sb.append(lastCommitTime); +// final Formatter f = new Formatter(sb); +// +// f.format("%020d", lastCommitTime); +// f.flush(); +// f.close(); + + // zpath for the lastCommitTime. + final String lastCommitTimeZPath = sb.toString(); + + return lastCommitTimeZPath; + + } + @Override protected void doCastVote(final long lastCommitTime) { - // zpath for the lastCommitTime. - final String lastCommitTimeZPath = logicalServiceId + "/" + QUORUM - + "/" + QUORUM_VOTES + "/" + lastCommitTime; + final String lastCommitTimeZPath = getLastCommitTimeZPath(lastCommitTime); if (log.isInfoEnabled()) log.info("lastCommitTime=" + lastCommitTime + ", lastCommitTimeZPath=" + lastCommitTimeZPath); @@ -588,8 +633,7 @@ @Override protected void doWithdrawVote() { // zpath for votes. - final String votesZPath = logicalServiceId + "/" + QUORUM + "/" - + QUORUM_VOTES; + final String votesZPath = getVotesZPath(); if (log.isInfoEnabled()) log.info("votesZPath=" + votesZPath); // get a valid zookeeper connection object. @@ -602,9 +646,12 @@ return; } // find all lastCommitTimes for which votes have been cast. - final List<String> lastCommitTimes; + final String[] lastCommitTimes; try { - lastCommitTimes = zk.getChildren(votesZPath, false/* watch */); + final List<String> tmp = zk + .getChildren(votesZPath, false/* watch */); + lastCommitTimes = tmp.toArray(new String[tmp.size()]); + Arrays.sort(lastCommitTimes,VoteComparator.INSTANCE); // Note: ascending order! } catch (KeeperException e) { throw new RuntimeException(e); } catch (InterruptedException e) { @@ -880,11 +927,12 @@ final QuorumTokenState newState = new QuorumTokenState(oldState .lastValidToken(), Quorum.NO_QUORUM); try { +// log.fatal("Setting quorum state: \noldState="+oldState+"\nnewState="+newState); zk.setData(logicalServiceId + "/" + QUORUM, SerializerUtil .serialize(newState), stat.getVersion()); // done. if (log.isInfoEnabled()) - log.info("Cleared: serviceId=" + serviceId); + log.info("Cleared token: serviceId=" + serviceId); return; } catch (BadVersionException e) { /* @@ -907,6 +955,8 @@ @Override protected void doSetToken(final long newToken) { + if (newToken == NO_QUORUM) + throw new IllegalArgumentException(); // get a valid zookeeper connection object. final ZooKeeper zk; try { @@ -967,6 +1017,7 @@ // new local state object. final QuorumTokenState newState = new QuorumTokenState( newToken, newToken); +// log.fatal("Setting quorum state: \noldState="+oldState+"\nnewState="+newState); // update data (verifying the version!) zk.setData(logicalServiceId + "/" + QUORUM, SerializerUtil .serialize(newState), stat.getVersion()); @@ -1700,7 +1751,8 @@ lock.lock(); try { - if (state.token() == NO_QUORUM && token() != NO_QUORUM) { + final long tok = token(); + if (state.token() == NO_QUORUM && tok != NO_QUORUM) { clearToken(); } else if (lastValidToken() != state.lastValidToken()) { final UUID[] joined = getJoined(); @@ -1711,6 +1763,10 @@ log.warn("Can not set token - not enough joined services: k=" + k + ", joined=" + joined.length); } + } else { + if (log.isInfoEnabled()) + log.info("Nothing to do: currentState=" + state + + ", token()=" + tok); } } finally { lock.unlock(); @@ -1927,38 +1983,45 @@ log.info("Starting with quorum that has already met in the past: " + tokenState); - if (tokenState.token() != NO_QUORUM && !isQuorumMet()) { - - try { - - /* - * Replace the QuorumTokenState. - * - * There is a durable QuorumTokenState, but it has a valid - * token even though the quorum is not met. This can occur - * on restart since it is quorum clients that are - * responsible for managing the state in zookeeper. If all - * clients were killed, then none of them will have had the - * opportunity to update the QuorumTokenState in zookeeper - * and it will falsely report a valid quorum. We fix that - * now by replacing the data in the znode. - */ - QuorumTokenState tmp = new QuorumTokenState(// - tokenState.lastValidToken(),// lastValidToken - Quorum.NO_QUORUM// currentToken - ); - - // update znode unless version is changed. - zka.getZookeeper().setData(logicalServiceId + "/" + QUORUM, - SerializerUtil.serialize(tmp), stat.getVersion()); - - } catch (BadVersionException ex) { - // ignore. - if (log.isInfoEnabled()) - log.info("Version no longer current: " + ex); - } - - } + /* + * Note: This code can not run in AbstractQuorum.start(C) because + * the watcher has not yet had enough time to discover the current + * state of the quorum, therefore it is premature to force a quorum + * break. + */ +// if (tokenState.token() != NO_QUORUM && !isQuorumMet()) { +// +// try { +// +// /* +// * Replace the QuorumTokenState. +// * +// * There is a durable QuorumTokenState, but it has a valid +// * token even though the quorum is not met. This can occur +// * on restart since it is quorum clients that are +// * responsible for managing the state in zookeeper. If all +// * clients were killed, then none of them will have had the +// * opportunity to update the QuorumTokenState in zookeeper +// * and it will falsely report a valid quorum. We fix that +// * now by replacing the data in the znode. +// */ +// final QuorumTokenState tmp = new QuorumTokenState(// +// tokenState.lastValidToken(),// lastValidToken +// Quorum.NO_QUORUM// currentToken +// ); +// +// // update znode unless version is changed. +// zka.getZookeeper().setData(logicalServiceId + "/" + QUORUM, +// SerializerUtil.serialize(tmp), stat.getVersion()); +// +// } catch (BadVersionException ex) { +// // ignore. +// if (log.isInfoEnabled()) +// log.info("Version no longer current: " + ex); +// } +// +// } + return tokenState.lastValidToken(); } catch (NoNodeException e) { // This is Ok. The node just does not exist yet. @@ -1989,12 +2052,11 @@ final ZooKeeperAccessor zka, final List<ACL> acl) throws KeeperException, InterruptedException { - QuorumTokenState tokenState = new QuorumTokenState(// - Quorum.NO_QUORUM,// lastValidToken - Quorum.NO_QUORUM// currentToken - ); - try { + final QuorumTokenState tokenState = new QuorumTokenState(// + Quorum.NO_QUORUM,// lastValidToken + Quorum.NO_QUORUM// currentToken + ); zka.getZookeeper().create(logicalServiceId + "/" + QUORUM, SerializerUtil.serialize(tokenState), acl, CreateMode.PERSISTENT); @@ -2038,4 +2100,25 @@ } + /** + * Compares string values representing <code>lastCommitTime</code> votes by + * decoding them to <code>long</code> values. + */ + private static class VoteComparator implements Comparator<String> { + + final static VoteComparator INSTANCE = new VoteComparator(); + + @Override + public int compare(final String arg0, final String arg1) { + final long v0 = Long.parseLong(arg0); + final long v1 = Long.parseLong(arg1); + if (v0 < v1) + return -1; + if (v0 > v1) + return 1; + return 0; + } + + } + } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-10 19:29:57
|
Revision: 6668 http://bigdata.svn.sourceforge.net/bigdata/?rev=6668&view=rev Author: thompsonbry Date: 2012-10-10 19:29:47 +0000 (Wed, 10 Oct 2012) Log Message: ----------- Working through the resynchronization protocol for HA. - done. Not pruning lastCommitTimes for which no votes exist in a timely fashion. These lastCommitTimes are durable znodes with ephemeral children. As such, application logic is required in order to remove the lastCommitTimes for which no votes remain. When all services are suddenly terminated, the lastCommitTime znodes can not be cleared by application logic. In order to amerliorate this issue, I modified to sort the lastCommitTime votes in withdrawVote() in order to provide a more stable behavior. Since the votes are now processed in lastCommitTime order, the next time a service withdraws its current vote, it should clear out any votes for earlier lastCommitTimes that have no remaining votes. - done. add sequenceCount to the root block. this will help us know how many messages are in an HALog file. - done. the resynchronization messages and write cache blocks are now being transmitted over the existing write pipeline. The messages currently originate with the leader and must originate with a service that is upstream of the service that requests the replay of the HALog for a given commit point. The IHALogRequest message is now passed with the IHAWriteMessage. When non-null, it indicates the replay of a historical IHAWriteMessage and the associated WriteCache block. Various code paths were made conditional on whether or not the IHALogRequest is non-null. For example, you can not do asserts on the quorum token in a historical message and you do not want to truncate the store when passing an historical message along to the next service in the write pipeline. Note: By deliberate design, the replay of IHAWriteMessages and the associated WriteCache buffers will be observed by all services in the write pipeline. While this does mean that services may have to forward buffers that they would not have otherwise observed, it provides a simple and reliable pattern for moving the data. We can leverage that pattern to provide things like off site replication (by introducing a proxy service that becomes a pipeline member and then stores and forwards the IHAWriteMessages and WriteCache blocks). - I have made the HA_LOG_DIR option for the HAJournal optional and given it a default value. ---- Updated the HAJournal sample deployment files: - HAJournal.env now specifies a java logging file and JAVAOPTS that will be used by HAJournalServer.sh - HAJournalServer.sh was modified to use those options. - README was updated to clarify some aspects of service setup and configuration. - log4j.properties and logging.properties were modified to log on files. Note: TEST FIRST. The shellScript may not work once committed from OSX. Verify the logging setups. ---- Bug fix to performance counter reporting to avoid problem with AssertionError when reporting performance counters with a concurrent service shutdown. The access to the live index counters was lifted into the AbstractJournal which has direct access to the _name2Addr reference and then exposed to the Journal through a protected method. The live index counters will now be omitted if there is a concurrent shutdown. java.lang.AssertionError ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at com.bigdata.journal.AbstractJournal._getName2Addr(AbstractJournal.java:500) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at com.bigdata.journal.Journal.getCounters(Journal.java:974) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at com.bigdata.counters.ganglia.QueryEngineMetricsCollector.collect(QueryEngineMetricsCollector.java:92) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at com.bigdata.ganglia.GangliaService.gatherMetrics(GangliaService.java:1108) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at com.bigdata.ganglia.GangliaService$GatherMetricsTask.run(GangliaService.java:1066) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:165) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:267) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) ?\194?\160 ?\194?\160 ?\194?\160 ?\194?\160at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) ---- Bug fix to problem that we just addressed in ZkQuorumImpl. The problem has two sides. (A) The code in getLastValidTokenState() can not reliably clear the old token because the watcher has not yet developed a complete view of the quorum state. (B) If the old token is not cleared, then we can not set the new token when the quorum meets. At this point we do have enough state to know that the quorum is meeting, but it is possible that another service has already observed this. I think that what we need to do is reliably clear the old token once we have a coherent picture of the quorum state, or at least once we have observed that we do not have enough joined services to justify a met quorum. I have modified the ZkQuorumImpl code in setupWatchers() to use synchronous processing when pushing mock events through the code to build up the initial model of the quorum state. We can not use synchronous handling when the events originate in zookeeper (no blocking operations in the event thread), but this is Ok with the mock events. Since these mock events are now handled synchronously, we know that we have a valid reflection of the quorum by the end of that method. At that point, we can conditionally clear the token if necessary. I also modified QuorumTokenStateWatcher (in ZkQuorumImpl) to call through to setToken() if either the lastValidToken and/or the token differs and QuorumWatcherBase.setToken() to only return immediately if BOTH the lastValidToken and the token are equal to the newToken. This appears to fix the issue. The stack trace that these changes were made to handle is below: {{{ ERROR: 13216 2012-10-09 12:50:57,940 WatcherActionService1 com.bigdata.quorum.AbstractQuorum$QuorumWatcherBase$1.run(AbstractQuorum.java:2050): com.bigdata.quorum.QuorumException: The quorum token has not been cleared com.bigdata.quorum.QuorumException: The quorum token has not been cleared at com.bigdata.quorum.zk.ZKQuorumImpl$ZkQuorumActor.doSetToken(ZKQuorumImpl.java:1013) at com.bigdata.quorum.AbstractQuorum$QuorumActorBase.conditionalSetToken(AbstractQuorum.java:1735) at com.bigdata.quorum.AbstractQuorum$QuorumActorBase.setToken(AbstractQuorum.java:1423) at com.bigdata.quorum.AbstractQuorum$QuorumWatcherBase$6.run(AbstractQuorum.java:2837) at com.bigdata.quorum.AbstractQuorum$QuorumWatcherBase$1.run(AbstractQuorum.java:2048) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:680) }}} We need to add test coverage for the case when the 2 services meet in a quorum before the third quorum starts. This is the condition under which these failure modes can be observed. The current set of HA3 unit tests for the Quorum logic always start all three quorums at the same time. @see https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseAbortMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseCommitMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhasePrepareMessage.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAReadRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAReadResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHARootBlockRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHARootBlockResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAWriteMessageBase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCache.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/io/writecache/WriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/FileMetadata.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IHABufferStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/IRootBlockView.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/Journal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RWStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RootBlockView.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/WORMStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/quorum/AbstractQuorum.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/RWStore.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/rwstore/sector/MemStrategy.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/io/writecache/TestWORMWriteCacheService.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/TestRootBlockView.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/journal/ha/AbstractHAJournalTestCase.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/quorum/MockQuorumFixture.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/test/com/bigdata/quorum/TestHA3QuorumSemantics.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/AbstractServer.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-B.config branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal-C.config branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.config branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/quorum/zk/ZKQuorumImpl.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/service/jini/JiniFederation.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/service/jini/lookup/AbstractCachingServiceClient.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/test/com/bigdata/quorum/zk/MockQuorumMember.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/test/com/bigdata/quorum/zk/TestZkHA3QuorumSemantics.java branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.config branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournal.env branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/HAJournalServer.sh branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/README branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/log4j.properties branches/BIGDATA_RELEASE_1_2_0/src/resources/HAJournal/logging.properties Added Paths: ----------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRootBlocksRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRootBlocksResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHAMessage.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAGlueDelegate.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -32,6 +32,9 @@ import com.bigdata.ha.msg.IHA2PhaseAbortMessage; import com.bigdata.ha.msg.IHA2PhaseCommitMessage; import com.bigdata.ha.msg.IHA2PhasePrepareMessage; +import com.bigdata.ha.msg.IHALogRequest; +import com.bigdata.ha.msg.IHALogRootBlocksRequest; +import com.bigdata.ha.msg.IHALogRootBlocksResponse; import com.bigdata.ha.msg.IHAReadRequest; import com.bigdata.ha.msg.IHAReadResponse; import com.bigdata.ha.msg.IHARootBlockRequest; @@ -97,9 +100,9 @@ return delegate.abort2Phase(abortMessage); } - public Future<Void> receiveAndReplicate(IHAWriteMessage msg) - throws IOException { - return delegate.receiveAndReplicate(msg); + public Future<Void> receiveAndReplicate(IHALogRequest req, + IHAWriteMessage msg) throws IOException { + return delegate.receiveAndReplicate(req, msg); } @Override @@ -162,4 +165,16 @@ return delegate.getReleaseTime(); } + @Override + public IHALogRootBlocksResponse getHALogRootBlocksForWriteSet( + IHALogRootBlocksRequest msg) throws IOException { + return delegate.getHALogRootBlocksForWriteSet(msg); + } + + @Override + public Future<Void> sendHALogForWriteSet(IHALogRequest msg) + throws IOException { + return delegate.sendHALogForWriteSet(msg); + } + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -16,9 +16,11 @@ import com.bigdata.io.FileChannelUtility; import com.bigdata.io.IBufferAccess; import com.bigdata.io.IReopenChannel; +import com.bigdata.journal.IHABufferStrategy; import com.bigdata.journal.IRootBlockView; import com.bigdata.journal.RootBlockUtility; import com.bigdata.journal.StoreTypeEnum; +import com.bigdata.journal.WORMStrategy; import com.bigdata.util.ChecksumError; import com.bigdata.util.ChecksumUtility; @@ -247,25 +249,18 @@ * Attempts to read the next {@link IHAWriteMessage} and then the expected * buffer, that is read into the client buffer. The {@link IHAWriteMessage} * is returned to the caller. + * <p> + * Note: The caller's buffer will be filled in IFF the data is on the HALog. + * For some {@link IHABufferStrategy} implementations, that data is not + * present in the HALog. The caller's buffer will not be modified and the + * caller is responsible for getting the data from the + * {@link IHABufferStrategy} (e.g., for the {@link WORMStrategy}). + * <p> + * Note: IF the buffer is filled, then the limit will be the #of bytes ready + * to be transmitted and the position will be zero. * * @param clientBuffer * A buffer from the {@link DirectBufferPool#INSTANCE}. - * - * FIXME We need to pass in the backing store against which the - * log would be resolved. This should be done using the - * {@link HAReadGlue} API (failover reads). We should be able to - * put together the [addr] from the - * {@link IHAWriteMessage#getFirstOffset()} (the offset) plus the - * {@link IHAWriteMessage#getSize()} (the byte length). There - * might also be a checksum in the write cache block, in which - * case we need to back that out of the data that we are trying - * to read, but we still want to verify that checksum once we get - * the data block from the quorum. - * - * FIXME Encapsulate as an iterator pattern where we pass in the - * quorum token (if necessary), and the {@link QuorumServiceBase} - * so we can resolve the nodes on which we can read the raw - * records. */ public IHAWriteMessage processNextBuffer(final ByteBuffer clientBuffer) throws IOException { @@ -326,13 +321,14 @@ } case WORM: { /* - * FIXME The WriteCache block needs to be recovered from the quorum - * using a failover read. + * Note: The WriteCache block needs to be recovered from the + * WORMStrategy by the caller. */ + break; + } + default: throw new UnsupportedOperationException(); -// break; } - } return msg; Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -96,6 +96,90 @@ /** current write point on the channel. */ private long m_position = headerSize0; + /** + * Return the commit counter that is expected for the writes that will be + * logged (the same commit counter that is on the opening root block). + */ + public long getCommitCounter() { + + assertOpen(); + + return m_rootBlock.getCommitCounter(); + + } + + /** + * Return the sequence number that is expected for the next write. + */ + public long getSequence() { + + assertOpen(); + + return m_sequence; + + } + + private void assertOpen() { + + if (m_raf == null) + throw new IllegalStateException(); + + } + + /** + * Return the log file (if any). + */ + public File getFile() { + + return m_log; + + } + + /** + * Return the local name of the HA Log file associated with the + * @param commitCounter + * @return + */ + public static String getHALogFileName(final long commitCounter) { + + /* + * Format the name of the log file. + * + * Note: The commit counter in the file name should be zero filled to 20 + * digits so we have the files in lexical order in the file system (for + * convenience). + */ + final String logFile; + { + + final StringBuilder sb = new StringBuilder(); + + final Formatter f = new Formatter(sb); + + f.format("%020d" + HA_LOG_EXT, commitCounter); + f.flush(); + f.close(); + + logFile = sb.toString(); + + } + + return logFile; + + } + + public String toString() { + + final IRootBlockView tmp = m_rootBlock; + + final long seq = m_sequence; + + return getClass().getName() + "{" + m_raf == null ? "closed" + : "commitCounter=" + tmp.getCommitCounter() + ",nextSequence=" + + seq + "}"; + + } + public HALogWriter(final File logDir) { m_dir = logDir; @@ -132,30 +216,16 @@ * Note: The commit counter in the file name should be zero filled to 20 * digits so we have the files in lexical order in the file system (for * convenience). + * + * Note: We use commitCounter+1 so the file will be labeled by the + * commit point that will be achieved when that log file is applied to a + * journal whose current commit point is [commitCounter]. */ - final String logFile; - { - final StringBuilder sb = new StringBuilder(); + final long commitCounter = rootBlock.getCommitCounter(); - final Formatter f = new Formatter(sb); + final String logFile = getHALogFileName(commitCounter + 1); - /* - * Note: We use commitCounter+1 so the file will be labeled by the - * commit point that will be achieved when that log file is applied - * to a journal whose current commit point is [commitCounter]. - */ - - final long commitCounter = rootBlock.getCommitCounter(); - - f.format("%020d" + HA_LOG_EXT, (commitCounter + 1)); - f.flush(); - f.close(); - - logFile = sb.toString(); - - } - m_log = new File(m_dir, logFile); // Must delete file if it exists. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HAPipelineGlue.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -32,8 +32,14 @@ import java.rmi.Remote; import java.util.concurrent.Future; +import com.bigdata.ha.msg.HAWriteMessage; +import com.bigdata.ha.msg.IHALogRequest; +import com.bigdata.ha.msg.IHALogRootBlocksRequest; +import com.bigdata.ha.msg.IHALogRootBlocksResponse; import com.bigdata.ha.msg.IHAWriteMessage; +import com.bigdata.io.writecache.WriteCache; import com.bigdata.journal.WriteExecutorService; +import com.bigdata.service.proxy.ThickFuture; /** * A {@link Remote} interface supporting the write replication pipeline. The @@ -113,12 +119,61 @@ * pipeline. This method is never invoked on the master. It is only invoked * on the failover nodes, including the last node in the failover chain. * + * @param req + * A request for an HALog (optional). This is only non-null when + * historical {@link WriteCache} blocks are being replayed down + * the write pipeline in order to synchronize a service. * @param msg * The metadata. * * @return The {@link Future} which will become available once the buffer * transfer is complete. */ - Future<Void> receiveAndReplicate(IHAWriteMessage msg) throws IOException; + Future<Void> receiveAndReplicate(IHALogRequest req, IHAWriteMessage msg) + throws IOException; + /** + * Request the root blocks for the HA Log for the specified commit point. + * + * @param msg + * The request (specifies the desired HA Log by the commit + * counter of the closing root block). + * + * @return The requested root blocks. + */ + IHALogRootBlocksResponse getHALogRootBlocksForWriteSet( + IHALogRootBlocksRequest msg) throws IOException; + + /** + * The recipient will send the {@link WriteCache} blocks for the specified + * write set on the write pipeline. These {@link WriteCache} blocks will be + * visible to ALL services in the write pipeline. It is important that all + * services <em>ignore</em> {@link WriteCache} blocks that are not in + * sequence for the current write set unless they have explicitly requested + * an HALog using this method. + * <p> + * Note: The {@link WriteCache} blocks are sent on the write pipeline. + * Therefore, a service MUST NOT request the write set from a service that + * is downstream from it in the write pipeline. It is always safe to request + * the data from the leader. + * <p> + * Note: The {@link HAWriteMessage} includes a quorum token. When historical + * {@link WriteCache} blocks are being replicated in response to this + * method, the will be replicated using the quorum token that was in effect + * for that write set. Implementations MUST NOT perform asserts on the + * quorum token until after they have determined that the message is for the + * <em>current</em> write set. + * <p> + * Note: DO NOT use a {@link ThickFuture} for the returned {@link Future}. + * That will defeat the ability of the requester to cancel the + * {@link Future}. + * + * @param req + * A request for an HALog. + * + * @return A {@link Future} that may be used to cancel the remote process + * sending the data through the write pipeline. + */ + Future<Void> sendHALogForWriteSet(IHALogRequest msg) throws IOException; + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipeline.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -31,6 +31,7 @@ import java.nio.ByteBuffer; import java.util.concurrent.Future; +import com.bigdata.ha.msg.IHALogRequest; import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.io.writecache.WriteCache; import com.bigdata.journal.IRootBlockView; @@ -52,22 +53,32 @@ * pipeline. That follower will accept the payload (and replicate it if * necessary) using {@link #receiveAndReplicate(IHAWriteMessage)}. * + * @param req + * A request for an HALog (optional). This is only non-null when + * historical {@link WriteCache} blocks are being replayed down + * the write pipeline in order to synchronize a service. * @param msg * The RMI metadata about the payload. * @param b * The payload. */ - Future<Void> replicate(IHAWriteMessage msg, ByteBuffer b) throws IOException; + Future<Void> replicate(IHALogRequest req, IHAWriteMessage msg, ByteBuffer b) + throws IOException; /** * Return a {@link Future} for a task which will replicate an NIO buffer * along the write pipeline. This method is invoked for any node except the * master, including the last node in the failover chain. * + * @param req + * A request for an HALog (optional). This is only non-null when + * historical {@link WriteCache} blocks are being replayed down + * the write pipeline in order to synchronize a service. * @param msg * The RMI metadata about the payload. */ - Future<Void> receiveAndReplicate(IHAWriteMessage msg) throws IOException; + Future<Void> receiveAndReplicate(IHALogRequest req, IHAWriteMessage msg) + throws IOException; /* * Note: Method removed since it does not appear necessary to let this @@ -142,7 +153,11 @@ * goes through a commit point in which the quorum is fully met. At that * moment, we no longer require these log files to resynchronize any * service. + * + * @param includeCurrent + * When <code>true</code>, the current HA Log file will also be + * purged. */ - void purgeHALogs(); + void purgeHALogs(boolean includeCurrent); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumPipelineImpl.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -31,16 +31,21 @@ import java.nio.ByteBuffer; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.log4j.Logger; +import com.bigdata.ha.msg.HAWriteMessageBase; +import com.bigdata.ha.msg.IHALogRequest; +import com.bigdata.ha.msg.IHAMessage; import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.ha.pipeline.HAReceiveService; import com.bigdata.ha.pipeline.HAReceiveService.IHAReceiveCallback; @@ -172,7 +177,7 @@ /** * The receive service (iff this is a follower in a met quorum). */ - private HAReceiveService<IHAWriteMessage> receiveService; + private HAReceiveService<HAMessageWrapper> receiveService; /** * The buffer used to relay the data. This is only allocated for a @@ -268,7 +273,7 @@ * @return The buffer -or- <code>null</code> if the pipeline has been torn * down or if this is the leader. */ - private HAReceiveService<IHAWriteMessage> getHAReceiveService() { + private HAReceiveService<HAMessageWrapper> getHAReceiveService() { if (!lock.isHeldByCurrentThread()) { @@ -594,6 +599,37 @@ } /** + * Glue class wraps the {@link IHAWriteMessage} and the + * {@link IHALogRequest} message and exposes the requires {@link IHAMessage} + * interface to the {@link HAReceiveService}. This class is never persisted. + * It just let's us handshake with the {@link HAReceiveService} and get back + * out the original {@link IHAWriteMessage} as well as the optional + * {@link IHALogRequest} message. + * + * @author <a href="mailto:tho...@us...">Bryan + * Thompson</a> + */ + private static class HAMessageWrapper extends HAWriteMessageBase { + + private static final long serialVersionUID = 1L; + + final IHALogRequest req; + final IHAWriteMessage msg; + + public HAMessageWrapper(final IHALogRequest req, + final IHAWriteMessage msg) { + + // Use size and checksum from real IHAWriteMessage. + super(msg.getSize(),msg.getChk()); + + this.req = req; // MAY be null; + this.msg = msg; + + } + + } + + /** * Setup the service to receive pipeline writes and to relay them (if there * is a downstream service). */ @@ -615,12 +651,12 @@ final InetSocketAddress addrNext = downstreamId == null ? null : member.getService(downstreamId).getWritePipelineAddr(); // Setup the receive service. - receiveService = new HAReceiveService<IHAWriteMessage>(addrSelf, - addrNext, new IHAReceiveCallback<IHAWriteMessage>() { - public void callback(IHAWriteMessage msg, ByteBuffer data) - throws Exception { + receiveService = new HAReceiveService<HAMessageWrapper>(addrSelf, + addrNext, new IHAReceiveCallback<HAMessageWrapper>() { + public void callback(final HAMessageWrapper msg, + final ByteBuffer data) throws Exception { // delegate handling of write cache blocks. - handleReplicatedWrite(msg, data); + handleReplicatedWrite(msg.req, msg.msg, data); } }); // Start the receive service - will not return until service is @@ -647,8 +683,9 @@ /* * This is the leader, so send() the buffer. */ - public Future<Void> replicate(final IHAWriteMessage msg, final ByteBuffer b) - throws IOException { + @Override + public Future<Void> replicate(final IHALogRequest req, + final IHAWriteMessage msg, final ByteBuffer b) throws IOException { final RunnableFuture<Void> ft; @@ -658,14 +695,19 @@ if (log.isTraceEnabled()) log.trace("Leader will send: " + b.remaining() + " bytes"); - member.assertLeader(msg.getQuorumToken()); + // Note: disable assert if we allow non-leaders to replicate HALog + // messages (or just verify service joined with the quorum). + if (req == null) { + // Note: Do not test quorum token for historical writes. + member.assertLeader(msg.getQuorumToken()); + } final PipelineState<S> downstream = pipelineStateRef.get(); final HASendService sendService = getHASendService(); - ft = new FutureTask<Void>(new SendBufferTask<S>(msg, b, downstream, - sendService)); + ft = new FutureTask<Void>(new SendBufferTask<S>(req, msg, b, + downstream, sendService, sendLock)); } finally { @@ -673,61 +715,6 @@ } -// /* -// * This is the leader, so send() the buffer. -// */ -// -// ft = new FutureTask<Void>(new Callable<Void>() { -// -// public Void call() throws Exception { -// -// // Get Future for send() outcome on local service. -// final Future<Void> futSnd = getHASendService().send(b); -// -// try { -// -// // Get Future for receive outcome on the remote service (RMI). -// final Future<Void> futRec = downstream.service -// .receiveAndReplicate(msg); -// -// try { -// -// /* -// * Await the Futures, but spend more time waiting on the -// * local Future and only check the remote Future every -// * second. Timeouts are ignored during this loop. -// */ -// while (!futSnd.isDone() && !futRec.isDone()) { -// try { -// futSnd.get(1L, TimeUnit.SECONDS); -// } catch (TimeoutException ignore) { -// } -// try { -// futRec.get(10L, TimeUnit.MILLISECONDS); -// } catch (TimeoutException ignore) { -// } -// } -// futSnd.get(); -// futRec.get(); -// -// } finally { -// if (!futRec.isDone()) { -// // cancel remote Future unless done. -// futRec.cancel(true/* mayInterruptIfRunning */); -// } -// } -// -// } finally { -// // cancel the local Future. -// futSnd.cancel(true/* mayInterruptIfRunning */); -// } -// -// // done -// return null; -// } -// -// }); - // execute the FutureTask. member.getExecutor().execute(ft); @@ -741,23 +728,52 @@ static private class SendBufferTask<S extends HAPipelineGlue> implements Callable<Void> { + private final IHALogRequest req; private final IHAWriteMessage msg; private final ByteBuffer b; private final PipelineState<S> downstream; private final HASendService sendService; + private final Lock sendLock; - public SendBufferTask(final IHAWriteMessage msg, final ByteBuffer b, - final PipelineState<S> downstream, final HASendService sendService) { + public SendBufferTask(final IHALogRequest req, + final IHAWriteMessage msg, final ByteBuffer b, + final PipelineState<S> downstream, + final HASendService sendService, final Lock sendLock) { + this.req = req; // Note: MAY be null. this.msg = msg; this.b = b; this.downstream = downstream; this.sendService = sendService; - + this.sendLock = sendLock; + } public Void call() throws Exception { + /* + * Lock ensures that we do not have more than one request on the + * write pipeline at a time. + */ + + sendLock.lock(); + try { + + doRunWithLock(); + + return null; + + } finally { + + sendLock.unlock(); + + } + + } + + private void doRunWithLock() throws InterruptedException, + ExecutionException, IOException { + // Get Future for send() outcome on local service. final Future<Void> futSnd = sendService.send(b); @@ -765,7 +781,7 @@ // Get Future for receive outcome on the remote service (RMI). final Future<Void> futRec = downstream.service - .receiveAndReplicate(msg); + .receiveAndReplicate(req, msg); try { @@ -799,16 +815,20 @@ futSnd.cancel(true/* mayInterruptIfRunning */); } - // done - return null; - } } - public Future<Void> receiveAndReplicate(final IHAWriteMessage msg) - throws IOException { + /** + * Lock used to ensure that at most one message is being sent along the + * write pipeline at a time. + */ + private final Lock sendLock = new ReentrantLock(); + @Override + public Future<Void> receiveAndReplicate(final IHALogRequest req, + final IHAWriteMessage msg) throws IOException { + final RunnableFuture<Void> ft; lock.lock(); @@ -822,7 +842,8 @@ * possibly we have become a leader. * * TODO We should probably pass in the Quorum and then just - * assert that the msg.getQuorumToken() is valid for the quorum. + * assert that the msg.getQuorumToken() is valid for the quorum + * (but we can't do that for historical messages). */ throw new QuorumException(); @@ -838,7 +859,7 @@ final ByteBuffer b = getReceiveBuffer(); - final HAReceiveService<IHAWriteMessage> receiveService = getHAReceiveService(); + final HAReceiveService<HAMessageWrapper> receiveService = getHAReceiveService(); if (downstream == null) { @@ -852,7 +873,12 @@ try { - return receiveService.receiveData(msg, b); + // wrap the messages together. + final HAMessageWrapper wrappedMsg = new HAMessageWrapper( + req, msg); + + // receive. + return receiveService.receiveData(wrappedMsg, b); } catch (InterruptedException e) { @@ -867,8 +893,8 @@ * not the last). */ - ft = new FutureTask<Void>(new ReceiveAndReplicateTask<S>(msg, b, - downstream, receiveService)); + ft = new FutureTask<Void>(new ReceiveAndReplicateTask<S>(req, msg, + b, downstream, receiveService)); } finally { @@ -876,62 +902,6 @@ } -// final RunnableFuture<Void> ft = new FutureTask<Void>( -// new Callable<Void>() { -// -// public Void call() throws Exception { -// -// // Get Future for send() outcome on local service. -// final Future<Void> futSnd = getHAReceiveService() -// .receiveData(msg, b); -// -// try { -// -// // Get future for receive outcome on the remote -// // service. -// final Future<Void> futRec = downstream.service -// .receiveAndReplicate(msg); -// -// try { -// -// /* -// * Await the Futures, but spend more time -// * waiting on the local Future and only check -// * the remote Future every second. Timeouts are -// * ignored during this loop. -// */ -// while (!futSnd.isDone() && !futRec.isDone()) { -// try { -// futSnd.get(1L, TimeUnit.SECONDS); -// } catch (TimeoutException ignore) { -// } -// try { -// futRec.get(10L, TimeUnit.MILLISECONDS); -// } catch (TimeoutException ignore) { -// } -// } -// futSnd.get(); -// futRec.get(); -// -// } finally { -// if (!futRec.isDone()) { -// // cancel remote Future unless done. -// futRec -// .cancel(true/* mayInterruptIfRunning */); -// } -// } -// -// } finally { -// // cancel the local Future. -// futSnd.cancel(true/* mayInterruptIfRunning */); -// } -// -// // done -// return null; -// } -// -// }); - // execute the FutureTask. member.getExecutor().execute(ft); @@ -946,15 +916,18 @@ private static class ReceiveAndReplicateTask<S extends HAPipelineGlue> implements Callable<Void> { + private final IHALogRequest req; private final IHAWriteMessage msg; private final ByteBuffer b; private final PipelineState<S> downstream; - private final HAReceiveService<IHAWriteMessage> receiveService; + private final HAReceiveService<HAMessageWrapper> receiveService; - public ReceiveAndReplicateTask(final IHAWriteMessage msg, - final ByteBuffer b, final PipelineState<S> downstream, - final HAReceiveService<IHAWriteMessage> receiveService) { + public ReceiveAndReplicateTask(final IHALogRequest req, + final IHAWriteMessage msg, final ByteBuffer b, + final PipelineState<S> downstream, + final HAReceiveService<HAMessageWrapper> receiveService) { + this.req = req; // Note: MAY be null. this.msg = msg; this.b = b; this.downstream = downstream; @@ -963,15 +936,20 @@ public Void call() throws Exception { + // wrap the messages together. + final HAMessageWrapper wrappedMsg = new HAMessageWrapper( + req, msg); + // Get Future for send() outcome on local service. - final Future<Void> futSnd = receiveService.receiveData(msg, b); + final Future<Void> futSnd = receiveService.receiveData(wrappedMsg, + b); try { // Get future for receive outcome on the remote // service. final Future<Void> futRec = downstream.service - .receiveAndReplicate(msg); + .receiveAndReplicate(req, msg); try { @@ -1025,17 +1003,9 @@ * * @throws Exception */ - abstract protected void handleReplicatedWrite(final IHAWriteMessage msg, - final ByteBuffer data) throws Exception; + abstract protected void handleReplicatedWrite(final IHALogRequest req, + final IHAWriteMessage msg, final ByteBuffer data) throws Exception; -// @Override -// abstract public void logWriteCacheBlock(final HAWriteMessage msg, -// final ByteBuffer data) throws IOException; -// -// @Override -// abstract public void logRootBlock(final IRootBlockView rootBlock) -// throws IOException; - /** * A utility class that bundles together the Internet address and port at which * the downstream service will accept and relay cache blocks for the write Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumService.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -28,8 +28,8 @@ package com.bigdata.ha; import java.io.File; -import java.io.IOException; +import com.bigdata.journal.IRootBlockView; import com.bigdata.quorum.Quorum; import com.bigdata.quorum.QuorumMember; @@ -84,4 +84,19 @@ */ long getPrepareTimeout(); + /** + * Install the root blocks from the quorum on the local service. + * <p> + * Note: The initial root blocks on a service are identical, so this root + * block will be installed as both root block ZERO (0) and root block ONE + * (1). + * + * @param rootBlock + * The root block. + * + * @throws IllegalStateException + * if the local service already has writes. + */ + void installRootBlocksFromQuorum(final IRootBlockView rootBlock); + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/QuorumServiceBase.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -39,6 +39,7 @@ import org.apache.log4j.Logger; +import com.bigdata.ha.msg.IHALogRequest; import com.bigdata.ha.msg.IHAWriteMessage; import com.bigdata.journal.AbstractJournal; import com.bigdata.journal.IResourceManager; @@ -102,10 +103,11 @@ addListener(this.pipelineImpl = new QuorumPipelineImpl<S>(this) { @Override - protected void handleReplicatedWrite(final IHAWriteMessage msg, - final ByteBuffer data) throws Exception { + protected void handleReplicatedWrite(final IHALogRequest req, + final IHAWriteMessage msg, final ByteBuffer data) + throws Exception { - QuorumServiceBase.this.handleReplicatedWrite(msg, data); + QuorumServiceBase.this.handleReplicatedWrite(req, msg, data); } @@ -140,9 +142,9 @@ } @Override - public void purgeHALogs() { + public void purgeHALogs(final boolean includeCurrent) { - QuorumServiceBase.this.purgeHALogs(); + QuorumServiceBase.this.purgeHALogs(includeCurrent); } @@ -208,19 +210,19 @@ // } @Override - public Future<Void> receiveAndReplicate(final IHAWriteMessage msg) - throws IOException { + public Future<Void> receiveAndReplicate(final IHALogRequest req, + final IHAWriteMessage msg) throws IOException { - return pipelineImpl.receiveAndReplicate(msg); + return pipelineImpl.receiveAndReplicate(req, msg); } @Override - public Future<Void> replicate(final IHAWriteMessage msg, final ByteBuffer b) - throws IOException { - - return pipelineImpl.replicate(msg, b); - + public Future<Void> replicate(final IHALogRequest req, + final IHAWriteMessage msg, final ByteBuffer b) throws IOException { + + return pipelineImpl.replicate(req, msg, b); + } /** @@ -237,8 +239,8 @@ * * @see QuorumPipelineImpl#handleReplicatedWrite(IHAWriteMessage, ByteBuffer) */ - abstract protected void handleReplicatedWrite(IHAWriteMessage msg, - ByteBuffer data) throws Exception; + abstract protected void handleReplicatedWrite(IHALogRequest req, + IHAWriteMessage msg, ByteBuffer data) throws Exception; /** * {@inheritDoc} @@ -259,7 +261,7 @@ * Note: The default implementation is a NOP. */ @Override - public void purgeHALogs() { + public void purgeHALogs(final boolean includeCurrent) { // NOP Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -0,0 +1,54 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha.msg; + +public class HALogRequest implements IHALogRequest { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final long commitCounter; + + /** + * + * @param commitCounter + * The commit counter used to identify the desired commit point + * (the commit counter of the closing root block). + */ + public HALogRequest(final long commitCounter) { + + this.commitCounter = commitCounter; + + } + + @Override + public long getCommitCounter() { + + return commitCounter; + + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksRequest.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksRequest.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -0,0 +1,54 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha.msg; + +public class HALogRootBlocksRequest implements IHALogRootBlocksRequest { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final long commitCounter; + + /** + * + * @param commitCounter + * The commit counter used to identify the desired commit point + * (the commit counter of the closing root block). + */ + public HALogRootBlocksRequest(final long commitCounter) { + + this.commitCounter = commitCounter; + + } + + @Override + public long getCommitCounter() { + + return commitCounter; + + } + +} Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksResponse.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksResponse.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksResponse.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -0,0 +1,81 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.ha.msg; + +import java.nio.ByteBuffer; + +import com.bigdata.btree.BytesUtil; +import com.bigdata.journal.IRootBlockView; +import com.bigdata.journal.RootBlockView; +import com.bigdata.util.ChecksumUtility; + +public class HALogRootBlocksResponse implements IHALogRootBlocksResponse { + + /** + * + */ + private static final long serialVersionUID = 1L; + + final private boolean openIsRootBlock0; + final private boolean closeIsRootBlock0; + + final private byte[] openData; + final private byte[] closeData; + + public HALogRootBlocksResponse(final IRootBlockView openRootBlock, + final IRootBlockView closeRootBlock) { + + if (openRootBlock == null) + throw new IllegalArgumentException(); + + if (closeRootBlock == null) + throw new IllegalArgumentException(); + + this.openIsRootBlock0 = openRootBlock.isRootBlock0(); + + this.closeIsRootBlock0 = closeRootBlock.isRootBlock0(); + + this.openData = BytesUtil.toArray(openRootBlock.asReadOnlyBuffer()); + + this.closeData = BytesUtil.toArray(closeRootBlock.asReadOnlyBuffer()); + + } + + @Override + public IRootBlockView getOpenRootBlock() { + + return new RootBlockView(openIsRootBlock0, ByteBuffer.wrap(openData), + new ChecksumUtility()); + + } + + @Override + public IRootBlockView getCloseRootBlock() { + + return new RootBlockView(closeIsRootBlock0, ByteBuffer.wrap(closeData), + new ChecksumUtility()); + + } + +} Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseAbortMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseAbortMessage.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseAbortMessage.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -28,7 +28,7 @@ * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> */ -public interface IHA2PhaseAbortMessage { +public interface IHA2PhaseAbortMessage extends IHAMessage { /** * The token for the quorum for which this request was made. Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseCommitMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseCommitMessage.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhaseCommitMessage.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -35,7 +35,7 @@ * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> */ -public interface IHA2PhaseCommitMessage { +public interface IHA2PhaseCommitMessage extends IHAMessage { /** * <code>true</code> iff the service was recognized as being joined with the Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhasePrepareMessage.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhasePrepareMessage.java 2012-10-09 15:30:22 UTC (rev 6667) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHA2PhasePrepareMessage.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -36,7 +36,7 @@ * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> */ -public interface IHA2PhasePrepareMessage { +public interface IHA2PhasePrepareMessage extends IHAMessage { /** * <code>true</code> iff the service was recognized as being joined with the Added: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRequest.java (rev 0) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRequest.java 2012-10-10 19:29:47 UTC (rev 6668) @@ -0,0 +1,37 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modi... [truncated message content] |
From: <tho...@us...> - 2012-10-11 13:17:27
|
Revision: 6669 http://bigdata.svn.sourceforge.net/bigdata/?rev=6669&view=rev Author: thompsonbry Date: 2012-10-11 13:17:18 +0000 (Thu, 11 Oct 2012) Log Message: ----------- Failing to write on the store during resync. Failing to advance the nextSequence in HALogWriter such that the remaining write cache blocks were discarded. https://sourceforge.net/apps/trac/bigdata/ticket/530 (HA Journal) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-10 19:29:47 UTC (rev 6668) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-11 13:17:18 UTC (rev 6669) @@ -82,7 +82,7 @@ private IRootBlockView m_rootBlock; /** Current write cache block sequence counter. */ - private long m_sequence = 0; + private long m_nextSequence = 0; /** current log file. */ private File m_log = null; @@ -115,7 +115,7 @@ assertOpen(); - return m_sequence; + return m_nextSequence; } @@ -172,7 +172,7 @@ final IRootBlockView tmp = m_rootBlock; - final long seq = m_sequence; + final long seq = m_nextSequence; return getClass().getName() + "{" + m_raf == null ? "closed" : "commitCounter=" + tmp.getCommitCounter() + ",nextSequence=" @@ -208,7 +208,7 @@ m_rootBlock = rootBlock; - m_sequence = 0L; + m_nextSequence = 0L; /* * Format the name of the log file. @@ -368,24 +368,26 @@ public void write(final IHAWriteMessage msg, final ByteBuffer data) throws IOException { - if (m_channel == null) - return; + assertOpen(); /* * Check if this really is a valid message for this file. If it is not, * then close the file and return immediately */ if (m_rootBlock.getCommitCounter() != msg.getCommitCounter()) - return; + throw new IllegalStateException("lastCommitTime=" + + m_rootBlock.getLastCommitTime() + ", but msg=" + msg); if (m_rootBlock.getLastCommitTime() != msg.getLastCommitTime()) - return; + throw new IllegalStateException("lastCommitTime=" + + m_rootBlock.getLastCommitTime() + ", but msg=" + msg); - if (m_sequence != msg.getSequence()) - return; + if (m_nextSequence != msg.getSequence()) + throw new IllegalStateException("nextSequence=" + m_nextSequence + + ", but msg=" + msg); if (haLog.isInfoEnabled()) - haLog.info("msg=" + msg); + haLog.info("msg=" + msg + ", position=" + m_position); if (m_position < headerSize0) throw new AssertionError("position=" + m_position @@ -403,6 +405,9 @@ FileChannelUtility.writeAll(reopener, tmp, m_position); m_position += nbytes; + + m_nextSequence++; + } switch(m_rootBlock.getStoreType()) { @@ -472,7 +477,7 @@ m_rootBlock = null; - m_sequence = 0L; + m_nextSequence = 0L; } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-10 19:29:47 UTC (rev 6668) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-11 13:17:18 UTC (rev 6669) @@ -999,15 +999,14 @@ } - final HALogWriter logWriter = journal.getHALogWriter(); - // Make sure we have the correct HALogWriter open. - {//if (logWriter.getCommitCounter() != commitCounter) { - + logLock.lock(); + try { + final HALogWriter logWriter = journal.getHALogWriter(); logWriter.disable(); - logWriter.createLog(openRootBlock); - + } finally { + logLock.unlock(); } /* @@ -1050,9 +1049,15 @@ journal.doLocalCommit( (QuorumService<HAGlue>) HAQuorumService.this, closeRootBlock); - + // Close out the current HALog writer. - logWriter.closeLog(closeRootBlock); + logLock.lock(); + try { + final HALogWriter logWriter = journal.getHALogWriter(); + logWriter.closeLog(closeRootBlock); + } finally { + logLock.unlock(); + } } @@ -1210,7 +1215,8 @@ } - logWriteCacheBlock(msg, data); + // log and write cache block. + acceptHAWriteMessage(msg, data); } finally { @@ -1293,7 +1299,8 @@ } /** - * Verify commitCounter is appropriate, then log and apply. + * Verify commitCounter in the current log file and the message are + * consistent, then log and apply the {@link WriteCache} block. */ private void acceptHAWriteMessage(final IHAWriteMessage msg, final ByteBuffer data) throws IOException, InterruptedException { @@ -1315,37 +1322,8 @@ } - /* - * Note: the ByteBuffer is owned by the HAReceiveService. This - * just wraps up the reference to the ByteBuffer with an - * interface that is also used by the WriteCache to control - * access to ByteBuffers allocated from the DirectBufferPool. - * However, release() is a NOP on this implementation since the - * ByteBuffer is owner by the HAReceiveService. - */ - final IBufferAccess b = new IBufferAccess() { + writeWriteCacheBlock(msg,data); - @Override - public void release(long timeout, TimeUnit unit) - throws InterruptedException { - // NOP - } - - @Override - public void release() throws InterruptedException { - // NOP - } - - @Override - public ByteBuffer buffer() { - return data; - } - }; - - ((IHABufferStrategy) journal.getBufferStrategy()) - .writeRawBuffer(msg, b); - - } /** @@ -1374,6 +1352,45 @@ } } + + /** + * Write the raw {@link WriteCache} block onto the backing store. + */ + private void writeWriteCacheBlock(final IHAWriteMessage msg, + final ByteBuffer data) throws IOException, InterruptedException { + + /* + * Note: the ByteBuffer is owned by the HAReceiveService. This just + * wraps up the reference to the ByteBuffer with an interface that + * is also used by the WriteCache to control access to ByteBuffers + * allocated from the DirectBufferPool. However, release() is a NOP + * on this implementation since the ByteBuffer is owner by the + * HAReceiveService. + */ + + final IBufferAccess b = new IBufferAccess() { + + @Override + public void release(long timeout, TimeUnit unit) + throws InterruptedException { + // NOP + } + + @Override + public void release() throws InterruptedException { + // NOP + } + + @Override + public ByteBuffer buffer() { + return data; + } + }; + + ((IHABufferStrategy) journal.getBufferStrategy()) + .writeRawBuffer(msg, b); + + } /** * {@inheritDoc} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-11 15:26:15
|
Revision: 6670 http://bigdata.svn.sourceforge.net/bigdata/?rev=6670&view=rev Author: thompsonbry Date: 2012-10-11 15:26:05 +0000 (Thu, 11 Oct 2012) Log Message: ----------- Added doLocalAbort() on the AbstractJournal and HAJournal. It is now invoked from the resync code when we start to resync a service. Modified HAJournalServer to not cast a vote if the quorum is met. This is not an atomic decision, but it really needs to be. If the quorum is met, then we need to do a resync rather than vote and join. Added toString() to various HA messages. Took the lock in doLocalCommit(). https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksResponse.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java 2012-10-11 13:17:18 UTC (rev 6669) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java 2012-10-11 15:26:05 UTC (rev 6670) @@ -51,4 +51,10 @@ } + public String toString() { + + return getClass() + "{commitCounter=" + getCommitCounter() + "}"; + + } + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksRequest.java 2012-10-11 13:17:18 UTC (rev 6669) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksRequest.java 2012-10-11 15:26:05 UTC (rev 6670) @@ -51,4 +51,10 @@ } + public String toString() { + + return getClass() + "{commitCounter=" + getCommitCounter() + "}"; + + } + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksResponse.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksResponse.java 2012-10-11 13:17:18 UTC (rev 6669) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRootBlocksResponse.java 2012-10-11 15:26:05 UTC (rev 6670) @@ -78,4 +78,11 @@ } + public String toString() { + + return getClass() + "{openRootBlock=" + getOpenRootBlock() + + ", closeRootBlock=" + getCloseRootBlock() + "}"; + + } + } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-11 13:17:18 UTC (rev 6669) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-11 15:26:05 UTC (rev 6670) @@ -4919,57 +4919,77 @@ /** * Local commit protocol (HA). */ + protected void doLocalAbort() { + + _abort(); + + } + + /** + * Local commit protocol (HA). + */ protected void doLocalCommit(final QuorumService<HAGlue> localService, final IRootBlockView rootBlock) { - // The timestamp for this commit point. - final long commitTime = rootBlock.getLastCommitTime(); + final WriteLock lock = _fieldReadWriteLock.writeLock(); - // write the root block on to the backing store. - _bufferStrategy.writeRootBlock(rootBlock, forceOnCommit); + lock.lock(); - // set the new root block. - _rootBlock = rootBlock; + try { - final boolean leader = localService - .isLeader(rootBlock.getQuorumToken()); + // The timestamp for this commit point. + final long commitTime = rootBlock.getLastCommitTime(); - if (!leader) { + // write the root block on to the backing store. + _bufferStrategy.writeRootBlock(rootBlock, forceOnCommit); - /* - * Ensure allocators are synced after commit. This is only done for - * the followers. The leader has been updating the in-memory - * allocators as it lays down the writes. The followers have not be - * updating the allocators. - */ + // set the new root block. + _rootBlock = rootBlock; - if (haLog.isInfoEnabled()) - haLog.error("Reset from root block: serviceUUID=" - + localService.getServiceId()); + final boolean leader = localService.isLeader(rootBlock + .getQuorumToken()); - ((IHABufferStrategy) _bufferStrategy) - .resetFromHARootBlock(rootBlock); + if (!leader) { - /* - * Clear reference and reload from the store. - * - * The leader does not need to do this since it is writing on the - * unisolated commit record index and thus the new commit record is - * already visible in the commit record index before the commit. - * However, the follower needs to do this since it will otherwise - * not see the new commit points. - */ + /* + * Ensure allocators are synced after commit. This is only done + * for the followers. The leader has been updating the in-memory + * allocators as it lays down the writes. The followers have not + * be updating the allocators. + */ - _commitRecordIndex = _getCommitRecordIndex(); + if (haLog.isInfoEnabled()) + haLog.error("Reset from root block: serviceUUID=" + + localService.getServiceId()); - } + ((IHABufferStrategy) _bufferStrategy) + .resetFromHARootBlock(rootBlock); - // reload the commit record from the new root block. - _commitRecord = _getCommitRecord(); + /* + * Clear reference and reload from the store. + * + * The leader does not need to do this since it is writing on + * the unisolated commit record index and thus the new commit + * record is already visible in the commit record index before + * the commit. However, the follower needs to do this since it + * will otherwise not see the new commit points. + */ - if (txLog.isInfoEnabled()) - txLog.info("COMMIT: commitTime=" + commitTime); + _commitRecordIndex = _getCommitRecordIndex(); + } + + // reload the commit record from the new root block. + _commitRecord = _getCommitRecord(); + + if (txLog.isInfoEnabled()) + txLog.info("COMMIT: commitTime=" + commitTime); + + } finally { + + lock.unlock(); + } + } /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-11 13:17:18 UTC (rev 6669) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournal.java 2012-10-11 15:26:05 UTC (rev 6670) @@ -398,6 +398,18 @@ } /** + * {@inheritDoc} + * <p> + * Extended to expose this method to the {@link HAQuorumService}. + */ + @Override + protected void doLocalAbort() { + + super.doLocalAbort(); + + } + + /** * Extended implementation supports RMI. */ protected class HAGlueService extends BasicHA { @@ -473,15 +485,23 @@ final HALogReader r = new HALogReader(logFile); - return new HALogRootBlocksResponse(r.getOpeningRootBlock(), - r.getClosingRootBlock()); + final HALogRootBlocksResponse resp = new HALogRootBlocksResponse( + r.getOpeningRootBlock(), r.getClosingRootBlock()); + if (haLog.isDebugEnabled()) + haLog.debug("msg=" + msg + ", resp=" + resp); + + return resp; + } @Override public Future<Void> sendHALogForWriteSet(final IHALogRequest req) throws IOException { + if (haLog.isDebugEnabled()) + haLog.debug("req=" + req); + // The commit counter of the desired closing root block. final long commitCounter = req.getCommitCounter(); @@ -530,6 +550,9 @@ final IHAWriteMessage msg = r.processNextBuffer(buf .buffer()); + if (haLog.isDebugEnabled()) + haLog.debug("req=" + req + ", msg=" + msg); + // drop them into the write pipeline. final Future<Void> ft = ((IHABufferStrategy) HAJournal.this .getBufferStrategy()).sendHALogBuffer(req, msg, Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-11 13:17:18 UTC (rev 6669) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-11 15:26:05 UTC (rev 6670) @@ -476,7 +476,32 @@ final QuorumActor<?,?> actor = quorum.getActor(); actor.memberAdd(); actor.pipelineAdd(); - actor.castVote(journal.getLastCommitTime()); +// try { +// quorum.awaitQuorum(1000, TimeUnit.MILLISECONDS); +// } catch (AsynchronousQuorumCloseException e1) { +// // Shutdown. +// return; +// } catch (InterruptedException e1) { +// // Shutdown. +// return; +// } catch (TimeoutException e1) { +// // Quorum is not already met. +// } + if (!quorum.isQuorumMet()) { + /* + * FIXME RESYNC : There needs to be an atomic decision whether to + * cast a vote and then join or to start synchronizing. We need to + * synchronize if a quorum is already met. We need to cast our vote + * iff a quorum has not met. However, I can not see any (easy) way + * to make this operation atomic. Maybe the leader simply needs to + * verify the actual lastCommitTime of each service when the quorum + * meets, or maybe a service can only join the quorum if it can + * verify that the leader has the same lastCommitTime for its + * current root block. Once writes start, we need to be + * resynchronizing rather than joining the quorum. + */ + actor.castVote(journal.getLastCommitTime()); + } /* * Wait until the server is terminated. @@ -887,6 +912,14 @@ haLog.warn("RESYNCH: " + server.getServiceName()); + /* + * Note: We need to discard any writes that might have been + * buffered before we start the resynchronization of the local + * store. + */ + + journal.doLocalAbort(); + while (true) { // The current commit point on the local store. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2012-10-11 17:55:46
|
Revision: 6671 http://bigdata.svn.sourceforge.net/bigdata/?rev=6671&view=rev Author: thompsonbry Date: 2012-10-11 17:55:39 +0000 (Thu, 11 Oct 2012) Log Message: ----------- Bug fix to AbstractJournal to go through a low level abort for the leader also when a quorum meets (corrects the previous commit). Modified HALogWriter/Reader to support the alternating root block pattern. 100% binary compatible journal and HALog files established through resynchronization protocol. Added the serviceId to the IHALogRequest and now checking it as part of the resynchronization protocol to make sure that we only accept the HALog replay messages that we requested. https://sourceforge.net/apps/trac/bigdata/ticket/530 (Journal HA) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRequest.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RootBlockUtility.java branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java 2012-10-11 15:26:05 UTC (rev 6670) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogReader.java 2012-10-11 17:55:39 UTC (rev 6671) @@ -71,14 +71,8 @@ * closing root block has not been written and the data in the log * is useless). * - * The root block is slot 0 is always the root block for the - * previous commit point. - * - * The root block in slot 1 is either identical to the root block in - * slot zero (in which case the log file is logically empty) or it - * is the root block that closes the write set in the HA Log file - * (and its commit counter must be exactly one more than the commit - * counter for the opening root block). + * We figure out which root block is the opening root block based on + * standard logic. */ /* * Read the MAGIC and VERSION. @@ -107,9 +101,10 @@ true/* validateChecksum */, false/* alternateRootBlock */, false/* ignoreBadRootBlock */); - m_openRootBlock = tmp.rootBlock0; + m_closeRootBlock = tmp.chooseRootBlock(); - m_closeRootBlock = tmp.rootBlock1; + m_openRootBlock = tmp.rootBlock0 == m_closeRootBlock ? tmp.rootBlock1 + : tmp.rootBlock0; final long cc0 = m_openRootBlock.getCommitCounter(); Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-11 15:26:05 UTC (rev 6670) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/HALogWriter.java 2012-10-11 17:55:39 UTC (rev 6671) @@ -333,8 +333,18 @@ flush(); // current streamed data - // The closing root block is always in slot 1. - writeRootBlock(false/* isRootBlock0 */, rootBlock); + /* + * The closing root block is written into which ever slot corresponds to + * its whether that root block is root block zero. Both root blocks are + * identical up to this point, so we can write the closing root block + * into either slot. HALogReader will use the commit counters to figure + * out which root block is the opening root block and which root block + * is the closing root block. + */ + writeRootBlock(rootBlock.isRootBlock0(), rootBlock); + +// // The closing root block is always in slot 1. +// writeRootBlock(false/* isRootBlock0 */, rootBlock); close(); @@ -375,8 +385,8 @@ * then close the file and return immediately */ if (m_rootBlock.getCommitCounter() != msg.getCommitCounter()) - throw new IllegalStateException("lastCommitTime=" - + m_rootBlock.getLastCommitTime() + ", but msg=" + msg); + throw new IllegalStateException("commitCounter=" + + m_rootBlock.getCommitCounter() + ", but msg=" + msg); if (m_rootBlock.getLastCommitTime() != msg.getLastCommitTime()) throw new IllegalStateException("lastCommitTime=" Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java 2012-10-11 15:26:05 UTC (rev 6670) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/HALogRequest.java 2012-10-11 17:55:39 UTC (rev 6671) @@ -23,6 +23,8 @@ */ package com.bigdata.ha.msg; +import java.util.UUID; + public class HALogRequest implements IHALogRequest { /** @@ -30,16 +32,19 @@ */ private static final long serialVersionUID = 1L; + private final UUID serviceId; private final long commitCounter; /** - * + * @param serviceId + * The {@link UUID} of the service that made the request. * @param commitCounter * The commit counter used to identify the desired commit point * (the commit counter of the closing root block). */ - public HALogRequest(final long commitCounter) { + public HALogRequest(final UUID serviceId, final long commitCounter) { + this.serviceId = serviceId; this.commitCounter = commitCounter; } @@ -51,10 +56,18 @@ } - public String toString() { + @Override + public UUID getServiceId() { - return getClass() + "{commitCounter=" + getCommitCounter() + "}"; + return serviceId; } + + public String toString() { + + return getClass() + "{serviceId=" + getServiceId() + ", commitCounter=" + + getCommitCounter() + "}"; + + } } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRequest.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRequest.java 2012-10-11 15:26:05 UTC (rev 6670) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/ha/msg/IHALogRequest.java 2012-10-11 17:55:39 UTC (rev 6671) @@ -23,6 +23,8 @@ */ package com.bigdata.ha.msg; +import java.util.UUID; + /** * Message requesting the root blocks and other metadata for an HA Log file. */ @@ -34,4 +36,8 @@ */ long getCommitCounter(); + /** + * The UUID of the service that issued this request. + */ + UUID getServiceId(); } Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-11 15:26:05 UTC (rev 6670) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/AbstractJournal.java 2012-10-11 17:55:39 UTC (rev 6671) @@ -4790,8 +4790,9 @@ // This quorum member. final QuorumService<HAGlue> localService = quorum.getClient(); - - if (_rootBlock.getCommitCounter() == 0 + + if (localService.isJoinedMember(quorumToken) + && _rootBlock.getCommitCounter() == 0 && localService.isFollower(quorumToken)) { /* @@ -4810,35 +4811,13 @@ throw new RuntimeException(e); } + // Installs the root blocks and does a local abort. localService.installRootBlocksFromQuorum(tmp); - - } - - if (localService.isJoinedMember(quorumToken)) { - /* - * We need to reset the backing store with the token for the - * new quorum. There should not be any active writers since - * there was no quorum. Thus, this should just cause the - * backing store to become aware of the new quorum and - * enable writes. - * - * Note: This is done using a local abort, not a 2-phase - * abort. Each node in the quorum should handle this locally - * when it sees the quorum meet event. - * - * TODO This assumes that a service that is not joined with - * the quorum will not go through an _abort(). Such a - * service will have to go through the synchronization - * protocol. If the service is in the pipeline when the - * quorum meets, even through it is not joined, and votes - * the same lastCommitTime, then it MIGHT see all necessary - * replicated writes and if it does, then it could - * synchronize immediately. There is basically a data race - * here. - */ + } else { - _abort(); + // The leader also needs to do a local abort. + doLocalAbort(); } @@ -4914,6 +4893,27 @@ log.info("Synchronized root blocks with qourum: rootBlock=" + _rootBlock); + /* + * We need to reset the backing store with the token for the new quorum. + * There should not be any active writers since there was no quorum. + * Thus, this should just cause the backing store to become aware of the + * new quorum and enable writes. + * + * Note: This is done using a local abort, not a 2-phase abort. Each + * node in the quorum should handle this locally when it sees the quorum + * meet event. + * + * TODO This assumes that a service that is not joined with the quorum + * will not go through an _abort(). Such a service will have to go + * through the synchronization protocol. If the service is in the + * pipeline when the quorum meets, even through it is not joined, and + * votes the same lastCommitTime, then it MIGHT see all necessary + * replicated writes and if it does, then it could synchronize + * immediately. There is basically a data race here. + */ + + _abort(); + } /** Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RootBlockUtility.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RootBlockUtility.java 2012-10-11 15:26:05 UTC (rev 6670) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata/src/java/com/bigdata/journal/RootBlockUtility.java 2012-10-11 17:55:39 UTC (rev 6671) @@ -163,6 +163,26 @@ * Note: For historical compatibility, <code>rootBlock1</code> is chosen if * both root blocks have the same {@link IRootBlockView#getCommitCounter()}. * + * @return The chosen root block. + * + * @throws RuntimeException + * if no root block satisfies the criteria. + */ + public IRootBlockView chooseRootBlock() { + + return chooseRootBlock(rootBlock0, rootBlock1, + false/* alternateRootBlock */, false/* ignoreBadRootBlock */); + + } + + /** + * Return the chosen root block. The root block having the greater + * {@link IRootBlockView#getCommitCounter() commit counter} is chosen by + * default. + * <p> + * Note: For historical compatibility, <code>rootBlock1</code> is chosen if + * both root blocks have the same {@link IRootBlockView#getCommitCounter()}. + * * @param rootBlock0 * Root block 0 (may be <code>null</code> if this root block is * bad). Modified: branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-11 15:26:05 UTC (rev 6670) +++ branches/BIGDATA_RELEASE_1_2_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2012-10-11 17:55:39 UTC (rev 6671) @@ -916,6 +916,10 @@ * Note: We need to discard any writes that might have been * buffered before we start the resynchronization of the local * store. + * + * TODO This might not be necessary. We do a low-level abort + * when we install the root blocks from the quorum leader before + * we sync the first commit point. */ journal.doLocalAbort(); @@ -1061,7 +1065,7 @@ try { ft = leader.sendHALogForWriteSet(new HALogRequest( - commitCounter)); + server.serviceUUID, commitCounter)); // Wait until all write cache blocks are received. ft.get(); @@ -1113,8 +1117,13 @@ if (resyncFuture != null && !resyncFuture.isDone()) { + /* + * If we are resynchronizing, then pass ALL messages (both + * live and historical) into handleResyncMessage(). + */ + setExtent(msg); - handleResyncMessage(msg, data); + handleResyncMessage(req, msg, data); } else if (commitCounter == msg.getCommitCounter() && isJoinedMember(msg.getQuorumToken())) { @@ -1190,30 +1199,18 @@ * * @throws InterruptedException * @throws IOException - * - * FIXME RESYNC : There is only one {@link HALogWriter}. It - * can only have one file open. We need to explicitly - * coordinate which log file is open when so we never - * attempt to write a cache block on the write log file (or - * one that is out of sequence). [Consider making those - * things errors in the {@link HALogWriter} - it quietly - * ignores this right now.] */ - private void handleResyncMessage(final IHAWriteMessage msg, - final ByteBuffer data) throws IOException, InterruptedException { + private void handleResyncMessage(final IHALogRequest req, + final IHAWriteMessage msg, final ByteBuffer data) + throws IOException, InterruptedException { logLock.lock(); try { /* - * FIXME RESYNC : Review the transition conditions. [msg.seq+q == - * log.nextSeq] implies that we have observed and logged this - * write block already. That is the duplicate write cache block - * that let's us know that we are fully synchronized with the - * quorum. - * - * FIXME RESYNC : Review when (and where) we open and close log files. + * TODO RESYNC : Review when (and where) we open and close log + * files. */ final HALogWriter logWriter = journal.getHALogWriter(); @@ -1221,36 +1218,64 @@ final long journalCommitCounter = journal.getRootBlockView() .getCommitCounter(); - if (msg.getCommitCounter() == journalCommitCounter - && msg.getSequence() + 1 == logWriter.getSequence()) { + if (req == null) { + + /* + * Live message. + */ + if (msg.getCommitCounter() == journalCommitCounter + && msg.getSequence() + 1 == logWriter.getSequence()) { + + /* + * We just received the last resync message that we need + * to join the met quorum. + */ + + resyncTransitionToMetQuorum(msg, data); + + return; + + } else { + + /* + * Drop live messages since we are not caught up. + */ + + if (haLog.isDebugEnabled()) + log.debug("Ignoring write cache block: msg=" + msg); + + return; + + } + + } else { + /* - * We just received the last resync message that we need to - * join the met quorum. + * A historical message (replay of an HALog file). + * + * Note: We will see ALL messages. We can only log the + * message if it is for our commit point. */ - - resyncTransitionToMetQuorum(msg,data); - - return; - - } - /* - * Log the message and write cache block. - */ + if (!server.serviceUUID.equals(req.getServiceId())) { - if (logWriter.getCommitCounter() != msg.getCommitCounter()) { + /* + * Not our request. Drop the message. + */ + + if (haLog.isDebugEnabled()) + log.debug("Ignoring write cache block: msg=" + msg); - if (haLog.isDebugEnabled()) - log.debug("Ignoring write cache block: msg=" + msg); + return; - return; + } + // log and write cache block. + acceptHAWriteMessage(msg, data); + } - // log and write cache block. - acceptHAWriteMessage(msg, data); - } finally { logLock.unlock(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |