| 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 | |