aboutsummaryrefslogtreecommitdiffstats
path: root/configserver/src/main/java/com/yahoo/vespa/config/server/SuperModelRequestHandler.java
blob: 9291e6030e2a6a0fd5313ca2fd18a4f8ab1e7f84 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server;

import com.google.inject.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.ConfigInstance;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.provision.Version;
import com.yahoo.config.provision.Zone;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.vespa.config.GetConfigRequest;
import com.yahoo.vespa.config.protocol.ConfigResponse;
import com.yahoo.vespa.config.server.application.Application;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.config.GenerationCounter;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.model.SuperModel;
import com.yahoo.vespa.config.server.rpc.ConfigResponseFactory;
import com.yahoo.vespa.config.server.rpc.ConfigResponseFactoryFactory;

import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * Handles request for supermodel config
 *
 * @author lulf
 * @since 5.9
 */
public class SuperModelRequestHandler implements RequestHandler {

    private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(SuperModelRequestHandler.class.getName());
    private volatile SuperModelController handler;
    private final GenerationCounter generationCounter;
    private final Zone zone;
    private final long masterGeneration;
    private final ConfigDefinitionRepo configDefinitionRepo;
    private final ConfigResponseFactory responseFactory;
    private volatile boolean enabled = false;

    /**
     * Creates a supermodel controller
     */
    @Inject
    public SuperModelRequestHandler(GenerationCounter generationCounter, ConfigDefinitionRepo configDefinitionRepo, 
                                    ConfigserverConfig configserverConfig) {
        this.generationCounter = generationCounter;
        this.configDefinitionRepo = configDefinitionRepo;
        this.masterGeneration = configserverConfig.masterGeneration();
        this.responseFactory = ConfigResponseFactoryFactory.createFactory(configserverConfig);
        this.zone = new Zone(configserverConfig);
        this.handler = createNewHandler(Collections.emptyMap());
    }

    /**
     * Signals that config has been reloaded for an {@link com.yahoo.vespa.config.server.application.Application}
     * belonging to a tenant.
     *
     * TODO: This is a bit too complex I think.
     *
     * @param tenant Name of tenant owning the application.
     * @param applicationSet The reloaded set of {@link com.yahoo.vespa.config.server.application.Application}.
     */
    public synchronized void reloadConfig(TenantName tenant, ApplicationSet applicationSet) {
        Map<TenantName, Map<ApplicationId, Application>> newModels = createModelCopy();
        if (!newModels.containsKey(tenant)) {
            newModels.put(tenant, new LinkedHashMap<>());
        }
        // TODO: Should supermodel care about multiple versions?
        newModels.get(tenant).put(applicationSet.getId(), applicationSet.getForVersionOrLatest(Optional.empty()));
        handler = createNewHandler(newModels);
    }

    public synchronized void removeApplication(ApplicationId applicationId) {
        Map<TenantName, Map<ApplicationId, Application>> newModels = createModelCopy();
        if (newModels.containsKey(applicationId.tenant())) {
            newModels.get(applicationId.tenant()).remove(applicationId);
            if (newModels.get(applicationId.tenant()).isEmpty()) {
                newModels.remove(applicationId.tenant());
            }
        }
        handler = createNewHandler(newModels);
    }

    private SuperModelController createNewHandler(Map<TenantName, Map<ApplicationId, Application>> newModels) {
        long generation = generationCounter.get() + masterGeneration;
        SuperModel model = new SuperModel(newModels, zone);
        return new SuperModelController(model, configDefinitionRepo, generation, responseFactory);
    }

    private Map<TenantName, Map<ApplicationId, Application>> getCurrentModels() {
        if (handler != null) {
            return handler.getSuperModel().applicationModels();
        } else {
            return new LinkedHashMap<>();
        }
    }

    private Map<TenantName, Map<ApplicationId, Application>> createModelCopy() {
        Map<TenantName, Map<ApplicationId, Application>> currentModels = getCurrentModels();
        Map<TenantName, Map<ApplicationId, Application>> newModels = new LinkedHashMap<>();
        for (Map.Entry<TenantName, Map<ApplicationId, Application>> entry : currentModels.entrySet()) {
            Map<ApplicationId, Application> appMap = new LinkedHashMap<>();
            newModels.put(entry.getKey(), appMap);
            for (Map.Entry<ApplicationId, Application> appEntry : entry.getValue().entrySet()) {
                appMap.put(appEntry.getKey(), appEntry.getValue());
            }
        }
        return newModels;
    }

    public SuperModelController getHandler() { return handler; }

    @Override
    public ConfigResponse resolveConfig(ApplicationId appId, GetConfigRequest req, Optional<Version> vespaVersion) {
        log.log(LogLevel.DEBUG, "SuperModelRequestHandler resolving " + req + " for app id '" + appId + "'");
        if (handler != null) {
            return handler.resolveConfig(req);
        }
        return null;
    }

    public <CONFIGTYPE extends ConfigInstance> CONFIGTYPE getConfig(Class<CONFIGTYPE> configClass, ApplicationId applicationId, String configId) throws IOException {
        return handler.getConfig(configClass, applicationId, configId);
    }

    @Override
    public Set<ConfigKey<?>> listConfigs(ApplicationId appId, Optional<Version> vespaVersion, boolean recursive) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set<ConfigKey<?>> listNamedConfigs(ApplicationId appId, Optional<Version> vespaVersion, ConfigKey<?> key, boolean recursive) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set<ConfigKey<?>> allConfigsProduced(ApplicationId appId, Optional<Version> vespaVersion) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set<String> allConfigIds(ApplicationId appID, Optional<Version> vespaVersion) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean hasApplication(ApplicationId appId, Optional<Version> vespaVersion) {
        return enabled && appId.equals(ApplicationId.global());
    }

    @Override
    public ApplicationId resolveApplicationId(String hostName) {
        return ApplicationId.global();
    }

    public void enable() {
        enabled = true;
    }
}