I'm trying to send async transaction to my Fabric network using the java gateway sdk but i receive the error Channel [CHANNEL NAME] has been shutdown
.
Here some example code:
Gateway.Builder builder = Gateway.createBuilder()
.discovery(true)
.identity(wallet, user.getName())
.networkConfig([PATH TO CONNECTION PROFILE]);
try(Gateway gateway = builder.connect()) {
Network channel = gateway.getNetwork(CHANNEL_NAME);
Contract someChaincode = channel.getContract(CHAINCODE_NAME);
int coresNumber = (Runtime.getRuntime().availableProcessors());
ExecutorService executor = Executors.newFixedThreadPool(coresNumber);
for(String elemt : elements) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try{
//Exception thrown here
byte[] res = someChaincode.submitTransaction("someFunction", elemt);
return new String(res);
} catch (ContractException e) {
e.printStackTrace();
}
}, executor);
}
} catch (Exception e) {
// Handle Exception
}
And here the exception:
java.util.concurrent.ExecutionException: org.hyperledger.fabric.gateway.GatewayRuntimeException: org.hyperledger.fabric.sdk.exception.InvalidArgumentException: Channel [CHANNEL NAME] has been shutdown.
Precisely, the exception is thrown in the method checkChannelState()
. I have a sense that I'm not handling multithreading correctly.
You don't look to be waiting for completion of the futures you have created in your code snippet. So you are scheduling transaction invocations for execution on different threads but then, before this code is executed, dropping out of a try-with-resources block which closes the Gateway instance you are using to connect. Closing the Gateway causes all the associated resources and connections to be closed, including the underlying Channel. So when your transaction invocations actually get run, you have already closed the connection and resources needed for them to execute.
You need to get the results from the Future objects you have created before closing the Gateway instance; in other words, before dropping out of the try-with-resources block that creates the Gateway. Something vaguely like this:
Collection<Callable<String>> tasks = elements.stream()
.map(element -> new Callable<String>() {
public String call() throws ContractException, TimeoutException, InterruptedException {
byte[] result = contract.submitTransaction("someFunction", element);
return new String(result);
}
}).collect(Collectors.toList());
try {
Collection<String> results = new ArrayList<>();
Collection<Future<String>> futures = executor.invokeAll(tasks, timeout, timeUnit);
for (Future<String> future : futures) {
try {
String result = future.get(timeout, timeUnit);
results.add(result);
} catch (CancellationException | InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
System.out.println("Results: " + results);
} catch (InterruptedException e ) {
e.printStackTrace();
}