1 /* This file is part of Metaproxy.
2 Copyright (C) 2005-2009 Index Data
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "session.hpp"
21 #include "package.hpp"
23 #include "filter_load_balance.hpp"
27 #include <boost/thread/mutex.hpp>
29 #include <yaz/diagbib1.h>
33 // remove max macro if already defined (defined later in <limits>)
43 namespace mp = metaproxy_1;
44 namespace yf = mp::filter;
50 class LoadBalance::Impl
55 void process(metaproxy_1::Package & package);
56 void configure(const xmlNode * ptr);
58 // statistic manipulating functions,
59 void add_dead(unsigned long session_id);
60 //void clear_dead(unsigned long session_id);
61 void add_package(unsigned long session_id);
62 void remove_package(unsigned long session_id);
63 void add_session(unsigned long session_id, std::string target);
64 void remove_session(unsigned long session_id);
65 std::string find_session_target(unsigned long session_id);
68 unsigned int cost(std::string target);
69 unsigned int dead(std::string target);
74 unsigned int sessions;
75 unsigned int packages;
78 unsigned int c = sessions + packages + deads;
79 //std::cout << "stats c:" << c
80 // << " s:" << sessions
81 // << " p:" << packages
88 // local protected databases
90 std::map<std::string, TargetStat> m_target_stat;
91 std::map<unsigned long, std::string> m_session_target;
96 // define Pimpl wrapper forwarding to Impl
98 yf::LoadBalance::LoadBalance() : m_p(new Impl)
102 yf::LoadBalance::~LoadBalance()
103 { // must have a destructor because of boost::scoped_ptr
106 void yf::LoadBalance::configure(const xmlNode *xmlnode, bool test_only)
108 m_p->configure(xmlnode);
111 void yf::LoadBalance::process(mp::Package &package) const
113 m_p->process(package);
117 yf::LoadBalance::Impl::Impl()
121 yf::LoadBalance::Impl::~Impl()
125 void yf::LoadBalance::Impl::configure(const xmlNode *xmlnode)
129 void yf::LoadBalance::Impl::process(mp::Package &package)
131 bool is_closed_front = false;
133 // checking for closed front end packages
134 if (package.session().is_closed())
136 is_closed_front = true;
139 Z_GDU *gdu_req = package.request().get();
141 // passing anything but z3950 packages
142 if (gdu_req && gdu_req->which == Z_GDU_Z3950)
144 // target selecting only on Z39.50 init request
145 if (gdu_req->u.z3950->which == Z_APDU_initRequest)
147 yazpp_1::GDU base_req(gdu_req);
148 Z_APDU *apdu = base_req.get()->u.z3950;
150 Z_InitRequest *org_init = base_req.get()->u.z3950->u.initRequest;
151 mp::odr odr_en(ODR_ENCODE);
153 std::list<std::string> vhosts;
154 mp::util::remove_vhost_otherinfo(&(org_init->otherInfo), vhosts);
155 // get lowest of all vhosts.. Remove them if individually if
156 // they turn out to be bad..
160 std::list<std::string>::iterator ivh = vhosts.begin();
161 unsigned int cost = std::numeric_limits<unsigned int>::max();
163 boost::mutex::scoped_lock scoped_lock(m_mutex);
165 for (; ivh != vhosts.end(); )
167 if ((*ivh).size() != 0)
170 = yf::LoadBalance::Impl::cost(*ivh);
171 yaz_log(YLOG_LOG, "Consider %s cost=%u vhcost=%u",
172 (*ivh).c_str(), cost, vhcost);
177 ivh = vhosts.erase(ivh);
186 if (target.length() == 0)
188 // copying new target into init package
190 yazpp_1::GDU init_gdu(base_req);
191 Z_InitRequest *init_req = init_gdu.get()->u.z3950->u.initRequest;
193 mp::util::set_vhost_otherinfo(&(init_req->otherInfo),
196 package.request() = init_gdu;
198 // moving all package types
201 // checking for closed back end packages
202 if (!package.session().is_closed())
204 add_session(package.session().id(), target);
207 yaz_log(YLOG_LOG, "Other round..");
210 package.response() = odr.create_initResponse(
211 apdu, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
212 "load_balance: no available targets");
213 package.session().close();
216 // frontend Z39.50 close request is added to statistics and marked
217 else if (gdu_req->u.z3950->which == Z_APDU_close)
219 is_closed_front = true;
220 boost::mutex::scoped_lock scoped_lock(m_mutex);
221 add_package(package.session().id());
223 // any other Z39.50 package is added to statistics
226 boost::mutex::scoped_lock scoped_lock(m_mutex);
227 add_package(package.session().id());
231 // moving all package types
234 bool is_closed_back = false;
236 // checking for closed back end packages
237 if (package.session().is_closed())
238 is_closed_back = true;
240 Z_GDU *gdu_res = package.response().get();
242 // passing anything but z3950 packages
243 if (gdu_res && gdu_res->which == Z_GDU_Z3950)
245 // session closing only on Z39.50 close response
246 if (gdu_res->u.z3950->which == Z_APDU_close)
248 is_closed_back = true;
249 boost::mutex::scoped_lock scoped_lock(m_mutex);
250 remove_package(package.session().id());
252 // any other Z39.50 package is removed from statistics
255 boost::mutex::scoped_lock scoped_lock(m_mutex);
256 remove_package(package.session().id());
260 // finally removing sessions and marking deads
261 if (is_closed_back || is_closed_front)
263 boost::mutex::scoped_lock scoped_lock(m_mutex);
265 // marking backend dead if backend closed without fronted close
266 if (is_closed_front == false)
267 add_dead(package.session().id());
269 remove_session(package.session().id());
271 // making sure that package is closed
272 package.session().close();
276 // statistic manipulating functions,
277 void yf::LoadBalance::Impl::add_dead(unsigned long session_id)
279 std::string target = find_session_target(session_id);
281 if (target.size() != 0)
283 std::map<std::string, TargetStat>::iterator itarg;
284 itarg = m_target_stat.find(target);
285 if (itarg != m_target_stat.end()
286 && itarg->second.deads < std::numeric_limits<unsigned int>::max())
288 itarg->second.deads += 1;
289 // std:.cout << "add_dead " << session_id << " " << target
290 // << " d:" << itarg->second.deads << "\n";
295 //void yf::LoadBalance::Impl::clear_dead(unsigned long session_id){
296 // std::cout << "clear_dead " << session_id << "\n";
299 void yf::LoadBalance::Impl::add_package(unsigned long session_id)
301 std::string target = find_session_target(session_id);
303 if (target.size() != 0)
305 std::map<std::string, TargetStat>::iterator itarg;
306 itarg = m_target_stat.find(target);
307 if (itarg != m_target_stat.end()
308 && itarg->second.packages
309 < std::numeric_limits<unsigned int>::max())
311 itarg->second.packages += 1;
316 void yf::LoadBalance::Impl::remove_package(unsigned long session_id)
318 std::string target = find_session_target(session_id);
320 if (target.size() != 0)
322 std::map<std::string, TargetStat>::iterator itarg;
323 itarg = m_target_stat.find(target);
324 if (itarg != m_target_stat.end()
325 && itarg->second.packages > 0)
327 itarg->second.packages -= 1;
332 void yf::LoadBalance::Impl::add_session(unsigned long session_id,
335 // finding and adding session
336 std::map<unsigned long, std::string>::iterator isess;
337 isess = m_session_target.find(session_id);
338 if (isess == m_session_target.end())
340 m_session_target.insert(std::make_pair(session_id, target));
343 // finding and adding target statistics
344 std::map<std::string, TargetStat>::iterator itarg;
345 itarg = m_target_stat.find(target);
346 if (itarg == m_target_stat.end())
352 m_target_stat.insert(std::make_pair(target, stat));
354 else if (itarg->second.sessions < std::numeric_limits<unsigned int>::max())
356 itarg->second.sessions += 1;
360 void yf::LoadBalance::Impl::remove_session(unsigned long session_id)
365 std::map<unsigned long, std::string>::iterator isess;
366 isess = m_session_target.find(session_id);
367 if (isess == m_session_target.end())
370 target = isess->second;
372 // finding target statistics
373 std::map<std::string, TargetStat>::iterator itarg;
374 itarg = m_target_stat.find(target);
375 if (itarg == m_target_stat.end())
377 m_session_target.erase(isess);
381 // counting session down
382 if (itarg->second.sessions > 0)
383 itarg->second.sessions -= 1;
385 // std:.cout << "remove_session " << session_id << " " << target
386 // << " s:" << itarg->second.sessions << "\n";
388 // clearing empty sessions and targets
389 if (itarg->second.sessions == 0 && itarg->second.deads == 0)
391 m_target_stat.erase(itarg);
392 m_session_target.erase(isess);
396 std::string yf::LoadBalance::Impl::find_session_target(unsigned long session_id)
399 std::map<unsigned long, std::string>::iterator isess;
400 isess = m_session_target.find(session_id);
401 if (isess != m_session_target.end())
402 target = isess->second;
408 unsigned int yf::LoadBalance::Impl::cost(std::string target)
410 unsigned int cost = 0;
412 if (target.size() != 0)
414 std::map<std::string, TargetStat>::iterator itarg;
415 itarg = m_target_stat.find(target);
416 if (itarg != m_target_stat.end())
417 cost = itarg->second.cost();
420 //std::cout << "cost " << target << " c:" << cost << "\n";
424 unsigned int yf::LoadBalance::Impl::dead(std::string target)
426 unsigned int dead = 0;
428 if (target.size() != 0)
430 std::map<std::string, TargetStat>::iterator itarg;
431 itarg = m_target_stat.find(target);
432 if (itarg != m_target_stat.end())
433 dead = itarg->second.deads;
436 //std::cout << "dead " << target << " d:" << dead << "\n";
441 static mp::filter::Base* filter_creator()
443 return new mp::filter::LoadBalance;
447 struct metaproxy_1_filter_struct metaproxy_1_filter_load_balance = {
458 * c-file-style: "Stroustrup"
459 * indent-tabs-mode: nil
461 * vim: shiftwidth=4 tabstop=8 expandtab