LCOV - code coverage report
Current view: top level - snapwebsites - loadavg.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2 73 2.7 %
Date: 2019-12-15 17:13:15 Functions: 2 12 16.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Load Balancing -- class used to handle the load average file
       2             : // Copyright (c) 2017-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // This program is free software; you can redistribute it and/or modify
       5             : // it under the terms of the GNU General Public License as published by
       6             : // the Free Software Foundation; either version 2 of the License, or
       7             : // (at your option) any later version.
       8             : //
       9             : // This program is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : // GNU General Public License for more details.
      13             : //
      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
      17             : 
      18             : 
      19             : // self
      20             : //
      21             : #include "snapwebsites/loadavg.h"
      22             : 
      23             : 
      24             : // our lib
      25             : //
      26             : #include "snapwebsites/log.h"
      27             : 
      28             : 
      29             : // snapdev lib
      30             : //
      31             : #include <snapdev/raii_generic_deleter.h>
      32             : 
      33             : 
      34             : // C lib
      35             : //
      36             : #include <fcntl.h>
      37             : #include <sys/file.h>
      38             : #include <sys/stat.h>
      39             : #include <sys/types.h>
      40             : #include <unistd.h>
      41             : 
      42             : 
      43             : // C++ lib
      44             : //
      45             : #include <memory>
      46             : 
      47             : 
      48             : // last include
      49             : //
      50             : #include <snapdev/poison.h>
      51             : 
      52             : 
      53             : 
      54             : namespace snap
      55             : {
      56             : 
      57             : 
      58             : namespace
      59             : {
      60             : 
      61             : 
      62           2 : std::string g_filename;
      63             : 
      64             : 
      65             : 
      66             : 
      67             : int const LOADAVG_VERSION = 1;
      68             : 
      69             : struct loadavg_magic
      70             : {
      71             :     char                    f_name[4]{'L', 'A', 'V', 'G'};  // 'LAVG'
      72             :     uint16_t                f_version = LOADAVG_VERSION;    // 1+ representing the version
      73             : };
      74             : 
      75             : 
      76             : } // no name namespace
      77             : 
      78             : 
      79           0 : bool loadavg_file::load()
      80             : {
      81             :     // open the file
      82             :     //
      83           0 :     raii_fd_t safe_fd(open(g_filename.c_str(), O_RDONLY));
      84           0 :     if(!safe_fd)
      85             :     {
      86           0 :         return false;
      87             :     }
      88             : 
      89             :     // lock the file in share mode (multiple read, no writes)
      90             :     //
      91           0 :     if(flock(safe_fd.get(), LOCK_SH) != 0)
      92             :     {
      93           0 :         return false;
      94             :     }
      95             : 
      96             :     // verify the magic
      97             :     //
      98           0 :     loadavg_magic magic;
      99           0 :     if(read(safe_fd.get(), &magic, sizeof(magic)) != sizeof(magic))
     100             :     {
     101           0 :         return false;
     102             :     }
     103           0 :     if(magic.f_name[0] != 'L'
     104           0 :     || magic.f_name[1] != 'A'
     105           0 :     || magic.f_name[2] != 'V'
     106           0 :     || magic.f_name[3] != 'G'
     107           0 :     || magic.f_version != LOADAVG_VERSION)
     108             :     {
     109           0 :         return false;
     110             :     }
     111             : 
     112             :     // load each item
     113             :     //
     114             :     for(;;)
     115             :     {
     116           0 :         loadavg_item item;
     117           0 :         ssize_t const r(read(safe_fd.get(), &item, sizeof(item)));
     118           0 :         if(r < 0)
     119             :         {
     120           0 :             return false;
     121             :         }
     122           0 :         if(r == 0)
     123             :         {
     124             :             // we got EOF
     125           0 :             break;
     126             :         }
     127           0 :         f_items.push_back(item);
     128           0 :     }
     129             : 
     130             :     // it worked
     131             :     //
     132           0 :     return true;
     133             : }
     134             : 
     135             : 
     136           0 : bool loadavg_file::save() const
     137             : {
     138             :     // open the file
     139             :     //
     140           0 :     raii_fd_t safe_fd(open(g_filename.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
     141           0 :     if(!safe_fd)
     142             :     {
     143           0 :         return false;
     144             :     }
     145             : 
     146             :     // lock the file in share mode (multiple read, no writes)
     147             :     //
     148           0 :     if(flock(safe_fd.get(), LOCK_EX) != 0)
     149             :     {
     150           0 :         return false;
     151             :     }
     152             : 
     153             :     // write the magic each time (in case the version changed
     154             :     // or we are creating a new file)
     155             :     //
     156           0 :     loadavg_magic magic;
     157           0 :     if(write(safe_fd.get(), &magic, sizeof(magic)) != sizeof(magic))
     158             :     {
     159           0 :         return false;
     160             :     }
     161             : 
     162             :     // write each item
     163             :     //
     164           0 :     for(auto const & item : f_items)
     165             :     {
     166           0 :         ssize_t const r(write(safe_fd.get(), &item, sizeof(item)));
     167           0 :         if(r < 0)
     168             :         {
     169           0 :             return false;
     170             :         }
     171             :     }
     172             : 
     173             :     // it worked
     174             :     //
     175           0 :     return true;
     176             : }
     177             : 
     178             : 
     179           0 : void loadavg_file::add(loadavg_item const & new_item)
     180             : {
     181             :     auto const & it(std::find_if(
     182             :             f_items.begin(),
     183             :             f_items.end(),
     184           0 :             [new_item](auto const & item)
     185             :             {
     186           0 :                 return item.f_address == new_item.f_address;
     187           0 :             }));
     188             : 
     189           0 :     if(it == f_items.end())
     190             :     {
     191           0 :         f_items.push_back(new_item);
     192             :     }
     193             :     else
     194             :     {
     195             :         // replace existing item with new avg and timestamp
     196           0 :         it->f_timestamp = new_item.f_timestamp;
     197           0 :         it->f_avg = new_item.f_avg;
     198             :     }
     199           0 : }
     200             : 
     201             : 
     202             : /** \brief Remove old entries from the list of items.
     203             :  *
     204             :  * This function checks each item. If one has a date which is too
     205             :  * old (i.e. less than now minus \p how_old), then it gets removed
     206             :  * from the list. The computer may get re-added later.
     207             :  *
     208             :  * Assuming everything works as expected, a computer that stops
     209             :  * sending us the LOADAVG message is considered hanged in some
     210             :  * way so we do not want to send it any additional work.
     211             :  *
     212             :  * In most cases, you want to use the following code to find
     213             :  * the least busy system to connect to:
     214             :  *
     215             :  * \code
     216             :  *      snap::loadavg_file avg;
     217             :  *      avg.load();
     218             :  *      if(avg.remove_old_entries(10))
     219             :  *      {
     220             :  *          avg.save();
     221             :  *      }
     222             :  *      snap::loadavg_item const * item(avg.find_least_busy());
     223             :  * \endcode
     224             :  *
     225             :  * \param[in] how_old  The number of seconds after which an entry
     226             :  *                     is considered too old to be kept around.
     227             :  *
     228             :  * \return true if one or more items were removed.
     229             :  */
     230           0 : bool loadavg_file::remove_old_entries(int how_old)
     231             : {
     232           0 :     size_t const size(f_items.size());
     233           0 :     time_t const now(time(nullptr) - how_old);
     234           0 :     f_items.erase(std::remove_if(
     235             :             f_items.begin(),
     236             :             f_items.end(),
     237           0 :             [now](auto const & item)
     238           0 :             {
     239           0 :                 return item.f_timestamp < now;
     240           0 :             }),
     241           0 :             f_items.end());
     242           0 :     return f_items.size() != size;
     243             : }
     244             : 
     245             : 
     246             : /** \brief Retrieve an entry using its IP address.
     247             :  *
     248             :  * This function searches for an item using the specified IP address.
     249             :  *
     250             :  * \param[in] addr  The address used to search the item.
     251             :  *
     252             :  * \return nullptr if no item matched, the pointer of the item if one
     253             :  *         was found with the proper information.
     254             :  */
     255           0 : loadavg_item const * loadavg_file::find(struct sockaddr_in6 const & addr) const
     256             : {
     257             :     auto const & it(std::find_if(
     258             :             f_items.begin(),
     259             :             f_items.end(),
     260           0 :             [addr](auto const & item)
     261             :             {
     262           0 :                 return item.f_address == addr;
     263           0 :             }));
     264             : 
     265           0 :     if(it == f_items.end())
     266             :     {
     267           0 :         return nullptr;
     268             :     }
     269             : 
     270           0 :     return &*it;
     271             : }
     272             : 
     273             : 
     274             : /** \brief Search for least busy server.
     275             :  *
     276             :  * This function searches the list of servers and returns the one
     277             :  * which has the smallest load average amount.
     278             :  *
     279             :  * If you want to make sure only fresh data is considered, you
     280             :  * probably want to call the remove_old_entries() function first.
     281             :  *
     282             :  * Note that the function will always return an item if there is
     283             :  * at least one registered with a mostly current average load.
     284             :  * If somehow all the servers get removed (too old, unregistered,
     285             :  * etc.) then the function will return a null pointer.
     286             :  *
     287             :  * \return The least busy server or nullptr if no server is available.
     288             :  *
     289             :  * \sa remove_old_entries()
     290             :  */
     291           0 : loadavg_item const * loadavg_file::find_least_busy() const
     292             : {
     293             :     auto const & it(std::min_element(
     294             :             f_items.begin(),
     295             :             f_items.end(),
     296           0 :             [](auto const & a, auto const & b)
     297             :             {
     298           0 :                 return a.f_avg < b.f_avg;
     299           0 :             }));
     300             : 
     301           0 :     if(it == f_items.end())
     302             :     {
     303           0 :         return nullptr;
     304             :     }
     305             : 
     306           0 :     return &*it;
     307             : }
     308             : 
     309             : 
     310           6 : } // namespace snap
     311             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13