Added FOR UPDATE mode to Block.php, to fix memcached concurrency problem in BlockCach...
[lhc/web/wiklou.git] / includes / BlockCache.php
1 <?php
2
3 # Object for fast lookup of IP blocks
4 # Represents a memcached value, and in some sense, the entire ipblocks table
5
6 class BlockCache
7 {
8 var $mData = false, $mMemcKey;
9
10 function BlockCache( $deferLoad = false, $dbName = '' ) {
11 global $wgDBname;
12
13 if ( $dbName == '' ) {
14 $dbName = $wgDBname;
15 }
16
17 $this->mMemcKey = $dbName.':ipblocks';
18
19 if ( !$deferLoad ) {
20 $this->load();
21 }
22 }
23
24 # Load the blocks from the database and save them to memcached
25 function loadFromDB() {
26 global $wgUseMemCached, $wgMemc;
27 $this->mData = array();
28 # Selecting FOR UPDATE is a convenient way to serialise the memcached and DB operations,
29 # which is necessary even though we don't update the DB
30 if ( $wgUseMemCached ) {
31 Block::enumBlocks( 'wfBlockCacheInsert', '', EB_FOR_UPDATE );
32 $wgMemc->set( $this->mMemcKey, $this->mData, 0 );
33 } else {
34 Block::enumBlocks( 'wfBlockCacheInsert', '' );
35 }
36 }
37
38 # Load the cache from memcached or, if that's not possible, from the DB
39 function load() {
40 global $wgUseMemCached, $wgMemc;
41
42 if ( $this->mData === false) {
43 # Try memcached
44 if ( $wgUseMemCached ) {
45 $this->mData = $wgMemc->get( $this->mMemcKey );
46 }
47
48 if ( !is_array( $this->mData ) ) {
49 $this->loadFromDB();
50 }
51 }
52 }
53
54 # Add a block to the cache
55 function insert( &$block ) {
56 if ( $block->mUser == 0 ) {
57 $nb = $block->getNetworkBits();
58 $ipint = $block->getIntegerAddr();
59 $index = $ipint >> ( 32 - $nb );
60
61 if ( !array_key_exists( $nb, $this->mData ) ) {
62 $this->mData[$nb] = array();
63 }
64
65 $this->mData[$nb][$index] = 1;
66 }
67 }
68
69 # Find out if a given IP address is blocked
70 function get( $ip ) {
71 $this->load();
72 $ipint = ip2long( $ip );
73 $blocked = false;
74
75 foreach ( $this->mData as $networkBits => $blockInts ) {
76 if ( array_key_exists( $ipint >> ( 32 - $networkBits ), $blockInts ) ) {
77 $blocked = true;
78 break;
79 }
80 }
81 if ( $blocked ) {
82 # Clear low order bits
83 if ( $networkBits != 32 ) {
84 $ip .= '/'.$networkBits;
85 $ip = Block::normaliseRange( $ip );
86 }
87 $block = new Block();
88 $block->load( $ip );
89 } else {
90 $block = false;
91 }
92
93 return $block;
94 }
95
96 # Clear the local cache
97 # There was once a clear() to clear memcached too, but I deleted it
98 function clearLocal() {
99 $this->mData = false;
100 }
101 }
102
103 function wfBlockCacheInsert( $block, $tag ) {
104 global $wgBlockCache;
105 $wgBlockCache->insert( $block );
106 }