package org.corfudb.runtime.view; import lombok.extern.slf4j.Slf4j; import org.corfudb.runtime.CorfuRuntime; import org.corfudb.runtime.exceptions.WrongEpochException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; /** * All views inherit from AbstractView. * <p> * AbstractView requires a runtime, and provides a layoutHelper function. * <p> * The layoutHelper function is called whenever a view tries to access a layout. * If the layoutHelper catches an exception which is due to connection issues * or an incorrect epoch, it asks the runtime to invalidate that layout * by reporting it to a layout server, and retries the function. * <p> * Created by mwei on 12/10/15. */ @Slf4j public abstract class AbstractView { CorfuRuntime runtime; public AbstractView(CorfuRuntime runtime) { this.runtime = runtime; } /** * Get the current layout. * * @return The current layout. */ public Layout getCurrentLayout() { while (true) { try { return runtime.layout.get(); } catch (Exception ex) { log.warn("Error executing remote call, invalidating view and retrying in {}s", runtime.retryRate, ex); runtime.invalidateLayout(); try { Thread.sleep(runtime.retryRate * 1000); } catch (InterruptedException ie) { } } } } /** * Helper function for view to retrieve layouts. * This function will retry the given function indefinitely, invalidating the view if there was a exception * contacting the endpoint. * * @param function The function to execute. * @param <T> The return type of the function. * @param <A> Any exception the function may throw. * @param <B> Any exception the function may throw. * @param <C> Any exception the function may throw. * @param <D> Any exception the function may throw. * @return The return value of the function. */ public <T, A extends RuntimeException, B extends RuntimeException, C extends RuntimeException, D extends RuntimeException> T layoutHelper(LayoutFunction<Layout, T, A, B, C, D> function) throws A, B, C, D { while (true) { try { return function.apply(runtime.layout.get()); } catch (RuntimeException re) { if (re.getCause() instanceof TimeoutException) { log.warn("Timeout executing remote call, invalidating view and retrying in {}s", runtime.retryRate); runtime.invalidateLayout(); try { Thread.sleep(runtime.retryRate * 1000); } catch (InterruptedException ie) { } } else if (re instanceof WrongEpochException){ WrongEpochException we = (WrongEpochException) re; log.warn("Got a wrong epoch exception, updating epoch to {} and invalidate view", we.getCorrectEpoch()); Long newEpoch = (we.getCorrectEpoch()); runtime.nodeRouters.values().forEach(x -> x.setEpoch(newEpoch)); runtime.invalidateLayout(); } else { throw re; } } catch (InterruptedException | ExecutionException ex) { log.warn("Error executing remote call, invalidating view and retrying in {}s", runtime.retryRate, ex); runtime.invalidateLayout(); try { Thread.sleep(runtime.retryRate * 1000); } catch (InterruptedException ie) { } } } } @FunctionalInterface public interface LayoutFunction<v, R, A extends Throwable, B extends Throwable, C extends Throwable, D extends Throwable> { R apply(Layout l) throws A, B, C, D; } }