Like much of the world I use Amazon services and the Aws SDK (Java in our case) to support our scalable web service. In an effort to maximize the number of users we could support per machine we use a asynchronous request processing architecture using jetty continuations. In such a setup we needed an asynchronous AWS SDK. Now you might say the AWS SDK already provides asynchronous an API, and you would be right. However, the asynchronous AWS still return a future and require the calling code to poll the future and look at responses once it is done. We wanted a really really asynchronous API which we could say “Hey do X, and when you are done do Y if there is an error Z but no matter what happens don’t bother me again”. Since there was no such API we decided to implement it ourselves and you can find it here. The use case I describe above is facetious but its remarkably useful. For example say your server has to save something to DynamoDB. You callputItemAsync and get a future object back. The SDK uses an internal thread pool to get a thread and uses that thread to send a blocking http call to Amazon. Once that is done the internal thread sets the done field of the future to true. In the meantime you own code is periodically checking (or waiting) for the future to complete.

Future<PutItemResult> future = awssdk.putItemAsync(putItemRequest)
while(future.isDone() == false) {
        //Twiddle my thumbs.
}
PutItemResult result = future.get();
//No errors, cool lets ignore the result

 

You should begin to see the problem here. There are two threads blocked doing nothing (or one blocked and one polling) while they could be doing meaningful work. Furthermore for a lot of times we don’t even really care about the response from Amazon other than to log an error if needed. There is no reason why the rest of the request should wait for a put item to complete its not like we are retrieving information we need later. To get around this issue we added the AsyncServiceHandler.java.

awssdk.putItemAsync(putItemRequest,new AsyncServiceHandler<PutItemResult, 
                PutItemRequest>() {
        @Override
        public void handleResult(PutItemResult arg0, PutItemRequest arg1) {
                //Do something here if you must  
        }
        @Override
        public void handleException(Exception arg0) {
                //Log an exception and retry if you want    
        }
        @Override
        public void handleException(AmazonClientException arg0) {
                //Log an exception and retry if you want
        }
        @Override
        public void handleException(AmazonServiceException arg0) {
                //Log an exception and retry if you want
        }
    });

 

In this implementation you get the same great feature but with 0% thumb twiddling. The internal thread still blocks when making the request to Amazon but once its done it just calls the result or error handlers as needed. Your main thread never check request status.

As a side bar you may ask why did we implement three error case handlers, and if you look at it closely they are all in the same inheritance tree. AmazonServiceException is a child of AmazonClientException which a child of Exception. The AmazonServiceException has a much cleaner interface to find out what went wrong if the error was server side, AmazonClientException will catch server side as well as client side errors but its much more difficult finding out what happened in case of server side errors. And we define a handler for the plain old exception class to catch any other errors that may happen. We could have used if (arg0 instanceOf AmazonServiceException) to check the error type but that is just gross.

Do I hear anyone ask “How do I get this awesome new API?” well we have finished the implementation for DynamoDB and will integrate with the rest of the services very soon. We have already submitted a patch to Amazon hopefully this code or something similar will be in the master branch soon. Until then you can build and deploy the code as follows:

git clone git@github.com:techtraits/aws-sdk-for-java.git
cd aws-sdk-for-java
#Some hacking see below
mvn clean install
#Copy to your nexus repo

 

The hacking required is to edit the pom.xml file as follows:

  • Change the version from <version>1.3.2</version> to something like <version>1.3.2_hacked</version>

  • You should also delete the gpg signing plugin unless you really really want signed code

      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-gpg-plugin</artifactId>
          <version>1.4</version>
          <executions>
            <execution>
              <id>sign-artifacts</id>
              <phase>verify</phase>
              <goals>
                <goal>sign</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      </plugins>

 

  • And you are all set just deploy the compiled jar to your maven repo
  • In true cooking show fashion here is one I built earlier.