Completed
Pull Request — master (#8)
by
unknown
02:03
created

RedisLockStrategy::isAcquired()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Tourstream\RedisLockStrategy;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2017 Alexander Miehe ([email protected])
9
 *  All rights reserved
10
 *
11
 *  You may not remove or change the name of the author above. See:
12
 *  http://www.gnu.org/licenses/gpl-faq.html#IWantCredit
13
 *
14
 *  This script is part of the Typo3 project. The Typo3 project is
15
 *  free software; you can redistribute it and/or modify
16
 *  it under the terms of the GNU General Public License as published by
17
 *  the Free Software Foundation; either version 3 of the License, or
18
 *  (at your option) any later version.
19
 *
20
 *  The GNU General Public License can be found at
21
 *  http://www.gnu.org/copyleft/gpl.html.
22
 *  A copy is found in the LICENSE and distributed with these scripts.
23
 *
24
 *
25
 *  This script is distributed in the hope that it will be useful,
26
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28
 *  GNU General Public License for more details.
29
 *
30
 *  This copyright notice MUST APPEAR in all copies of the script!
31
 ***************************************************************/
32
33
use TYPO3\CMS\Core\Locking\Exception\LockAcquireException;
34
use TYPO3\CMS\Core\Locking\Exception\LockCreateException;
35
use TYPO3\CMS\Core\Locking\Exception\LockAcquireWouldBlockException;
36
use TYPO3\CMS\Core\Locking\LockingStrategyInterface;
37
38
/**
39
 * @author Alexander Miehe <[email protected]>
40
 */
41
class RedisLockStrategy implements LockingStrategyInterface
42
{
43
    /**
44
     * @var \Redis A key-value data store
45
     */
46
    private $redis;
47
48
    /**
49
     * @var string The locking subject, i.e. the string to discriminate a lock
50
     */
51
    private $subject;
52
53
    /**
54
     * @var boolean TRUE if lock is acquired
55
     */
56
    private $isAcquired = false;
57
58
    /**
59
     * @var int Seconds the lock remains persistent
60
     */
61
    private $ttl = 3600;
62
63
    /**
64
     * @var int Seconds to wait for a lock
65
     */
66
    private $blTo = 30;
67
68
    /**
69
     * @inheritdoc
70
     */
71 9
    public function __construct($subject)
72
    {
73 9
        $config = null;
74
75 9
        if (\array_key_exists('redis_lock', $GLOBALS['TYPO3_CONF_VARS']['SYS'])) {
76 8
            $config = $GLOBALS['TYPO3_CONF_VARS']['SYS']['redis_lock'];
77
        }
78
79 9
        if (!\is_array($config)) {
80 2
            throw new LockCreateException('no configuration for redis lock strategy found');
81
        }
82
83 7
        if (!\array_key_exists('host', $config)) {
84 1
            throw new LockCreateException('no host for redis lock strategy found');
85
        }
86 6
        $port = 6379;
87
88 6
        if (\array_key_exists('port', $config)) {
89 5
            $port = (int) $config['port'];
90
        }
91
92 6
        if (!\array_key_exists('database', $config)) {
93 1
            throw new LockCreateException('no database for redis lock strategy found');
94
        }
95
96 5
        if (\array_key_exists('ttl', $config)) {
97
            $this->ttl = (int) $config['ttl'];
98
        }
99
100 5
        $this->subject = $subject;
101 5
        $this->redis   = new \Redis();
102 5
        $this->redis->connect($config['host'], $port);
103
104 5
        if (\array_key_exists('auth', $config)) {
105
            $this->redis->auth($config['auth']);
106
        }
107
108 5
        $this->redis->select((int) $config['database']);
109
110 5
        if (!$this->redis->exists($this->subject)) {
111
112
            // initialize synchronization object,
113
            // i.e. a simple list with some single random value
114 5
            if (!$this->redis->rPush($this->subject, uniqid())) {
115
                throw new LockCreateException('could not create lock entry');
116
            }
117
118 5
            if (!$this->redis->expire($this->subject, $this->ttl)) {
119
                throw new LockCreateException('could not set ttl to lock entry');
120
            }
121
        }
122 5
    }
123
124
    /**
125
     * @inheritdoc
126
     */
127 9
    public static function getCapabilities()
128
    {
129 9
        return self::LOCK_CAPABILITY_EXCLUSIVE | self::LOCK_CAPABILITY_NOBLOCK;
130
    }
131
132
    /**
133
     * @inheritdoc
134
     */
135 9
    public static function getPriority()
136
    {
137 9
        return 100;
138
    }
139
140
    /**
141
     * @inheritdoc
142
     */
143 3
    public function acquire($mode = self::LOCK_CAPABILITY_EXCLUSIVE)
144
    {
145 3
        if ($this->isAcquired) {
146
            return true;
147
        }
148
149 3
        if (!$this->redis->exists($this->subject)) {
150
            throw new LockAcquireException('lock entry could not be found');
151
        }
152
153 3
        if ($mode & self::LOCK_CAPABILITY_NOBLOCK) {
154
155
            // this does not block
156
            $this->isAcquired = (bool) $this->redis->lPop($this->subject);
157
158
            if (!$this->isAcquired) {
159
                throw new LockAcquireWouldBlockException('could not acquire lock');
160
            }
161
        } else {
162
163
            // this blocks iff the list is empty
164 3
            $this->isAcquired = (bool) $this->redis->blPop([$this->subject], $this->blTo);
165
166 3
            if (!$this->isAcquired) {
167
                throw new LockAcquireException('could not acquire lock');
168
            }
169
        }
170
171 3
        return true;
172
    }
173
174
    /**
175
     * @inheritdoc
176
     */
177 1
    public function isAcquired()
178
    {
179 1
        return $this->isAcquired;
180
    }
181
182
    /**
183
     * @inheritdoc
184
     */
185 2
    public function destroy()
186
    {
187 2
        $this->redis->del($this->subject);
188 2
    }
189
190
    /**
191
     * @inheritdoc
192
     */
193
    public function release()
194
    {
195
        if (!$this->isAcquired) {
196
            return true;
197
        }
198
199
        $this->isAcquired = !$this->redis->rPush($this->subject, uniqid());
200
201
        return !$this->isAcquired;
202
    }
203
204
}
205