comparison 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
comparison
equal deleted inserted replaced
134:cc3dc759c989 135:e26690365c25
33 33
34 #include <errmsg.h> 34 #include <errmsg.h>
35 #include <mysqld_error.h> 35 #include <mysqld_error.h>
36 36
37 #include <memory> 37 #include <memory>
38 #include <boost/thread.hpp>
38 39
39 namespace OrthancDatabases 40 namespace OrthancDatabases
40 { 41 {
41 void MySQLDatabase::Close() 42 void MySQLDatabase::Close()
42 { 43 {
287 return false; 288 return false;
288 } 289 }
289 } 290 }
290 291
291 292
293 bool MySQLDatabase::RunAdvisoryLockStatement(const std::string& s)
294 {
295 Query query(s, false);
296 MySQLStatement statement(*this, query);
297
298 MySQLTransaction t(*this);
299 Dictionary args;
300
301 std::auto_ptr<IResult> result(t.Execute(statement, args));
302
303 bool success = (!result->IsDone() &&
304 result->GetField(0).GetType() == ValueType_Integer64 &&
305 dynamic_cast<const Integer64Value&>(result->GetField(0)).GetValue() == 1);
306
307 t.Commit();
308
309 return success;
310 }
311
312
313 bool MySQLDatabase::AcquireAdvisoryLock(int32_t lock)
314 {
315 return RunAdvisoryLockStatement("SELECT GET_LOCK('Lock" +
316 boost::lexical_cast<std::string>(lock) + "', 0);");
317 }
318
319
320 bool MySQLDatabase::ReleaseAdvisoryLock(int32_t lock)
321 {
322 return RunAdvisoryLockStatement("SELECT RELEASE_LOCK('Lock" +
323 boost::lexical_cast<std::string>(lock) + "');");
324 }
325
326
292 void MySQLDatabase::AdvisoryLock(int32_t lock) 327 void MySQLDatabase::AdvisoryLock(int32_t lock)
293 { 328 {
294 try 329 if (!AcquireAdvisoryLock(lock))
295 {
296 Query query("SELECT GET_LOCK('Lock" +
297 boost::lexical_cast<std::string>(lock) + "', 0);", false);
298 MySQLStatement statement(*this, query);
299
300 MySQLTransaction t(*this);
301 Dictionary args;
302
303 std::auto_ptr<IResult> result(t.Execute(statement, args));
304
305 if (result->IsDone() ||
306 result->GetField(0).GetType() != ValueType_Integer64 ||
307 dynamic_cast<const Integer64Value&>(result->GetField(0)).GetValue() != 1)
308 {
309 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
310 }
311
312 t.Commit();
313 }
314 catch (Orthanc::OrthancException&)
315 { 330 {
316 LOG(ERROR) << "The MySQL database is locked by another instance of Orthanc"; 331 LOG(ERROR) << "The MySQL database is locked by another instance of Orthanc";
317 Close();
318 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 332 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
319 } 333 }
320 } 334 }
321 335
322 336
493 } 507 }
494 } 508 }
495 509
496 return true; 510 return true;
497 } 511 }
512
513
514 MySQLDatabase::TransientAdvisoryLock::
515 TransientAdvisoryLock(MySQLDatabase& database,
516 int32_t lock) :
517 database_(database),
518 lock_(lock)
519 {
520 bool locked = true;
521
522 for (unsigned int i = 0; i < 10; i++)
523 {
524 if (database_.AcquireAdvisoryLock(lock_))
525 {
526 locked = false;
527 break;
528 }
529 else
530 {
531 boost::this_thread::sleep(boost::posix_time::milliseconds(500));
532 }
533 }
534
535 if (locked)
536 {
537 LOG(ERROR) << "Cannot acquire a transient advisory lock";
538 throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
539 }
540 }
541
542
543 MySQLDatabase::TransientAdvisoryLock::~TransientAdvisoryLock()
544 {
545 database_.ReleaseAdvisoryLock(lock_);
546 }
498 } 547 }