Completed
Pull Request — master (#27312)
by Jörn Friedrich
21:59
created

Storage::getAvailability()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 * @author Jörn Friedrich Dreyer <[email protected]>
5
 * @author Morris Jobke <[email protected]>
6
 * @author Robin Appelman <[email protected]>
7
 * @author Robin McCorkell <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 * @author Vincent Petry <[email protected]>
10
 *
11
 * @copyright Copyright (c) 2017, ownCloud GmbH
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\Files\Cache;
29
use OC\Cache\CappedMemoryCache;
30
use OCP\ICache;
31
32
/**
33
 * Handle the mapping between the string and numeric storage ids
34
 *
35
 * Each storage has 2 different ids
36
 * 	a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
37
 * 	and a numeric storage id which is referenced in the file cache
38
 *
39
 * A mapping between the two storage ids is stored in the database and accessible trough this class
40
 *
41
 * @package OC\Files\Cache
42
 */
43
class Storage {
44
	private $storageId;
45
	private $numericId;
46
47
	/** @var CappedMemoryCache */
48
	protected static $localCache = null;
49
50
	/** @var ICache  */
51
	private static $distributedCache = null;
52
53
	private static $distributedCacheTTL = 300; // 5 Min
54
55
	/**
56
	 * @param \OC\Files\Storage\Storage|string $storage
57
	 * @param bool $isAvailable
58
	 * @throws \RuntimeException
59
	 */
60
	public function __construct($storage, $isAvailable = true) {
61
		if ($storage instanceof \OC\Files\Storage\Storage) {
62
			$this->storageId = $storage->getId();
63
		} else {
64
			$this->storageId = $storage;
65
		}
66
		$this->storageId = self::adjustStorageId($this->storageId);
67
68
		if ($row = self::getStorageById($this->storageId)) {
69
			$this->numericId = (int)$row['numeric_id'];
70
		} else {
71
			self::unsetCache($this->storageId);
72
73
			$connection = \OC::$server->getDatabaseConnection();
74
			$available = $isAvailable ? 1 : 0;
75
			$storageData = ['id' => $this->storageId, 'available' => $available];
76
77
			if ($connection->insertIfNotExist('*PREFIX*storages', $storageData)) {
78
				$this->numericId = (int)$connection->lastInsertId('*PREFIX*storages');
79
80
				// add missing fields before caching
81
				$storageData['numeric_id'] = $this->numericId;
82
				$storageData['last_checked'] = null;
83
84
				// local cache has been initialized by self::getStorageById
85
				self::$localCache->set($this->storageId, $storageData);
86
87
				// distributed cache may need initializing
88
				self::getDistributedCache()->set(
89
					$this->storageId, $storageData, self::$distributedCacheTTL
90
				);
91
92
			} else {
93
				if ($row = self::getStorageById($this->storageId)) {
94
					$this->numericId = (int)$row['numeric_id'];
95
				} else {
96
					throw new \RuntimeException('Storage could neither be inserted nor be selected from the database');
97
				}
98
			}
99
		}
100
	}
101
102
	/**
103
	 * query the local cache, a distributed cache and the db for a storageid
104
	 * @param string $storageId
105
	 * @return array|false
106
	 */
107
	public static function getStorageById($storageId) {
108
		if (self::$localCache === null) {
109
			self::$localCache = new CappedMemoryCache();
110
		}
111
		$result = self::$localCache->get($storageId);
112
		if ($result === null) {
113
			$result = self::getStorageByIdFromCache($storageId);
114
			self::$localCache->set($storageId, $result);
115
		}
116
		return $result;
117
	}
118
119
	/**
120
	 * @return ICache
121
	 */
122
	private static function getDistributedCache() {
123
		if (self::$distributedCache === null) {
124
			self::$distributedCache =
125
				\OC::$server->getMemCacheFactory()->create('getStorageById');
126
		}
127
		return self::$distributedCache;
128
	}
129
130
	/**
131
	 * query the distributed cache for a storageid
132
	 * @param string $storageId
133
	 * @return array|false
134
	 */
135
	private static function getStorageByIdFromCache($storageId) {
136
		$result = self::getDistributedCache()->get($storageId);
137
		if ($result === null) {
138
			$result = self::getStorageByIdFromDb($storageId);
139
			self::getDistributedCache()->set(
140
				$storageId,	$result, self::$distributedCacheTTL
141
			);
142
		}
143
		return $result;
144
	}
145
146
	/**
147
	 * query the db for a storageid
148
	 * @param string $storageId
149
	 * @return array|false
150
	 */
151
	private static function getStorageByIdFromDb($storageId) {
152
		$sql = 'SELECT * FROM `*PREFIX*storages` WHERE `id` = ?';
153
		$resultSet = \OC_DB::executeAudited($sql, [$storageId]);
154
		return $resultSet->fetchRow();
155
	}
156
157
	private static function unsetCache($storageId) {
158
		// delete from local cache
159
		if(self::$localCache !== null) {
160
			self::$localCache->remove($storageId);
161
		}
162
		// delete from distributed cache
163
		self::getDistributedCache()->remove($storageId);
164
	}
165
166
	/**
167
	 * Adjusts the storage id to use md5 if too long
168
	 * @param string $storageId storage id
169
	 * @return string unchanged $storageId if its length is less than 64 characters,
170
	 * else returns the md5 of $storageId
171
	 */
172
	public static function adjustStorageId($storageId) {
173
		if (strlen($storageId) > 64) {
174
			return md5($storageId);
175
		}
176
		return $storageId;
177
	}
178
179
	/**
180
	 * Get the numeric id for the storage
181
	 *
182
	 * @return int
183
	 */
184
	public function getNumericId() {
185
		return $this->numericId;
186
	}
187
188
	/**
189
	 * Get the string id for the storage
190
	 *
191
	 * @param int $numericId
192
	 * @return string|null either the storage id string or null if the numeric id is not known
193
	 */
194
	public static function getStorageId($numericId) {
195
196
		$sql = 'SELECT `id` FROM `*PREFIX*storages` WHERE `numeric_id` = ?';
197
		$result = \OC_DB::executeAudited($sql, [$numericId]);
198
		if ($row = $result->fetchRow()) {
199
			return $row['id'];
200
		} else {
201
			return null;
202
		}
203
	}
204
205
	/**
206
	 * Get the numeric of the storage with the provided string id
207
	 *
208
	 * @param $storageId
209
	 * @return int|null either the numeric storage id or null if the storage id is not knwon
210
	 */
211
	public static function getNumericStorageId($storageId) {
212
		$storageId = self::adjustStorageId($storageId);
213
214
		if ($row = self::getStorageById($storageId)) {
215
			return (int)$row['numeric_id'];
216
		} else {
217
			return null;
218
		}
219
	}
220
221
	/**
222
	 * @return array|null [ available, last_checked ]
223
	 */
224
	public function getAvailability() {
225
		if ($row = self::getStorageById($this->storageId)) {
226
			return [
227
				'available' => ((int)$row['available'] === 1),
228
				'last_checked' => $row['last_checked']
229
			];
230
		} else {
231
			return null;
232
		}
233
	}
234
235
	/**
236
	 * @param bool $isAvailable
237
	 */
238
	public function setAvailability($isAvailable) {
239
		// delete from local cache
240
		self::unsetCache($this->storageId);
241
		$sql = 'UPDATE `*PREFIX*storages` SET `available` = ?, `last_checked` = ? WHERE `id` = ?';
242
		$available = $isAvailable ? 1 : 0;
243
		\OC_DB::executeAudited($sql, [$available, time(), $this->storageId]);
244
	}
245
246
	/**
247
	 * Check if a string storage id is known
248
	 *
249
	 * @param string $storageId
250
	 * @return bool
251
	 */
252
	public static function exists($storageId) {
253
		return !is_null(self::getNumericStorageId($storageId));
254
	}
255
256
	/**
257
	 * remove the entry for the storage
258
	 *
259
	 * @param string $storageId
260
	 */
261
	public static function remove($storageId) {
262
		$storageId = self::adjustStorageId($storageId);
263
		$numericId = self::getNumericStorageId($storageId);
264
		$sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?';
265
		\OC_DB::executeAudited($sql, [$storageId]);
266
267
		// delete from local cache
268
		self::unsetCache($storageId);
269
		// delete from db
270
		if (!is_null($numericId)) {
271
			$sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?';
272
			\OC_DB::executeAudited($sql, [$numericId]);
273
		}
274
	}
275
}
276