Completed
Pull Request — master (#8)
by
unknown
01:41
created

RedisLockStrategy::acquire()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 26
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6.2017

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 26
ccs 7
cts 11
cp 0.6364
rs 8.439
cc 5
eloc 12
nc 6
nop 1
crap 6.2017
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\LockingStrategyInterface;
36
37
/**
38
 * @author Alexander Miehe <[email protected]>
39
 */
40
class RedisLockStrategy implements LockingStrategyInterface
41
{
42
    /**
43
     * @var \Redis
44
     */
45
    private $redis;
46
47
    /**
48
     * @var string
49
     */
50
    private $subject;
51
52
    /**
53
     * @var boolean TRUE if lock is acquired
54
     */
55
    private $isAcquired = false;
56
57
    /**
58
     * @var int Seconds the lock remains persistent
59
     */
60
    private $ttl = 3600;
61
62
    /**
63
     * @var int Seconds to wait for a lock
64
     */
65
    private $blTo = 60;
66
67
    /**
68
     * @inheritdoc
69
     */
70 9
    public function __construct($subject)
71
    {
72 9
        $config = null;
73
74 9
        if (\array_key_exists('redis_lock', $GLOBALS['TYPO3_CONF_VARS']['SYS'])) {
75 8
            $config = $GLOBALS['TYPO3_CONF_VARS']['SYS']['redis_lock'];
76
        }
77
78 9
        if (!\is_array($config)) {
79 2
            throw new LockCreateException('no configuration for redis lock strategy found');
80
        }
81
82 7
        if (!\array_key_exists('host', $config)) {
83 1
            throw new LockCreateException('no host for redis lock strategy found');
84
        }
85 6
        $port = 6379;
86
87 6
        if (\array_key_exists('port', $config)) {
88 5
            $port = (int) $config['port'];
89
        }
90
91 6
        if (!\array_key_exists('database', $config)) {
92 1
            throw new LockCreateException('no database for redis lock strategy found');
93
        }
94
95 5
        if (\array_key_exists('ttl', $config)) {
96
            $this->ttl = (int) $config['ttl'];
97
        }
98
99 5
        $this->subject = $subject;
100 5
        $this->redis   = new \Redis();
101 5
        $this->redis->connect($config['host'], $port);
102
103 5
        if (\array_key_exists('auth', $config)) {
104
            $this->redis->auth($config['auth']);
105
        }
106
107 5
        $this->redis->select((int) $config['database']);
108
109 5
        if (!$this->redis->exists($this->subject)) {
110
111
            // initialize synchronization object,
112
            // i.e. a simple list with some single random value
113 5
            if (!$this->redis->rPush($this->subject, uniqid())) {
114
                throw new LockCreateException('could not create lock entry');
115
            }
116
117 5
            if (!$this->redis->expire($this->subject, $this->ttl)) {
118
                throw new LockCreateException('could not set ttl to lock entry');
119
            }
120
        }
121 5
    }
122
123
    /**
124
     * @inheritdoc
125
     */
126 9
    public static function getCapabilities()
127
    {
128 9
        return self::LOCK_CAPABILITY_EXCLUSIVE | self::LOCK_CAPABILITY_NOBLOCK;
129
    }
130
131
    /**
132
     * @inheritdoc
133
     */
134 9
    public static function getPriority()
135
    {
136 9
        return 100;
137
    }
138
139
    /**
140
     * @inheritdoc
141
     */
142 3
    public function acquire($mode = self::LOCK_CAPABILITY_EXCLUSIVE)
143
    {
144 3
        if ($this->isAcquired) {
145
            return true;
146
        }
147
148 3
        if (!$this->redis->exists($this->subject)) {
149
            throw new LockAcquireException('lock entry could not be found');
150
        }
151
152 3
        if ($mode & self::LOCK_CAPABILITY_NOBLOCK) {
153
154
            // this does not block
155
            $this->isAcquired = (bool) $this->redis->lPop($this->subject);
156
        } else {
157
158
            // this blocks iff the list is empty
159 3
            $this->isAcquired = (bool) $this->redis->blPop([$this->subject], $this->blTo);
160
        }
161
162 3
        if (!$this->isAcquired) {
163
            throw new LockAcquireException('could not acquire lock');
164
        }
165
166 3
        return true;
167
    }
168
169
    /**
170
     * @inheritdoc
171
     */
172 1
    public function isAcquired()
173
    {
174 1
        return $this->isAcquired;
175
    }
176
177
    /**
178
     * @inheritdoc
179
     */
180 2
    public function destroy()
181
    {
182 2
        $this->release();
183 2
        $this->redis->del($this->subject);
184 2
    }
185
186
    /**
187
     * @inheritdoc
188
     */
189 2
    public function release()
190
    {
191 2
        if (!$this->isAcquired) {
192 2
            return true;
193
        }
194
195
        $this->isAcquired = !$this->redis->rPush($this->subject, uniqid());
196
197
        return !$this->isAcquired;
198
    }
199
200
}