docs: public static ICompletableFuture<Object> invokeOnStableClusterSerial(NodeEngine nodeEngine,
                                                                           Supplier<? extends Operation> operationSupplier,
                                                                           int maxRetries) {
  
          ClusterService clusterService = nodeEngine.getClusterService();
          if (!clusterService.isJoined()) {
              return new CompletedFuture<Object>(null, null, new CallerRunsExecutor());
          }
  
          RestartingMemberIterator memberIterator = new RestartingMemberIterator(clusterService, maxRetries);
  
          // we are going to iterate over all members and invoke an operation on each of them
          InvokeOnMemberFunction invokeOnMemberFunction = new InvokeOnMemberFunction(operationSupplier, nodeEngine,
                  memberIterator);
          Iterator<ICompletableFuture<Object>> invocationIterator = map(memberIterator, invokeOnMemberFunction);
  
          ILogger logger = nodeEngine.getLogger(ChainingFuture.class);
          ExecutionService executionService = nodeEngine.getExecutionService();
          ManagedExecutorService executor = executionService.getExecutor(ExecutionService.ASYNC_EXECUTOR);
  
          // ChainingFuture uses the iterator to start invocations
          // it invokes on another member only when the previous invocation is completed (so invocations are serial)
          // the future itself completes only when the last invocation completes (or if there is an error)
          return new ChainingFuture<Object>(invocationIterator, executor, memberIterator, logger);
      }