PluginProbe
Gianism / 1.0
Gianism v1.0
4.3.0 4.3.1 4.3.2 4.3.3 4.3.4 4.4.0 5.0.0 5.0.1 5.0.2 5.1.0 5.2.1 5.2.2 5.3.0 6.0.0 6.0.1 trunk 1.0 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 All 65 releases
gianism / sdks / google / cache / apiFileCache.php

apiFileCache.php in Gianism 1.0, at sdks/google/cache/apiFileCache.php

136 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * Copyright 2008 Google Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 /*
19 * This class implements a basic on disk storage. While that does
20 * work quite well it's not the most elegant and scalable solution.
21 * It will also get you into a heap of trouble when you try to run
22 * this in a clustered environment. In those cases please use the
23 * MySql back-end
24 *
25 * @author Chris Chabot <chabotc@google.com>
26 */
27 class apiFileCache extends apiCache {
28 private $path;
29
30 public function __construct() {
31 global $apiConfig;
32 $this->path = $apiConfig['ioFileCache_directory'];
33 }
34
35 private function isLocked($storageFile) {
36 // our lock file convention is simple: /the/file/path.lock
37 return file_exists($storageFile . '.lock');
38 }
39
40 private function createLock($storageFile) {
41 $storageDir = dirname($storageFile);
42 if (! is_dir($storageDir)) {
43 if (! @mkdir($storageDir, 0755, true)) {
44 // make sure the failure isn't because of a concurrency issue
45 if (! is_dir($storageDir)) {
46 throw new apiCacheException("Could not create storage directory: $storageDir");
47 }
48 }
49 }
50 @touch($storageFile . '.lock');
51 }
52
53 private function removeLock($storageFile) {
54 // suppress all warnings, if some other process removed it that's ok too
55 @unlink($storageFile . '.lock');
56 }
57
58 private function waitForLock($storageFile) {
59 // 20 x 250 = 5 seconds
60 $tries = 20;
61 $cnt = 0;
62 do {
63 // make sure PHP picks up on file changes. This is an expensive action but really can't be avoided
64 clearstatcache();
65 // 250 ms is a long time to sleep, but it does stop the server from burning all resources on polling locks..
66 usleep(250);
67 $cnt ++;
68 } while ($cnt <= $tries && $this->isLocked($storageFile));
69 if ($this->isLocked($storageFile)) {
70 // 5 seconds passed, assume the owning process died off and remove it
71 $this->removeLock($storageFile);
72 }
73 }
74
75 private function getCacheDir($hash) {
76 // use the first 2 characters of the hash as a directory prefix
77 // this should prevent slowdowns due to huge directory listings
78 // and thus give some basic amount of scalability
79 return $this->path . '/' . substr($hash, 0, 2);
80 }
81
82 private function getCacheFile($hash) {
83 return $this->getCacheDir($hash) . '/' . $hash;
84 }
85
86 public function get($key, $expiration = false) {
87 $storageFile = $this->getCacheFile(md5($key));
88 // See if this storage file is locked, if so we wait upto 5 seconds for the lock owning process to
89 // complete it's work. If the lock is not released within that time frame, it's cleaned up.
90 // This should give us a fair amount of 'Cache Stampeding' protection
91 if ($this->isLocked($storageFile)) {
92 $this->waitForLock($storageFile);
93 }
94 if (file_exists($storageFile) && is_readable($storageFile)) {
95 $now = time();
96 if (! $expiration || (($mtime = @filemtime($storageFile)) !== false && ($now - $mtime) < $expiration)) {
97 if (($data = @file_get_contents($storageFile)) !== false) {
98 $data = unserialize($data);
99 return $data;
100 }
101 }
102 }
103 return false;
104 }
105
106 public function set($key, $value) {
107 $storageDir = $this->getCacheDir(md5($key));
108 $storageFile = $this->getCacheFile(md5($key));
109 if ($this->isLocked($storageFile)) {
110 // some other process is writing to this file too, wait until it's done to prevent hickups
111 $this->waitForLock($storageFile);
112 }
113 if (! is_dir($storageDir)) {
114 if (! @mkdir($storageDir, 0755, true)) {
115 throw new apiCacheException("Could not create storage directory: $storageDir");
116 }
117 }
118 // we serialize the whole request object, since we don't only want the
119 // responseContent but also the postBody used, headers, size, etc
120 $data = serialize($value);
121 $this->createLock($storageFile);
122 if (! @file_put_contents($storageFile, $data)) {
123 $this->removeLock($storageFile);
124 throw new apiCacheException("Could not store data in the file");
125 }
126 $this->removeLock($storageFile);
127 }
128
129 public function delete($key) {
130 $file = $this->getCacheFile(md5($key));
131 if (! @unlink($file)) {
132 throw new apiCacheException("Cache file could not be deleted");
133 }
134 }
135 }
136