summaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorArne H Juul <arnej27959@users.noreply.github.com>2017-06-20 09:20:48 +0200
committerGitHub <noreply@github.com>2017-06-20 09:20:48 +0200
commit60162a9ca2efd83c95b5b06e2944e75c379c5223 (patch)
treee5b32fffbf0b945cd66cc1b099619d23c9cf0a77 /vespalib
parent45153e0675764fd7dce503a8fd308816362dfcfc (diff)
parent466a87693515e9f03677b9780b3434f3d79367d0 (diff)
Merge pull request #2809 from yahoo/havardpe/lazy-resolver
lazy resolver
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/CMakeLists.txt1
-rw-r--r--vespalib/src/tests/net/lazy_resolver/CMakeLists.txt8
-rw-r--r--vespalib/src/tests/net/lazy_resolver/lazy_resolver_test.cpp174
-rw-r--r--vespalib/src/tests/net/socket_spec/socket_spec_test.cpp16
-rw-r--r--vespalib/src/vespa/vespalib/net/CMakeLists.txt1
-rw-r--r--vespalib/src/vespa/vespalib/net/lazy_resolver.cpp205
-rw-r--r--vespalib/src/vespa/vespalib/net/lazy_resolver.h108
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_spec.cpp9
-rw-r--r--vespalib/src/vespa/vespalib/net/socket_spec.h1
9 files changed, 523 insertions, 0 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt
index 3ac5000fe79..52ae68051af 100644
--- a/vespalib/CMakeLists.txt
+++ b/vespalib/CMakeLists.txt
@@ -47,6 +47,7 @@ vespa_define_module(
src/tests/linkedptr
src/tests/make_fixture_macros
src/tests/memory
+ src/tests/net/lazy_resolver
src/tests/net/selector
src/tests/net/socket
src/tests/net/socket_spec
diff --git a/vespalib/src/tests/net/lazy_resolver/CMakeLists.txt b/vespalib/src/tests/net/lazy_resolver/CMakeLists.txt
new file mode 100644
index 00000000000..440ee7ab873
--- /dev/null
+++ b/vespalib/src/tests/net/lazy_resolver/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_lazy_resolver_test_app TEST
+ SOURCES
+ lazy_resolver_test.cpp
+ DEPENDS
+ vespalib
+)
+vespa_add_test(NAME vespalib_lazy_resolver_test_app COMMAND vespalib_lazy_resolver_test_app)
diff --git a/vespalib/src/tests/net/lazy_resolver/lazy_resolver_test.cpp b/vespalib/src/tests/net/lazy_resolver/lazy_resolver_test.cpp
new file mode 100644
index 00000000000..ba448c32cb6
--- /dev/null
+++ b/vespalib/src/tests/net/lazy_resolver/lazy_resolver_test.cpp
@@ -0,0 +1,174 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/net/lazy_resolver.h>
+#include <vespa/vespalib/net/socket_spec.h>
+
+using namespace vespalib;
+
+TEST("require that lazy resolver internal duration type is appropriate") {
+ LazyResolver::seconds my_secs = std::chrono::milliseconds(500);
+ EXPECT_EQUAL(my_secs.count(), 0.5);
+}
+
+TEST("require that lazy resolver can be used to resolve connect spec") {
+ vespalib::string spec("tcp/localhost:123");
+ auto resolver = LazyResolver::create();
+ auto address = resolver->make_address(spec);
+ auto resolved = address->resolve();
+ fprintf(stderr, "resolver(spec:%s) -> '%s'\n", spec.c_str(), resolved.c_str());
+ EXPECT_EQUAL(spec, address->spec());
+ EXPECT_NOT_EQUAL(resolved, address->spec());
+ EXPECT_EQUAL(resolved, SocketSpec(spec).client_address().spec());
+ EXPECT_EQUAL(resolved, SocketAddress::select_remote(123, "localhost").spec());
+}
+
+TEST("require that lazy resolver can be used to resolve host name") {
+ vespalib::string host_name("localhost");
+ auto resolver = LazyResolver::create();
+ auto host = resolver->make_host(host_name);
+ auto resolved = host->resolve();
+ fprintf(stderr, "resolver(host_name:%s) -> '%s'\n", host_name.c_str(), resolved.c_str());
+ EXPECT_EQUAL(host_name, host->host_name());
+ EXPECT_NOT_EQUAL(resolved, host->host_name());
+ EXPECT_EQUAL(resolved, SocketSpec("tcp/localhost:123").client_address().ip_address());
+ EXPECT_EQUAL(resolved, SocketAddress::select_remote(123, "localhost").ip_address());
+ EXPECT_EQUAL(resolved, LazyResolver::default_resolve_host(host_name));
+}
+
+vespalib::string dummy_resolve_host(const vespalib::string &) { return "ip.addr"; }
+
+TEST("require that host name resolve function can be overridden (bonus: slow resolve warning)") {
+ LazyResolver::Params params;
+ params.resolve_host = dummy_resolve_host;
+ params.max_resolve_time = LazyResolver::seconds(0);
+ auto resolver = LazyResolver::create(params);
+ EXPECT_EQUAL(resolver->make_address("tcp/host_name:123")->resolve(), "tcp/ip.addr:123");
+}
+
+struct ResolveFixture {
+ std::mutex ip_lock;
+ std::map<vespalib::string,vespalib::string> ip_map;
+ std::map<vespalib::string, size_t> ip_cnt;
+ LazyResolver::SP resolver;
+ void set_ip_addr(const vespalib::string &host, const vespalib::string &ip_addr) {
+ std::lock_guard<std::mutex> guard(ip_lock);
+ ip_map[host] = ip_addr;
+ }
+ vespalib::string get_ip_addr(const vespalib::string &host) {
+ std::lock_guard<std::mutex> guard(ip_lock);
+ ++ip_cnt[host];
+ return ip_map[host];
+ }
+ size_t get_cnt(const vespalib::string &host) {
+ std::lock_guard<std::mutex> guard(ip_lock);
+ return ip_cnt[host];
+ }
+ size_t get_total_cnt() {
+ size_t total = 0;
+ std::lock_guard<std::mutex> guard(ip_lock);
+ for (const auto &entry: ip_cnt) {
+ total += entry.second;
+ }
+ return total;
+ }
+ ResolveFixture(double max_age) : ip_lock(), ip_map(), ip_cnt(), resolver() {
+ LazyResolver::Params params;
+ params.resolve_host = [this](const vespalib::string &host_name){ return get_ip_addr(host_name); };
+ params.max_result_age = LazyResolver::seconds(max_age);
+ resolver = LazyResolver::create(std::move(params));
+ set_ip_addr("localhost", "127.0.0.1");
+ set_ip_addr("127.0.0.1", "127.0.0.1");
+ }
+ LazyResolver::Address::SP make(const vespalib::string &spec) { return resolver->make_address(spec); }
+};
+
+TEST_F("require that lazy resolver can be used to resolve connect specs without host names", ResolveFixture(300)) {
+ EXPECT_EQUAL(f1.make("this is bogus")->resolve(), "this is bogus");
+ EXPECT_EQUAL(f1.make("tcp/123")->resolve(), "tcp/123");
+ EXPECT_EQUAL(f1.make("ipc/file:my_socket")->resolve(), "ipc/file:my_socket");
+ EXPECT_EQUAL(f1.make("ipc/name:my_socket")->resolve(), "ipc/name:my_socket");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(f1.get_total_cnt(), 0u);
+}
+
+TEST_F("require that resolved hosts can be shared between addresses", ResolveFixture(300)) {
+ auto addr1 = f1.make("tcp/localhost:123");
+ auto addr2 = f1.make("tcp/localhost:456");
+ EXPECT_EQUAL(addr1->resolve(), "tcp/127.0.0.1:123");
+ EXPECT_EQUAL(addr2->resolve(), "tcp/127.0.0.1:456");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(f1.get_cnt("localhost"), 1u);
+ EXPECT_EQUAL(f1.get_total_cnt(), 1u);
+}
+
+TEST_F("require that resolved hosts are discarded when not used", ResolveFixture(300)) {
+ EXPECT_EQUAL(f1.make("tcp/localhost:123")->resolve(), "tcp/127.0.0.1:123");
+ EXPECT_EQUAL(f1.make("tcp/localhost:456")->resolve(), "tcp/127.0.0.1:456");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(f1.get_cnt("localhost"), 2u);
+ EXPECT_EQUAL(f1.get_total_cnt(), 2u);
+}
+
+TEST_F("require that host names resolving to themselves (ip addresses) are not shared", ResolveFixture(300)) {
+ auto addr1 = f1.make("tcp/127.0.0.1:123");
+ auto addr2 = f1.make("tcp/127.0.0.1:456");
+ EXPECT_EQUAL(addr1->resolve(), "tcp/127.0.0.1:123");
+ EXPECT_EQUAL(addr2->resolve(), "tcp/127.0.0.1:456");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(f1.get_cnt("127.0.0.1"), 2u);
+ EXPECT_EQUAL(f1.get_total_cnt(), 2u);
+}
+
+TEST_F("require that resolve changes can be detected", ResolveFixture(0)) {
+ auto addr = f1.make("tcp/localhost:123");
+ f1.set_ip_addr("localhost", "127.0.0.2");
+ EXPECT_EQUAL(addr->resolve(), "tcp/127.0.0.1:123");
+ f1.resolver->wait_for_pending_updates();
+ f1.set_ip_addr("localhost", "127.0.0.3");
+ EXPECT_EQUAL(addr->resolve(), "tcp/127.0.0.2:123");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(addr->resolve(), "tcp/127.0.0.3:123");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(f1.get_cnt("localhost"), 4u);
+ EXPECT_EQUAL(f1.get_total_cnt(), 4u);
+}
+
+TEST_F("require that resolve changes are not detected when old results are still fresh", ResolveFixture(300)) {
+ auto addr = f1.make("tcp/localhost:123");
+ f1.set_ip_addr("localhost", "127.0.0.2");
+ EXPECT_EQUAL(addr->resolve(), "tcp/127.0.0.1:123");
+ f1.resolver->wait_for_pending_updates();
+ f1.set_ip_addr("localhost", "127.0.0.3");
+ EXPECT_EQUAL(addr->resolve(), "tcp/127.0.0.1:123");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(addr->resolve(), "tcp/127.0.0.1:123");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(f1.get_cnt("localhost"), 1u);
+ EXPECT_EQUAL(f1.get_total_cnt(), 1u);
+}
+
+TEST_F("require that missing ip address gives invalid spec", ResolveFixture(300)) {
+ f1.set_ip_addr("localhost", "");
+ auto addr = f1.make("tcp/localhost:123");
+ EXPECT_EQUAL(addr->resolve(), "invalid");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(f1.get_cnt("localhost"), 1u);
+ EXPECT_EQUAL(f1.get_total_cnt(), 1u);
+}
+
+TEST_F("require that all ip address results are treated equally (including empty ones)", ResolveFixture(0)) {
+ auto addr = f1.make("tcp/localhost:123");
+ f1.set_ip_addr("localhost", "");
+ EXPECT_EQUAL(addr->resolve(), "tcp/127.0.0.1:123");
+ f1.resolver->wait_for_pending_updates();
+ f1.set_ip_addr("localhost", "127.0.0.2");
+ EXPECT_EQUAL(addr->resolve(), "invalid");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(addr->resolve(), "tcp/127.0.0.2:123");
+ f1.resolver->wait_for_pending_updates();
+ EXPECT_EQUAL(f1.get_cnt("localhost"), 4u);
+ EXPECT_EQUAL(f1.get_total_cnt(), 4u);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp b/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp
index 8e2e2f0e8b7..0c2b92bbb53 100644
--- a/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp
+++ b/vespalib/src/tests/net/socket_spec/socket_spec_test.cpp
@@ -107,4 +107,20 @@ TEST("require that port-only spec resolves to non-wildcard client address") {
EXPECT_TRUE(!SocketSpec("tcp/123").client_address().is_wildcard());
}
+TEST("require that replace_host makes new spec with replaced host") {
+ SocketSpec old_spec("tcp/host:123");
+ const SocketSpec &const_spec = old_spec;
+ SocketSpec new_spec = const_spec.replace_host("foo");
+ TEST_DO(verify_host_port(old_spec, "host", 123));
+ TEST_DO(verify_host_port(new_spec, "foo", 123));
+}
+
+TEST("require that replace_host gives invalid spec when used with less than 2 host names") {
+ TEST_DO(verify_invalid(SocketSpec("bogus").replace_host("foo")));
+ TEST_DO(verify_invalid(SocketSpec("tcp/123").replace_host("foo")));
+ TEST_DO(verify_invalid(SocketSpec("tcp/host:123").replace_host("")));
+ TEST_DO(verify_invalid(SocketSpec("ipc/file:my_socket").replace_host("foo")));
+ TEST_DO(verify_invalid(SocketSpec("ipc/name:my_socket").replace_host("foo")));
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/net/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/CMakeLists.txt
index ce9a4c87661..26c5c867631 100644
--- a/vespalib/src/vespa/vespalib/net/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/net/CMakeLists.txt
@@ -1,6 +1,7 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
vespa_add_library(vespalib_vespalib_net OBJECT
SOURCES
+ lazy_resolver.cpp
selector.cpp
server_socket.cpp
socket.cpp
diff --git a/vespalib/src/vespa/vespalib/net/lazy_resolver.cpp b/vespalib/src/vespa/vespalib/net/lazy_resolver.cpp
new file mode 100644
index 00000000000..ef2b44958c9
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/lazy_resolver.cpp
@@ -0,0 +1,205 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "lazy_resolver.h"
+#include "socket_spec.h"
+
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.net.lazy_resolver");
+
+namespace vespalib {
+
+VESPA_THREAD_STACK_TAG(lazy_resolver_executor_thread);
+
+LazyResolver::Params::Params()
+ : resolve_host(default_resolve_host),
+ max_result_age(seconds(300.0)),
+ max_resolve_time(seconds(1.0))
+{
+}
+
+//-----------------------------------------------------------------------------
+
+LazyResolver::Host::Host(const vespalib::string &host_name, LazyResolver::SP resolver,
+ const vespalib::string &ip_address)
+ : _host_name(host_name),
+ _resolver(std::move(resolver)),
+ _ip_lock(),
+ _ip_pending(false),
+ _ip_address(ip_address),
+ _ip_updated(clock::now())
+{
+}
+
+void
+LazyResolver::Host::update_ip_address(const vespalib::string &ip_address)
+{
+ std::lock_guard<std::mutex> guard(_ip_lock);
+ _ip_pending = false;
+ _ip_address = ip_address;
+ _ip_updated = clock::now();
+}
+
+LazyResolver::Host::~Host()
+{
+ // clean up weak_ptr to this
+ _resolver->try_lookup_host(_host_name);
+}
+
+vespalib::string
+LazyResolver::Host::resolve()
+{
+ std::lock_guard<std::mutex> guard(_ip_lock);
+ if (!_ip_pending && _resolver->should_request_update(_ip_updated)) {
+ // TODO(havardpe): switch to weak_from_this() when available
+ _ip_pending = _resolver->try_request_update(shared_from_this());
+ }
+ return _ip_address;
+}
+
+//-----------------------------------------------------------------------------
+
+vespalib::string
+LazyResolver::Address::resolve()
+{
+ if (_host) {
+ return SocketSpec(_spec).replace_host(_host->resolve()).spec();
+ }
+ return _spec;
+}
+
+//-----------------------------------------------------------------------------
+
+void
+LazyResolver::UpdateTask::run()
+{
+ if (Host::SP host = weak_host.lock()) {
+ host->update_ip_address(resolver.resolve_host_now(host->host_name()));
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+LazyResolver::LazyResolver(Params params)
+ : _host_lock(),
+ _host_map(),
+ _params(std::move(params)),
+ _executor(1, 128*1024, lazy_resolver_executor_thread, 4096)
+{
+}
+
+LazyResolver::Host::SP
+LazyResolver::try_lookup_host(const vespalib::string &host_name,
+ const std::lock_guard<std::mutex> &guard)
+{
+ (void) guard;
+ auto pos = _host_map.find(host_name);
+ if (pos != _host_map.end()) {
+ Host::SP host = pos->second.lock();
+ if (host) {
+ return host;
+ } else {
+ _host_map.erase(pos);
+ }
+ }
+ return Host::SP(nullptr);
+}
+
+LazyResolver::Host::SP
+LazyResolver::try_lookup_host(const vespalib::string &host_name)
+{
+ std::lock_guard<std::mutex> guard(_host_lock);
+ return try_lookup_host(host_name, guard);
+}
+
+LazyResolver::Host::SP
+LazyResolver::insert_host(const vespalib::string &host_name, const vespalib::string &ip_address)
+{
+ std::lock_guard<std::mutex> guard(_host_lock);
+ Host::SP host = try_lookup_host(host_name, guard);
+ if (!host) {
+ host.reset(new Host(host_name, shared_from_this(), ip_address));
+ _host_map.emplace(host_name, host);
+ }
+ return host;
+}
+
+vespalib::string
+LazyResolver::resolve_host_now(const vespalib::string &host_name)
+{
+ auto before = clock::now();
+ vespalib::string ip_address = _params.resolve_host(host_name);
+ seconds resolve_time = (clock::now() - before);
+ if (resolve_time >= _params.max_resolve_time) {
+ LOG(warning, "slow resolve time: '%s' -> '%s' (%g s)",
+ host_name.c_str(), ip_address.c_str(), resolve_time.count());
+ }
+ if (ip_address.empty()) {
+ LOG(warning, "could not resolve host name: '%s'", host_name.c_str());
+ }
+ return ip_address;
+}
+
+bool
+LazyResolver::should_request_update(clock::time_point ip_updated)
+{
+ seconds result_age = (clock::now() - ip_updated);
+ return (result_age >= _params.max_result_age);
+}
+
+bool
+LazyResolver::try_request_update(std::weak_ptr<Host> self)
+{
+ Executor::Task::UP task(new UpdateTask(*this, std::move(self)));
+ auto rejected = _executor.execute(std::move(task));
+ return !rejected;
+}
+
+//-----------------------------------------------------------------------------
+
+LazyResolver::~LazyResolver()
+{
+ _executor.shutdown().sync();
+}
+
+LazyResolver::Host::SP
+LazyResolver::make_host(const vespalib::string &host_name)
+{
+ if (host_name.empty()) {
+ return Host::SP(nullptr);
+ }
+ Host::SP host = try_lookup_host(host_name);
+ if (host) {
+ return host;
+ }
+ vespalib::string ip_address = resolve_host_now(host_name);
+ if (ip_address == host_name) {
+ return Host::SP(nullptr);
+ }
+ return insert_host(host_name, ip_address);
+}
+
+LazyResolver::Address::SP
+LazyResolver::make_address(const vespalib::string &spec_str)
+{
+ SocketSpec spec(spec_str);
+ if (!spec.valid()) {
+ LOG(warning, "invalid socket spec: '%s'\n", spec_str.c_str());
+ }
+ return Address::SP(new Address(spec_str, make_host(spec.host())));
+}
+
+//-----------------------------------------------------------------------------
+
+vespalib::string
+LazyResolver::default_resolve_host(const vespalib::string &host_name)
+{
+ return SocketAddress::select_remote(80, host_name.c_str()).ip_address();
+}
+
+std::shared_ptr<LazyResolver>
+LazyResolver::create(Params params)
+{
+ return std::shared_ptr<LazyResolver>(new LazyResolver(std::move(params)));
+}
+
+} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/net/lazy_resolver.h b/vespalib/src/vespa/vespalib/net/lazy_resolver.h
new file mode 100644
index 00000000000..e740f3de463
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/lazy_resolver.h
@@ -0,0 +1,108 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "socket_address.h"
+#include "socket_spec.h"
+#include <vespa/vespalib/util/threadstackexecutor.h>
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <map>
+
+namespace vespalib {
+
+/**
+ * Component used to perform lazy re-resolving of host names. The goal
+ * of this class is to allow applications to (re-)connect from within
+ * a network thread without stalling everything due to slow dns
+ * responses while still being able to pick up on dns changes
+ * (eventually). The idea is that the make_address function is called
+ * up front during configuration from a non-critical thread. It will
+ * (potentially) perform an initial synchronous resolve and return an
+ * Address object that can later be used to obtain a string-based
+ * connect spec where any host names have been replaced by ip
+ * addresses without blocking. The host names are resolved under the
+ * assumption that they will be used to connect to a remote server.
+ **/
+class LazyResolver : public std::enable_shared_from_this<LazyResolver>
+{
+public:
+ using resolve_host_t = std::function<vespalib::string(const vespalib::string &)>;
+ using clock = std::chrono::steady_clock;
+ using seconds = std::chrono::duration<double>;
+ using SP = std::shared_ptr<LazyResolver>;
+
+ struct Params
+ {
+ resolve_host_t resolve_host;
+ seconds max_result_age;
+ seconds max_resolve_time;
+ Params();
+ };
+
+ class Host : public std::enable_shared_from_this<Host>
+ {
+ private:
+ friend class LazyResolver;
+ vespalib::string _host_name;
+ LazyResolver::SP _resolver;
+ std::mutex _ip_lock;
+ bool _ip_pending;
+ vespalib::string _ip_address;
+ clock::time_point _ip_updated;
+ Host(const vespalib::string &host_name, LazyResolver::SP resolver,
+ const vespalib::string &ip_address);
+ void update_ip_address(const vespalib::string &ip_address);
+ public:
+ ~Host();
+ using SP = std::shared_ptr<Host>;
+ const vespalib::string &host_name() const { return _host_name; }
+ vespalib::string resolve();
+ };
+
+ class Address
+ {
+ private:
+ friend class LazyResolver;
+ vespalib::string _spec;
+ Host::SP _host;
+ Address(const vespalib::string &spec, Host::SP host)
+ : _spec(spec), _host(std::move(host)) {}
+ public:
+ using SP = std::shared_ptr<Address>;
+ const vespalib::string &spec() const { return _spec; }
+ vespalib::string resolve();
+ };
+
+private:
+ struct UpdateTask : Executor::Task {
+ LazyResolver &resolver;
+ std::weak_ptr<Host> weak_host;
+ UpdateTask(LazyResolver &resolver_in, std::weak_ptr<Host> weak_host_in)
+ : resolver(resolver_in), weak_host(std::move(weak_host_in)) {}
+ void run() override;
+ };
+
+ std::mutex _host_lock;
+ std::map<vespalib::string, std::weak_ptr<Host> > _host_map;
+ Params _params;
+ ThreadStackExecutor _executor;
+ LazyResolver(Params params);
+ Host::SP try_lookup_host(const vespalib::string &host_name,
+ const std::lock_guard<std::mutex> &guard);
+ Host::SP try_lookup_host(const vespalib::string &host_name);
+ Host::SP insert_host(const vespalib::string &host_name, const vespalib::string &ip_address);
+ vespalib::string resolve_host_now(const vespalib::string &host_name);
+ bool should_request_update(clock::time_point ip_updated);
+ bool try_request_update(std::weak_ptr<Host> self);
+public:
+ ~LazyResolver();
+ void wait_for_pending_updates() { _executor.sync(); }
+ Host::SP make_host(const vespalib::string &host_name);
+ Address::SP make_address(const vespalib::string &spec);
+ static vespalib::string default_resolve_host(const vespalib::string &host_name);
+ static SP create(Params params = Params());
+};
+
+} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/net/socket_spec.cpp b/vespalib/src/vespa/vespalib/net/socket_spec.cpp
index 7870dd435f8..d1376ce1dd7 100644
--- a/vespalib/src/vespa/vespalib/net/socket_spec.cpp
+++ b/vespalib/src/vespa/vespalib/net/socket_spec.cpp
@@ -99,4 +99,13 @@ SocketSpec::spec() const
return "invalid";
}
+SocketSpec
+SocketSpec::replace_host(const vespalib::string &new_host) const
+{
+ if ((_type == Type::HOST_PORT) && !new_host.empty()) {
+ return from_host_port(new_host, _port);
+ }
+ return SocketSpec();
+}
+
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/net/socket_spec.h b/vespalib/src/vespa/vespalib/net/socket_spec.h
index 13761387450..f28b14573ac 100644
--- a/vespalib/src/vespa/vespalib/net/socket_spec.h
+++ b/vespalib/src/vespa/vespalib/net/socket_spec.h
@@ -26,6 +26,7 @@ private:
public:
explicit SocketSpec(const vespalib::string &spec);
vespalib::string spec() const;
+ SocketSpec replace_host(const vespalib::string &new_host) const;
static SocketSpec from_path(const vespalib::string &path) {
return SocketSpec(Type::PATH, path, -1);
}