Skip to content

feature/user-model #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ coverage:
default: off
tests:
target: 95%
flags: tests
paths: "tests/"
app:
target: 98%
flags: app
paths: "app/"
ignore:
- "thirdparty/"
- "configs/"
Expand Down
3 changes: 1 addition & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ build_test_coverage:
coverage: '/lines[\.]+\: (\d+\.\d+)\%/'
script:
- mkdir -p test_build; cd test_build
- cmake ../tests; make -j
- ./ppic_test
- cmake ../tests; make -j build_and_test
- lcov -b . -d . -c -o cov.info
- lcov -r cov.info "/usr/*" "*/thirdparty/*" "*/tests/*" "*/test_build/*" -o cov.info -q
- lcov -l cov.info
Expand Down
4 changes: 3 additions & 1 deletion Dockerfiles/Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ RUN apk add --no-cache --virtual build-dependencies make cmake gcompat linux-hea

COPY lcov /usr/bin
COPY genhtml /usr/bin
COPY geninfo /usr/bin
COPY geninfo /usr/bin

RUN apk add --no-cache gdb
34 changes: 19 additions & 15 deletions app/db/session_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
\*****************************************************************************/

#include "db/session_pool.h"
#include "db/smart_session.h"
#include <cstdlib>
#include <cassert>

Expand All @@ -37,7 +36,7 @@ SessionPoolOption::SessionPoolOption()

