Revert 40741. Causes "Division by zero in includes/LinksUpdate.php on line 210"
[lhc/web/wiklou.git] / includes / ExternalStoreDB.php
1 <?php
2
3 /**
4 * External database storage will use one (or more) separate connection pools
5 * from what the main wiki uses. If we load many revisions, such as when doing
6 * bulk backups or maintenance, we want to keep them around over the lifetime
7 * of the script.
8 *
9 * Associative array of LoadBalancer objects, indexed by cluster name.
10 */
11 global $wgExternalLoadBalancers;
12 $wgExternalLoadBalancers = array();
13
14 /**
15 * One-step cache variable to hold base blobs; operations that
16 * pull multiple revisions may often pull multiple times from
17 * the same blob. By keeping the last-used one open, we avoid
18 * redundant unserialization and decompression overhead.
19 */
20 global $wgExternalBlobCache;
21 $wgExternalBlobCache = array();
22
23 /**
24 * DB accessable external objects
25 * @ingroup ExternalStorage
26 */
27 class ExternalStoreDB {
28
29 /** @todo Document.*/
30 function &getLoadBalancer( $cluster ) {
31 return wfGetLBFactory()->getExternalLB( $cluster );
32 }
33
34 /** @todo Document.*/
35 function &getSlave( $cluster ) {
36 $lb =& $this->getLoadBalancer( $cluster );
37 // Make only two connection attempts, since we still have the master to try
38 return $lb->getConnection( DB_SLAVE, array(), false, 2 );
39 }
40
41 /** @todo Document.*/
42 function &getMaster( $cluster, $retry = true ) {
43 $lb =& $this->getLoadBalancer( $cluster );
44 // Make only two connection attempts if there are other clusters to try
45 $attempts = $retry ? false : 2;
46 return $lb->getConnection( DB_MASTER, array(), false, $attempts, LoadBalancer::GRACEFUL );
47 }
48
49 /** @todo Document.*/
50 function getTable( &$db ) {
51 $table = $db->getLBInfo( 'blobs table' );
52 if ( is_null( $table ) ) {
53 $table = 'blobs';
54 }
55 return $table;
56 }
57
58 /**
59 * Fetch data from given URL
60 * @param string $url An url of the form DB://cluster/id or DB://cluster/id/itemid for concatened storage.
61 */
62 function fetchFromURL( $url ) {
63 $path = explode( '/', $url );
64 $cluster = $path[2];
65 $id = $path[3];
66 if ( isset( $path[4] ) ) {
67 $itemID = $path[4];
68 } else {
69 $itemID = false;
70 }
71
72 $ret =& $this->fetchBlob( $cluster, $id, $itemID );
73
74 if ( $itemID !== false && $ret !== false ) {
75 return $ret->getItem( $itemID );
76 }
77 return $ret;
78 }
79
80 /**
81 * Fetch a blob item out of the database; a cache of the last-loaded
82 * blob will be kept so that multiple loads out of a multi-item blob
83 * can avoid redundant database access and decompression.
84 * @param $cluster
85 * @param $id
86 * @param $itemID
87 * @return mixed
88 * @private
89 */
90 function &fetchBlob( $cluster, $id, $itemID ) {
91 global $wgExternalBlobCache;
92 $cacheID = ( $itemID === false ) ? "$cluster/$id" : "$cluster/$id/";
93 if( isset( $wgExternalBlobCache[$cacheID] ) ) {
94 wfDebug( "ExternalStoreDB::fetchBlob cache hit on $cacheID\n" );
95 return $wgExternalBlobCache[$cacheID];
96 }
97
98 wfDebug( "ExternalStoreDB::fetchBlob cache miss on $cacheID\n" );
99
100 $dbr =& $this->getSlave( $cluster );
101 $ret = $dbr->selectField( $this->getTable( $dbr ), 'blob_text', array( 'blob_id' => $id ) );
102 if ( $ret === false ) {
103 wfDebugLog( 'ExternalStoreDB', "ExternalStoreDB::fetchBlob master fallback on $cacheID\n" );
104 // Try the master
105 $dbw =& $this->getMaster( $cluster );
106 $ret = $dbw->selectField( $this->getTable( $dbw ), 'blob_text', array( 'blob_id' => $id ) );
107 if( $ret === false) {
108 wfDebugLog( 'ExternalStoreDB', "ExternalStoreDB::fetchBlob master failed to find $cacheID\n" );
109 }
110 }
111 if( $itemID !== false && $ret !== false ) {
112 // Unserialise object; caller extracts item
113 $ret = unserialize( $ret );
114 }
115
116 $wgExternalBlobCache = array( $cacheID => &$ret );
117 return $ret;
118 }
119
120 /**
121 * Insert a data item into a given cluster
122 *
123 * @param $cluster String: the cluster name
124 * @param $data String: the data item
125 * @param $retry bool: allows an extra DB connection retry after 1 second
126 * @return string URL
127 */
128 function store( $cluster, $data, $retry = true ) {
129 if( !$dbw = $this->getMaster( $cluster, $retry ) ) {
130 return false; // failed, maybe another cluster is up...
131 }
132 $id = $dbw->nextSequenceValue( 'blob_blob_id_seq' );
133 $dbw->insert( $this->getTable( $dbw ),
134 array( 'blob_id' => $id, 'blob_text' => $data ),
135 __METHOD__ );
136 $id = $dbw->insertId();
137 if ( $dbw->getFlag( DBO_TRX ) ) {
138 $dbw->immediateCommit();
139 }
140 return "DB://$cluster/$id";
141 }
142 }