aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningCompleteHostCalculatorTest.java
blob: c18815fc4396c15f2769fcd9b1e2c39d2cc1006e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.provisioning;

import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.Nodelike;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;

/**
 * @author bratseth
 */
public class VirtualNodeProvisioningCompleteHostCalculatorTest {

    @Test
    public void changing_to_different_range_preserves_allocation() {
        Flavor hostFlavor = new Flavor(new NodeResources(40, 40, 1000, 4));
        ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east")))
                                                                    .resourcesCalculator(new CompleteResourcesCalculator(hostFlavor))
                                                                    .flavors(List.of(hostFlavor))
                                                                    .build();
        tester.makeReadyHosts(9, hostFlavor.resources()).activateTenantHosts();

        ApplicationId app1 = ProvisioningTester.applicationId("app1");
        ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build();

        var initialResources = new NodeResources(20, 16, 50, 1);
        tester.activate(app1, cluster1, Capacity.from(new ClusterResources(2, 1, initialResources)));
        tester.assertNodes("Initial allocation",
                           2, 1, 20, 16, 50, 1.0,
                           app1, cluster1);

        var newMinResources = new NodeResources( 5,  4, 11, 1);
        var newMaxResources = new NodeResources(20, 10, 30, 1);

        tester.activate(app1, cluster1, Capacity.from(new ClusterResources(7, 1, newMinResources),
                                                      new ClusterResources(7, 1, newMaxResources)));
        tester.assertNodes("New allocation preserves (redundancy adjusted) total resources",
                           7, 1, 5, 4.0, 11, 1.0,
                           app1, cluster1);
        tester.activate(app1, cluster1, Capacity.from(new ClusterResources(7, 1, newMinResources),
                                                      new ClusterResources(7, 1, newMaxResources)));
        tester.assertNodes("Redeploying the same ranges does not cause changes",
                           7, 1, 5, 4.0, 11, 1.0,
                           app1, cluster1);
    }

    @Test
    public void testResourcesCalculator() {
        Flavor hostFlavor = new Flavor(new NodeResources(20, 40, 1000, 4));
        var calculator = new CompleteResourcesCalculator(hostFlavor);
        var originalReal = new NodeResources(0.7, 6.0, 12.9, 1.0);
        var realToRequest = calculator.realToRequest(originalReal, false, false);
        var requestToReal = calculator.requestToReal(realToRequest, false, false);
        var realResourcesOf = calculator.realResourcesOf(realToRequest);
        assertEquals(originalReal, requestToReal);
        assertEquals(originalReal, realResourcesOf);
    }

    private static class CompleteResourcesCalculator implements HostResourcesCalculator {

        private final Flavor hostFlavor; // Has the real resources
        private final double memoryOverhead = 1;
        private final double diskOverhead = 100;

        public CompleteResourcesCalculator(Flavor hostFlavor) {
            this.hostFlavor = hostFlavor;
        }

        @Override
        public NodeResources realResourcesOf(Nodelike node, NodeRepository nodeRepository) {
            if (node.parentHostname().isEmpty()) return node.resources(); // hosts use configured flavors
            return realResourcesOf(node.resources());
        }

        NodeResources realResourcesOf(NodeResources advertisedResources) {
            return advertisedResources.withMemoryGb(advertisedResources.memoryGb() -
                                                    memoryOverhead(advertisedResourcesOf(hostFlavor).memoryGb(), advertisedResources, false))
                                      .withDiskGb(advertisedResources.diskGb() -
                                                  diskOverhead(advertisedResourcesOf(hostFlavor).diskGb(), advertisedResources, false));
        }

        @Override
        public NodeResources requestToReal(NodeResources advertisedResources, boolean exclusive, boolean bestCase) {
            double memoryOverhead = memoryOverhead(advertisedResourcesOf(hostFlavor).memoryGb(), advertisedResources, false);
            double diskOverhead = diskOverhead(advertisedResourcesOf(hostFlavor).diskGb(), advertisedResources, false);
            return advertisedResources.withMemoryGb(advertisedResources.memoryGb() - memoryOverhead)
                                      .withDiskGb(advertisedResources.diskGb() - diskOverhead);
        }

        @Override
        public NodeResources advertisedResourcesOf(Flavor flavor) {
            if ( ! flavor.equals(hostFlavor)) return flavor.resources(); // Node 'flavors' just wrap the advertised resources
            return hostFlavor.resources().withMemoryGb(hostFlavor.resources().memoryGb() + memoryOverhead)
                             .withDiskGb(hostFlavor.resources().diskGb() + diskOverhead);
        }

        @Override
        public NodeResources realToRequest(NodeResources realResources, boolean exclusive, boolean bestCase) {
            double memoryOverhead = memoryOverhead(advertisedResourcesOf(hostFlavor).memoryGb(), realResources, true);
            double diskOverhead = diskOverhead(advertisedResourcesOf(hostFlavor).diskGb(), realResources, true);
            return realResources.withMemoryGb(realResources.memoryGb() + memoryOverhead)
                                .withDiskGb(realResources.diskGb() + diskOverhead);
        }

        @Override
        public long reservedDiskSpaceInBase2Gb(NodeType nodeType, boolean sharedHost) { return 0; }

        /**
         * Returns the memory overhead resulting if the given advertised resources are placed on the given node
         *
         * @param real true if the given resources are in real values, false if they are in advertised
         */
        private double memoryOverhead(double hostAdvertisedMemoryGb, NodeResources resources, boolean real) {
            double memoryShare = resources.memoryGb() /
                                 ( hostAdvertisedMemoryGb - (real ? memoryOverhead : 0));
            return memoryOverhead * memoryShare;
        }

        /**
         * Returns the disk overhead resulting if the given advertised resources are placed on the given node
         *
         * @param real true if the resources are in real values, false if they are in advertised
         */
        private double diskOverhead(double hostAdvertisedDiskGb, NodeResources resources, boolean real) {
            double diskShare = resources.diskGb() /
                               ( hostAdvertisedDiskGb - (real ? diskOverhead : 0) );
            return diskOverhead * diskShare;
        }

    }

}