MDL-69521 core: Move all comments in code from 4.0 to 3.10
[moodle.git] / lib / classes / lock / mysql_lock_factory.php
CommitLineData
262061d6
BH
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * MySQL / MariaDB locking factory.
19 *
20 * @package core
21 * @category lock
22 * @copyright Brendan Heywood <brendan@catalyst-au.net>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26namespace core\lock;
27
28defined('MOODLE_INTERNAL') || die();
29
30/**
31 * MySQL / MariaDB locking factory.
32 *
33 * @package core
34 * @category lock
35 * @copyright Brendan Heywood <brendan@catalyst-au.net>
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 */
38class mysql_lock_factory implements lock_factory {
39
40 /** @var string $dbprefix - used as a namespace for these types of locks */
41 protected $dbprefix = '';
42
43 /** @var \moodle_database $db Hold a reference to the global $DB */
44 protected $db;
45
46 /** @var array $openlocks - List of held locks - used by auto-release */
47 protected $openlocks = [];
48
49 /**
50 * Return a unique prefix based on the database name and prefix.
51 * @param string $type - Used to prefix lock keys.
52 * @return string.
53 */
54 protected function get_unique_db_prefix($type) {
55 global $CFG;
56 $prefix = $CFG->dbname . ':';
57 if (isset($CFG->prefix)) {
58 $prefix .= $CFG->prefix;
59 }
60 $prefix .= '_' . $type . '_';
61 return $prefix;
62 }
63
64 /**
65 * Lock constructor.
66 * @param string $type - Used to prefix lock keys.
67 */
68 public function __construct($type) {
69 global $DB;
70
71 $this->dbprefix = $this->get_unique_db_prefix($type);
72 // Save a reference to the global $DB so it will not be released while we still have open locks.
73 $this->db = $DB;
74
75 \core_shutdown_manager::register_function([$this, 'auto_release']);
76 }
77
78 /**
79 * Is available.
80 * @return boolean - True if this lock type is available in this environment.
81 */
82 public function is_available() {
83 return $this->db->get_dbfamily() === 'mysql';
84 }
85
86 /**
87 * Return information about the blocking behaviour of the lock type on this platform.
88 * @return boolean - Defer to the DB driver.
89 */
90 public function supports_timeout() {
91 return true;
92 }
93
94 /**
95 * Will this lock type will be automatically released when a process ends.
96 *
97 * @return boolean - Via shutdown handler.
98 */
99 public function supports_auto_release() {
100 return true;
101 }
102
103 /**
104 * Multiple locks for the same resource can NOT be held by a single process.
105 *
106 * Hard coded to false and workaround inconsistent support in different
107 * versions of MySQL / MariaDB.
108 *
709b46db 109 * @deprecated since Moodle 3.10.
262061d6
BH
110 * @return boolean - false
111 */
112 public function supports_recursion() {
4b71cdcd
MG
113 debugging('The function supports_recursion() is deprecated, please do not use it anymore.',
114 DEBUG_DEVELOPER);
262061d6
BH
115 return false;
116 }
117
118 /**
119 * Create and get a lock
120 * @param string $resource - The identifier for the lock. Should use frankenstyle prefix.
121 * @param int $timeout - The number of seconds to wait for a lock before giving up.
122 * @param int $maxlifetime - Unused by this lock type.
123 * @return boolean - true if a lock was obtained.
124 */
125 public function get_lock($resource, $timeout, $maxlifetime = 86400) {
126
127 // We sha1 to avoid long key names hitting the mysql str limit.
128 $resourcekey = sha1($this->dbprefix . $resource);
129
130 // Even though some versions of MySQL and MariaDB can support stacked locks
131 // just never stack them and always fail fast.
132 if (isset($this->openlocks[$resourcekey])) {
133 return false;
134 }
135
136 $params = [
137 'resourcekey' => $resourcekey,
138 'timeout' => $timeout
139 ];
140
141 $result = $this->db->get_record_sql('SELECT GET_LOCK(:resourcekey, :timeout) AS locked', $params);
142 $locked = $result->locked == 1;
143
144 if ($locked) {
145 $this->openlocks[$resourcekey] = 1;
146 return new lock($resourcekey, $this);
147 }
148 return false;
149 }
150
151 /**
152 * Release a lock that was previously obtained with @lock.
153 * @param lock $lock - a lock obtained from this factory.
154 * @return boolean - true if the lock is no longer held (including if it was never held).
155 */
156 public function release_lock(lock $lock) {
157
158 $params = [
159 'resourcekey' => $lock->get_key()
160 ];
161 $result = $this->db->get_record_sql('SELECT RELEASE_LOCK(:resourcekey) AS unlocked', $params);
162 $result = $result->unlocked == 1;
163 if ($result) {
164 unset($this->openlocks[$lock->get_key()]);
165 }
166 return $result;
167 }
168
169 /**
170 * Extend a lock that was previously obtained with @lock.
80736a93 171 *
709b46db 172 * @deprecated since Moodle 3.10.
262061d6
BH
173 * @param lock $lock - a lock obtained from this factory.
174 * @param int $maxlifetime - the new lifetime for the lock (in seconds).
175 * @return boolean - true if the lock was extended.
176 */
177 public function extend_lock(lock $lock, $maxlifetime = 86400) {
80736a93
MG
178 debugging('The function extend_lock() is deprecated, please do not use it anymore.',
179 DEBUG_DEVELOPER);
262061d6
BH
180 // Not supported by this factory.
181 return false;
182 }
183
184 /**
185 * Auto release any open locks on shutdown.
186 * This is required, because we may be using persistent DB connections.
187 */
188 public function auto_release() {
189 // Called from the shutdown handler. Must release all open locks.
190 foreach ($this->openlocks as $key => $unused) {
191 $lock = new lock($key, $this);
192 $lock->release();
193 }
194 }
195
196}