SessionPoolOption::SessionPoolOption(const string& user, const string& password, const string& host, uint16_t capacity, uint16_t port)
: user_(user), password_(password), host_(host), capacity_(capacity), port_(port) {
char url[512] = {};
char url[512] = {0};
sprintf(url, "%s:%s@%s:%u", user_.c_str(), password_.c_str(), host_.c_str(), port_);
url_ = url;
}
Expand All @@ -64,7 +63,7 @@ SessionPoolOption& SessionPoolOption::FromEnv(const char* url_env, const char* d


SessionPool::SessionPool()
: current_size_(0) {
: current_size_(0), usable_(true) {
}

SessionPool& SessionPool::InitPool(const SessionPoolOption& option) {
Expand All @@ -73,17 +72,17 @@ SessionPool& SessionPool::InitPool(const SessionPoolOption& option) {
option_ = option;
// TODO: 根据一定的策略初始化SessionPool的初始连接数,减少使用连接时的开销。
// 并在可用连接不足时扩容(按照一定策略扩),在闲置连接富余时缩容(按照一定策略缩)。
for (uint16_t i = 0; i < option.capacity() / 2; ++i) {
for (uint16_t i = 0; i < option.capacity() / 2 + 1; ++i) {
try {
std::shared_ptr<Session> session{new SmartSession(option_)};
pool_.push_back(session);
pool_.emplace_back(std::make_shared<SmartSession>(option_));
current_size_++;
} catch (const mysqlx::Error &err) {
char msg[128];
sprintf(msg, "[ERROR] Connect to mysql %s failed.", option.url().c_str());
sprintf(msg, "[ERROR] Connect to mysql %s failed.", option.url());
throw std::runtime_error(msg);
}
}
usable_ = true;
return *this;
}

Expand All @@ -93,31 +92,36 @@ void SessionPool::DestroyPool() {
session->close();
}
current_size_ = 0;
usable_ = false;
pool_.clear();
pool_cv_.notify_all();
}

std::shared_ptr<Session> SessionPool::ObtainSession() {
std::shared_ptr<SmartSession> SessionPool::ObtainSession() {
std::unique_lock<std::mutex> lock(pool_mtx_);
if (pool_.size() == 0
&& current_size_ < option_.capacity()) {
std::shared_ptr<Session> session{new SmartSession(option_)};

if (usable_ && pool_.empty() && current_size_ < option_.capacity()) {
auto session = std::make_shared<SmartSession>(option_);
current_size_++;
return session;
}

while (pool_.size() == 0) {
pool_cv_.wait(lock);
pool_cv_.wait(lock, [this]{return !usable_ || !pool_.empty();});

if (!usable_ && pool_.empty()) {
return nullptr;
}

auto session = pool_.front();
pool_.pop_front();
return session;
}

void SessionPool::ReleaseSession(std::shared_ptr<Session>& session) {
void SessionPool::ReleaseSession(std::shared_ptr<SmartSession>& session) {
std::unique_lock<std::mutex> lock(pool_mtx_);
// 只有引用个数为1时才能释放,不然可能会导致两个不同的线程持有相同的session
if (session.use_count() == 1) {
pool_.push_back(session);
pool_.emplace_back(session);
pool_cv_.notify_one();
}
}
Expand Down
14 changes: 9 additions & 5 deletions app/db/session_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#define PPIC_DB_SESSION_POOL_H_

#include "common/singleton.h"
#include "db/smart_session.h"
#include "mysqlx/xdevapi.h"
#include <string>
#include <list>
Expand All @@ -51,8 +52,8 @@ class SessionPoolOption {
SessionPoolOption& FromEnv(const char* url_env="MYSQL_CONNECTION_URL",
const char* db_env="MYSQL_DATABASE");
SessionPoolOption& set_capacity(uint16_t capacity) { capacity_ = capacity; return *this; }
const string& url() const { return url_; }
const string& db() const { return db_; }
const char* url() const { return url_.c_str(); }
const char* db() const { return db_.c_str(); }
const uint16_t capacity() const { return capacity_; }
private:
string url_;
Expand All @@ -66,17 +67,20 @@ class SessionPoolOption {

class SessionPool {
public:
SessionPool(const SessionPool&) = delete;
SessionPool& operator=(const SessionPool&) = delete;
~SessionPool() { DestroyPool(); }
SessionPool& InitPool(const SessionPoolOption&);
void DestroyPool();
std::shared_ptr<Session> ObtainSession();
void ReleaseSession(std::shared_ptr<Session>&);
std::shared_ptr<SmartSession> ObtainSession();
void ReleaseSession(std::shared_ptr<SmartSession>&);
private:
friend class Singleton<SessionPool>;
SessionPool();

std::list<std::shared_ptr<Session>> pool_;
std::list<std::shared_ptr<SmartSession>> pool_;
SessionPoolOption option_;
bool usable_;
uint16_t current_size_;
std::mutex pool_mtx_;
std::condition_variable pool_cv_;
Expand Down
8 changes: 8 additions & 0 deletions app/db/smart_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,25 @@ using mysqlx::Schema;
SmartSession::SmartSession(const SessionPoolOption& option)
: Session(option.url()) {
current_schema_ = std::make_shared<Schema>(createSchema(option.db(), true));
UseDatabase(option.db());
}

SmartSession& SmartSession::SelectSchema(const char* schema_name) {
current_schema_ = std::make_shared<Schema>(createSchema(schema_name, true));
UseDatabase(schema_name);
return *this;
}

Schema& SmartSession::GetCurrentSchema() const {
return *current_schema_.get();
}

void SmartSession::UseDatabase(const char* db_name) {
char sql_str[128] = {0};
snprintf(sql_str, 128, "USE %s;", db_name);
sql(sql_str).execute();
}

} // namespace db

} // namespace ppic
1 change: 1 addition & 0 deletions app/db/smart_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class SmartSession : public mysqlx::Session {
// Return current schema, if there are no schema it will throw an error.
mysqlx::Schema& GetCurrentSchema() const;
private:
void UseDatabase(const char* db_name);
std::shared_ptr<mysqlx::Schema> current_schema_;
};

Expand Down
69 changes: 56 additions & 13 deletions app/models/user.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,85 @@

#include "models/user.h"
#include "db/session_pool.h"
#include "db/smart_session.h"

namespace ppic {

namespace model {

using ppic::db::SessionPoolSingleton;

mysqlx::SqlResult UserDbManager::CreateTable(const char* table_name) {
auto session = SessionPoolSingleton::instance()->ObtainSession();
std::shared_ptr<Table> UserDbManager::GetOrCreateTable(const char* table_name) {
std::lock_guard<std::mutex> lock(table_mtx_);
if (!table_name_.empty() && table_name_ != table_name) {
if (table_ && table_name_ != table_name) {
char msg[128];
snprintf(msg, 128, "[ERROR] Can't create `user` model twice with different table name.");
throw std::runtime_error(msg);
}
table_name_ = table_name;
if (!table_) {
table_name_ = table_name;
} else {
return table_;
}

auto session = SessionPoolSingleton::instance()->ObtainSession();
char create_sql[1024];
snprintf(create_sql, 1024,
"CREATE TABLE IF NOT EXISTS %s ( \
id INT UNSIGNED NOT NULL AUTO_INCREMENT, \
name VARCHAR(128) NOT NULL, \
registration_date TIMESTAMP NOT NULL \
registration_at TIMESTAMP NOT NULL \
DEFAULT CURRENT_TIMESTAMP, \
PRIMARY KEY (id) \
);", table_name);
session->sql("use ppic_test;").execute();
return session->sql(create_sql).execute();
session->sql(create_sql).execute();
table_ = std::make_shared<Table>(session->GetCurrentSchema().getTable(table_name));
SessionPoolSingleton::instance()->ReleaseSession(session);
return table_;
}

std::unique_ptr<User> UserDbManager::CreateUser(const string& name) {
void UserDbManager::DropTable() {
std::lock_guard<std::mutex> lock(table_mtx_);
if (!table_) {
return;
}
auto session = SessionPoolSingleton::instance()->ObtainSession();
std::unique_ptr<User> user{new User()};
user->id_ = 0;
user->name_ = name;
user->registration_date_ = "";
return user;
session->dropSchema(table_name_);
SessionPoolSingleton::instance()->ReleaseSession(session);
table_.reset();
}

std::shared_ptr<User> UserDbManager::CreateUser(const string& name) {
std::unique_lock<std::mutex> lock(table_mtx_);
if (!table_) {
lock.unlock();
GetOrCreateTable();
lock.lock();
}
auto res = table_->insert("name").values(name).execute();
uint64_t id = res.getAutoIncrementValue();
lock.unlock();
return GetUserById(id);
}

std::shared_ptr<User> UserDbManager::GetUserById(uint64_t id) {
std::unique_lock<std::mutex> lock(table_mtx_);
if (!table_) {
lock.unlock();
GetOrCreateTable();
lock.lock();
}
auto res = table_->select("id", "name", "registration_at").where("id = :id").bind("id", id).execute();
auto row = res.fetchOne();
lock.unlock();
return RowToUser(row);
}

std::shared_ptr<User> UserDbManager::RowToUser(const mysqlx::Row& row) {
if (row.isNull()) {
return nullptr;
}
return std::shared_ptr<User>{new User(row[0], row[1], row[2])};
}

} // namespace model
Expand Down
17 changes: 10 additions & 7 deletions app/models/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,33 @@ class User {
User& operator=(const User&) = delete;

uint64_t id() { return id_; }
const string& name() { return name_; }
const string& registration_date() { return registration_date_ ; }
const char* name() { return name_.c_str(); }
const char* registration_at() { return registration_at_.c_str() ; }
private:
friend class UserDbManager;
User() {}
User(uint64_t id, const string& name, const string& registration_at)
: id_(id), name_(name), registration_at_(registration_at) {}

uint64_t id_;
string name_;
string registration_date_;
string registration_at_;
};

class UserDbManager {
public:
UserDbManager(const UserDbManager&) = delete;
UserDbManager& operator=(const UserDbManager&) = delete;

std::shared_ptr<Table> table() { return table_; }
mysqlx::SqlResult CreateTable(const char* table_name="user");
std::unique_ptr<User> CreateUser(const string& name);
//User GetUserById(uint64_t id);
std::shared_ptr<Table> GetOrCreateTable(const char* table_name="user");
void DropTable();
std::shared_ptr<User> CreateUser(const string& name);
std::shared_ptr<User> GetUserById(uint64_t id);
//mysqlx::Result DeleteById(uint64_t id);
private:
friend class Singleton<UserDbManager>;
UserDbManager() : table_name_("") {}
std::shared_ptr<User> RowToUser(const mysqlx::Row&);

string table_name_;
std::mutex table_mtx_;
Expand Down
Loading