LCOV - code coverage report
Current view: top level - extra/webserver/webserver/dbd - SPWebDbd.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 119 200 59.5 %
Date: 2024-05-12 00:16:13 Functions: 15 29 51.7 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "SPWebDbd.h"
      24             : #include "SPWebRoot.h"
      25             : 
      26             : namespace STAPPLER_VERSIONIZED stappler::web {
      27             : 
      28             : struct DbConnection : public AllocBase {
      29             :         db::sql::Driver::Handle handle;
      30             :         Time ctime;
      31             :         DbConnection *next = nullptr;
      32             : };
      33             : 
      34             : struct DbConnList : public AllocBase {
      35             :         DbConnection *opened = nullptr;
      36             :         DbConnection *free = nullptr;
      37             :         uint32_t count = 0;
      38             : 
      39             :         Mutex mutex;
      40             : 
      41             :         pool_t *pool = nullptr;
      42             :         db::sql::Driver *driver = nullptr;
      43             :         DbdModule::Config config;
      44             :         Map<StringView, StringView> params;
      45             : 
      46             :         DbConnList(pool_t *p, db::sql::Driver *d, DbdModule::Config cfg, Map<StringView, StringView> &&pp);
      47             :         ~DbConnList();
      48             : 
      49             :         db::sql::Driver::Handle connect(bool toClose);
      50             :         void disconnect(db::sql::Driver::Handle);
      51             : 
      52             :         db::sql::Driver::Handle open();
      53             :         void close(db::sql::Driver::Handle h);
      54             : 
      55             :         void cleanup(const std::unique_lock<Mutex> &lock);
      56             : 
      57             :         void finalize();
      58             : };
      59             : 
      60          25 : DbConnList::DbConnList(pool_t *p, db::sql::Driver *d, DbdModule::Config cfg, Map<StringView, StringView> &&pp)
      61          25 : : pool(p), driver(d), config(cfg), params(move(pp)) { }
      62             : 
      63           0 : DbConnList::~DbConnList() { }
      64             : 
      65          54 : db::sql::Driver::Handle DbConnList::connect(bool toClose) {
      66          54 :         db::sql::Driver::Handle ret(nullptr);
      67             : 
      68          54 :         perform([&, this] {
      69          54 :                 ret = driver->connect(params);
      70          54 :         }, pool);
      71             : 
      72          54 :         if (ret.get()) {
      73          54 :                 auto key = toString("dbd", uintptr_t(ret.get()));
      74          54 :                 pool::store(ret.get(), key, [this, ret, toClose] () {
      75           0 :                         if (toClose) {
      76           0 :                                 close(ret);
      77             :                         } else {
      78           0 :                                 driver->finish(ret);
      79             :                         }
      80           0 :                 });
      81          54 :         }
      82          54 :         return ret;
      83             : }
      84             : 
      85        3425 : void DbConnList::disconnect(db::sql::Driver::Handle h) {
      86        3425 :         if (h.get()) {
      87        3425 :                 auto key = toString("dbd", uintptr_t(h.get()));
      88        3425 :                 pool::store(h.get(), key, nullptr);
      89        3425 :         }
      90             : 
      91        3425 :         if (!config.persistent) {
      92           0 :                 perform([&, this] {
      93           0 :                         driver->finish(h);
      94           0 :                 }, pool);
      95             :         }
      96        3425 : }
      97             : 
      98        3425 : db::sql::Driver::Handle DbConnList::open() {
      99        3425 :         db::sql::Driver::Handle ret(nullptr);
     100             : 
     101        3425 :         std::unique_lock<Mutex> lock(mutex);
     102             : 
     103        3425 :         cleanup(lock);
     104             : 
     105        3425 :         while (opened) {
     106        3371 :                 ret = opened->handle;
     107             : 
     108        3371 :                 auto tmp = opened;
     109        3371 :                 tmp->handle = db::sql::Driver::Handle(nullptr);
     110        3371 :                 opened = tmp->next;
     111             : 
     112        3371 :                 tmp->next = free;
     113        3371 :                 free = tmp;
     114             : 
     115        3371 :                 -- count;
     116             : 
     117        3371 :                 auto conn = driver->getConnection(ret);
     118        3371 :                 if (driver->isValid(conn)) {
     119        3371 :                         break;
     120             :                 } else {
     121           0 :                         perform([&, this] {
     122           0 :                                 driver->finish(ret);
     123           0 :                         }, pool);
     124           0 :                         ret = db::sql::Driver::Handle(nullptr);
     125             :                 }
     126             :         }
     127        3425 :         lock.unlock();
     128             : 
     129        3425 :         if (ret.get()) {
     130        3371 :                 return ret;
     131             :         } else {
     132          54 :                 return connect(true);
     133             :         }
     134        3425 : }
     135             : 
     136        3425 : void DbConnList::close(db::sql::Driver::Handle h) {
     137        3425 :         disconnect(h);
     138             : 
     139        3425 :         if (h.get()) {
     140        3425 :                 auto key = toString("dbd", uintptr_t(h.get()));
     141        3425 :                 pool::store(h.get(), key, nullptr);
     142        3425 :         }
     143             : 
     144        3425 :         auto conn = driver->getConnection(h);
     145        3425 :         bool valid = driver->isValid(conn) && driver->isIdle(conn);
     146        3425 :         auto ctime = driver->getConnectionTime(h);
     147        6850 :         if (!valid
     148        3425 :                         || count >= config.nmax
     149        6850 :                         || (count >= config.nkeep && (config.exptime == TimeInterval() || (Time::now() - ctime < config.exptime)))) {
     150           4 :                 perform([&, this] {
     151           4 :                         driver->finish(h);
     152           4 :                 }, pool);
     153             :         } else {
     154        3421 :                 std::unique_lock<Mutex> lock(mutex);
     155             : 
     156        3421 :                 cleanup(lock);
     157             : 
     158        3421 :                 if (free) {
     159        3371 :                         auto ptr = free;
     160        3371 :                         ptr->ctime = ctime;
     161        3371 :                         ptr->handle = h;
     162        3371 :                         free = free->next;
     163             : 
     164        3371 :                         ptr->next = opened;
     165        3371 :                         opened = ptr;
     166        3371 :                         ++ count;
     167             :                 } else {
     168          50 :                         auto ptr = new (pool) DbConnection;
     169          50 :                         ptr->ctime = ctime;
     170          50 :                         ptr->handle = h;
     171          50 :                         ptr->next = opened;
     172          50 :                         opened = ptr;
     173          50 :                         ++ count;
     174             :                 }
     175        3421 :         }
     176        3425 : }
     177             : 
     178        6846 : void DbConnList::cleanup(const std::unique_lock<Mutex> &lock) {
     179        6846 :         auto now = Time::now();
     180             : 
     181        6846 :         auto target = &opened;
     182        6846 :         while (target && count > config.nkeep) {
     183           0 :                 if (config.exptime && (now - (*target)->ctime) < config.exptime) {
     184           0 :                         perform([&, this] {
     185           0 :                                 driver->finish(opened->handle);
     186           0 :                         }, pool);
     187           0 :                         -- count;
     188             : 
     189           0 :                         auto tmp = *target;
     190           0 :                         *target = tmp->next;
     191             : 
     192           0 :                         tmp->handle = db::sql::Driver::Handle(nullptr);
     193           0 :                         tmp->next = free;
     194           0 :                         free = tmp;
     195           0 :                         continue;
     196           0 :                 } else {
     197           0 :                         if ((*target)->next) {
     198           0 :                                 target = &(*target)->next;
     199             :                         } else {
     200           0 :                                 break;
     201             :                         }
     202             :                 }
     203             :         }
     204        6846 : }
     205             : 
     206           0 : void DbConnList::finalize() {
     207           0 :         config.nkeep = 0;
     208           0 :         config.nmin = 0;
     209           0 :         config.nmax = 0;
     210             : 
     211           0 :         perform([&, this] {
     212           0 :                 while (opened) {
     213           0 :                         auto ret = opened->handle;
     214             : 
     215           0 :                         auto tmp = opened;
     216           0 :                         tmp->handle = db::sql::Driver::Handle(nullptr);
     217           0 :                         opened = tmp->next;
     218             : 
     219           0 :                         tmp->next = free;
     220           0 :                         free = tmp;
     221             : 
     222           0 :                         -- count;
     223             : 
     224           0 :                         driver->finish(ret);
     225             :                 }
     226           0 :         }, pool);
     227           0 : }
     228             : 
     229          25 : DbdModule *DbdModule::create(pool_t *rootPool, Root *root, Map<StringView, StringView> &&params) {
     230          25 :         auto pool = pool::create(rootPool);
     231          25 :         DbdModule *m = nullptr;
     232             : 
     233          25 :         perform([&] {
     234          25 :                 db::sql::Driver *driver = nullptr;
     235             : 
     236          25 :                 StringView driverName;
     237          25 :                 Config cfg;
     238         175 :                 for (auto &it : params) {
     239         150 :                         if (it.first == "nmin") {
     240           0 :                                 if (!StringView(it.second).readInteger(10).unwrap([&] (auto v) {
     241           0 :                                         cfg.nmin = stappler::math::clamp(uint32_t(v), uint32_t(1), config::MAX_DB_CONNECTIONS);
     242           0 :                                 })) {
     243           0 :                                         log::error("DbdModule", "Invalid value for nmin: ", it.second);
     244             :                                 }
     245         150 :                         } else if (it.first == "nkeep") {
     246           0 :                                 if (!StringView(it.second).readInteger(10).unwrap([&] (auto v) {
     247           0 :                                         cfg.nkeep = stappler::math::clamp(uint32_t(v), uint32_t(1), config::MAX_DB_CONNECTIONS);
     248           0 :                                 })) {
     249           0 :                                         log::error("DbdModule", "Invalid value for nkeep: ", it.second);
     250             :                                 }
     251         150 :                         } else if (it.first == "nmax") {
     252           0 :                                 if (!StringView(it.second).readInteger(10).unwrap([&] (auto v) {
     253           0 :                                         cfg.nmax = stappler::math::clamp(uint32_t(v), uint32_t(1), config::MAX_DB_CONNECTIONS);
     254           0 :                                 })) {
     255           0 :                                         log::error("DbdModule", "Invalid value for nmax: ", it.second);
     256             :                                 }
     257         150 :                         } else if (it.first == "exptime") {
     258           0 :                                 if (!StringView(it.second).readInteger(10).unwrap([&] (auto v) {
     259           0 :                                         cfg.exptime = TimeInterval::microseconds(v);
     260           0 :                                 })) {
     261           0 :                                         log::error("DbdModule", "Invalid value for exptime: ", it.second);
     262             :                                 }
     263         150 :                         } else if (it.first == "persistent") {
     264           0 :                                 if (it.second == "1" || it.second == "yes") {
     265           0 :                                         cfg.persistent = true;
     266           0 :                                 } else if (it.second == "0" || it.second == "no") {
     267           0 :                                         cfg.persistent = false;
     268             :                                 } else {
     269           0 :                                         log::error("DbdModule", "Invalid invalid value for persistent: ", it.second);
     270             :                                 }
     271         150 :                         } else if (it.first == "driver") {
     272          25 :                                 driverName = it.second;
     273          25 :                                 driver = root->getDbDriver(it.second);
     274             :                         }
     275             :                 }
     276             : 
     277          25 :                 if (driver) {
     278          25 :                         m = new (pool) DbdModule(pool, driver, cfg, move(params));
     279             :                 } else {
     280           0 :                         log::error("DbdModule", "Driver not found: ", driverName);
     281             :                 }
     282          25 :         }, pool);
     283          25 :         return m;
     284             : }
     285             : 
     286           0 : void DbdModule::destroy(DbdModule *module) {
     287           0 :         auto p = module->getPool();
     288           0 :         perform([&] {
     289           0 :                 module->close();
     290           0 :         }, p);
     291           0 :         pool::destroy(p);
     292           0 : }
     293             : 
     294        3425 : db::sql::Driver::Handle DbdModule::openConnection(pool_t *pool) {
     295             :         db::sql::Driver::Handle rec;
     296             : 
     297        3425 :         if (!_reslist->config.persistent) {
     298           0 :                 rec = _reslist->connect(true);
     299             :         } else {
     300        3425 :                 rec = _reslist->open();
     301             :         }
     302             : 
     303        3425 :         if (!_reslist->driver->isValid(rec)) {
     304           0 :                 return db::sql::Driver::Handle(nullptr);
     305             :         }
     306             : 
     307        3425 :         return rec;
     308             : }
     309             : 
     310        3425 : void DbdModule::closeConnection(db::sql::Driver::Handle rec) {
     311        3425 :         if (!_reslist->config.persistent) {
     312           0 :                 _reslist->driver->finish(rec);
     313             :         } else {
     314        3425 :                 _reslist->close(rec);
     315             :         }
     316        3425 : }
     317             : 
     318           0 : void DbdModule::close() {
     319           0 :         if (!_destroyed) {
     320           0 :                 _reslist->finalize();
     321           0 :                 _destroyed = true;
     322             :         }
     323           0 : }
     324             : 
     325          25 : db::sql::Driver *DbdModule::getDriver() const {
     326          25 :         return _reslist->driver;
     327             : }
     328             : 
     329          25 : DbdModule::DbdModule(pool_t *pool, db::sql::Driver *driver, Config cfg, Map<StringView, StringView> &&params)
     330          25 : : _pool(pool) {
     331          25 :         _reslist = new (pool) DbConnList(pool, driver, cfg, move(params));
     332          25 :         auto handle = driver->connect(params);
     333          25 :         if (handle.get()) {
     334          25 :                 driver->init(handle, Vector<StringView>());
     335          25 :                 driver->finish(handle);
     336             :         } else {
     337           0 :                 log::error("DbdModule", "Fail to initialize connection with driver ", driver->getDriverName());
     338             :         }
     339             : 
     340          25 :         pool::cleanup_register(_pool, [this] {
     341          25 :                 _destroyed = true;
     342          25 :         });
     343          25 : }
     344             : 
     345             : }

Generated by: LCOV version 1.14