diff options
author | Kate Butler <kate@innocraft.com> | 2019-12-23 00:21:28 +0300 |
---|---|---|
committer | Thomas Steur <tsteur@users.noreply.github.com> | 2019-12-23 00:21:28 +0300 |
commit | 954f6ce2936818e35cc80083526e9ffbe6ea58c6 (patch) | |
tree | b49bf1708fd3d307ef86bd346ecb7eb0366181f6 /core/Concurrency | |
parent | a9a94e937b5faec70e5388c527517ac8dd531fa2 (diff) |
Prevent race conditions when updating plugin settings (#15249)
Diffstat (limited to 'core/Concurrency')
-rw-r--r-- | core/Concurrency/Lock.php | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/core/Concurrency/Lock.php b/core/Concurrency/Lock.php index 7a7de32a37..e3200310bd 100644 --- a/core/Concurrency/Lock.php +++ b/core/Concurrency/Lock.php @@ -12,6 +12,8 @@ use Piwik\Common; class Lock { + const MAX_KEY_LEN = 70; + /** * @var LockBackend */ @@ -39,6 +41,30 @@ class Lock return $this->backend->getKeysMatchingPattern($this->lockKeyStart . '*'); } + public function execute($id, $callback) + { + if (Common::mb_strlen($id) > self::MAX_KEY_LEN) { + // Lock key might be too long for DB column, so we hash it but leave the start of the original as well + // to make it more readable + $md5Len = 32; + $id = Common::mb_substr($id, 0, self::MAX_KEY_LEN - $md5Len - 1) . md5($id); + } + + $i = 0; + while (!$this->acquireLock($id)) { + $i++; + usleep( 100 * 1000 ); // 100ms + if ($i > 50) { // give up after 5seconds (50 * 100ms) + throw new \Exception('Could not get the lock for ID: ' . $id); + } + }; + try { + return $callback(); + } finally { + $this->unlock(); + } + } + public function acquireLock($id, $ttlInSeconds = 60) { $this->lockKey = $this->lockKeyStart . $id; |