Integrate asynco in library, optimize maintenance on both engine

for_fork
mbandic 2 months ago
parent 4c50ba7416
commit c910c78633
  1. 4
      .vscode/settings.json
  2. 55
      README.md
  3. 66
      lib/mysql.hpp
  4. 125
      src/mysql.cpp
  5. 4
      test/compile.sh
  6. 12
      test/test.cpp

@ -61,6 +61,8 @@
"cinttypes": "cpp", "cinttypes": "cpp",
"typeinfo": "cpp", "typeinfo": "cpp",
"any": "cpp", "any": "cpp",
"variant": "cpp" "variant": "cpp",
"*.ipp": "cpp",
"bitset": "cpp"
} }
} }

@ -13,7 +13,7 @@ A small framework for basic MySQL database operations via MySQL/Connector++
- Response object - Response object
- Thread safe - Thread safe
- Exceptions and log error callback - Exceptions and log error callback
- Can use external time loop for connection management - Support my Asynco wrapper around Boost ASIO and support threads
## Installation ## Installation
@ -22,6 +22,11 @@ First install dependency MySQL/Connector++
``` ```
sudo apt install libmysqlcppconn-dev sudo apt install libmysqlcppconn-dev
``` ```
If you are going to use with an Asynco wrapper, download the archive from the profile and install the Boost dependencies
```
sudo apt install libboost-all-dev
```
Just download the latest release and unzip it into your project. You can turn it on with: Just download the latest release and unzip it into your project. You can turn it on with:
@ -29,7 +34,15 @@ Just download the latest release and unzip it into your project. You can turn it
#include "mysql/lib/mysql.hpp" #include "mysql/lib/mysql.hpp"
using namespace marcelb; using namespace marcelb;
``` ```
Compiling
```
# use with my Asynco lib
g++ -DMYSQL_USE_ASYNCO test.cpp ../src/* ../../asynco/src/* -o test.o -lmysqlcppconn -lpthread
# or use without asnyco (in multithread)
g++ test.cpp ../src/* ../../asynco/src/* -o test.o -lmysqlcppconn -lpthread
```
## Usage ## Usage
### Internal engine ### Internal engine
@ -46,6 +59,14 @@ using namespace marcelb::mysql;
*/ */
MySQL mydb("tcp://192.168.2.10:3306", "user_nm", "passss", "my_db", 5); MySQL mydb("tcp://192.168.2.10:3306", "user_nm", "passss", "my_db", 5);
mydb.on_error = [](const string& error) {
cout << error << endl;
};
mydb.on_connect = []() {
cout << "Init all pool connection done" << endl;
};
/** /**
* Use ------------------ * Use ------------------
*/ | | */ | |
@ -72,34 +93,16 @@ try { | | | |
} }
``` ```
### External engine ### Run async with Asynco
As I developed quite a few wrappers that have some internal thread, I realized that it was inefficient and made it possible to call the necessary functions periodically outside (one thread per whole application or timer (ASIO), or my asynco wrapper). As I developed quite a few wrappers that have some internal thread, I realized that it was inefficient and made it possible to call the necessary functions periodically outside (one thread per whole application or timer (ASIO), or my asynco wrapper).
```c++ ```c++
#include "../lib/mysql.hpp"
using namespace marcelb::mysql;
#include "../../asynco/lib/timers.hpp"
using namespace marcelb::asynco;
/**
* Init
*/
MySQL mydb("tcp://192.168.2.10:3306", "user_nm", "passss", "my_db", 5, time_loop_type::external);
periodic mysql_maintenance ( [&mydb] () {
mydb.periodic_maintenance();
}, MYSQL_PERIODIC_INTERNAL_TIME);
mydb.set_on_error( [](const string& error) {
cout << error << endl; // print or log
});
/** /**
* You can call multiple queries asynchronously * You can call multiple queries asynchronously
*/ */
auto a1 = atask ( [&mydb] () { auto a1 = async_ ( [&mydb] () {
try { try {
auto response = mydb.exec<int,string>("SELECT id,domain FROM records WHERE enabled = 1;"); auto response = mydb.exec<int,string>("SELECT id,domain FROM records WHERE enabled = 1;");
for (auto row : response) { for (auto row : response) {
@ -110,7 +113,7 @@ auto a1 = atask ( [&mydb] () {
} }
}); });
auto a2 = atask ( [&mydb] () { auto a2 = async_ ( [&mydb] () {
try { try {
auto response = mydb.exec<string,string>("SELECT zonename,auth_key FROM zones;"); auto response = mydb.exec<string,string>("SELECT zonename,auth_key FROM zones;");
for (auto row : response) { for (auto row : response) {
@ -121,7 +124,7 @@ auto a2 = atask ( [&mydb] () {
} }
}); });
auto a3 = atask ( [&mydb] () { auto a3 = async_ ( [&mydb] () {
try { try {
auto response = mydb.exec<string,string>("SELECT username,email FROM users WHERE enabled = 1;"); auto response = mydb.exec<string,string>("SELECT username,email FROM users WHERE enabled = 1;");
for (auto row : response) { for (auto row : response) {
@ -132,9 +135,9 @@ auto a3 = atask ( [&mydb] () {
} }
}); });
wait(a1); await(a1);
wait(a2); await(a2);
wait(a3); await(a3);
``` ```
## License ## License

@ -8,7 +8,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <tuple> #include <tuple>
#include <ctime>
#include <chrono> #include <chrono>
using namespace std; using namespace std;
@ -39,6 +38,8 @@ namespace mysql {
* *
*/ */
#define MYSQL_PERIODIC_INTERNAL_TIME 5000 #define MYSQL_PERIODIC_INTERNAL_TIME 5000
#define MYSQL_MAX_CONNECTION_INIT_SAME_TIME 10
#define MYSQL_DELAYED_CONNECT_TIME 1000
/** /**
* A class for creating sql responses * A class for creating sql responses
@ -99,13 +100,16 @@ class MySQL {
queue<Connection*> connection_pool; queue<Connection*> connection_pool;
string path, username, password, database; string path, username, password, database;
uint32_t pool_size; uint32_t pool_size;
bool run_tloop = true; const uint32_t max_t = thread::hardware_concurrency();
uint32_t t = 1;
#ifdef MYSQL_USE_ASYNCO #ifdef MYSQL_USE_ASYNCO
periodic p_loop; periodic p_loop;
// delayed d_connect;
#else #else
future<void> tloop_future; future<void> tloop_future, connect_f;
bool run_tloop = true;
#endif #endif
time_t last_loop_time;
/** /**
* Open one database server connection * Open one database server connection
@ -154,8 +158,57 @@ class MySQL {
void _tloop(uint32_t b, uint32_t e); void _tloop(uint32_t b, uint32_t e);
/**
*
*/
vector<pair<int, int>> divide_and_conquer(int n, int t) {
vector<pair<int, int>> pairs;
int part_size = n / t;
int residue = n % t; // Ostatak koji treba rasporediti
for (int i = 0; i < t; i++) {
int start = i * part_size + std::min(i, residue);
int end = start + part_size + (i < residue ? 1 : 0);
pairs.push_back(make_pair(start, end));
}
return pairs;
}
/**
* Automatically adjusts
*/
void auto_adjusts(const uint32_t duration) {
if (duration > (MYSQL_PERIODIC_INTERNAL_TIME*2)/3) {
if (t < max_t) {
t++; // povećaj broj asinkronih zadataka drugi put
}
else if (on_error) {
on_error("Periodic maintenance takes too long");
}
}
else if (duration < MYSQL_PERIODIC_INTERNAL_TIME/3 && t > 1) {
t--; // smanji nema potrebe
}
}
#ifndef MYSQL_USE_ASYNCO
/**
*
*/
uint64_t timestamp() {
return chrono::duration_cast<chrono::milliseconds>(
chrono::system_clock::now().time_since_epoch()
).count();
};
#endif
public: public:
function<void(const string&)> on_error; function<void(const string&)> on_error;
function<void()> on_connect;
uint32_t connect_trys = 3; uint32_t connect_trys = 3;
@ -206,13 +259,12 @@ public:
release_connection(connection); release_connection(connection);
} catch (sql::SQLException& e) { } catch (sql::SQLException& e) {
throw runtime_error(e.what()); throw runtime_error(e.what());
// std::cerr << "SQLState: " << e.getSQLState() << std::endl; // cerr << "SQLState: " << e.getSQLState() << endl;
// std::cerr << "Error code: " << e.getErrorCode() << std::endl; // cerr << "Error code: " << e.getErrorCode() << endl;
} }
return result; return result;
} }
/** /**
* Destruktor * Destruktor
* close all connections * close all connections

@ -1,26 +1,57 @@
#include "../lib/mysql.hpp" #include "../lib/mysql.hpp"
marcelb::mysql::MySQL::MySQL(const string _path, const string _username, const string _password, const string _db, const uint32_t _available): marcelb::mysql::MySQL::MySQL(const string _path, const string _username, const string _password, const string _db, const uint32_t _available):
#ifdef MYSQL_USE_ASYNCO #ifdef MYSQL_USE_ASYNCO
p_loop(periodic( [&] () { p_loop(periodic( [&] () {
cout << "U asynco" << endl; const uint64_t start = rtime_ms();
try { auto pairs = divide_and_conquer(connection_pool.size(), t);
auto start = rtime_ms();
_tloop(0, connection_pool.size()); vector<future<void>> fuve;
cout << "loop--------------------------- nema error, trajalo: " << rtime_ms() - start << endl; for (auto& pair : pairs) {
} catch (...) { fuve.push_back(async_ ([&, pair](){
// cout << "Bude neki error u loopu" << endl; _tloop(pair.first, pair.second);
if(on_error) { }));
on_error("Bude neki error u loopu"); }
for (auto& fu : fuve) {
try {
await_(fu);
} catch (...) {
if(on_error) {
on_error("Error in maintenance periodic loop.");
}
} }
} }
auto_adjusts(rtime_ms() - start);
}, MYSQL_PERIODIC_INTERNAL_TIME)), }, MYSQL_PERIODIC_INTERNAL_TIME)),
#else #else
tloop_future (async(launch::async, [&](){ tloop_future (async(launch::async, [&](){
while (run_tloop) { while (run_tloop) {
cout << "U STD async" << endl;
usleep(MYSQL_PERIODIC_INTERNAL_TIME*1000); usleep(MYSQL_PERIODIC_INTERNAL_TIME*1000);
_tloop(0, connection_pool.size());
const uint64_t start = timestamp();
auto pairs = divide_and_conquer(connection_pool.size(), t);
vector<future<void>> fuve;
for (auto& pair : pairs) {
fuve.push_back(async (launch::async, [&, pair](){
_tloop(pair.first, pair.second);
}));
}
for (auto& fu : fuve) {
try {
fu.get();
} catch (...) {
if(on_error) {
on_error("Error in maintenance periodic loop.");
}
}
}
auto_adjusts(timestamp() - start);
} }
return; return;
})), })),
@ -33,9 +64,6 @@ marcelb::mysql::MySQL::MySQL(const string _path, const string _username, const s
drv = get_mysql_driver_instance(); drv = get_mysql_driver_instance();
connect_pool(); connect_pool();
// set on initialization to avoid the error
last_loop_time = time(nullptr);
} }
@ -68,11 +96,28 @@ Connection* marcelb::mysql::MySQL::create_connection() {
} }
void marcelb::mysql::MySQL::connect_pool() { void marcelb::mysql::MySQL::connect_pool() {
lock_guard<mutex> lock(io); auto connect_ = [&]() -> void {
for (uint32_t i=0; i<pool_size; i++) { const uint32_t tens = pool_size/MYSQL_MAX_CONNECTION_INIT_SAME_TIME;
Connection* connection = create_connection(); for (uint32_t i=0; i<pool_size; i++) {
connection_pool.push(connection); Connection* connection = create_connection();
} {
lock_guard<mutex> lock(io);
connection_pool.push(connection);
}
if ((i+1)%tens == 0) {
usleep(MYSQL_DELAYED_CONNECT_TIME*1000);
}
}
if (on_connect) {
on_connect();
}
};
#ifdef MYSQL_USE_ASYNCO
async_ (connect_);
#else
connect_f = async (launch::async, connect_);
#endif
} }
void marcelb::mysql::MySQL::disconnect_pool() { void marcelb::mysql::MySQL::disconnect_pool() {
@ -108,35 +153,18 @@ bool marcelb::mysql::MySQL::disconnect_connection(Connection* connection) {
} }
void marcelb::mysql::MySQL::_tloop(uint32_t b, uint32_t e) { void marcelb::mysql::MySQL::_tloop(uint32_t b, uint32_t e) {
if (!run_tloop) {
return;
}
for (size_t i=b; i<connection_pool.size() && i<e; i++) { for (size_t i=b; i<connection_pool.size() && i<e; i++) {
try { try {
Connection *conn = nullptr; Connection *conn = nullptr; // provjeri ovdje svugdje možeš li koristiti release i ocupacy sada
{ conn = occupy_connection();
lock_guard<mutex> lock(io);
conn = connection_pool.front();
connection_pool.pop();
}
if (conn->isValid()) { if (conn->isValid()) {
cout << "Validno----" << endl; release_connection(conn);
connection_pool.push(conn);
condition.notify_one();
} else { } else {
cout << "Nije validno----" << endl;
if (!conn->isClosed()){ if (!conn->isClosed()){
cout << "Zatvori----" << endl; disconnect_connection(conn);
conn->close();
} }
Connection *n_conn = create_connection(); Connection *n_conn = create_connection();
{ release_connection(n_conn);
cout << "Otvori----" << endl;
lock_guard<mutex> lock(io);
connection_pool.push(n_conn);
condition.notify_one();
}
} }
} catch (const SQLException &error) { } catch (const SQLException &error) {
@ -145,16 +173,9 @@ void marcelb::mysql::MySQL::_tloop(uint32_t b, uint32_t e) {
} }
} }
} }
last_loop_time = time(nullptr);
} }
Connection* marcelb::mysql::MySQL::occupy_connection() { Connection* marcelb::mysql::MySQL::occupy_connection() {
if (last_loop_time + (MYSQL_PERIODIC_INTERNAL_TIME*3/1000) < time(nullptr)) {
if (on_error) {
on_error("The time loop is not executing properly");
}
}
unique_lock<mutex> lock(io); unique_lock<mutex> lock(io);
while (connection_pool.empty()) { while (connection_pool.empty()) {
condition.wait(lock); condition.wait(lock);
@ -171,9 +192,11 @@ void marcelb::mysql::MySQL::release_connection(Connection* connection) {
} }
marcelb::mysql::MySQL::~MySQL() { marcelb::mysql::MySQL::~MySQL() {
#ifdef MYSQL_USE_ASYNCO
p_loop.stop();
#else
run_tloop = false; run_tloop = false;
#ifndef MYSQL_USE_ASYNCO tloop_future.get();
tloop_future.get();
#endif #endif
disconnect_pool(); disconnect_pool();
} }

@ -1,2 +1,2 @@
# g++ -DMYSQL_USE_ASYNCO test.cpp ../src/* ../../asynco/src/* -o test.o -lmysqlcppconn -lpthread g++ -DMYSQL_USE_ASYNCO test.cpp ../src/* ../../asynco/src/* -o test.o -lmysqlcppconn -lpthread
g++ test.cpp ../src/* ../../asynco/src/* -o test.o -lmysqlcppconn -lpthread # g++ test.cpp ../src/* ../../asynco/src/* -o test.o -lmysqlcppconn -lpthread

@ -15,17 +15,21 @@ using namespace marcelb::mysql;
int main() { int main() {
auto inis = rtime_ms(); auto inis = rtime_ms();
try { try {
const int n = 5; const int n = 10;
// MySQL mydb("tcp://192.168.2.10:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", 5, time_loop_type::internal); // MySQL mydb("tcp://192.168.2.10:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", 5, time_loop_type::internal);
MySQL mydb("tcp://bitelex.ddns.net:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", n); MySQL mydb("tcp://bitelex.ddns.net:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", n);
// MySQL mydb("tcp://bitelex.ddns.net:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", 5); // MySQL mydb("tcp://bitelex.ddns.net:3306", "dinio", "H€r5elfInd1aH@nds", "dinio", 5);
cout << "init: " << rtime_ms() - inis << endl; cout << "--------------init: " << rtime_ms() - inis << endl;
mydb.on_error = [](const string& error) { mydb.on_error = [](const string& error) {
cout << error << endl; cout << error << endl;
}; };
mydb.on_connect = []() {
cout << "Init all pool connection done" << endl;
};
// periodic mysql_tloop ( [&mydb] () { // periodic mysql_tloop ( [&mydb] () {
// auto l_start = rtime_ms(); // auto l_start = rtime_ms();
// vector<future<void>> to_wait; // vector<future<void>> to_wait;
@ -203,7 +207,9 @@ while (true) {
} catch (const SQLException error) { } catch (const SQLException error) {
cout << error.what() << endl; cout << error.what() << endl;
} catch (const string error) { } catch (const exception& error) {
cout << error.what() << endl;
} catch (const string& error) {
cout << error << endl; cout << error << endl;
} catch (...) { } catch (...) {
cout << "Jebi ga" << endl; cout << "Jebi ga" << endl;

Loading…
Cancel
Save