Skip to main content

Developing with XCC

Retrying Multi-statement Transactions

MarkLogic Server sometimes detects the need to retry a transaction. For example, if the server detects a deadlock, it may cancel one of the deadlocked transactions, allowing the other to complete; the canceled transaction should be re-tried.

With single-statement, auto-commit transactions, the server can usually retry automatically because it has the entire transaction available at the point of detection. However, the statements in multi-statement transactions from XCC clients may be interleaved with arbitrary application-specific code of which the server has no knowledge.

In such cases, instead of automatically retrying, the server throws a RetryableXQueryException. The calling application is then responsible for re-trying all the requests in the transaction up to that point. This exception is more likely to occur when using multi-statement transactions.

The following example demonstrates logic for re-trying a multi-statement transaction. The multi-statement transaction code is wrapped in a retry loop with an exception handler that waits between retry attempts. The number of retries and the time between attempts is up to the application.

import java.net.URI;
import com.marklogic.xcc.ContentSource;
import com.marklogic.xcc.ContentSourceFactory;
import com.marklogic.xcc.Session;
import com.marklogic.xcc.exceptions.RetryableXQueryException;
public class TransactionRetry {
  public static final int MAX_RETRY_ATTEMPTS = 5;
  public static final int RETRY_WAIT_TIME = 1;
  
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("usage: xcc://user:password@host:port/contentbase");
      return;
    }
    // Obtain a ContentSource object for the server at the URI.
    URI uri = new URI(args[0]);
    ContentSource contentSource =
        ContentSourceFactory.newContentSource(uri);
    // Create a Session and set the transaction mode to trigger
    // multi-statement transaction use.
    Session session = contentSource.newSession();
    Session.setAutoCommit(false);
    Session.setUpdate(Session.Update.TRUE);
    // Re-try logic for a multi-statement transaction
    for (int i = 0; i < MAX_RETRY_ATTEMPTS; i++) {
      try {
        session.submitRequest(session.newAdhocQuery(
          "xdmp:document-insert('/docs/mst1.xml', <data/>)"));
        session.submitRequest(session.newAdhocQuery(
          "xdmp:document-insert('/docs/mst2.xml', fn:doc('/docs/mst1.xml'));"));
        session.commit();
        break;
      } catch (RetryableXQueryException e) {
        Thread.sleep(RETRY_WAIT_TIME);
      }
    }
    session.close(); 
  }
}