This code can work well .
void DBhelper::Save(OrganizationInfo &info) {
if (!info.updateTime) {
info.updateTime = time(0);
}
auto app = RXApplication::Instance();
auto logger = app->Logger();
auto storage{*db};
auto items = storage.get_all<OrganizationInfo>(where(c(&OrganizationInfo::domain) == info.domain));
storage.begin_transaction();
try {
if (items.size() == 0) {
storage.insert(info);
logger->info("Insert Org:{}", info.domain);
}
storage.commit();
std::cout << "SUCCESS THREAD:" << std::this_thread::get_id() << endl;
} catch (std::exception &e) {
storage.rollback();
logger->error("WRITE DB ERROR:{}", e.what());
std::cout << "FAILED THREAD:" << std::this_thread::get_id() << endl;
}
}
This code can not work well in multithread.
void DBhelper::Save(OrganizationInfo &info) {
if (!info.updateTime) {
info.updateTime = time(0);
}
auto app = RXApplication::Instance();
auto logger = app->Logger();
auto storage{*db};
storage.begin_transaction();
try {
auto items = storage.get_all<OrganizationInfo>(where(c(&OrganizationInfo::domain) == info.domain));
if (items.size() == 0) {
storage.insert(info);
logger->info("Insert Org:{}", info.domain);
}
storage.commit();
std::cout << "SUCCESS THREAD:" << std::this_thread::get_id() << endl;
} catch (std::exception &e) {
storage.rollback();
logger->error("WRITE DB ERROR:{}", e.what());
std::cout << "FAILED THREAD:" << std::this_thread::get_id() << endl;
}
}
Thanks.
The first code work well in multithread ,but the second code only work well in single thread.
The only difference of these two code segment is the position of
auto items = storage.get_all
The error is " database is locked".
Thanks.
@yuanlida please wrap all your code snippets with the ` character or use GitHub's "Insert code" option. And please try to summarize your question in one message.
@yuanlida both your code examples can raise this exception but the second version has more chance to do it cause it has a longer transaction. You need to understand that only one thread can keep a transaction at the same time. Other threads can call storage.begin_transaction() only after last working thread called storage.commit(); or storage.rollback();. This is not an sqlite_orm issue but data races issue. Imagine you have std::map with your objects instead of storage and you try to write inside this map using several threads simultaneously. You'll get data races and undefined behavior cause map inserting operations are not atomic. If you write a multithreaded app you must understand how you need to handle data races cause otherwise your app will be illformed. Please take your time to read some articles about it. Example https://www.modernescpp.com/index.php/race-condition-versus-data-race .
To fix your case fast I can advice you adding a mutex for accessing database, lock it before calling storage.begin_transaction() and unlock it right after calling storage.commit(); or storage.rollback();. More details on SO. You don't need a mutex to read data but you need it to write.
SQLite3 doesn't handle multithread logic for you but it just allows you to receive a concrete exception instead of undefined behavior by data races and that is good.
Thanks, I have add a lock to the code.