[ExternalStore] Refactored external store classes to use a base class.
authorAaron Schulz <aschulz@wikimedia.org>
Mon, 21 Jan 2013 22:40:15 +0000 (14:40 -0800)
committerAaron Schulz <aschulz@wikimedia.org>
Fri, 25 Jan 2013 23:54:15 +0000 (15:54 -0800)
* Added ExternalStoreMedium base class
* Moved up two functions in ExternalStoreDB
* Various documentation tweaks

Change-Id: I6790aab7799609422e7af4fb31d2ad60610298ec

includes/AutoLoader.php
includes/externalstore/ExternalStore.php
includes/externalstore/ExternalStoreDB.php
includes/externalstore/ExternalStoreHttp.php
includes/externalstore/ExternalStoreMedium.php [new file with mode: 0644]

index c188975..8096ed6 100644 (file)
@@ -94,6 +94,7 @@ $wgAutoloadLocalClasses = array(
        'ExternalStore' => 'includes/externalstore/ExternalStore.php',
        'ExternalStoreDB' => 'includes/externalstore/ExternalStoreDB.php',
        'ExternalStoreHttp' => 'includes/externalstore/ExternalStoreHttp.php',
+       'ExternalStoreMedium' => 'includes/externalstore/ExternalStoreMedium.php',
        'ExternalUser' => 'includes/ExternalUser.php',
        'FakeTitle' => 'includes/FakeTitle.php',
        'Fallback' => 'includes/Fallback.php',
index 55c1acf..8bf1ca9 100644 (file)
  */
 
 /**
- * Constructor class for data kept in external repositories.
+ * Constructor class for key/value blob data kept in external repositories.
  *
  * Objects in external stores are defined by a special URL. The URL is of
- * the form "<store protocal>://<location>/<object name>". When an object
- * is inserted into a store, the calling code uses a partial URL of the form
- * "<store protocal>://<location>" and receives the full object URL on success.
+ * the form "<store protocal>://<location>/<object name>". The protocal is used
+ * to determine what ExternalStoreMedium class is used. The location identifies
+ * particular storage instances or database clusters for store class to use.
+ *
+ * When an object is inserted into a store, the calling code uses a partial URL of
+ * the form "<store protocal>://<location>" and receives the full object URL on success.
  * This is useful since object names can be sequential IDs, UUIDs, or hashes.
  * Callers are not responsible for unique name generation.
  *
  * External repositories might be populated by maintenance/async
  * scripts, thus partial moving of data may be possible, as well
- * as possibility to have any storage format (i.e. for archives)
+ * as the possibility to have any storage format (i.e. for archives).
  *
  * @ingroup ExternalStorage
  */
@@ -44,9 +47,9 @@ class ExternalStore {
        /**
         * Get an external store object of the given type, with the given parameters
         *
-        * @param $proto String: type of external storage, should be a value in $wgExternalStores
-        * @param $params Array: associative array of parameters for the ExternalStore object.
-        * @return ExternalStore|bool ExternalStore class or false on error
+        * @param $proto string Type of external storage, should be a value in $wgExternalStores
+        * @param $params array Associative array of ExternalStoreMedium parameters
+        * @return ExternalStoreMedium|bool The store class or false on error
         */
        public static function getStoreObject( $proto, array $params = array() ) {
                global $wgExternalStores;
@@ -63,8 +66,8 @@ class ExternalStore {
        /**
         * Fetch data from given URL
         *
-        * @param $url String: The URL of the text to get
-        * @param $params Array: associative array of parameters for the ExternalStore object.
+        * @param $url string The URL of the text to get
+        * @param $params array Associative array of ExternalStoreMedium parameters
         * @return string|bool The text stored or false on error
         * @throws MWException
         */
@@ -91,9 +94,10 @@ class ExternalStore {
         * Store a data item to an external store, identified by a partial URL
         * The protocol part is used to identify the class, the rest is passed to the
         * class itself as a parameter.
-        * @param $url
-        * @param $data
-        * @param $params array
+        *
+        * @param $url String A partial external store URL ("<store type>://<location>")
+        * @param $data string
+        * @param $params array Associative array of ExternalStoreMedium parameters
         * @return string|bool The URL of the stored data item, or false on error
         * @throws MWException
         */
@@ -121,8 +125,8 @@ class ExternalStore {
         * This function does not need a url param, it builds it by
         * itself. It also fails-over to the next possible clusters.
         *
-        * @param $data String
-        * @param $params Array: associative array of parameters for the ExternalStore object.
+        * @param $data string
+        * @param $params array Associative array of ExternalStoreMedium parameters
         * @return string|bool The URL of the stored data item, or false on error
         * @throws MWException
         */
@@ -163,8 +167,8 @@ class ExternalStore {
        }
 
        /**
-        * @param $data
-        * @param $wiki
+        * @param $data string
+        * @param $wiki string
         * @return string|bool The URL of the stored data item, or false on error
         * @throws MWException
         */
index 172e4f6..8e05cfe 100644 (file)
  */
 
 /**
- * DB accessable external objects
+ * DB accessable external objects.
+ *
+ * In this system, each store "location" maps to a database "cluster".
+ * The clusters must be defined in the normal LBFactory configuration.
+ *
  * @ingroup ExternalStorage
  */
-class ExternalStoreDB {
+class ExternalStoreDB extends ExternalStoreMedium {
+       /**
+        * The URL returned is of the form of the form DB://cluster/id
+        * or DB://cluster/id/itemid for concatened storage.
+        *
+        * @see ExternalStoreMedium::fetchFromURL()
+        */
+       public function fetchFromURL( $url ) {
+               $path = explode( '/', $url );
+               $cluster = $path[2];
+               $id = $path[3];
+               if ( isset( $path[4] ) ) {
+                       $itemID = $path[4];
+               } else {
+                       $itemID = false;
+               }
+
+               $ret =& $this->fetchBlob( $cluster, $id, $itemID );
+
+               if ( $itemID !== false && $ret !== false ) {
+                       return $ret->getItem( $itemID );
+               }
+               return $ret;
+       }
 
        /**
-        * @param $params array
+        * @see ExternalStoreMedium::store()
         */
-       function __construct( $params = array() ) {
-               $this->mParams = $params;
+       public function store( $cluster, $data ) {
+               $dbw = $this->getMaster( $cluster );
+               $id = $dbw->nextSequenceValue( 'blob_blob_id_seq' );
+               $dbw->insert( $this->getTable( $dbw ),
+                       array( 'blob_id' => $id, 'blob_text' => $data ),
+                       __METHOD__ );
+               $id = $dbw->insertId();
+               if ( !$id ) {
+                       throw new MWException( __METHOD__.': no insert ID' );
+               }
+               if ( $dbw->getFlag( DBO_TRX ) ) {
+                       $dbw->commit( __METHOD__ );
+               }
+               return "DB://$cluster/$id";
        }
 
        /**
@@ -40,7 +79,7 @@ class ExternalStoreDB {
         * @return LoadBalancer object
         */
        function &getLoadBalancer( $cluster ) {
-               $wiki = isset($this->mParams['wiki']) ? $this->mParams['wiki'] : false;
+               $wiki = isset($this->params['wiki']) ? $this->params['wiki'] : false;
 
                return wfGetLBFactory()->getExternalLB( $cluster, $wiki );
        }
@@ -54,7 +93,7 @@ class ExternalStoreDB {
        function &getSlave( $cluster ) {
                global $wgDefaultExternalStore;
 
-               $wiki = isset($this->mParams['wiki']) ? $this->mParams['wiki'] : false;
+               $wiki = isset($this->params['wiki']) ? $this->params['wiki'] : false;
                $lb =& $this->getLoadBalancer( $cluster );
 
                if ( !in_array( "DB://" . $cluster, (array)$wgDefaultExternalStore ) ) {
@@ -74,7 +113,7 @@ class ExternalStoreDB {
         * @return DatabaseBase object
         */
        function &getMaster( $cluster ) {
-               $wiki = isset($this->mParams['wiki']) ? $this->mParams['wiki'] : false;
+               $wiki = isset($this->params['wiki']) ? $this->params['wiki'] : false;
                $lb =& $this->getLoadBalancer( $cluster );
                return $lb->getConnection( DB_MASTER, array(), $wiki );
        }
@@ -93,29 +132,6 @@ class ExternalStoreDB {
                return $table;
        }
 
-       /**
-        * Fetch data from given URL
-        * @param $url String: an url of the form DB://cluster/id or DB://cluster/id/itemid for concatened storage.
-        * @return mixed
-        */
-       function fetchFromURL( $url ) {
-               $path = explode( '/', $url );
-               $cluster = $path[2];
-               $id = $path[3];
-               if ( isset( $path[4] ) ) {
-                       $itemID = $path[4];
-               } else {
-                       $itemID = false;
-               }
-
-               $ret =& $this->fetchBlob( $cluster, $id, $itemID );
-
-               if ( $itemID !== false && $ret !== false ) {
-                       return $ret->getItem( $itemID );
-               }
-               return $ret;
-       }
-
        /**
         * Fetch a blob item out of the database; a cache of the last-loaded
         * blob will be kept so that multiple loads out of a multi-item blob
@@ -162,28 +178,4 @@ class ExternalStoreDB {
                $externalBlobCache = array( $cacheID => &$ret );
                return $ret;
        }
-
-       /**
-        * Insert a data item into a given cluster
-        *
-        * @param $cluster String: the cluster name
-        * @param $data String: the data item
-        * @throws MWException
-        * @return string URL
-        */
-       function store( $cluster, $data ) {
-               $dbw = $this->getMaster( $cluster );
-               $id = $dbw->nextSequenceValue( 'blob_blob_id_seq' );
-               $dbw->insert( $this->getTable( $dbw ),
-                       array( 'blob_id' => $id, 'blob_text' => $data ),
-                       __METHOD__ );
-               $id = $dbw->insertId();
-               if ( !$id ) {
-                       throw new MWException( __METHOD__.': no insert ID' );
-               }
-               if ( $dbw->getFlag( DBO_TRX ) ) {
-                       $dbw->commit( __METHOD__ );
-               }
-               return "DB://$cluster/$id";
-       }
 }
index 311e32b..345c17b 100644 (file)
  *
  * @ingroup ExternalStorage
  */
-class ExternalStoreHttp {
-
+class ExternalStoreHttp extends ExternalStoreMedium {
        /**
-        * Fetch data from given URL
-        *
-        * @param $url String: the URL
-        * @return String: the content at $url
+        * @see ExternalStoreMedium::fetchFromURL()
         */
-       function fetchFromURL( $url ) {
-               $ret = Http::get( $url );
-               return $ret;
+       public function fetchFromURL( $url ) {
+               return Http::get( $url );
        }
 
-       /* XXX: may require other methods, for store, delete,
-        * whatever, for initial ext storage
+       /**
+        * @see ExternalStoreMedium::store()
         */
+       public function store( $cluster, $data ) {
+               throw new MWException( "ExternalStoreHttp is read-only and does not support store()." );
+       }
 }
diff --git a/includes/externalstore/ExternalStoreMedium.php b/includes/externalstore/ExternalStoreMedium.php
new file mode 100644 (file)
index 0000000..6794dd5
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * External storage in some particular medium.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup ExternalStorage
+ * @author Aaron Schulz
+ */
+
+/**
+ * Accessable external objects
+ * @ingroup ExternalStorage
+ */
+abstract class ExternalStoreMedium {
+       /** @var Array */
+       protected $params = array();
+
+       /**
+        * @param $params array Options
+        */
+       public function __construct( array $params = array() ) {
+               $this->params = $params;
+       }
+
+       /**
+        * Fetch data from given external store URL
+        *
+        * @param $url string An external store URL
+        * @return string|bool The text stored or false on error
+        * @throws MWException
+        */
+       public abstract function fetchFromURL( $url );
+
+       /**
+        * Insert a data item into a given location
+        *
+        * @param $location string: the location name
+        * @param $data string: the data item
+        * @return string|bool The URL of the stored data item, or false on error
+        * @throws MWException
+        */
+       public abstract function store( $location, $data );
+}