private <R> ChannelBuffer encode(final KuduRpc<R> rpc) { final int rpcid = this.rpcid.incrementAndGet(); ChannelBuffer payload; final String service = rpc.serviceName(); final String method = rpc.method(); try { final RpcHeader.RequestHeader.Builder headerBuilder = RpcHeader.RequestHeader.newBuilder() .setCallId(rpcid) .addAllRequiredFeatureFlags(rpc.getRequiredFeatures()) .setRemoteMethod( RpcHeader.RemoteMethodPB.newBuilder() .setServiceName(service) .setMethodName(method)); // If any timeout is set, find the lowest non-zero one, since this will be the deadline that // the server must respect. if (rpc.deadlineTracker.hasDeadline() || socketReadTimeoutMs > 0) { long millisBeforeDeadline = Long.MAX_VALUE; if (rpc.deadlineTracker.hasDeadline()) { millisBeforeDeadline = rpc.deadlineTracker.getMillisBeforeDeadline(); } long localRpcTimeoutMs = Long.MAX_VALUE; if (socketReadTimeoutMs > 0) { localRpcTimeoutMs = socketReadTimeoutMs; } headerBuilder.setTimeoutMillis((int) Math.min(millisBeforeDeadline, localRpcTimeoutMs)); } payload = rpc.serialize(headerBuilder.build()); } catch (Exception e) { LOG.error("Uncaught exception while serializing RPC: " + rpc, e); rpc.errback(e); // Make the RPC fail with the exception. return null; } final KuduRpc<?> oldrpc = rpcs_inflight.put(rpcid, rpc); if (oldrpc != null) { final String wtf = getPeerUuidLoggingString() + "WTF? There was already an RPC in flight with" + " rpcid=" + rpcid + ": " + oldrpc + ". This happened when sending out: " + rpc; LOG.error(wtf); // Make it fail. This isn't an expected failure mode. oldrpc.errback(new NonRecoverableException(wtf)); } if (LOG.isDebugEnabled()) { LOG.debug( getPeerUuidLoggingString() + chan + " Sending RPC #" + rpcid + ", payload=" + payload + ' ' + Bytes.pretty(payload)); } payload = secureRpcHelper.wrap(payload); return payload; }
<R> void sendRpc(KuduRpc<R> rpc) { if (!rpc.deadlineTracker.hasDeadline()) { LOG.warn(getPeerUuidLoggingString() + " sending an rpc without a timeout " + rpc); } if (chan != null) { if (!rpc.getRequiredFeatures().isEmpty() && !secureRpcHelper .getServerFeatures() .contains(RpcHeader.RpcFeatureFlag.APPLICATION_FEATURE_FLAGS)) { rpc.errback( new NonRecoverableException( "the server does not support the APPLICATION_FEATURE_FLAGS RPC feature")); } final ChannelBuffer serialized = encode(rpc); if (serialized == null) { // Error during encoding. return; // Stop here. RPC has been failed already. } final Channel chan = this.chan; // Volatile read. if (chan != null) { // Double check if we disconnected during encode(). Channels.write(chan, serialized); } else { // The RPC was already added to rpcs_inflight so we don't need to fall down in the next big // block of code, cleanup() will take care of it. if (LOG.isDebugEnabled()) { LOG.debug( getPeerUuidLoggingString() + " connection was closed before sending rpcid {}, rpc=", rpcid, rpc); } } return; } boolean tryagain = false; boolean copyOfDead; synchronized (this) { copyOfDead = this.dead; // Check if we got connected while entering this synchronized block. if (chan != null) { tryagain = true; } else if (!copyOfDead) { if (pending_rpcs == null) { pending_rpcs = new ArrayList<KuduRpc<?>>(); } pending_rpcs.add(rpc); } } if (copyOfDead) { failOrRetryRpc(rpc, new ConnectionResetException(null)); return; } else if (tryagain) { // This recursion will not lead to a loop because we only get here if we // connected while entering the synchronized block above. So when trying // a second time, we will either succeed to send the RPC if we're still // connected, or fail through to the code below if we got disconnected // in the mean time. sendRpc(rpc); return; } }