Mads
Multi-Agent Distributed System
Loading...
Searching...
No Matches
socket_options.hpp
Go to the documentation of this file.
1/*
2Internal helper: resolves plain libzmq transport-tuning knobs from settings
3and applies them to a socket. Not part of the installed SDK (src/detail/ is
4excluded from the LIB_HEADERS install glob in CMakeLists.txt).
5
6See ZMQ_DEVELOPMENT.md §1.4.
7*/
8#pragma once
9
10#include <optional>
11#include <string_view>
12#include <toml++/toml.hpp>
13#include <zmq.hpp>
14
15namespace Mads::detail {
16
17// Resolves `key` the same way Agent::init() resolves wire_format/compression
18// (src/agent.cpp:452-469): the agent's own section wins over the fleet-wide
19// [agents] table, which wins over "not configured" (nullopt). A nullopt means
20// "leave the libzmq default untouched" -- callers must not fall back to a
21// hardcoded default themselves, or an unconfigured deployment would start
22// issuing setsockopt() calls it never asked for.
23template <typename T>
24std::optional<T> resolve_setting(toml::node_view<toml::node> fleet_cfg,
25 toml::node_view<toml::node> agent_cfg,
26 std::string_view key) {
27 if (auto v = agent_cfg[key].value<T>()) return v;
28 if (auto v = fleet_cfg[key].value<T>()) return v;
29 return std::nullopt;
30}
31
32// Plain transport-tuning knobs, none of which touch the wire format
33// (ZMQ_DEVELOPMENT.md §1.4, §3.2-§3.5). Every field defaults to
34// "unconfigured", which means apply() makes no setsockopt() call at all and
35// the socket keeps libzmq's own default -- so an unedited mads.ini produces
36// byte-identical behaviour to before this struct existed.
38 std::optional<int> tcp_keepalive;
39 std::optional<int> tcp_keepalive_idle;
40 std::optional<int> tcp_keepalive_cnt;
41 std::optional<int> tcp_keepalive_intvl;
42 std::optional<int> sndbuf;
43 std::optional<int> rcvbuf;
44 // ZMTP heartbeats (§3.2). IVL/TIMEOUT are milliseconds; TTL is in units of
45 // 100ms (libzmq caps it at 6553.5s). Leaving heartbeat_ivl unset keeps
46 // heartbeats off, exactly as today.
47 std::optional<int> heartbeat_ivl;
48 std::optional<int> heartbeat_ttl;
49 std::optional<int> heartbeat_timeout;
50 // Reconnect backoff (§3.3).
51 std::optional<int> reconnect_ivl;
52 std::optional<int> reconnect_ivl_max;
53 // Refuse to queue toward a not-yet-connected peer instead of buffering
54 // into a pipe that may never drain (§3.4).
55 std::optional<bool> immediate;
56 // Rejects oversized frames at the transport by disconnecting the peer,
57 // rather than silently allocating for them (§3.5). int64_t, NOT 0 for
58 // "unlimited" -- libzmq's own sentinel is -1.
59 std::optional<int64_t> max_msg_size;
60
61 static SocketOptions resolve(toml::node_view<toml::node> fleet_cfg,
62 toml::node_view<toml::node> agent_cfg) {
63 SocketOptions opts;
64 opts.tcp_keepalive =
65 resolve_setting<int>(fleet_cfg, agent_cfg, "tcp_keepalive");
67 resolve_setting<int>(fleet_cfg, agent_cfg, "tcp_keepalive_idle");
69 resolve_setting<int>(fleet_cfg, agent_cfg, "tcp_keepalive_cnt");
71 resolve_setting<int>(fleet_cfg, agent_cfg, "tcp_keepalive_intvl");
72 opts.sndbuf = resolve_setting<int>(fleet_cfg, agent_cfg, "sndbuf");
73 opts.rcvbuf = resolve_setting<int>(fleet_cfg, agent_cfg, "rcvbuf");
74 opts.heartbeat_ivl =
75 resolve_setting<int>(fleet_cfg, agent_cfg, "heartbeat_ivl");
76 opts.heartbeat_ttl =
77 resolve_setting<int>(fleet_cfg, agent_cfg, "heartbeat_ttl");
79 resolve_setting<int>(fleet_cfg, agent_cfg, "heartbeat_timeout");
80 opts.reconnect_ivl =
81 resolve_setting<int>(fleet_cfg, agent_cfg, "reconnect_ivl");
83 resolve_setting<int>(fleet_cfg, agent_cfg, "reconnect_ivl_max");
84 opts.immediate = resolve_setting<bool>(fleet_cfg, agent_cfg, "immediate");
85 opts.max_msg_size =
86 resolve_setting<int64_t>(fleet_cfg, agent_cfg, "max_msg_size");
87 return opts;
88 }
89
90 void apply(zmq::socket_t &socket) const {
91 if (tcp_keepalive)
92 socket.set(zmq::sockopt::tcp_keepalive, *tcp_keepalive);
94 socket.set(zmq::sockopt::tcp_keepalive_idle, *tcp_keepalive_idle);
96 socket.set(zmq::sockopt::tcp_keepalive_cnt, *tcp_keepalive_cnt);
98 socket.set(zmq::sockopt::tcp_keepalive_intvl, *tcp_keepalive_intvl);
99 if (sndbuf) socket.set(zmq::sockopt::sndbuf, *sndbuf);
100 if (rcvbuf) socket.set(zmq::sockopt::rcvbuf, *rcvbuf);
101 if (heartbeat_ivl)
102 socket.set(zmq::sockopt::heartbeat_ivl, *heartbeat_ivl);
103 if (heartbeat_ttl)
104 socket.set(zmq::sockopt::heartbeat_ttl, *heartbeat_ttl);
106 socket.set(zmq::sockopt::heartbeat_timeout, *heartbeat_timeout);
107 if (reconnect_ivl)
108 socket.set(zmq::sockopt::reconnect_ivl, *reconnect_ivl);
110 socket.set(zmq::sockopt::reconnect_ivl_max, *reconnect_ivl_max);
111 if (immediate)
112 socket.set(zmq::sockopt::immediate, *immediate);
113 if (max_msg_size)
114 socket.set(zmq::sockopt::maxmsgsize, *max_msg_size);
115 }
116};
117
118} // namespace Mads::detail
std::optional< T > resolve_setting(toml::node_view< toml::node > fleet_cfg, toml::node_view< toml::node > agent_cfg, std::string_view key)
void apply(zmq::socket_t &socket) const
std::optional< int > tcp_keepalive_intvl
std::optional< int > heartbeat_timeout
std::optional< bool > immediate
static SocketOptions resolve(toml::node_view< toml::node > fleet_cfg, toml::node_view< toml::node > agent_cfg)
std::optional< int > rcvbuf
std::optional< int > reconnect_ivl_max
std::optional< int > sndbuf
std::optional< int64_t > max_msg_size
std::optional< int > heartbeat_ivl
std::optional< int > tcp_keepalive_cnt
std::optional< int > tcp_keepalive_idle
std::optional< int > reconnect_ivl
std::optional< int > tcp_keepalive
std::optional< int > heartbeat_ttl