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

com.yahoo.vespa.hosted.provision.maintenance.SnapshotExpirer Maven / Gradle / Ivy

The newest version!
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.maintenance;

import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeType;
import com.yahoo.jdisc.Metric;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.backup.Snapshot;
import com.yahoo.yolean.Exceptions;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * Remove stale node {@link Snapshot}s.
 *
 * @author mpolden
 */
public class SnapshotExpirer extends NodeRepositoryMaintainer {

    private static final Logger LOG = Logger.getLogger(SnapshotExpirer.class.getName());
    private static final Duration MIN_IDLE_PERIOD = Duration.ofDays(1);

    public SnapshotExpirer(NodeRepository nodeRepository, Duration interval, Metric metric) {
        super(nodeRepository, interval, metric);
    }

    @Override
    protected double maintain() {
        Map> snapshotsByHostname = nodeRepository().snapshots().read().stream()
                                                                            .collect(Collectors.groupingBy(Snapshot::hostname));
        NodeList nodes = nodeRepository().nodes().list().nodeType(NodeType.tenant);
        Instant now = nodeRepository().clock().instant();
        snapshotsByHostname.forEach((hostname, snapshots) -> {
            if (!shouldRemoveAny(snapshots, nodes, now)) return;
            try (var lock = nodeRepository().snapshots().lock(hostname.value())) {
                // Re-read and check while holding lock
                snapshots = nodeRepository().snapshots().read(hostname.value());
                for (var snapshot : snapshots) {
                    if (shouldRemove(snapshot, nodes, now)) {
                        try {
                            remove(snapshot, hostname);
                        } catch (Exception e) {
                            LOG.warning("Failed to remove snapshot " + snapshot.id() + " of " + hostname + ": " +
                                        Exceptions.toMessageString(e) +
                                        ". Will retry in " + interval());
                        }
                    }
                }
            }
        });
        return 0;
    }

    private void remove(Snapshot snapshot, HostName hostname) {
        LOG.info("Removing snapshot " + snapshot.id() + " of " + hostname);
        nodeRepository().snapshots().remove(snapshot.id(), hostname.value(), true);
    }

    private boolean shouldRemoveAny(List snapshots, NodeList nodes, Instant now) {
        return snapshots.stream().anyMatch(s -> shouldRemove(s, nodes, now));
    }

    /** Returns whether given snapshot should be removed */
    private boolean shouldRemove(Snapshot snapshot, NodeList nodes, Instant now) {
        Duration idle = snapshot.idle(now);
        if (idle.compareTo(MIN_IDLE_PERIOD) < 0) return false;              // No:  Snapshot not idle long enough
        // TODO(mpolden): Replace this with a proper policy when implementing application-level backups
        if (nodes.node(snapshot.hostname().value()).isEmpty()) return true; // Yes: Snapshot belongs to non-existent node
        return snapshot.state() == Snapshot.State.restored;                 // Yes: Snapshot has been restored
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy