Exemplo n.º 1
0
  /**
   * Collapsed requests are triggered for batch execution and the array of arguments is passed in.
   *
   * <p>IMPORTANT IMPLEMENTATION DETAILS => The expected contract (responsibilities) of this method
   * implementation is:
   *
   * <p>
   *
   * <ul>
   *   <li>Do NOT block => Do the work on a separate worker thread. Do not perform inline otherwise
   *       it will block other requests.
   *   <li>Set ALL CollapsedRequest response values => Set the response values T on each
   *       CollapsedRequest<T, R>, even if the response is NULL otherwise the user thread waiting on
   *       the response will think a response was never received and will either block indefinitely
   *       or will timeout while waiting.
   * </ul>
   *
   * @param args
   */
  public void executeBatchIfNotAlreadyStarted() {
    /*
     * - check that we only execute once since there's multiple paths to do so (timer, waiting thread or max batch size hit)
     * - close the gate so 'offer' can no longer be invoked and we turn those threads away so they create a new batch
     */
    if (batchStarted.compareAndSet(false, true)) {
      /* wait for 'offer' threads to finish before executing the batch so 'requests' is complete */
      batchLock.writeLock().lock();
      try {
        // shard batches
        Collection<Collection<CollapsedRequest<ResponseType, RequestArgumentType>>> shards =
            commandCollapser.shardRequests(requests);
        // for each shard execute its requests
        for (final Collection<CollapsedRequest<ResponseType, RequestArgumentType>> shardRequests :
            shards) {
          try {
            // create a new command to handle this batch of requests
            Observable<BatchReturnType> o = commandCollapser.createObservableCommand(shardRequests);
            o.subscribe(
                new RequestBatch.BatchRequestObserver<
                    ResponseType, RequestArgumentType, BatchReturnType>(
                    commandCollapser, shardRequests));
          } catch (Exception e) {
            logger.error("Exception while creating and queueing command with batch.", e);
            // if a failure occurs we want to pass that exception to all of the Futures that we've
            // returned
            for (CollapsedRequest<ResponseType, RequestArgumentType> request : shardRequests) {
              try {
                request.setException(e);
              } catch (IllegalStateException e2) {
                logger.debug("Failed trying to setException on CollapsedRequest", e2);
              }
            }
          }
        }

      } catch (Exception e) {
        logger.error("Exception while sharding requests.", e);
        // same error handling as we do around the shards, but this is a wider net in case the
        // shardRequest method fails
        for (CollapsedRequest<ResponseType, RequestArgumentType> request : requests) {
          try {
            request.setException(e);
          } catch (IllegalStateException e2) {
            logger.debug("Failed trying to setException on CollapsedRequest", e2);
          }
        }
      } finally {
        batchLock.writeLock().unlock();
      }
    }
  }
Exemplo n.º 2
0
  /**
   * Collapsed requests are triggered for batch execution and the array of arguments is passed in.
   *
   * <p>IMPORTANT IMPLEMENTATION DETAILS => The expected contract (responsibilities) of this method
   * implementation is:
   *
   * <p>
   *
   * <ul>
   *   <li>Do NOT block => Do the work on a separate worker thread. Do not perform inline otherwise
   *       it will block other requests.
   *   <li>Set ALL CollapsedRequest response values => Set the response values T on each
   *       CollapsedRequest<T, R>, even if the response is NULL otherwise the user thread waiting on
   *       the response will think a response was never received and will either block indefinitely
   *       or will timeout while waiting.
   * </ul>
   */
  public void executeBatchIfNotAlreadyStarted() {
    /*
     * - check that we only execute once since there's multiple paths to do so (timer, waiting thread or max batch size hit)
     * - close the gate so 'offer' can no longer be invoked and we turn those threads away so they create a new batch
     */
    if (batchStarted.compareAndSet(false, true)) {
      /* wait for 'offer'/'remove' threads to finish before executing the batch so 'requests' is complete */
      batchLock.writeLock().lock();

      try {
        // shard batches
        Collection<Collection<CollapsedRequest<ResponseType, RequestArgumentType>>> shards =
            commandCollapser.shardRequests(batchArgumentQueue);
        // for each shard execute its requests
        for (final Collection<CollapsedRequest<ResponseType, RequestArgumentType>> shardRequests :
            shards) {
          try {
            // create a new command to handle this batch of requests
            Observable<BatchReturnType> o = commandCollapser.createObservableCommand(shardRequests);

            commandCollapser
                .mapResponseToRequests(o, shardRequests)
                .doOnError(
                    new Action1<Throwable>() {

                      /** This handles failed completions */
                      @Override
                      public void call(Throwable e) {
                        // handle Throwable in case anything is thrown so we don't block Observers
                        // waiting for onError/onCompleted
                        Exception ee;
                        if (e instanceof Exception) {
                          ee = (Exception) e;
                        } else {
                          ee =
                              new RuntimeException(
                                  "Throwable caught while executing batch and mapping responses.",
                                  e);
                        }
                        logger.debug("Exception mapping responses to requests.", e);
                        // if a failure occurs we want to pass that exception to all of the Futures
                        // that we've returned
                        for (CollapsedRequest<ResponseType, RequestArgumentType> request :
                            batchArgumentQueue) {
                          try {
                            ((CollapsedRequestSubject<ResponseType, RequestArgumentType>) request)
                                .setExceptionIfResponseNotReceived(ee);
                          } catch (IllegalStateException e2) {
                            // if we have partial responses set in mapResponseToRequests
                            // then we may get IllegalStateException as we loop over them
                            // so we'll log but continue to the rest
                            logger.error(
                                "Partial success of 'mapResponseToRequests' resulted in IllegalStateException while setting Exception. Continuing ... ",
                                e2);
                          }
                        }
                      }
                    })
                .doOnCompleted(
                    new Action0() {

                      /** This handles successful completions */
                      @Override
                      public void call() {
                        // check that all requests had setResponse or setException invoked in case
                        // 'mapResponseToRequests' was implemented poorly
                        Exception e = null;
                        for (CollapsedRequest<ResponseType, RequestArgumentType> request :
                            shardRequests) {
                          try {
                            e =
                                ((CollapsedRequestSubject<ResponseType, RequestArgumentType>)
                                        request)
                                    .setExceptionIfResponseNotReceived(
                                        e,
                                        "No response set by "
                                            + commandCollapser.getCollapserKey().name()
                                            + " 'mapResponseToRequests' implementation.");
                          } catch (IllegalStateException e2) {
                            logger.debug(
                                "Partial success of 'mapResponseToRequests' resulted in IllegalStateException while setting 'No response set' Exception. Continuing ... ",
                                e2);
                          }
                        }
                      }
                    })
                .subscribe();

          } catch (Exception e) {
            logger.error("Exception while creating and queueing command with batch.", e);
            // if a failure occurs we want to pass that exception to all of the Futures that we've
            // returned
            for (CollapsedRequest<ResponseType, RequestArgumentType> request : shardRequests) {
              try {
                request.setException(e);
              } catch (IllegalStateException e2) {
                logger.debug("Failed trying to setException on CollapsedRequest", e2);
              }
            }
          }
        }

      } catch (Exception e) {
        logger.error("Exception while sharding requests.", e);
        // same error handling as we do around the shards, but this is a wider net in case the
        // shardRequest method fails
        for (CollapsedRequest<ResponseType, RequestArgumentType> request : batchArgumentQueue) {
          try {
            request.setException(e);
          } catch (IllegalStateException e2) {
            logger.debug("Failed trying to setException on CollapsedRequest", e2);
          }
        }
      } finally {
        batchLock.writeLock().unlock();
      }
    }
  }