Mercurial > hg > orthanc-databases
diff Framework/MySQL/MySQLDatabase.cpp @ 135:e26690365c25
MySQL: Added an advisory lock to avoid race conditions during database setup
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 08 May 2019 21:09:18 +0200 |
parents | 714c5d2bee76 |
children | 52b3859ee0b7 |
line wrap: on
line diff
--- a/Framework/MySQL/MySQLDatabase.cpp Wed May 08 20:21:13 2019 +0200 +++ b/Framework/MySQL/MySQLDatabase.cpp Wed May 08 21:09:18 2019 +0200 @@ -35,6 +35,7 @@ #include <mysqld_error.h> #include <memory> +#include <boost/thread.hpp> namespace OrthancDatabases { @@ -289,32 +290,45 @@ } + bool MySQLDatabase::RunAdvisoryLockStatement(const std::string& s) + { + Query query(s, false); + MySQLStatement statement(*this, query); + + MySQLTransaction t(*this); + Dictionary args; + + std::auto_ptr<IResult> result(t.Execute(statement, args)); + + bool success = (!result->IsDone() && + result->GetField(0).GetType() == ValueType_Integer64 && + dynamic_cast<const Integer64Value&>(result->GetField(0)).GetValue() == 1); + + t.Commit(); + + return success; + } + + + bool MySQLDatabase::AcquireAdvisoryLock(int32_t lock) + { + return RunAdvisoryLockStatement("SELECT GET_LOCK('Lock" + + boost::lexical_cast<std::string>(lock) + "', 0);"); + } + + + bool MySQLDatabase::ReleaseAdvisoryLock(int32_t lock) + { + return RunAdvisoryLockStatement("SELECT RELEASE_LOCK('Lock" + + boost::lexical_cast<std::string>(lock) + "');"); + } + + void MySQLDatabase::AdvisoryLock(int32_t lock) { - try - { - Query query("SELECT GET_LOCK('Lock" + - boost::lexical_cast<std::string>(lock) + "', 0);", false); - MySQLStatement statement(*this, query); - - MySQLTransaction t(*this); - Dictionary args; - - std::auto_ptr<IResult> result(t.Execute(statement, args)); - - if (result->IsDone() || - result->GetField(0).GetType() != ValueType_Integer64 || - dynamic_cast<const Integer64Value&>(result->GetField(0)).GetValue() != 1) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); - } - - t.Commit(); - } - catch (Orthanc::OrthancException&) + if (!AcquireAdvisoryLock(lock)) { LOG(ERROR) << "The MySQL database is locked by another instance of Orthanc"; - Close(); throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); } } @@ -495,4 +509,39 @@ return true; } + + + MySQLDatabase::TransientAdvisoryLock:: + TransientAdvisoryLock(MySQLDatabase& database, + int32_t lock) : + database_(database), + lock_(lock) + { + bool locked = true; + + for (unsigned int i = 0; i < 10; i++) + { + if (database_.AcquireAdvisoryLock(lock_)) + { + locked = false; + break; + } + else + { + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + } + } + + if (locked) + { + LOG(ERROR) << "Cannot acquire a transient advisory lock"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); + } + } + + + MySQLDatabase::TransientAdvisoryLock::~TransientAdvisoryLock() + { + database_.ReleaseAdvisoryLock(lock_); + } }