All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.vespa.config.server.maintenance.ReindexingMaintainer Maven / Gradle / Ivy

There is a newer version: 8.441.21
Show newest version
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.maintenance;

import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.config.server.ApplicationRepository;
import com.yahoo.vespa.config.server.application.Application;
import com.yahoo.vespa.config.server.application.ApplicationCuratorDatabase;
import com.yahoo.vespa.config.server.application.ApplicationReindexing;
import com.yahoo.vespa.config.server.application.ApplicationReindexing.Cluster;
import com.yahoo.vespa.config.server.application.ConfigConvergenceChecker;
import com.yahoo.vespa.config.server.tenant.Tenant;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.yolean.Exceptions;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

import static java.util.Comparator.naturalOrder;

/**
 * Watches pending reindexing, and sets these to ready when config convergence is observed.
 * Also removes data for clusters or document types which no longer exist.
 *
 * @author jonmv
 */
public class ReindexingMaintainer extends ConfigServerMaintainer {

    private static final Logger log = Logger.getLogger(ReindexingMaintainer.class.getName());

    /** Timeout per service when getting config generations. */
    private static final Duration timeout = Duration.ofSeconds(10);

    /** Relative reindexing speed. */
    public static final double SPEED = 1;
    static final String CAUSE = "reindexing due to a schema change";

    private final ConfigConvergenceChecker convergence;
    private final Clock clock;

    public ReindexingMaintainer(ApplicationRepository applicationRepository, Curator curator,
                                Duration interval, ConfigConvergenceChecker convergence, Clock clock) {
        super(applicationRepository, curator, applicationRepository.flagSource(), clock, interval, true);
        this.convergence = convergence;
        this.clock = clock;
    }

    @Override
    protected double maintain() {
        AtomicInteger attempts = new AtomicInteger(0);
        AtomicInteger failures = new AtomicInteger(0);
        for (Tenant tenant : applicationRepository.tenantRepository().getAllTenants()) {
            ApplicationCuratorDatabase database = tenant.getApplicationRepo().database();
            for (ApplicationId id : database.activeApplications())
                applicationRepository.getActiveApplicationVersions(id)
                                     .map(application -> application.getForVersionOrLatest(Optional.empty(), clock.instant()))
                                     .ifPresent(application -> {
                                         try {
                                             attempts.incrementAndGet();
                                             applicationRepository.modifyReindexing(id, reindexing -> {
                                                 reindexing = withNewReady(reindexing, lazyGeneration(application), clock.instant());
                                                 reindexing = withOnlyCurrentData(reindexing, application);
                                                 return reindexing;
                                             });
                                         }
                                         catch (RuntimeException e) {
                                             log.log(Level.INFO, "Failed to update reindexing status for " + id + ": " + Exceptions.toMessageString(e));
                                             failures.incrementAndGet();
                                         }
                                     });
        }
        return asSuccessFactorDeviation(attempts.get(), failures.get());
    }

    private Supplier lazyGeneration(Application application) {
        AtomicLong oldest = new AtomicLong();
        return () -> {
            if (oldest.get() == 0)
                oldest.set(convergence.getServiceConfigGenerations(application, timeout).values().stream()
                                      .min(naturalOrder())
                                      .orElse(-1L));

            return oldest.get();
        };
    }

    static ApplicationReindexing withNewReady(ApplicationReindexing reindexing, Supplier oldestGeneration, Instant now) {
        // Config convergence means reindexing of detected reindex actions may begin.
        for (var cluster : reindexing.clusters().entrySet())
            for (var pending : cluster.getValue().pending().entrySet())
                if (pending.getValue() <= oldestGeneration.get()) {
                    reindexing = reindexing.withReady(cluster.getKey(), pending.getKey(), now, SPEED, CAUSE)
                                           .withoutPending(cluster.getKey(), pending.getKey());
                }

        return reindexing;
    }

    static ApplicationReindexing withOnlyCurrentData(ApplicationReindexing reindexing, Application application) {
        return withOnlyCurrentData(reindexing, application.getModel().documentTypesByCluster());
    }

    static ApplicationReindexing withOnlyCurrentData(ApplicationReindexing reindexing, Map> clusterDocumentTypes) {
        for (String clusterId : reindexing.clusters().keySet()) {
            if ( ! clusterDocumentTypes.containsKey(clusterId))
                reindexing = reindexing.without(clusterId);
            else {
                Cluster cluster = reindexing.clusters().get(clusterId);
                Collection documentTypes = clusterDocumentTypes.get(clusterId);
                for (String pending : cluster.pending().keySet())
                    if ( ! documentTypes.contains(pending))
                        reindexing = reindexing.withoutPending(clusterId, pending);
                for (String ready : cluster.ready().keySet())
                    if ( ! documentTypes.contains(ready))
                        reindexing = reindexing.without(clusterId, ready);
            }
        }
        return reindexing;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy