public synchronized boolean updateTo()

in hollow/src/main/java/com/netflix/hollow/api/client/HollowClientUpdater.java [136:245]


    public synchronized boolean updateTo(HollowConsumer.VersionInfo requestedVersionInfo) throws Throwable {
        long requestedVersion = requestedVersionInfo.getVersion();
        if (requestedVersion == getCurrentVersionId()) {
            if (requestedVersion == HollowConstants.VERSION_NONE && hollowDataHolderVolatile == null) {
                LOG.warning("No versions to update to, initializing to empty state");
                // attempting to refresh, but no available versions - initialize to empty state
                hollowDataHolderVolatile = newHollowDataHolder();
                forceDoubleSnapshotNextUpdate(); // intentionally ignore doubleSnapshotConfig
            }
            return true;
        }
        metrics.setLastRefreshStartNs(System.nanoTime());

        // Take a snapshot of the listeners to ensure additions or removals may occur concurrently
        // but will not take effect until a subsequent refresh
        final HollowConsumer.RefreshListener[] localListeners =
                refreshListeners.toArray(new HollowConsumer.RefreshListener[0]);

        for (HollowConsumer.RefreshListener listener : localListeners) {
            listener.versionDetected(requestedVersionInfo);
        }

        long beforeVersion = getCurrentVersionId();

        for (HollowConsumer.RefreshListener listener : localListeners) {
            listener.refreshStarted(beforeVersion, requestedVersion);
        }

        try {
            HollowUpdatePlan updatePlan = shouldCreateSnapshotPlan(requestedVersionInfo)
                ? planner.planInitializingUpdate(requestedVersionInfo)
                : planner.planUpdate(hollowDataHolderVolatile.getCurrentVersion(), requestedVersionInfo,
                        doubleSnapshotConfig.allowDoubleSnapshot());
            LOG.info(String.format("Hollow update plan: beforeVersion=%s, requestedVersion=%s, updatePlan={%s}", beforeVersion, requestedVersion, updatePlan));

            for (HollowConsumer.RefreshListener listener : localListeners)
                if (listener instanceof HollowConsumer.TransitionAwareRefreshListener)
                    ((HollowConsumer.TransitionAwareRefreshListener)listener).transitionsPlanned(beforeVersion, requestedVersion, updatePlan.isSnapshotPlan(), updatePlan.getTransitionSequence());

            if (updatePlan.destinationVersion() == HollowConstants.VERSION_NONE
                    && requestedVersion != HollowConstants.VERSION_LATEST) {
                String msg = String.format("Could not create an update plan for version %s, because "
                        + "that version or any qualifying previous versions could not be retrieved.", requestedVersion);
                if (beforeVersion != HollowConstants.VERSION_NONE) {
                    msg += String.format(" Consumer will remain at current version %s until next update attempt.", beforeVersion);
                }
                throw new IllegalArgumentException(msg);
            }

            if (updatePlan.equals(HollowUpdatePlan.DO_NOTHING)
                    && requestedVersion == HollowConstants.VERSION_LATEST)
                throw new IllegalArgumentException("Could not create an update plan, because no existing versions could be retrieved.");

            if (updatePlan.destinationVersion(requestedVersion) == getCurrentVersionId()) {
                metrics.setLastRefreshEndNs(System.nanoTime());
                return true;
            }

            if (updatePlan.isSnapshotPlan()) {  // 1 snapshot and 0+ delta transitions
                HollowDataHolder oldDh = hollowDataHolderVolatile;
                if (oldDh == null || doubleSnapshotConfig.allowDoubleSnapshot()) {
                    HollowDataHolder newDh = newHollowDataHolder();
                    try {
                        /* We need to assign the volatile field after API init since it may be
                         * accessed during the update plan application, for example via a refresh
                         * listener (such as a unique key indexer) that calls getAPI. If we do it after
                         * newDh.update(), refresh listeners will see the old API. If we do it
                         * before then we open ourselves up to a race where a caller will get back
                         * null if they call getAPI after assigning the volatile but before the API
                         * is initialized in HollowDataHolder#initializeAPI.
                         * Also note that hollowDataHolderVolatile only changes for snapshot plans,
                         * and it is only for snapshot plans that HollowDataHolder#initializeAPI is
                         * called. */
                        newDh.update(updatePlan, localListeners, () -> hollowDataHolderVolatile = newDh);
                    } catch (Throwable t) {
                        // If the update plan failed then revert back to the old holder
                        hollowDataHolderVolatile = oldDh;
                        throw t;
                    }
                    forceDoubleSnapshot = false;
                }
            } else {    // 0 snapshot and 1+ delta transitions
                hollowDataHolderVolatile.update(updatePlan, localListeners, () -> {});
            }

            for(HollowConsumer.RefreshListener refreshListener : localListeners)
                refreshListener.refreshSuccessful(beforeVersion, getCurrentVersionId(), requestedVersion);

            metrics.updateTypeStateMetrics(getStateEngine(), requestedVersion);
            if(metricsCollector != null)
                metricsCollector.collect(metrics);

            initialLoad.complete(getCurrentVersionId()); // only set the first time
            metrics.setLastRefreshEndNs(System.nanoTime());
            return getCurrentVersionId() == requestedVersion;
        } catch(Throwable th) {
            forceDoubleSnapshotNextUpdate();
            metrics.updateRefreshFailed();
            if(metricsCollector != null)
                metricsCollector.collect(metrics);
            for(HollowConsumer.RefreshListener refreshListener : localListeners)
                refreshListener.refreshFailed(beforeVersion, getCurrentVersionId(), requestedVersion, th);

            // intentionally omitting a call to initialLoad.completeExceptionally(th), for producers
            // that write often a consumer has a chance to try another snapshot that might succeed
            metrics.setLastRefreshEndNs(System.nanoTime());

            throw th;
        }
    }