comparison Framework/PostgreSQL/PostgreSQLDatabase.cpp @ 134:cc3dc759c989

Added an advisory lock to avoid race conditions during database setup
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 08 May 2019 20:21:13 +0200
parents 5765cc5fd268
children 4cd7e45b671e
comparison
equal deleted inserted replaced
133:b1d7d255fb73 134:cc3dc759c989
29 29
30 #include <Core/Logging.h> 30 #include <Core/Logging.h>
31 #include <Core/OrthancException.h> 31 #include <Core/OrthancException.h>
32 32
33 #include <boost/lexical_cast.hpp> 33 #include <boost/lexical_cast.hpp>
34 #include <boost/thread.hpp>
34 35
35 36
36 namespace OrthancDatabases 37 namespace OrthancDatabases
37 { 38 {
38 void PostgreSQLDatabase::ThrowException(bool log) 39 void PostgreSQLDatabase::ThrowException(bool log)
107 throw Orthanc::OrthancException(Orthanc::ErrorCode_DatabaseUnavailable); 108 throw Orthanc::OrthancException(Orthanc::ErrorCode_DatabaseUnavailable);
108 } 109 }
109 } 110 }
110 111
111 112
113 bool PostgreSQLDatabase::RunAdvisoryLockStatement(const std::string& statement)
114 {
115 PostgreSQLTransaction transaction(*this);
116
117 Query query(statement, false);
118 PostgreSQLStatement s(*this, query);
119
120 PostgreSQLResult result(s);
121
122 bool success = (!result.IsDone() &&
123 result.GetBoolean(0));
124
125 transaction.Commit();
126
127 return success;
128 }
129
130
131 bool PostgreSQLDatabase::AcquireAdvisoryLock(int32_t lock)
132 {
133 return RunAdvisoryLockStatement(
134 "select pg_try_advisory_lock(" +
135 boost::lexical_cast<std::string>(lock) + ")");
136 }
137
138
139 bool PostgreSQLDatabase::ReleaseAdvisoryLock(int32_t lock)
140 {
141 return RunAdvisoryLockStatement(
142 "select pg_advisory_unlock(" +
143 boost::lexical_cast<std::string>(lock) + ")");
144 }
145
146
112 void PostgreSQLDatabase::AdvisoryLock(int32_t lock) 147 void PostgreSQLDatabase::AdvisoryLock(int32_t lock)
113 { 148 {
114 PostgreSQLTransaction transaction(*this); 149 if (!AcquireAdvisoryLock(lock))
115
116 Query query("select pg_try_advisory_lock(" +
117 boost::lexical_cast<std::string>(lock) + ");", false);
118 PostgreSQLStatement s(*this, query);
119
120 PostgreSQLResult result(s);
121 if (result.IsDone() ||
122 !result.GetBoolean(0))
123 { 150 {
124 LOG(ERROR) << "The PostgreSQL database is locked by another instance of Orthanc"; 151 LOG(ERROR) << "The PostgreSQL database is locked by another instance of Orthanc";
125 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 152 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
126 } 153 }
127
128 transaction.Commit();
129 } 154 }
130 155
131 156
132 void PostgreSQLDatabase::Execute(const std::string& sql) 157 void PostgreSQLDatabase::Execute(const std::string& sql)
133 { 158 {
241 else 266 else
242 { 267 {
243 return new PostgreSQLTransaction(*this); 268 return new PostgreSQLTransaction(*this);
244 } 269 }
245 } 270 }
271
272
273 PostgreSQLDatabase::TransientAdvisoryLock::TransientAdvisoryLock(
274 PostgreSQLDatabase& database,
275 int32_t lock) :
276 database_(database),
277 lock_(lock)
278 {
279 bool locked = true;
280
281 for (unsigned int i = 0; i < 10; i++)
282 {
283 if (database_.AcquireAdvisoryLock(lock_))
284 {
285 locked = false;
286 break;
287 }
288 else
289 {
290 boost::this_thread::sleep(boost::posix_time::milliseconds(500));
291 }
292 }
293
294 if (locked)
295 {
296 LOG(ERROR) << "Cannot acquire a transient advisory lock";
297 throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
298 }
299 }
300
301
302 PostgreSQLDatabase::TransientAdvisoryLock::~TransientAdvisoryLock()
303 {
304 database_.ReleaseAdvisoryLock(lock_);
305 }
246 } 306 }