PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Concurrency / Lock.php
matomo / app / core / Concurrency Last commit date
LockBackend 6 years ago DistributedList.php 6 years ago Lock.php 6 years ago LockBackend.php 6 years ago
Lock.php
156 lines
1 <?php
2 /**
3 * Piwik - free/libre analytics platform
4 *
5 * @link http://piwik.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik\Concurrency;
10
11 use Piwik\ArchiveProcessor\ArchivingStatus;
12 use Piwik\Common;
13 use Piwik\Date;
14
15 class Lock
16 {
17 const MAX_KEY_LEN = 70;
18 const DEFAULT_TTL = 60;
19
20 /**
21 * @var LockBackend
22 */
23 private $backend;
24
25 private $lockKeyStart;
26
27 private $lockKey = null;
28 private $lockValue = null;
29 private $defaultTtl = null;
30 private $lastExpireTime = null;
31
32 public function __construct(LockBackend $backend, $lockKeyStart, $defaultTtl = null)
33 {
34 $this->backend = $backend;
35 $this->lockKeyStart = $lockKeyStart;
36 $this->lockKey = $this->lockKeyStart;
37 $this->defaultTtl = $defaultTtl ?: self::DEFAULT_TTL;
38 }
39
40 public function reexpireLock()
41 {
42 $timeBetweenReexpires = $this->defaultTtl - ($this->defaultTtl / 4);
43
44 $now = Date::getNowTimestamp();
45 if (!empty($this->lastExpireTime) &&
46 $now <= $this->lastExpireTime + $timeBetweenReexpires
47 ) {
48 return false;
49 }
50
51 return $this->expireLock($this->defaultTtl);
52 }
53
54 public function getNumberOfAcquiredLocks()
55 {
56 return count($this->getAllAcquiredLockKeys());
57 }
58
59 public function getAllAcquiredLockKeys()
60 {
61 return $this->backend->getKeysMatchingPattern($this->lockKeyStart . '*');
62 }
63
64 public function execute($id, $callback)
65 {
66 $i = 0;
67 while (!$this->acquireLock($id)) {
68 $i++;
69 usleep( 100 * 1000 ); // 100ms
70 if ($i > 50) { // give up after 5seconds (50 * 100ms)
71 throw new \Exception('Could not get the lock for ID: ' . $id);
72 }
73 };
74 try {
75 return $callback();
76 } finally {
77 $this->unlock();
78 }
79 }
80
81 public function acquireLock($id, $ttlInSeconds = 60)
82 {
83 $this->lockKey = $this->lockKeyStart . $id;
84
85 if (Common::mb_strlen($this->lockKey) > self::MAX_KEY_LEN) {
86 // Lock key might be too long for DB column, so we hash it but leave the start of the original as well
87 // to make it more readable
88 $md5Len = 32;
89 $this->lockKey = Common::mb_substr($id, 0, self::MAX_KEY_LEN - $md5Len - 1) . md5($id);
90 }
91
92 $lockValue = substr(Common::generateUniqId(), 0, 12);
93 $locked = $this->backend->setIfNotExists($this->lockKey, $lockValue, $ttlInSeconds);
94
95 if ($locked) {
96 $this->lockValue = $lockValue;
97 $this->ttlUsed = $ttlInSeconds;
98 $this->lastExpireTime = Date::getNowTimestamp();
99 }
100
101 return $locked;
102 }
103
104 public function isLocked()
105 {
106 if (!$this->lockValue) {
107 return false;
108 }
109
110 return $this->lockValue === $this->backend->get($this->lockKey);
111 }
112
113 public function unlock()
114 {
115 if ($this->lockValue) {
116 $this->backend->deleteIfKeyHasValue($this->lockKey, $this->lockValue);
117 $this->lockValue = null;
118 }
119 }
120
121 public function expireLock($ttlInSeconds)
122 {
123 if ($ttlInSeconds > 0) {
124 if ($this->lockValue) {
125 $success = $this->backend->expireIfKeyHasValue($this->lockKey, $this->lockValue, $ttlInSeconds);
126 if (!$success) {
127 $value = $this->backend->get($this->lockKey);
128 $message = sprintf('Failed to expire key %s (%s / %s).', $this->lockKey, $this->lockValue, (string)$value);
129
130 if ($value === false) {
131 Common::printDebug($message . ' It seems like the key already expired as it no longer exists.');
132 } elseif (!empty($value) && $value == $this->lockValue) {
133 Common::printDebug($message . ' We still have the lock but for some reason it did not expire.');
134 } elseif (!empty($value)) {
135 Common::printDebug($message . ' This lock has been acquired by another process/server.');
136 } else {
137 Common::printDebug($message . ' Failed to expire key.');
138 }
139
140 return false;
141 }
142
143 $this->lastExpireTime = Date::getNowTimestamp();
144
145 return true;
146 } else {
147 Common::printDebug('Lock is not acquired, cannot update expiration.');
148 }
149 } else {
150 Common::printDebug('Provided TTL ' . $ttlInSeconds . ' is in valid in Lock::expireLock().');
151 }
152
153 return false;
154 }
155 }
156