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

com.yahoo.vespa.hosted.controller.Instance Maven / Gradle / Ivy

There is a newer version: 8.253.3
Show 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.controller;

import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.application.AssignedRotation;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentActivity;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.QuotaUsage;
import com.yahoo.vespa.hosted.controller.routing.rotation.RotationStatus;

import java.time.Instant;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * An instance of an application.
 *
 * This is immutable.
 *
 * @author bratseth
 */
public class Instance {

    private final ApplicationId id;
    private final Map deployments;
    private final List rotations;
    private final RotationStatus rotationStatus;
    private final Map jobPauses;
    private final Change change;

    /** Creates an empty instance */
    public Instance(ApplicationId id) {
        this(id, Set.of(), Map.of(), List.of(), RotationStatus.EMPTY, Change.empty());
    }

    /** Creates an empty instance*/
    public Instance(ApplicationId id, Collection deployments, Map jobPauses,
                    List rotations, RotationStatus rotationStatus, Change change) {
        this.id = Objects.requireNonNull(id, "id cannot be null");
        this.deployments = Objects.requireNonNull(deployments, "deployments cannot be null").stream()
                                  .collect(Collectors.toUnmodifiableMap(Deployment::zone, Function.identity()));
        this.jobPauses = Map.copyOf(Objects.requireNonNull(jobPauses, "deploymentJobs cannot be null"));
        this.rotations = List.copyOf(Objects.requireNonNull(rotations, "rotations cannot be null"));
        this.rotationStatus = Objects.requireNonNull(rotationStatus, "rotationStatus cannot be null");
        this.change = Objects.requireNonNull(change, "change cannot be null");
    }

    public Instance withNewDeployment(ZoneId zone, ApplicationVersion applicationVersion, Version version,
                                      Instant instant, Map warnings, QuotaUsage quotaUsage) {
        // Use info from previous deployment if available, otherwise create a new one.
        Deployment previousDeployment = deployments.getOrDefault(zone, new Deployment(zone, applicationVersion,
                                                                                      version, instant,
                                                                                      DeploymentMetrics.none,
                                                                                      DeploymentActivity.none,
                                                                                      QuotaUsage.none,
                                                                                      OptionalDouble.empty()));
        Deployment newDeployment = new Deployment(zone, applicationVersion, version, instant,
                                                  previousDeployment.metrics().with(warnings),
                                                  previousDeployment.activity(),
                                                  quotaUsage,
                                                  previousDeployment.cost());
        return with(newDeployment);
    }

    public Instance withJobPause(JobType jobType, OptionalLong pausedUntil) {
        Map jobPauses = new HashMap<>(this.jobPauses);
        if (pausedUntil.isPresent())
            jobPauses.put(jobType, Instant.ofEpochMilli(pausedUntil.getAsLong()));
        else
            jobPauses.remove(jobType);

        return new Instance(id, deployments.values(), jobPauses, rotations, rotationStatus, change);
    }

    public Instance recordActivityAt(Instant instant, ZoneId zone) {
        Deployment deployment = deployments.get(zone);
        if (deployment == null) return this;
        return with(deployment.recordActivityAt(instant));
    }

    public Instance with(ZoneId zone, DeploymentMetrics deploymentMetrics) {
        Deployment deployment = deployments.get(zone);
        if (deployment == null) return this;    // No longer deployed in this zone.
        return with(deployment.withMetrics(deploymentMetrics));
    }

    public Instance withDeploymentCosts(Map costByZone) {
        Map deployments = this.deployments.entrySet().stream()
                .map(entry -> Optional.ofNullable(costByZone.get(entry.getKey()))
                        .map(entry.getValue()::withCost)
                        .orElseGet(entry.getValue()::withoutCost))
                .collect(Collectors.toUnmodifiableMap(Deployment::zone, deployment -> deployment));
        return with(deployments);
    }

    public Instance withoutDeploymentIn(ZoneId zone) {
        Map deployments = new LinkedHashMap<>(this.deployments);
        deployments.remove(zone);
        return with(deployments);
    }

    public Instance with(List assignedRotations) {
        return new Instance(id, deployments.values(), jobPauses, assignedRotations, rotationStatus, change);
    }

    public Instance with(RotationStatus rotationStatus) {
        return new Instance(id, deployments.values(), jobPauses, rotations, rotationStatus, change);
    }

    public Instance withChange(Change change) {
        return new Instance(id, deployments.values(), jobPauses, rotations, rotationStatus, change);
    }

    private Instance with(Deployment deployment) {
        Map deployments = new LinkedHashMap<>(this.deployments);
        deployments.put(deployment.zone(), deployment);
        return with(deployments);
    }

    private Instance with(Map deployments) {
        return new Instance(id, deployments.values(), jobPauses, rotations, rotationStatus, change);
    }

    public ApplicationId id() { return id; }

    public InstanceName name() { return id.instance(); }

    /** Returns an immutable map of the current deployments of this */
    public Map deployments() { return deployments; }

    /**
     * Returns an immutable map of the current *production* deployments of this
     * (deployments also includes manually deployed environments)
     */
    public Map productionDeployments() {
        return deployments.values().stream()
                          .filter(deployment -> deployment.zone().environment() == Environment.prod)
                          .collect(Collectors.toUnmodifiableMap(Deployment::zone, Function.identity()));
    }

    /** Returns the instant until which the given job is paused, or empty. */
    public Optional jobPause(JobType jobType) {
        return Optional.ofNullable(jobPauses.get(jobType));
    }

    /** Returns the set of instants until which any paused jobs of this instance should remain paused, indexed by job type. */
    public Map jobPauses() {
        return jobPauses;
    }

    /** Returns all rotations assigned to this */
    public List rotations() {
        return rotations;
    }

    /** Returns the status of the global rotation(s) assigned to this */
    public RotationStatus rotationStatus() {
        return rotationStatus;
    }

    /** Returns the currently deploying change for this instance. */
    public Change change() {
        return change;
    }

    /** Returns the total quota usage for this instance, excluding temporary deployments **/
    public QuotaUsage quotaUsage() {
        return deployments.values().stream()
                .filter(d -> !d.zone().environment().isTest()) // Exclude temporary deployments
                .map(Deployment::quota).reduce(QuotaUsage::add).orElse(QuotaUsage.none);
    }

    /** Returns the total quota usage for this instance, excluding one specific deployment (and temporary deployments) */
    public QuotaUsage quotaUsageExcluding(ApplicationId application, ZoneId zone) {
        return deployments.values().stream()
                .filter(d -> !d.zone().environment().isTest()) // Exclude temporary deployments
                .filter(d -> !(application.equals(id) && d.zone().equals(zone)))
                .map(Deployment::quota).reduce(QuotaUsage::add).orElse(QuotaUsage.none);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (! (o instanceof Instance)) return false;

        Instance that = (Instance) o;

        return id.equals(that.id);
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    @Override
    public String toString() {
        return "application '" + id + "'";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy