Skip to content

Commit

Permalink
Add the setTimeout() method to the DbClient class (drogonframework#823)
Browse files Browse the repository at this point in the history
  • Loading branch information
an-tao authored Apr 29, 2021
1 parent a33bf2b commit 685aaaa
Show file tree
Hide file tree
Showing 26 changed files with 821 additions and 124 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ set(DROGON_SOURCES
lib/src/SecureSSLRedirector.cc
lib/src/SessionManager.cc
lib/src/StaticFileRouter.cc
lib/src/TaskTimeoutFlag.cc
lib/src/Utilities.cc
lib/src/WebSocketClientImpl.cc
lib/src/WebSocketConnectionImpl.cc
Expand Down
7 changes: 5 additions & 2 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@
//"client_encoding": "",
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
//connections per IO thread, otherwise it is the total number of all connections.
"number_of_connections": 1
"number_of_connections": 1,
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
//zero or negative value means no timeout.
"timeout": -1.0
}
],
"redis_clients": [
Expand Down Expand Up @@ -287,4 +290,4 @@
}
]
}
}
}
7 changes: 5 additions & 2 deletions drogon_ctl/templates/config.csp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@
//"client_encoding": "",
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
//connections per IO thread, otherwise it is the total number of all connections.
"number_of_connections": 1
"number_of_connections": 1,
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
//zero or negative value means no timeout.
"timeout": -1.0
}
],
"redis_clients": [
Expand Down Expand Up @@ -278,4 +281,4 @@
],
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
"custom_config": {}
}
}
10 changes: 7 additions & 3 deletions lib/inc/drogon/HttpAppFramework.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
@endcode
*/
inline HttpAppFramework &enableSession(
const std::chrono::duration<long double> &timeout)
const std::chrono::duration<double> &timeout)
{
return enableSession((size_t)timeout.count());
}
Expand Down Expand Up @@ -959,7 +959,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
@endcode
*/
inline HttpAppFramework &setIdleConnectionTimeout(
const std::chrono::duration<long double> &timeout)
const std::chrono::duration<double> &timeout)
{
return setIdleConnectionTimeout((size_t)timeout.count());
}
Expand Down Expand Up @@ -1222,6 +1222,9 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* @param filename The file name of sqlite3 database file.
* @param name The client name.
* @param isFast Indicates if the client is a fast database client.
* @param characterSet The character set of the database server.
* @param timeout The timeout in seconds for executing SQL queries. zero or
* negative value means no timeout.
*
* @note
* This operation can be performed by an option in the configuration file.
Expand All @@ -1237,7 +1240,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
const std::string &filename = "",
const std::string &name = "default",
const bool isFast = false,
const std::string &characterSet = "") = 0;
const std::string &characterSet = "",
double timeout = -1.0) = 0;

/// Create a redis client
/**
Expand Down
2 changes: 1 addition & 1 deletion lib/inc/drogon/WebSocketConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class WebSocketConnection
*/
virtual void setPingMessage(
const std::string &message,
const std::chrono::duration<long double> &interval) = 0;
const std::chrono::duration<double> &interval) = 0;

/**
* @brief Disable sending ping messages to the peer.
Expand Down
4 changes: 2 additions & 2 deletions lib/inc/drogon/utils/coroutine.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ namespace internal
struct TimerAwaiter : CallbackAwaiter<void>
{
TimerAwaiter(trantor::EventLoop *loop,
const std::chrono::duration<long double> &delay)
const std::chrono::duration<double> &delay)
: loop_(loop), delay_(delay.count())
{
}
Expand All @@ -597,7 +597,7 @@ struct TimerAwaiter : CallbackAwaiter<void>

inline internal::TimerAwaiter sleepCoro(
trantor::EventLoop *loop,
const std::chrono::duration<long double> &delay) noexcept
const std::chrono::duration<double> &delay) noexcept
{
assert(loop);
return internal::TimerAwaiter(loop, delay);
Expand Down
4 changes: 3 additions & 1 deletion lib/src/ConfigLoader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ static void loadDbClients(const Json::Value &dbClients)
{
characterSet = client.get("client_encoding", "").asString();
}
auto timeout = client.get("timeout", -1.0).asDouble();
drogon::app().createDbClient(type,
host,
(unsigned short)port,
Expand All @@ -523,7 +524,8 @@ static void loadDbClients(const Json::Value &dbClients)
filename,
name,
isFast,
characterSet);
characterSet,
timeout);
}
}

Expand Down
8 changes: 5 additions & 3 deletions lib/src/DbClientManager.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
*
* DbClientManager.h
* An Tao
* @file DbClientManager.h
* @author An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
Expand Down Expand Up @@ -52,7 +52,8 @@ class DbClientManager : public trantor::NonCopyable
const std::string &filename,
const std::string &name,
const bool isFast,
const std::string &characterSet);
const std::string &characterSet,
double timeout);
bool areAllDbClientsAvailable() const noexcept;

private:
Expand All @@ -64,6 +65,7 @@ class DbClientManager : public trantor::NonCopyable
ClientType dbType_;
bool isFast_;
size_t connectionNumber_;
double timeout_;
};
std::vector<DbInfo> dbInfos_;
std::map<std::string, IOThreadStorage<orm::DbClientPtr>> dbFastClientsMap_;
Expand Down
3 changes: 2 additions & 1 deletion lib/src/DbClientManagerSkipped.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ void DbClientManager::createDbClient(const std::string & /*dbType*/,
const std::string & /*filename*/,
const std::string & /*name*/,
const bool /*isFast*/,
const std::string & /*characterSet*/)
const std::string & /*characterSet*/,
double /*timeout*/)
{
LOG_FATAL << "No database is supported by drogon, please install the "
"database development library first.";
Expand Down
6 changes: 4 additions & 2 deletions lib/src/HttpAppFrameworkImpl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,8 @@ HttpAppFramework &HttpAppFrameworkImpl::createDbClient(
const std::string &filename,
const std::string &name,
const bool isFast,
const std::string &characterSet)
const std::string &characterSet,
double timeout)
{
assert(!running_);
dbClientManagerPtr_->createDbClient(dbType,
Expand All @@ -971,7 +972,8 @@ HttpAppFramework &HttpAppFrameworkImpl::createDbClient(
filename,
name,
isFast,
characterSet);
characterSet,
timeout);
return *this;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/src/HttpAppFrameworkImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,8 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
const std::string &filename,
const std::string &name,
bool isFast,
const std::string &characterSet) override;
const std::string &characterSet,
double timeout) override;
HttpAppFramework &createRedisClient(const std::string &ip,
unsigned short port,
const std::string &name,
Expand Down
39 changes: 39 additions & 0 deletions lib/src/TaskTimeoutFlag.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
*
* @file TaskTimeoutFlag.cc
* @author An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/

#include "TaskTimeoutFlag.h"
using namespace drogon;

TaskTimeoutFlag::TaskTimeoutFlag(trantor::EventLoop *loop,
const std::chrono::duration<double> &timeout,
std::function<void()> timeoutCallback)
: loop_(loop), timeout_(timeout), timeoutFunc_(timeoutCallback)
{
}
void TaskTimeoutFlag::runTimer()
{
std::weak_ptr<TaskTimeoutFlag> weakPtr = shared_from_this();
loop_->runAfter(timeout_, [weakPtr]() {
auto thisPtr = weakPtr.lock();
if (!thisPtr)
return;
if (thisPtr->done())
return;
thisPtr->timeoutFunc_();
});
}
bool TaskTimeoutFlag::done()
{
return isDone_.exchange(true);
}
39 changes: 39 additions & 0 deletions lib/src/TaskTimeoutFlag.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
*
* @file TaskTimeoutFlag.h
* @author An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#include <trantor/utils/NonCopyable.h>
#include <trantor/net/EventLoop.h>
#include <chrono>
#include <functional>
#include <atomic>
#include <memory>

namespace drogon
{
class TaskTimeoutFlag : public trantor::NonCopyable,
public std::enable_shared_from_this<TaskTimeoutFlag>
{
public:
TaskTimeoutFlag(trantor::EventLoop *loop,
const std::chrono::duration<double> &timeout,
std::function<void()> timeoutCallback);
bool done();
void runTimer();

private:
std::atomic<bool> isDone_{false};
trantor::EventLoop *loop_;
std::chrono::duration<double> timeout_;
std::function<void()> timeoutFunc_;
};
} // namespace drogon
4 changes: 2 additions & 2 deletions lib/src/WebSocketConnectionImpl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ void WebSocketConnectionImpl::WebSocketConnectionImpl::forceClose()

void WebSocketConnectionImpl::setPingMessage(
const std::string &message,
const std::chrono::duration<long double> &interval)
const std::chrono::duration<double> &interval)
{
auto loop = tcpConnectionPtr_->getLoop();
if (loop->isInLoopThread())
Expand Down Expand Up @@ -397,7 +397,7 @@ void WebSocketConnectionImpl::disablePingInLoop()

void WebSocketConnectionImpl::setPingMessageInLoop(
std::string &&message,
const std::chrono::duration<long double> &interval)
const std::chrono::duration<double> &interval)
{
std::weak_ptr<WebSocketConnectionImpl> weakPtr = shared_from_this();
disablePingInLoop();
Expand Down
10 changes: 4 additions & 6 deletions lib/src/WebSocketConnectionImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ class WebSocketConnectionImpl final
const std::string &reason = "") override; // close write
void forceClose() override; // close

void setPingMessage(
const std::string &message,
const std::chrono::duration<long double> &interval) override;
void setPingMessage(const std::string &message,
const std::chrono::duration<double> &interval) override;

void disablePing() override;

Expand Down Expand Up @@ -120,9 +119,8 @@ class WebSocketConnectionImpl final
[](const WebSocketConnectionImplPtr &) {};
void sendWsData(const char *msg, uint64_t len, unsigned char opcode);
void disablePingInLoop();
void setPingMessageInLoop(
std::string &&message,
const std::chrono::duration<long double> &interval);
void setPingMessageInLoop(std::string &&message,
const std::chrono::duration<double> &interval);
};

} // namespace drogon
19 changes: 16 additions & 3 deletions orm_lib/inc/drogon/orm/DbClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ class DROGON_EXPORT DbClient : public trantor::NonCopyable
* transaction object to set the callback.
*/
virtual std::shared_ptr<Transaction> newTransaction(
const std::function<void(bool)> &commitCallback = nullptr) = 0;
const std::function<void(bool)> &commitCallback =
std::function<void(bool)>()) noexcept(false) = 0;

/// Create a transaction object in asynchronous mode.
virtual void newTransactionAsync(
Expand Down Expand Up @@ -269,6 +270,18 @@ class DROGON_EXPORT DbClient : public trantor::NonCopyable
return connectionInfo_;
}

/**
* @brief Set the Timeout value of execution of a SQL.
*
* @param timeout in seconds, if the SQL result is not returned from the
* server within the timeout, a TimeoutError exception with "SQL execution
* timeout" string is generated and returned to the caller.
* @note set the timeout value to zero or negative for no limit on time. The
* default value is -1.0, this means there is no time limit if this method
* is not called.
*/
virtual void setTimeout(double timeout) = 0;

private:
friend internal::SqlBinder;
virtual void execSql(
Expand Down Expand Up @@ -304,8 +317,8 @@ inline void internal::TrasactionAwaiter::await_suspend(
client_->newTransactionAsync(
[this, handle](const std::shared_ptr<Transaction> &transaction) {
if (transaction == nullptr)
setException(std::make_exception_ptr(
Failure("Failed to create transaction")));
setException(std::make_exception_ptr(TimeoutError(
"Timeout, no connection available for transaction")));
else
setValue(transaction);
handle.resume();
Expand Down
12 changes: 12 additions & 0 deletions orm_lib/inc/drogon/orm/Exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,18 @@ class InternalError : public DrogonDbException, public std::logic_error
DROGON_EXPORT explicit InternalError(const std::string &);
};

/// Timeout error in when executing the SQL statement.
class TimeoutError : public DrogonDbException, public std::logic_error
{
virtual const std::exception &base() const noexcept override
{
return *this;
}

public:
DROGON_EXPORT explicit TimeoutError(const std::string &);
};

/// Error in usage of drogon orm library, similar to std::logic_error
class UsageError : public DrogonDbException, public std::logic_error
{
Expand Down
Loading

0 comments on commit 685aaaa

Please sign in to comment.