When a peer or orderer node goes offline, applications built on Hyperledger Fabric can stall or fail if the Java SDK does not know how to route around the outage. This guide covers three SDK-level configurations that keep your application running during node failures: Service Discovery for automatic peer failover, timeout tuning for faster node switching, and gRPC message size limits for large payloads.
Enable Service Discovery
By default, the Java SDK reads peer information from a statically encoded connection-profile. Static configuration has two limitations: it cannot react to peers going temporarily offline, and it does not reflect endorsement policy changes (for example, when a new organization joins a channel).
Service Discovery solves both problems. Instead of reading a fixed list, the SDK asks a peer to compute the current peer set and endorsement requirements dynamically. In Alibaba Cloud BaaS (Fabric), each organization runs two endorsing peers, so if one peer goes offline, the SDK automatically routes proposals to the other.
For more information on how this mechanism works, see How service discovery works in Fabric.
Configure the connection profile
Add discover: true to each peer entry in the target channel's peer list. The SDK randomly selects one of these peers as the Service Discovery provider.
Configure multiple peers per organization to prevent a single point of failure. Service Discovery uses these entries as the starting points for resolving the live peer set.
channels:
mychannel:
peers:
peer1.org1.aliyunbaas.top:31111:
chaincodeQuery: true
endorsingPeer: true
eventSource: true
ledgerQuery: true
discover: true
peer2.org1.aliyunbaas.top:31121:
chaincodeQuery: true
endorsingPeer: true
eventSource: true
ledgerQuery: true
discover: true
peer1.org2.aliyunbaas.top:31111:
chaincodeQuery: true
endorsingPeer: true
eventSource: true
ledgerQuery: true
discover: true
peer2.org2.aliyunbaas.top:31121:
chaincodeQuery: true
endorsingPeer: true
eventSource: true
ledgerQuery: true
discover: trueSend proposals with DiscoveryOptions
When calling sendTransactionProposalToEndorsers, pass a DiscoveryOptions object to control how the SDK selects peers and validates endorsements.
`setEndorsementSelector` — controls which peers are chosen to satisfy the endorsement policy:
| Value | Behavior | When to use |
|---|---|---|
ENDORSEMENT_SELECTION_RANDOM | Randomly selects peers that satisfy the policy | General use; distributes load across peers |
ENDORSEMENT_SELECTION_LEAST_REQUIRED_BLOCKHEIGHT | Selects the minimum set of peers with the highest block height | When ledger consistency is critical |
`setForceDiscovery` — controls whether the SDK calls the discovery service on every proposal or uses a cached peer list:
| Value | Behavior | When to use |
|---|---|---|
true | Calls the discovery service on every proposal (higher resource usage) | When network topology changes frequently |
false | Uses the cached peer list; refreshes every 2 minutes | Default; suitable for stable networks |
`setInspectResults` — controls endorsement policy validation:
| Value | Behavior | When to use |
|---|---|---|
true | Skips automatic policy checks; your application validates endorsements | When you need custom endorsement logic |
false | SDK validates endorsements automatically and throws an exception if the policy is not satisfied | Default; recommended for most applications |
The following example uses random peer selection, cached discovery results, and application-side policy validation:
import org.hyperledger.fabric.sdk.exception.ServiceDiscoveryException;
import org.hyperledger.fabric.sdk.ServiceDiscovery;
import static org.hyperledger.fabric.sdk.Channel.DiscoveryOptions.createDiscoveryOptions;
// ...
Channel.DiscoveryOptions discoveryOptions = Channel.DiscoveryOptions.createDiscoveryOptions();
discoveryOptions.setEndorsementSelector(ServiceDiscovery.EndorsementSelector.ENDORSEMENT_SELECTION_RANDOM);
discoveryOptions.setForceDiscovery(false);
discoveryOptions.setInspectResults(true);
try {
transactionPropResp = channel.sendTransactionProposalToEndorsers(
transactionProposalRequest, discoveryOptions);
} catch (ProposalException e) {
System.out.printf("invokeTransactionSync fail, ProposalException: {}", e.getLocalizedMessage());
e.printStackTrace();
} catch (ServiceDiscoveryException e) {
System.out.printf("ServiceDiscoveryException fail: {}", e.getLocalizedMessage());
e.printStackTrace();
} catch (InvalidArgumentException e) {
System.out.printf("InvalidArgumentException fail: {}", e.getLocalizedMessage());
e.printStackTrace();
}
// ...Tip: Use Service Discovery together withNOfEventsso that a transaction completes as soon as the required subset of peers confirms it, rather than waiting for all peers to send atransactionEvent. This prevents transactions from stalling when a peer goes offline.
Tune timeout settings
When a peer or orderer node goes offline, the SDK waits until the configured timeout elapses before it tries another node. Reducing timeout values lets the SDK switch to a healthy node faster.
The Java SDK defines timeout constants in Config.java. Default values:
| Constant | Default | Controls |
|---|---|---|
PROPOSAL_WAIT_TIME | 20,000 ms | How long the SDK waits for a peer to respond to a transaction proposal (client → peer) |
ORDERER_WAIT_TIME | 10,000 ms | How long the SDK waits for an orderer node to accept a transaction (peer → orderer) |
CHANNEL_CONFIG_WAIT_TIME | 15,000 ms | Channel configuration update requests |
TRANSACTION_CLEANUP_UP_TIMEOUT_WAIT_TIME | 600,000 ms | Transaction cleanup timeout |
ORDERER_RETRY_WAIT_TIME | 200 ms | Delay between orderer retry attempts |
PEER_EVENT_REGISTRATION_WAIT_TIME | 5,000 ms | Peer event registration |
PEER_EVENT_RETRY_WAIT_TIME | 500 ms | Delay between peer event retry attempts |
PEER_EVENT_RECONNECTION_WARNING_RATE | 50 | Reconnection warning rate threshold |
GENESISBLOCK_WAIT_TIME | 5,000 ms | Genesis block retrieval |
For networks with stable connections and fast transaction processing, reduce PROPOSAL_WAIT_TIME and ORDERER_WAIT_TIME so the SDK switches to a healthy node more quickly.
Override via system property
Set system properties before the SDK initializes:
public static final String PROPOSAL_WAIT_TIME = "org.hyperledger.fabric.sdk.proposal.wait.time";
private static final String PROPOSAL_WAIT_TIME_VALUE = "5000";
public static final String ORDERER_WAIT_TIME = "org.hyperledger.fabric.sdk.orderer.ordererWaitTimeMilliSecs";
private static final String ORDERER_WAIT_TIME_VALUE = "5000";
static {
System.setProperty(PROPOSAL_WAIT_TIME, PROPOSAL_WAIT_TIME_VALUE);
System.setProperty(ORDERER_WAIT_TIME, ORDERER_WAIT_TIME_VALUE);
}Override via config.properties
## Timeout for proposal requests to endorsers, in milliseconds.
org.hyperledger.fabric.sdk.proposal.wait.time = 5000
## Timeout for transactions sent to orderers, in milliseconds.
org.hyperledger.fabric.sdk.orderer.ordererWaitTimeMilliSecs = 5000Increase the gRPC message size limit
The gRPC server rejects inbound messages larger than 4 MB (4,194,304 bytes) by default. When this limit is exceeded, the server throws:
rpc error: code = ResourceExhausted desc = grpc: received message larger than max (8653851 vs. 4194304)To increase the limit, set grpc.NettyChannelBuilderOption.maxInboundMessageSize on the properties of each peer and orderer node. This configuration supports FABJ-480 in connection-profile.
NetworkConfig networkConfig = NetworkConfig.fromYamlFile(f);
// Apply the increased message size limit to all peer nodes
for (String peerName : networkConfig.getPeerNames()) {
Properties peerProperties = networkConfig.getPeerProperties(peerName);
if (peerProperties == null) {
peerProperties = new Properties();
}
peerProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 100 * 1024 * 1024);
networkConfig.setPeerProperties(peerName, peerProperties);
}
// Apply the increased message size limit to all orderer nodes
for (String ordererName : networkConfig.getOrdererNames()) {
Properties ordererProperties = networkConfig.getOrdererProperties(ordererName);
if (ordererProperties == null) {
ordererProperties = new Properties();
}
ordererProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 100 * 1024 * 1024);
networkConfig.setPeerProperties(ordererName, ordererProperties);
}The example sets the limit to 100 MB (100 * 1024 * 1024 bytes). Adjust the value based on the maximum expected message size in your application.