Index: branches/RL2/extensions/Gadgets/backend/LocalGadgetRepo.php |
— | — | @@ -3,7 +3,22 @@ |
4 | 4 | * Gadget repository that gets its gadgets from the local database. |
5 | 5 | */ |
6 | 6 | class LocalGadgetRepo extends GadgetRepo { |
| 7 | + /** Cache for EXISTING gadgets. Nonexistent gadgets must not be cached here, |
| 8 | + * use $missCache instead. Values may be null, in which case only the gadget's |
| 9 | + * existence is cached and the data must still be retrieved from memc or the DB. |
| 10 | + * |
| 11 | + * array( id => null|array( 'json' => JSON blob, 'timestamp' => timestamp ) ) |
| 12 | + */ |
7 | 13 | protected $data = array(); |
| 14 | + |
| 15 | + /** Cache for gadget IDs that have been queried and found to be nonexistent. |
| 16 | + * |
| 17 | + * array( id => ignored ) |
| 18 | + */ |
| 19 | + protected $missCache = array(); |
| 20 | + |
| 21 | + /** If true, $data is assumed to contain all existing gadget IDs. |
| 22 | + */ |
8 | 23 | protected $idsLoaded = false; |
9 | 24 | |
10 | 25 | /** Memcached key of the gadget names list. Subclasses may override this in their constructor. |
— | — | @@ -54,7 +69,9 @@ |
55 | 70 | } |
56 | 71 | |
57 | 72 | public function clearInObjectCache() { |
58 | | - $this->data = null; |
| 73 | + $this->data = array(); |
| 74 | + $this->missCache = array(); |
| 75 | + $this->idsLoaded = false; |
59 | 76 | } |
60 | 77 | |
61 | 78 | public function isWriteable() { |
— | — | @@ -108,6 +125,8 @@ |
109 | 126 | // a clone. If it returned a reference to a cached object, the caller could change |
110 | 127 | // that object and cause weird things to happen. |
111 | 128 | $this->data[$id] = array( 'json' => $json, 'timestamp' => $newTs ); |
| 129 | + // Remove from the missing cache if present there |
| 130 | + unset( $this->missCache[$id] ); |
112 | 131 | // Write to memc too |
113 | 132 | $key = $this->getMemcKey( 'gadgets', 'localrepodata', $id ); |
114 | 133 | if ( $key !== false ) { |
— | — | @@ -134,12 +153,14 @@ |
135 | 154 | |
136 | 155 | // Remove gadget from in-object cache |
137 | 156 | unset( $this->data[$id] ); |
138 | | - // Remove from memc too |
| 157 | + // Add it to the missing cache |
| 158 | + $this->missCache[$id] = true; |
| 159 | + // Store nonexistence in memc too |
139 | 160 | $key = $this->getMemcKey( 'gadgets', 'localrepodata', $id ); |
140 | 161 | if ( $key !== false ) { |
141 | | - $wgMemc->delete( $key ); |
| 162 | + $wgMemc->set( $key, array() ); |
142 | 163 | } |
143 | | - // Clear the gadget names array in memc |
| 164 | + // Clear the gadget names array in memc so it'll be regenerated later |
144 | 165 | if ( $this->namesKey !== false ) { |
145 | 166 | $wgMemc->delete( $this->namesKey ); |
146 | 167 | } |
— | — | @@ -253,13 +274,30 @@ |
254 | 275 | // Already loaded, nothing to do here. |
255 | 276 | return $this->data[$id]; |
256 | 277 | } |
| 278 | + if ( isset( $this->missCache[$id] ) ) { |
| 279 | + // Gadget is already known to be missing |
| 280 | + return array(); |
| 281 | + } |
| 282 | + // Need to use array_key_exists() here because isset() returns true for nulls. !@#$ you, PHP |
| 283 | + if ( $this->idsLoaded && !array_key_exists( $id, $this->data ) ) { |
| 284 | + // All IDs have been loaded into $this->data but $id isn't in there, |
| 285 | + // therefore it doesn't exist. |
| 286 | + $this->missCache[$id] = true; |
| 287 | + return array(); |
| 288 | + } |
257 | 289 | |
258 | 290 | // Try cache |
259 | 291 | $key = $this->getMemcKey( 'gadgets', 'localrepodata', $id ); |
260 | 292 | $cached = $key !== false ? $wgMemc->get( $key ) : false; |
261 | 293 | if ( is_array( $cached ) ) { |
262 | 294 | // Yay, data is in cache |
263 | | - $this->data[$id] = $cached; |
| 295 | + if ( count( $cached ) ) { |
| 296 | + // Cache entry contains data |
| 297 | + $this->data[$id] = $cached; |
| 298 | + } else { |
| 299 | + // Cache entry signals nonexistence |
| 300 | + $this->missCache[$id] = true; |
| 301 | + } |
264 | 302 | return $cached; |
265 | 303 | } |
266 | 304 | |
— | — | @@ -273,11 +311,13 @@ |
274 | 312 | // Gadget doesn't exist |
275 | 313 | // Use empty array to prevent confusion with $wgMemc->get() return values for missing keys |
276 | 314 | $data = array(); |
| 315 | + // DO NOT store this in $this->data, because it's supposed to contain existing gadgets only |
| 316 | + $this->missCache[$id] = true; |
277 | 317 | } else { |
278 | 318 | $data = array( 'json' => $row->gd_blob, 'timestamp' => $row->gd_timestamp ); |
| 319 | + // Save to object cache |
| 320 | + $this->data[$id] = $data; |
279 | 321 | } |
280 | | - // Save to object cache |
281 | | - $this->data[$id] = $data; |
282 | 322 | // Save to memc |
283 | 323 | $wgMemc->set( $key, $data ); |
284 | 324 | |