Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #include <hpactor/net/hybrid_discovery.hpp>
16 : :
17 : : #include <unordered_map>
18 : :
19 : : namespace hpactor::net {
20 : :
21 : 5 : HybridDiscovery::HybridDiscovery(const RegistrarConfig& reg_cfg,
22 : : const GossipConfig& gossip_cfg,
23 : 5 : EndPoint local_ep, EventLoop* loop)
24 : 5 : : registrar_(reg_cfg, local_ep, loop), gossip_(gossip_cfg, loop),
25 : 10 : local_ep_(local_ep) {}
26 : :
27 : 5 : HybridDiscovery::~HybridDiscovery() {
28 : 5 : stop();
29 : 5 : }
30 : :
31 : 0 : void HybridDiscovery::start() {
32 : 0 : registrar_.start(); // first: determines server/client mode
33 : 0 : gossip_.start(); // second: cross-host gossip
34 : 0 : }
35 : :
36 : 5 : void HybridDiscovery::stop() {
37 : 5 : gossip_.stop(); // first: graceful Leave to peers
38 : 5 : registrar_.stop(); // second: close registrar sockets
39 : 5 : }
40 : :
41 : 1 : std::vector<Member> HybridDiscovery::discover_all() const {
42 : 1 : auto local = registrar_.discover_all();
43 : 1 : auto remote = gossip_.discover_all();
44 : : // Same-host entries take precedence on collision
45 : 1 : std::unordered_map<EndPoint, Member> merged;
46 : 1 : for (auto& m : remote)
47 : 0 : merged[m.identity.endpoint] = std::move(m);
48 : 1 : for (auto& m : local)
49 : 0 : merged[m.identity.endpoint] = std::move(m);
50 : 1 : std::vector<Member> result;
51 : 1 : result.reserve(merged.size());
52 : 1 : for (auto& [_, m] : merged)
53 : 0 : result.push_back(std::move(m));
54 : 1 : return result;
55 : 1 : }
56 : :
57 : 1 : const Member* HybridDiscovery::discover(EndPoint ep) const {
58 : 1 : auto* local = registrar_.discover(ep);
59 : 1 : if (local)
60 : 0 : return local;
61 : 1 : return gossip_.discover(ep);
62 : : }
63 : :
64 : 0 : void HybridDiscovery::announce(Member m) {
65 : : // Delegate to registrar if local endpoint
66 : 0 : if (m.identity.endpoint == local_ep_ || m.identity.host == "127.0.0.1") {
67 : 0 : registrar_.announce(m);
68 : : }
69 : : // Always propagate via gossip for cross-host visibility
70 : 0 : gossip_.announce(std::move(m));
71 : 0 : }
72 : :
73 : 0 : void HybridDiscovery::on_member_change(MemberChangeCallback cb) {
74 : 0 : user_callback_ = std::move(cb);
75 : : // Cross-host events forwarded directly
76 : 0 : gossip_.on_member_change(user_callback_);
77 : : // Same-host events piped through on_local_member_change
78 : 0 : registrar_.on_member_change([this](const Member& m, bool joined) {
79 : 0 : on_local_member_change(m, joined);
80 : 0 : });
81 : 0 : }
82 : :
83 : 0 : void HybridDiscovery::on_local_member_change(const Member& m, bool joined) {
84 : : // Push local changes into gossip layer for cross-host visibility
85 : 0 : gossip_.announce(m);
86 : 0 : if (user_callback_)
87 : 0 : user_callback_(m, joined);
88 : 0 : }
89 : :
90 : : } // namespace hpactor::net
|