1 /* This file is part of Metaproxy.
2 Copyright (C) 2005-2008 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>
31 // remove max macro if already defined (defined later in <limits>)
41 namespace mp = metaproxy_1;
42 namespace yf = mp::filter;
44 namespace metaproxy_1 {
46 class LoadBalance::Impl {
50 void process(metaproxy_1::Package & package);
51 void configure(const xmlNode * ptr);
53 // statistic manipulating functions,
54 void add_dead(unsigned long session_id);
55 //void clear_dead(unsigned long session_id);
56 void add_package(unsigned long session_id);
57 void remove_package(unsigned long session_id);
58 void add_session(unsigned long session_id, std::string target);
59 void remove_session(unsigned long session_id);
60 std::string find_session_target(unsigned long session_id);
63 unsigned int cost(std::string target);
64 unsigned int dead(std::string target);
69 unsigned int sessions;
70 unsigned int packages;
73 unsigned int c = sessions + packages + deads;
74 //std::cout << "stats c:" << c
75 // << " s:" << sessions
76 // << " p:" << packages
83 // local protected databases
85 std::map<std::string, TargetStat> m_target_stat;
86 std::map<unsigned long, std::string> m_session_target;
91 // define Pimpl wrapper forwarding to Impl
93 yf::LoadBalance::LoadBalance() : m_p(new Impl)
97 yf::LoadBalance::~LoadBalance()
98 { // must have a destructor because of boost::scoped_ptr
101 void yf::LoadBalance::configure(const xmlNode *xmlnode, bool test_only)
103 m_p->configure(xmlnode);
106 void yf::LoadBalance::process(mp::Package &package) const
108 m_p->process(package);
112 // define Implementation stuff
116 yf::LoadBalance::Impl::Impl()
120 yf::LoadBalance::Impl::~Impl()
124 void yf::LoadBalance::Impl::configure(const xmlNode *xmlnode)
128 void yf::LoadBalance::Impl::process(mp::Package &package)
131 bool is_closed_front = false;
132 bool is_closed_back = false;
134 // checking for closed front end packages
135 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 mp::odr odr_en(ODR_ENCODE);
148 Z_InitRequest *org_init = gdu_req->u.z3950->u.initRequest;
150 // extracting virtual hosts
151 std::list<std::string> vhosts;
153 mp::util::remove_vhost_otherinfo(&(org_init->otherInfo), vhosts);
155 // choosing one target according to load-balancing algorithm
159 unsigned int cost = std::numeric_limits<unsigned int>::max();
161 { //locking scope for local databases
162 boost::mutex::scoped_lock scoped_lock(m_mutex);
164 // load-balancing algorithm goes here
165 //target = *vhosts.begin();
166 for(std::list<std::string>::const_iterator ivh
170 if ((*ivh).size() != 0){
172 = yf::LoadBalance::Impl::cost(*ivh);
180 // updating local database
181 add_session(package.session().id(), target);
182 yf::LoadBalance::Impl::cost(target);
183 add_package(package.session().id());
186 // copying new target into init package
187 mp::util::set_vhost_otherinfo(&(org_init->otherInfo),
189 package.request() = gdu_req;
193 // frontend Z39.50 close request is added to statistics and marked
194 else if (gdu_req->u.z3950->which == Z_APDU_close){
195 is_closed_front = true;
196 boost::mutex::scoped_lock scoped_lock(m_mutex);
197 add_package(package.session().id());
199 // any other Z39.50 package is added to statistics
201 boost::mutex::scoped_lock scoped_lock(m_mutex);
202 add_package(package.session().id());
206 // moving all package types
210 // checking for closed back end packages
211 if (package.session().is_closed())
212 is_closed_back = true;
214 Z_GDU *gdu_res = package.response().get();
216 // passing anything but z3950 packages
217 if (gdu_res && gdu_res->which == Z_GDU_Z3950){
219 // session closing only on Z39.50 close response
220 if (gdu_res->u.z3950->which == Z_APDU_close){
221 is_closed_back = true;
222 boost::mutex::scoped_lock scoped_lock(m_mutex);
223 remove_package(package.session().id());
225 // any other Z39.50 package is removed from statistics
227 boost::mutex::scoped_lock scoped_lock(m_mutex);
228 remove_package(package.session().id());
232 // finally removing sessions and marking deads
233 if (is_closed_back || is_closed_front){
234 boost::mutex::scoped_lock scoped_lock(m_mutex);
236 // marking backend dead if backend closed without fronted close
237 if (is_closed_front == false)
238 add_dead(package.session().id());
240 remove_session(package.session().id());
242 // making sure that package is closed
243 package.session().close();
247 // statistic manipulating functions,
248 void yf::LoadBalance::Impl::add_dead(unsigned long session_id){
251 std::string target = find_session_target(session_id);
253 if (target.size() != 0){
254 std::map<std::string, TargetStat>::iterator itarg;
255 itarg = m_target_stat.find(target);
256 if (itarg != m_target_stat.end()
257 && itarg->second.deads < std::numeric_limits<unsigned int>::max()){
258 itarg->second.deads += 1;
259 // std:.cout << "add_dead " << session_id << " " << target
260 // << " d:" << itarg->second.deads << "\n";
265 //void yf::LoadBalance::Impl::clear_dead(unsigned long session_id){
266 // std::cout << "clear_dead " << session_id << "\n";
269 void yf::LoadBalance::Impl::add_package(unsigned long session_id){
271 std::string target = find_session_target(session_id);
273 if (target.size() != 0){
274 std::map<std::string, TargetStat>::iterator itarg;
275 itarg = m_target_stat.find(target);
276 if (itarg != m_target_stat.end()
277 && itarg->second.packages
278 < std::numeric_limits<unsigned int>::max()){
279 itarg->second.packages += 1;
280 // std:.cout << "add_package " << session_id << " " << target
281 // << " p:" << itarg->second.packages << "\n";
286 void yf::LoadBalance::Impl::remove_package(unsigned long session_id){
287 std::string target = find_session_target(session_id);
289 if (target.size() != 0){
290 std::map<std::string, TargetStat>::iterator itarg;
291 itarg = m_target_stat.find(target);
292 if (itarg != m_target_stat.end()
293 && itarg->second.packages > 0){
294 itarg->second.packages -= 1;
295 // std:.cout << "remove_package " << session_id << " " << target
296 // << " p:" << itarg->second.packages << "\n";
301 void yf::LoadBalance::Impl::add_session(unsigned long session_id,
304 // finding and adding session
305 std::map<unsigned long, std::string>::iterator isess;
306 isess = m_session_target.find(session_id);
307 if (isess == m_session_target.end()){
308 m_session_target.insert(std::make_pair(session_id, target));
311 // finding and adding target statistics
312 std::map<std::string, TargetStat>::iterator itarg;
313 itarg = m_target_stat.find(target);
314 if (itarg == m_target_stat.end()){
317 stat.packages = 0; // no idea why the defaut constructor TargetStat()
318 stat.deads = 0; // is not initializig this correctly to zero ??
319 m_target_stat.insert(std::make_pair(target, stat));
320 // std:.cout << "add_session " << session_id << " " << target
323 else if (itarg->second.sessions < std::numeric_limits<unsigned int>::max())
325 itarg->second.sessions += 1;
326 // std:.cout << "add_session " << session_id << " " << target
327 // << " s:" << itarg->second.sessions << "\n";
331 void yf::LoadBalance::Impl::remove_session(unsigned long session_id){
336 std::map<unsigned long, std::string>::iterator isess;
337 isess = m_session_target.find(session_id);
338 if (isess == m_session_target.end())
341 target = isess->second;
343 // finding target statistics
344 std::map<std::string, TargetStat>::iterator itarg;
345 itarg = m_target_stat.find(target);
346 if (itarg == m_target_stat.end()){
347 m_session_target.erase(isess);
351 // counting session down
352 if (itarg->second.sessions > 0)
353 itarg->second.sessions -= 1;
355 // std:.cout << "remove_session " << session_id << " " << target
356 // << " s:" << itarg->second.sessions << "\n";
358 // clearing empty sessions and targets
359 if (itarg->second.sessions == 0 && itarg->second.deads == 0 ){
360 m_target_stat.erase(itarg);
361 m_session_target.erase(isess);
366 yf::LoadBalance::Impl::find_session_target(unsigned long session_id){
369 std::map<unsigned long, std::string>::iterator isess;
370 isess = m_session_target.find(session_id);
371 if (isess != m_session_target.end())
372 target = isess->second;
378 unsigned int yf::LoadBalance::Impl::cost(std::string target){
382 if (target.size() != 0){
383 std::map<std::string, TargetStat>::iterator itarg;
384 itarg = m_target_stat.find(target);
385 if (itarg != m_target_stat.end()){
386 cost = itarg->second.cost();
390 //std::cout << "cost " << target << " c:" << cost << "\n";
394 unsigned int yf::LoadBalance::Impl::dead(std::string target){
398 if (target.size() != 0){
399 std::map<std::string, TargetStat>::iterator itarg;
400 itarg = m_target_stat.find(target);
401 if (itarg != m_target_stat.end()){
402 dead = itarg->second.deads;
406 //std::cout << "dead " << target << " d:" << dead << "\n";
413 static mp::filter::Base* filter_creator()
415 return new mp::filter::LoadBalance;
419 struct metaproxy_1_filter_struct metaproxy_1_filter_load_balance = {
430 * indent-tabs-mode: nil
431 * c-file-style: "stroustrup"
433 * vim: shiftwidth=4 tabstop=8 expandtab