@@ -11,13 +11,170 @@ See the License for the specific language governing permissions and
1111limitations under the License.
1212**************************************************************************/
1313
14+ #include < absl/strings/str_split.h>
15+ #include < boost/property_tree/ptree.hpp>
16+ #include < boost/property_tree/json_parser.hpp>
1417#include < spdlog/spdlog.h>
1518
1619#include " AppInfo.h"
1720
1821namespace gringofts ::app {
1922
20- void AppInfo::init (const INIReader &reader) {
23+ bool AppClusterParser::checkHasRoute (const std::string &routeStr, uint64_t clusterId, uint64_t epoch) {
24+ using boost::property_tree::ptree;
25+ using boost::property_tree::read_json;
26+ using boost::property_tree::write_json;
27+ using boost::property_tree::json_parser_error;
28+ std::stringstream ss (routeStr);
29+ ptree globalRoute;
30+ try {
31+ read_json (ss, globalRoute);
32+ auto routeEpoch = std::stoi (globalRoute.get_child (" epoch" ).data ());
33+ if (routeEpoch < epoch) {
34+ SPDLOG_WARN (" global epoch {} is less than local epoch {}" , routeEpoch, epoch);
35+ }
36+ auto infos = globalRoute.get_child (" routeInfos" );
37+ for (auto &[k, v] : infos) {
38+ auto clusterNode = v.get_child (" clusterId" );
39+ auto id = std::stoi (clusterNode.data ());
40+ if (clusterId == id) {
41+ std::stringstream sout;
42+ write_json (sout, v);
43+ SPDLOG_INFO (" find route for cluster {} : {}" , clusterId, sout.str ());
44+ return true ;
45+ }
46+ }
47+ return false ;
48+ } catch (const json_parser_error &err) {
49+ SPDLOG_ERROR (" error when parse json {} for {}" , routeStr, err.message ());
50+ return false ;
51+ } catch (const std::exception &err) {
52+ SPDLOG_ERROR (" error when parse json {} for {}" , routeStr, err.what ());
53+ return false ;
54+ }
55+ }
56+
57+ std::tuple<NodeId, ClusterId, ClusterParser::ClusterMap> AppClusterParser::parse (const INIReader &iniReader) {
58+ std::string storeType = iniReader.Get (" cluster" , " persistence.type" , " UNKNOWN" );
59+ assert (storeType == " raft" );
60+ bool externalEnabled = iniReader.GetBoolean (" raft.external" , " enable" , false );
61+ if (!externalEnabled) {
62+ // / load from local config, the cluster id and node id must be specified
63+ auto clusterConf = iniReader.Get (" cluster" , " cluster.conf" , " " );
64+ auto allClusterInfo = parseToClusterInfo (clusterConf);
65+ auto myClusterId = iniReader.GetInteger (" cluster" , " self.clusterId" , 0 );
66+ auto myNodeId = iniReader.GetInteger (" cluster" , " self.nodeId" , 0 );
67+ bool hasMe = false ;
68+ for (auto &[clusterId, info] : allClusterInfo) {
69+ if (myClusterId == clusterId) {
70+ for (auto &[nodeId, node] : info.getAllNodes ()) {
71+ if (nodeId == myNodeId) {
72+ hasMe = true ;
73+ break ;
74+ }
75+ }
76+ }
77+ }
78+ assert (hasMe);
79+
80+ Signal::hub.handle <RouteSignal>([](const Signal &s) {
81+ const auto &signal = dynamic_cast <const RouteSignal &>(s);
82+ SPDLOG_WARN (" for non-external controlled cluster direct start raft" );
83+ signal.passValue (true );
84+ });
85+
86+ SPDLOG_INFO (" read raft cluster conf from local, "
87+ " cluster.conf={}, self.clusterId={}, self.nodeId={}" ,
88+ clusterConf, myClusterId, myNodeId);
89+ return {myClusterId, myNodeId, allClusterInfo};
90+ } else {
91+ // if enable external kv store for cluster info, it must have kv factory
92+ assert (mKvFactory );
93+ std::string externalConfigFile = iniReader.Get (" raft.external" , " config.file" , " " );
94+ std::string clusterConfKey = iniReader.Get (" raft.external" , " cluster.conf.key" , " " );
95+ std::string clusterRouteKey = iniReader.Get (" raft.external" , " cluster.route.key" , " " );
96+ assert (!externalConfigFile.empty ());
97+ assert (!clusterConfKey.empty ());
98+
99+ // / init external client
100+ auto client = mKvFactory ->produce (INIReader (externalConfigFile));
101+ // / read raft cluster conf from external
102+ std::string clusterConf;
103+ auto r = client->getValue (clusterConfKey, &clusterConf);
104+ assert (!clusterConfKey.empty ());
105+ auto allClusterInfo = parseToClusterInfo (clusterConf);
106+ // / N.B.: when using external, the assumption is two nodes will never run on the same host,
107+ // / otherwise below logic will break.
108+ auto myHostname = Util::getHostname ();
109+ std::optional<ClusterId> myClusterId = std::nullopt ;
110+ std::optional<NodeId> myNodeId = std::nullopt ;
111+ for (auto &[clusterId, info] : allClusterInfo) {
112+ for (auto &[nodeId, node] : info.getAllNodes ()) {
113+ if (myHostname == node->hostName ()) {
114+ myClusterId = clusterId;
115+ myNodeId = nodeId;
116+ break ;
117+ }
118+ }
119+ }
120+ assert (myClusterId);
121+ assert (myNodeId);
122+
123+ SPDLOG_INFO (" raft cluster conf passed from external, "
124+ " cluster.conf={}, hostname={}" , clusterConf, myHostname);
125+ auto clusterId = *myClusterId;
126+
127+ Signal::hub.handle <RouteSignal>([client, clusterRouteKey, clusterId](const Signal &s) {
128+ const auto &signal = dynamic_cast <const RouteSignal &>(s);
129+ std::string val;
130+ SPDLOG_INFO (" receive signal for query route, cluster {}, epoch {}" , clusterId, signal.mEpoch );
131+ assert (clusterId == signal.mClusterId );
132+ assert (!clusterRouteKey.empty ());
133+ client->getValue (clusterRouteKey, &val);
134+ signal.passValue (checkHasRoute (val, clusterId, signal.mEpoch ));
135+ });
136+
137+ return {*myClusterId, *myNodeId, allClusterInfo};
138+ }
139+ }
140+
141+ std::unordered_map<ClusterId, Cluster> AppClusterParser::parseToClusterInfo (const std::string &infoStr) const {
142+ std::unordered_map<ClusterId, Cluster> result;
143+ std::vector<std::string> clusters = absl::StrSplit (infoStr, " ;" );
144+ for (auto &c : clusters) {
145+ Cluster info;
146+ std::pair<std::string, std::string> clusterIdWithNodes = absl::StrSplit (c, " #" );
147+ info.setClusterId (std::stoi (clusterIdWithNodes.first ));
148+ std::vector<std::string> nodes = absl::StrSplit (clusterIdWithNodes.second , " ," );
149+ for (auto &n : nodes) {
150+ std::pair<std::string, std::string> hostWithPort = absl::StrSplit (n, " :" );
151+ std::pair<std::string, std::string> idWithHost = absl::StrSplit (hostWithPort.first , " @" );
152+ auto nodeId = std::stoi (idWithHost.first );
153+ auto hostname = idWithHost.second ;
154+ std::shared_ptr<Node> node;
155+ if (hostWithPort.second .empty ()) {
156+ SPDLOG_INFO (" {} no specific port, using default one" , hostWithPort.second );
157+ node = std::make_shared<AppNode>(nodeId, hostname);
158+ } else {
159+ std::vector<std::string> ports = absl::StrSplit (hostWithPort.second , " |" );
160+ assert (ports.size () == 6 );
161+ auto portForRaft = std::stoi (ports[0 ]);
162+ auto portForGateway = std::stoi (ports[1 ]);
163+ auto portForDumper = std::stoi (ports[2 ]);
164+ auto portForStream = std::stoi (ports[3 ]);
165+ auto portForNetAdmin = std::stoi (ports[4 ]);
166+ auto portForScale = std::stoi (ports[5 ]);
167+ node = std::make_shared<AppNode>(nodeId, hostname, portForRaft, portForStream,
168+ portForGateway, portForDumper, portForNetAdmin, portForScale);
169+ }
170+ info.addNode (node);
171+ }
172+ result[info.id ()] = info;
173+ }
174+ return result;
175+ }
176+
177+ void AppInfo::init (const INIReader &reader, std::unique_ptr<ClusterParser> parser) {
21178 auto &appInfo = getInstance ();
22179
23180 auto &initialized = appInfo.initialized ;
@@ -28,7 +185,7 @@ void AppInfo::init(const INIReader &reader) {
28185 return ;
29186 }
30187
31- auto [myClusterId, myNodeId, allClusterInfo] = ClusterInfo::resolveAllClusters (reader, nullptr );
188+ auto [myClusterId, myNodeId, allClusterInfo] = parser-> parse (reader);
32189 appInfo.mMyClusterId = myClusterId;
33190 appInfo.mMyNodeId = myNodeId;
34191 appInfo.mAllClusterInfo = allClusterInfo;
@@ -54,4 +211,5 @@ void AppInfo::init(const INIReader &reader) {
54211 appInfo.mMyClusterId ,
55212 appInfo.mMyNodeId );
56213}
214+
57215} // / namespace gringofts::app
0 commit comments