All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazon.titan.diskstorage.dynamodb.ExponentialBackoff Maven / Gradle / Ivy

Go to download

The Amazon DynamoDB Storage Backend for Titan: Distributed Graph Database allows Titan graphs to use DynamoDB as a storage backend.

There is a newer version: 1.0.5
Show newest version
/*
 * Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
package com.amazon.titan.diskstorage.dynamodb;

import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest;
import com.amazonaws.services.dynamodbv2.model.DeleteItemResult;
import com.amazonaws.services.dynamodbv2.model.GetItemRequest;
import com.amazonaws.services.dynamodbv2.model.GetItemResult;
import com.amazonaws.services.dynamodbv2.model.QueryRequest;
import com.amazonaws.services.dynamodbv2.model.QueryResult;
import com.amazonaws.services.dynamodbv2.model.ScanRequest;
import com.amazonaws.services.dynamodbv2.model.ScanResult;
import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest;
import com.amazonaws.services.dynamodbv2.model.UpdateItemResult;
import com.thinkaurelius.titan.diskstorage.BackendException;
import com.thinkaurelius.titan.diskstorage.TemporaryBackendException;

/**
 * A wrapper for a client-side exponential backoff retry strategy for DynamoDB API calls
 * @author Alexander Patrikalakis
 *
 * @param  the type of AWS request that is input
 * @param  the type of AWS request that is returned
 *
 */
public abstract class ExponentialBackoff {
    public static final String RETRIES = "Retries";
    public static final String SCAN_RETRIES = DynamoDBDelegate.SCAN + RETRIES;
    public static final String QUERY_RETRIES = DynamoDBDelegate.QUERY + RETRIES;
    public static final String UPDATE_ITEM_RETRIES = DynamoDBDelegate.UPDATE_ITEM + RETRIES;
    public static final String DELETE_ITEM_RETRIES = DynamoDBDelegate.DELETE_ITEM + RETRIES;
    public static final String GET_ITEM_RETRIES = DynamoDBDelegate.GET_ITEM + RETRIES;

    public static final class Scan extends ExponentialBackoff {
        private final int permits;
        public Scan(ScanRequest request, DynamoDBDelegate delegate, int permits) {
            super(request, delegate, SCAN_RETRIES);
            this.permits = permits;
        }
        @Override
        protected ScanResult call() throws BackendException
        {
            return delegate.scan(request, permits);
        }
        @Override
        protected String getTableName()
        {
            return request.getTableName();
        }

    }

    public static final class Query extends ExponentialBackoff {
        private final int permits;
        public Query(QueryRequest request, DynamoDBDelegate delegate, int permits) {
            super(request, delegate, QUERY_RETRIES);
            this.permits = permits;
        }
        @Override
        protected QueryResult call() throws BackendException
        {
            return delegate.query(request, permits);
        }
        @Override
        protected String getTableName()
        {
            return request.getTableName();
        }

    }

    public static final class UpdateItem extends ExponentialBackoff {
        public UpdateItem(UpdateItemRequest request, DynamoDBDelegate delegate) {
            super(request, delegate, UPDATE_ITEM_RETRIES);
        }
        @Override
        protected UpdateItemResult call() throws BackendException
        {
            return delegate.updateItem(request);
        }
        @Override
        protected String getTableName()
        {
            return request.getTableName();
        }

    }

    public static final class DeleteItem extends ExponentialBackoff {
        public DeleteItem(DeleteItemRequest request, DynamoDBDelegate delegate) {
            super(request, delegate, DELETE_ITEM_RETRIES);
        }
        @Override
        protected DeleteItemResult call() throws BackendException
        {
            return delegate.deleteItem(request);
        }
        @Override
        protected String getTableName()
        {
            return request.getTableName();
        }

    }

    public static final class GetItem extends ExponentialBackoff {
        public GetItem(GetItemRequest request, DynamoDBDelegate delegate) {
            super(request, delegate, GET_ITEM_RETRIES);
        }
        @Override
        protected GetItemResult call() throws BackendException
        {
            return delegate.getItem(request);
        }
        @Override
        protected String getTableName()
        {
            return request.getTableName();
        }

    }

    private long exponentialBackoffTime;
    private long tries;
    private final String apiNameRetries;
    protected final RequestType request;
    protected ResultType result;
    protected final DynamoDBDelegate delegate;
    protected ExponentialBackoff(RequestType requestType, DynamoDBDelegate delegate, String apiNameTries) {
        this.request = requestType;
        this.delegate = delegate;
        this.exponentialBackoffTime = delegate.getRetryMillis();
        this.result = null;
        this.tries = 0;
        this.apiNameRetries = apiNameTries;
    }
    protected abstract ResultType call() throws BackendException;
    protected abstract String getTableName();

    public ResultType runWithBackoff() throws BackendException {
        boolean interrupted = false;
        try {
            do {
                tries++;
                try {
                    result = call();
                } catch(TemporaryBackendException e) { //retriable
                    if(tries > delegate.getMaxRetries()) {
                        throw new TemporaryBackendException("Max tries exceeded.", e);
                    }
                    try {
                        Thread.sleep(exponentialBackoffTime);
                    } catch(InterruptedException ie) {
                        interrupted = true;
                    } finally {
                        exponentialBackoffTime *= 2;
                    }
                    continue;
                }
            } while(result == null);
            return result;
        } finally {
            //meter tries
            delegate.getMeter(delegate.getMeterName(apiNameRetries, getTableName())).mark(tries - 1);

            if(interrupted) {
                Thread.currentThread().interrupt();
                throw new BackendRuntimeException("exponential backoff was interrupted");
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy