package org.corfudb.runtime.view; import lombok.extern.slf4j.Slf4j; import org.corfudb.runtime.clients.BaseClient; import org.corfudb.runtime.exceptions.NetworkException; import org.corfudb.runtime.exceptions.QuorumUnreachableException; import org.corfudb.util.CFUtils; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; /** * Helper class to seal requested servers. * <p> * Created by zlokhandwala on 3/10/17. */ @Slf4j public class SealServersHelper { /** * Asynchronously set remote epoch on all servers of layout. * * @param layout Layout to be sealed. * @return A map of completableFutures for every remoteSetEpoch call. */ public static Map<String, CompletableFuture<Boolean>> asyncSetRemoteEpoch(Layout layout) { Map<String, CompletableFuture<Boolean>> resultMap = new HashMap<>(); // Seal layout servers layout.getAllServers().forEach(server -> { BaseClient baseClient = layout.getRuntime().getRouter(server).getClient(BaseClient.class); CompletableFuture<Boolean> cf = new CompletableFuture<>(); try { cf = baseClient.setRemoteEpoch(layout.getEpoch()); } catch (NetworkException ne) { cf.completeExceptionally(ne); log.error("Remote seal SET_EPOCH failed for server {} with {}", server, ne); } resultMap.put(server, cf); }); return resultMap; } /** * Wait for a quorum of layout servers to respond to be sealed. * * @param layoutServers List of layout servers. * @param completableFutureMap A map of completableFutures for every remoteSetEpoch call. * @throws QuorumUnreachableException Thrown if responses not received from a majority of layout servers. */ public static void waitForLayoutSeal(List<String> layoutServers, Map<String, CompletableFuture<Boolean>> completableFutureMap) throws QuorumUnreachableException { CompletableFuture<Boolean>[] completableFutures = completableFutureMap.entrySet().stream() .filter(pair -> layoutServers.contains(pair.getKey())) .map(pair -> pair.getValue()) .toArray(CompletableFuture[]::new); waitForQuorum(completableFutures); } /** * Wait for all log unit servers in every stripe to be sealed. * * @param layoutSegment Layout segment to be sealed. * @param completableFutureMap A map of completableFutures for every remoteSetEpoch call. * @throws QuorumUnreachableException Thrown if responses not received from all the log unit servers. */ public static void waitForChainSegmentSeal(Layout.LayoutSegment layoutSegment, Map<String, CompletableFuture<Boolean>> completableFutureMap) throws QuorumUnreachableException { for (Layout.LayoutStripe layoutStripe : layoutSegment.getStripes()) { CompletableFuture<Boolean>[] completableFutures = completableFutureMap.entrySet().stream() .filter(pair -> layoutStripe.getLogServers().contains(pair.getKey())) .map(pair -> pair.getValue()) .toArray(CompletableFuture[]::new); try { for (CompletableFuture<Boolean> cf : completableFutures) { CFUtils.getUninterruptibly(cf, TimeoutException.class); } } catch (TimeoutException e) { throw new QuorumUnreachableException(0, layoutStripe.getLogServers().size()); } } } /** * Wait for a quorum of log unit servers to respond to be sealed. * * @param layoutSegment Layout segment to be sealed. * @param completableFutureMap A map of completableFutures for every remoteSetEpoch call. * @throws QuorumUnreachableException Thrown if responses not received from all the log unit servers. */ public static void waitForQuorumSegmentSeal(Layout.LayoutSegment layoutSegment, Map<String, CompletableFuture<Boolean>> completableFutureMap) throws QuorumUnreachableException { for (Layout.LayoutStripe layoutStripe : layoutSegment.getStripes()) { CompletableFuture<Boolean>[] completableFutures = completableFutureMap.entrySet().stream() .filter(pair -> layoutStripe.getLogServers().contains(pair.getKey())) .map(pair -> pair.getValue()) .toArray(CompletableFuture[]::new); waitForQuorum(completableFutures); } } /** * Wait for a quorum of responses from the completable futures. * * @param completableFutures An array of completableFutures of which a quorum is required. * @throws QuorumUnreachableException Thrown if enough responses not received. */ public static void waitForQuorum(CompletableFuture<Boolean>[] completableFutures) throws QuorumUnreachableException { Boolean success = false; QuorumFuturesFactory.CompositeFuture<Boolean> quorumFuture = QuorumFuturesFactory.getQuorumFuture(Boolean::compareTo, completableFutures); try { success = quorumFuture.get(); } catch (ExecutionException | InterruptedException e) { if (e.getCause() instanceof QuorumUnreachableException) { throw (QuorumUnreachableException) e.getCause(); } } int reachableServers = (int) Arrays.stream(completableFutures) .filter(booleanCompletableFuture -> !booleanCompletableFuture.isCompletedExceptionally()).count(); if (!success) throw new QuorumUnreachableException(reachableServers, completableFutures.length); } }