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> &¶ms) {
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> &¶ms)
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 : }
|