@Override
  protected void decode(ChannelHandlerContext ctx, RESPONSE msg, List<Object> out)
      throws Exception {
    if (currentDecodingState == DecodingState.INITIAL) {
      currentRequest = sentRequestQueue.poll();
      currentDecodingState = DecodingState.STARTED;
      if (currentRequest != null) {
        Long st = sentRequestTimings.poll();
        if (st != null) {
          currentOpTime = System.nanoTime() - st;
        } else {
          currentOpTime = -1;
        }
      }

      if (traceEnabled) {
        LOGGER.trace("{}Started decoding of {}", logIdent(ctx, endpoint), currentRequest);
      }
    }

    try {
      CouchbaseResponse response = decodeResponse(ctx, msg);
      if (response != null) {
        publishResponse(response, currentRequest.observable());

        if (currentDecodingState == DecodingState.FINISHED) {
          if (currentRequest != null
              && currentOpTime >= 0
              && env() != null
              && env().networkLatencyMetricsCollector().isEnabled()) {
            NetworkLatencyMetricsIdentifier identifier =
                new NetworkLatencyMetricsIdentifier(
                    remoteHostname,
                    serviceType().toString(),
                    currentRequest.getClass().getSimpleName(),
                    response.status().toString());
            env().networkLatencyMetricsCollector().record(identifier, currentOpTime);
          }
        }
      }
    } catch (CouchbaseException e) {
      currentRequest.observable().onError(e);
    } catch (Exception e) {
      currentRequest.observable().onError(new CouchbaseException(e));
    }

    if (currentDecodingState == DecodingState.FINISHED) {
      if (traceEnabled) {
        LOGGER.trace("{}Finished decoding of {}", logIdent(ctx, endpoint), currentRequest);
      }
      currentRequest = null;
      currentDecodingState = DecodingState.INITIAL;
    }
  }