@Override
    public Future<?> runBatchAsync(Batch<T> batch, AfterExecute<T> afterExecute) throws FullQueueException {
        /* 
         * Since ThreadPoolExecutor throws RejectedExecutionException when its queue is full,
         * we have to backoff and retry if execute() throws RejectedExecutionHandler.
         *
         * This will cause all other threads writing to this cube to block. This is the desired
         * behavior since we want to stop accepting new writes past a certain point if we can't
         * send them to the database.
         */
        try {
            // Submit this batch
            synchronized (batchesInFlight) {
                batchesInFlight.add(batch);
            }
            return flushExecutor.submit(new FlushWorkerRunnable(batch, afterExecute));
        } catch (RejectedExecutionException ree) {
            throw new FullQueueException();
        }
    }
@ Override public Future < ? > runBatchAsync ( Batch < T > batch , AfterExecute < T > afterExecute ) throws FullQueueException { try { synchronized ( batchesInFlight ) { batchesInFlight . add ( batch ) ; } return flushExecutor . submit ( new FlushWorkerRunnable ( batch , afterExecute ) ) ; } catch ( RejectedExecutionException ree ) { throw new FullQueueException ( ) ; } }
Hands off the given batch to the flush executor to be sent to the database soon. Doesn't
throw IOException, since batches are just asynchronously submitted for execution, but
will throw AsyncException if some previous batch had a RuntimeException.

@return a Future that the caller can use to detect when the database IO is done. The returned
future will never have an ExecutionException.