home shape

ArangoDB Java Driver: Batch & Asynchronous Mode | ArangoDB Blog

The current arangodb-java-driver supports the usage of ArangoDB’s batch and asynchronous interface. This post will guide you through the usage of these features.

The batch interface

The batch interface enables the user to stack a series of calls and execute them in a batch request. Each stacked request returns a request id that can be used to retrieve the single results from the batch response. So how do you use this feature in the java driver ?

First we create an instance of the java driver:

ArangoConfigure configure = new ArangoConfigure();
configure.init();
ArangoDriver driver = new ArangoDriver(configure);

Now we start the batch mode …

driver.startBatchMode();

So now every driver method will return an instance of the BaseEntity class which basically represents the response of a request to ArangoDB. The return code will always be 206 (Partial Content) and the requestId attribute will contain an unique request id.

As an example we will create a view collection, provoke an error in one of these requests and read all collections from the database:

BaseEntity result1 = driver.createCollection("testCollection1"); 
BaseEntity result2 = driver.createCollection("testCollection2"); 
BaseEntity result3 = driver.createCollection("testCollection3"); 
BaseEntity result4 = driver.createCollection(null); 
BaseEntity result5 = driver.getCollections(true);

System.out.println("ReturnCode: " + result1.getStatusCode() + " - RequestId: " + result1.getRequestId()); 
System.out.println("ReturnCode: " + result2.getStatusCode() + " - RequestId: " + result2.getRequestId()); 
System.out.println("ReturnCode: " + result3.getStatusCode() + " - RequestId: " + result3.getRequestId()); 
System.out.println("ReturnCode: " + result4.getStatusCode() + " - RequestId: " + result4.getRequestId()); 
System.out.println("ReturnCode: " + result5.getStatusCode() + " - RequestId: " + result5.getRequestId());

> ReturnCode: 206 - RequestId: request1 
> ReturnCode: 206 - RequestId: request2
> ReturnCode: 206 - RequestId: request3
> ReturnCode: 206 - RequestId: request4
> ReturnCode: 206 - RequestId: request5

Now as there are five calls on our call stack we can execute them in one single batch request.
Please note, that the batch request returns a DefaultEntity as an implementation of the abstract BaseEntity.

DefaultEntity batchResult = driver.executeBatch();
System.out.println("ReturnCode: " + batchResult.getStatusCode() + " - RequestId: " + batchResult.getRequestId());

>ReturnCode: 200 - RequestId: null

The method executeBatch sets the driver back to normal execution mode before executing the batch request. Hence the RequestId is now null.

Now lets analyze the single results. Note that we have to wait for each job to finish …

CollectionEntity testCollection1 = driver.getBatchResponseByRequestId("request1");
CollectionEntity testCollection2 = driver.getBatchResponseByRequestId("request2");
CollectionEntity testCollection3 = driver.getBatchResponseByRequestId("request3");
CollectionEntity testCollection4 = null;
try {
  testCollection4 = driver.getBatchResponseByRequestId("request4");
} catch (ArangoException e) {
  System.out.println("ReturnCode: " + e.getCode() + " - ErrorMessage: " + e.getErrorMessage());
}
CollectionsEntity collectionList = driver.getBatchResponseByRequestId("request5");

System.out.println("ReturnCode: " + testCollection1.getStatusCode() + " - CollectionName: " + testCollection1.getName());
System.out.println("ReturnCode: " + testCollection2.getStatusCode() + " - CollectionName: " + testCollection2.getName());
System.out.println("ReturnCode: " + testCollection3.getStatusCode() + " - CollectionName: " + testCollection3.getName());
System.out.println("ReturnCode: " + collectionList.getStatusCode() + " - Collections: " + collectionList.getNames().keySet());

>ReturnCode: 400 - ErrorMessage: name must be non-empty
>ReturnCode: 200 - CollectionName: testCollection1
>ReturnCode: 200 - CollectionName: testCollection2
>ReturnCode: 200 - CollectionName: testCollection3
>ReturnCode: 200 - Collections: [testCollection3, testCollection2, testCollection1]

If for any reason one wants to cancel the batch mode the method cancelBatchMode will do the trick. Please note that every stacked request is lost when the batch mode is canceled prematurely.

driver.cancelBatchMode();

The async interface

ArangoDB provides two methods of executing client requests asynchronously. Fire and Forget will execute requests without a chance for the user to retrieve any result. Async Execution and later Result Retrieval will execute requests asynchronously too but returns a jobId in the response that can be used to request the result.

We implemented this quite similar to the batch interface by enabling the user to “turn on/turn off” the desired asynchronous mode.

In our example we will explain the Async Execution and later Result Retrieval mode, there is not much to show for Fire and Forget.
So lets reuse the driver from the batch example and activate the async mode…

driver.startAsyncMode(false);

The boolean parameter tells that we do not want to start the async Fire and Forget mode.
So until we stop the async mode every request will return a status code 202 (Accepted). To actually get the results the driver keeps the job ids and offers the method getLastJobId to retrieve the id of the last request. To get all JobIds the method getJobIds can be used.

So lets replay the example from the batch interface section above in async mode …

BaseEntity result1 = driver.createCollection("testCollection1");
String jobId1 = driver.getLastJobId();
BaseEntity result2 = driver.createCollection("testCollection2");
String jobId2 = driver.getLastJobId();
BaseEntity result3 = driver.createCollection("testCollection3");
String jobId3 = driver.getLastJobId();
BaseEntity result4 = driver.createCollection(null);
String jobId4 = driver.getLastJobId();
BaseEntity result5 = driver.getCollections(true);
String jobId5 = driver.getLastJobId();

System.out.println("ReturnCode: " + result1.getStatusCode() + " - JobId: " + jobId1);
System.out.println("ReturnCode: " + result2.getStatusCode() + " - JobId: " + jobId2);
System.out.println("ReturnCode: " + result3.getStatusCode() + " - JobId: " + jobId3);
System.out.println("ReturnCode: " + result4.getStatusCode() + " - JobId: " + jobId4);
System.out.println("ReturnCode: " + result5.getStatusCode() + " - JobId: " + jobId5);

>ReturnCode: 202 - JobId: 68758346902
>ReturnCode: 202 - JobId: 68758412438
>ReturnCode: 202 - JobId: 68758477974
>ReturnCode: 202 - JobId: 68758543510
>ReturnCode: 202 - JobId: 68758609046

Now we load all jobIds :

List<String> jobIds = driver.getJobIds();
System.out.println("JobIds: " + jobIds);

>JobIds: [68758346902, 68758412438, 68758477974, 68758543510, 68758609046]    

So how can we use these JobIds? Well, primarily we need them to request the result of an async job. Furthermore one can request the execution state for a job from the server or even delete it.
We will now load the result from our previous example.

Note that we will have to check first that the asynchronous jobs have been executed on the server:

Boolean allJobsFinished = false;
while (!allJobsFinished) {
  allJobsFinished = true;
  for (String jobId : new String[]{jobId1, jobId2, jobId3, jobId4, jobId5}) {
    allJobsFinished = !allJobsFinished ? allJobsFinished : driver.isJobfinished(jobId);
  }
}

CollectionEntity testCollection1 = driver.getJobResult(jobId1);
CollectionEntity testCollection2 = driver.getJobResult(jobId2);
CollectionEntity testCollection3 = driver.getJobResult(jobId3);
try {
  CollectionEntity testCollection4 = driver.getJobResult(jobId4);
} catch (ArangoException e) {
  System.out.println("ReturnCode: " + e.getCode() + " - ErrorMessage: " + e.getErrorMessage());
}
CollectionsEntity collectionList = driver.getJobResult(jobId5);
System.out.println("ReturnCode: " + testCollection1.getStatusCode() + " - CollectionName: " + testCollection1.getName());
System.out.println("ReturnCode: " + testCollection2.getStatusCode() + " - CollectionName: " + testCollection2.getName());
System.out.println("ReturnCode: " + testCollection3.getStatusCode() + " - CollectionName: " + testCollection3.getName());
System.out.println("ReturnCode: " + collectionList.getStatusCode() + " - Collections: " + collectionList.getNames().keySet());

>ReturnCode: 400 - ErrorMessage: name must be non-empty
>ReturnCode: 200 - CollectionName: testCollection1
>ReturnCode: 200 - CollectionName: testCollection2
>ReturnCode: 200 - CollectionName: testCollection3
>ReturnCode: 200 - Collections: [testCollection3, testCollection2, testCollection1]

Both interfaces can really help to speed up your application. Give it a try and enjoy.

More Java related articles and tutorials: arangodb.com/java

Frank Celler

Frank Celler

Frank is both entrepreneur and backend developer, developing mostly memory databases for two decades. He is the CTO and co-founder of ArangoDB. Try to challenge Frank asking him questions on C, C++ and MRuby. Besides Frank organizes Cologne’s NoSQL group & is an active member of NoSQL community.

1 Comment

  1. Vyacheslav on March 4, 2021 at 4:04 pm

    Great guide,
    but how to use batch mode in modern driver?
    I did not found any information about this topic.

Leave a Comment





Get the latest tutorials, blog posts and news: