aboutsummaryrefslogtreecommitdiffstats
path: root/slobrok/src/vespa/slobrok/server/union_service_map.cpp
blob: 5a55299770a26f94d18a8d255fe19e89b5a9aac5 (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "union_service_map.h"
#include <vespa/log/log.h>

LOG_SETUP(".slobrok.server.union_service_map");

namespace slobrok {

UnionServiceMap::UnionServiceMap() = default;
UnionServiceMap::~UnionServiceMap() = default;

ServiceMappingList UnionServiceMap::currentConsensus() const {
    ServiceMappingList result;
    for (const auto & [ name, list ] : _mappings) {
        if (list.size() == 1u) {
            result.emplace_back(name, list[0].spec);
        }
    }
    return result;
}

bool UnionServiceMap::wouldConflict(const ServiceMapping &mapping) const {
    const vespalib::string &key = mapping.name;
    auto iter = _mappings.find(key);
    if (iter == _mappings.end()) {
        return false;
    }
    const Mappings &values = iter->second;
    if (values.size() != 1) {
        return true;
    }
    return (values[0].spec != mapping.spec);
}

void UnionServiceMap::add(const ServiceMapping &mapping)
{
    const vespalib::string &key = mapping.name;
    auto iter = _mappings.find(key);
    if (iter == _mappings.end()) {
        _mappings[key].emplace_back(mapping.spec, 1u);
        LOG(debug, "add new %s->%s", mapping.name.c_str(), mapping.spec.c_str());
        ProxyMapSource::add(mapping);
    } else {
        Mappings &values = iter->second;
        for (CountedSpec &old : values) {
            if (old.spec == mapping.spec) {
                LOG(debug, "add ref to existing %s->%s", mapping.name.c_str(), mapping.spec.c_str());
                ++old.count;
                return;
            }
        }
        values.emplace_back(mapping.spec, 1u);
        if (values.size() == 2u) {
            ServiceMapping toRemove{key, values[0].spec};
            LOG(warning, "Multiple specs seen for name '%s', un-publishing %s",
                toRemove.name.c_str(), toRemove.spec.c_str());
            ProxyMapSource::remove(toRemove);
        }
    }
}

void UnionServiceMap::remove(const ServiceMapping &mapping)
{
    const vespalib::string &key = mapping.name;
    auto iter = _mappings.find(key);
    if (iter == _mappings.end()) {
        LOG(error, "Broken invariant: did not find %s in mappings", key.c_str());
        return;
    }
    LOG(debug, "remove ref from %s->%s", mapping.name.c_str(), mapping.spec.c_str());
    Mappings &values = iter->second;
    bool found = false;
    for (CountedSpec &old : values) {
        if (old.spec == mapping.spec) {
            if (--old.count > 0u) return;
            found = true;
        }
    }
    if (! found) {
        LOG(error, "Broken invariant: did not find %s->%s in mappings",
            key.c_str(), mapping.spec.c_str());
        return;
    }
    size_t old_size = values.size();
    std::erase_if(values, [] (const CountedSpec &v) noexcept { return v.count == 0; });
    if (values.size() == 1u) {
        LOG_ASSERT(old_size == 2u);
        ServiceMapping toAdd{key, values[0].spec};
        LOG(info, "Had multiple mappings for %s, but now only %s remains",
            toAdd.name.c_str(), toAdd.spec.c_str());
        ProxyMapSource::add(toAdd);
    } else if (values.size() == 0u) {
        LOG_ASSERT(old_size == 1u);
        LOG(debug, "Last reference for %s -> %s removed",
            key.c_str(), mapping.spec.c_str());
        _mappings.erase(iter);
        ProxyMapSource::remove(mapping);
    }
}

void UnionServiceMap::update(const ServiceMapping &old_mapping,
                             const ServiceMapping &new_mapping)
{
    LOG_ASSERT(old_mapping.name == new_mapping.name);
    remove(old_mapping);
    add(new_mapping);
}

} // namespace slobrok