Aaron Schulz has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/188476

Change subject: [WIP] Added WANObectStash class with cassandra subclass
......................................................................

[WIP] Added WANObectStash class with cassandra subclass

bug: T88445
Change-Id: I5e020112954e69fec21c11309847cdcd00330597
---
M autoload.php
A includes/objectstash/WANObjectStash.php
A includes/objectstash/WANObjectStashCassandra.php
3 files changed, 280 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/76/188476/1

diff --git a/autoload.php b/autoload.php
index 90cd074..4104956 100644
--- a/autoload.php
+++ b/autoload.php
@@ -1275,6 +1275,8 @@
        'ViewAction' => __DIR__ . '/includes/actions/ViewAction.php',
        'VirtualRESTService' => __DIR__ . 
'/includes/libs/virtualrest/VirtualRESTService.php',
        'VirtualRESTServiceClient' => __DIR__ . 
'/includes/libs/virtualrest/VirtualRESTServiceClient.php',
+       'WANObjectStash' => __DIR__ . 
'/includes/objectstash/WANObjectStash.php',
+       'WANObjectStashCassandra' => __DIR__ . 
'/includes/objectstash/WANObjectStashCassandra.php',
        'WaitForSlave' => __DIR__ . '/maintenance/waitForSlave.php',
        'WantedCategoriesPage' => __DIR__ . 
'/includes/specials/SpecialWantedcategories.php',
        'WantedFilesPage' => __DIR__ . 
'/includes/specials/SpecialWantedfiles.php',
diff --git a/includes/objectstash/WANObjectStash.php 
b/includes/objectstash/WANObjectStash.php
new file mode 100755
index 0000000..57a5dc6
--- /dev/null
+++ b/includes/objectstash/WANObjectStash.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * 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 Stash
+ * @author Aaron Schulz
+ */
+
+/**
+ * @defgroup Stash Stash
+ */
+
+/**
+ * Multi-datacenter aware stashing interface
+ *
+ * All operations write to the local cluster stash immediately,
+ * and are broadcasted to the other data centers asynchronously.
+ *
+ * This class is intended for use as an ephemeral primary store.
+ * Callers should also be aware of the increased risk of race
+ * conditions as writes to the same key can happen in different
+ * data centers at the same time, one of them eventually winning.
+ * This store is generally appropriate for temporary data where:
+ *   - a) The value is immutable (stored once); or
+ *   - b) The value is validated on get(); or
+ *   - c) Anti-dependencies are expected to be serialized with
+ *        sufficient delay between r-w pairs to avoid races; or
+ *   - d) Race conditions are expected to be otherwise rare
+ *        or don't really matter that much
+ *
+ * Instances of this class must be configured to point to a valid
+ * PubSub endpoint, and there must be listeners that subscribe to
+ * the endpoint and update the caches. The listeners should be aware
+ * of any consistent hashing scheme used by the client so that writes
+ * do not need to go to every server.
+ *
+ * When used with memcached/redis, it might be simplest to point
+ * both MediaWiki and the purge daemon to twemproxy, mcrouter,
+ * or the like, to handle the consistent hashing. With memcached,
+ * one must be careful to deal with the usage of the "flags" field.
+ *
+ * @ingroup Cache
+ * @since 1.25
+ */
+abstract class WANObjectStash {
+       /** Possible values for getLastError() */
+       const ERR_NONE = 0; // no error
+       const ERR_NO_RESPONSE = 1; // no response
+       const ERR_UNREACHABLE = 2; // can't connect
+       const ERR_UNEXPECTED = 3; // response gave some error
+
+       /**
+        * Fetch the value of a key from the stash
+        *
+        * @param string $key Cache key
+        * @return mixed Returns false on failure
+        */
+       abstract public function get( $key );
+
+       /**
+        * Set the value of a key from stash
+        *
+        * @param string $key Cache key
+        * @param mixed $value
+        * @param integer $ttl Seconds to live [0=forever]
+        * @return bool Success
+        */
+       abstract public function set( $key, $value, $ttl = 0 );
+
+       /**
+        * Delete the value of a key from stash
+        *
+        * @param string $key Cache key
+        * @return bool True if the item was deleted or not found, false on 
failure
+        */
+       abstract public function delete( $key );
+
+       /**
+        * Get the "last error" registered; clearLastError() should be called 
manually
+        * @return int ERR_* constant for the "last error" registry
+        */
+       abstract public function getLastError();
+
+       /**
+        * Clear the "last error" registry
+        */
+       abstract public function clearLastError();
+}
diff --git a/includes/objectstash/WANObjectStashCassandra.php 
b/includes/objectstash/WANObjectStashCassandra.php
new file mode 100755
index 0000000..09cbb11
--- /dev/null
+++ b/includes/objectstash/WANObjectStashCassandra.php
@@ -0,0 +1,176 @@
+<?php
+/**
+ * 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 Stash
+ * @author Aaron Schulz
+ */
+
+/**
+ * @defgroup Stash Stash
+ */
+
+/**
+ * Multi-datacenter aware stashing interface using Cassandra
+ *
+ * All read/writes use LOCAL_ONE consistency, to avoid cross-DC traffic
+ *
+ * @ingroup Cache
+ * @since 1.25
+ */
+abstract class WANObjectStashCassandra extends WANObectStash {
+       /** Possible values for getLastError() */
+       const ERR_NONE = 0; // no error
+       const ERR_NO_RESPONSE = 1; // no response
+       const ERR_UNREACHABLE = 2; // can't connect
+       const ERR_UNEXPECTED = 3; // response gave some error
+
+       /** @var evseevnn\Cassandra\Database */
+       protected $db;
+       /** @var ConnectionException */
+       protected $lastConnError;
+       /** @var integer UNIX timestamp */
+       protected $lastConnErrorTime;
+       /** @var integer ERR_* constant */
+       protected $lastError = self::ERR_NONE;
+
+       /**
+        * @param array $params
+        */
+       function __construct( array $params ) {
+               if ( !class_exists( 'evseevnn\Cassandra\Database' ) ) {
+                       throw new Exception( "Missing cassandra binding; " .
+                               "see 
https://github.com/evseevnn/php-cassandra-binary"; );
+               }
+
+               $this->db = new evseevnn\Cassandra\Database(
+                       $params['cassandraConfig']['nodes'],
+                       $params['cassandraConfig']['keyspace']
+               );
+       }
+
+       /**
+        * Fetch the value of a key from the stash
+        *
+        * @param string $key Cache key
+        * @return mixed Returns false on failure
+        */
+       public function get( $key ) {
+               $res = array();
+               try {
+                       $this->connect();
+                       $res = $this->db->query(
+                               'SELECT value FROM "objectstash" WHERE "key" = 
:key LIMIT 1',
+                               array( 'key' => $key ),
+                               ConsistencyEnum::CONSISTENCY_LOCAL_ONE
+                       );
+               } catch ( evseevnn\Cassandra\ConnectionException $e ) {
+                       $this->lastError = self::ERR_UNREACHABLE;
+               } catch ( evseevnn\Cassandra\QueryException $e ) {
+                       $this->lastError = self::ERR_NO_RESPONSE;
+               } catch ( evseevnn\Cassandra\CassandraException $e ) {
+                       $this->lastError = self::ERR_UNEXPECTED;
+               }
+
+               return isset( $res[0] ) ? $res[0]['value'] : false;
+       }
+
+       /**
+        * Set the value of a key from stash
+        *
+        * @param string $key Cache key
+        * @param mixed $value
+        * @param integer $ttl Seconds to live [0=forever]
+        * @return bool Success
+        */
+       public function set( $key, $value, $ttl = 0 ) {
+               try {
+                       $this->connect();
+                       $res = $this->db->query(
+                               'INSERT INTO "objectstash" (key,value) VALUES 
(:key, :value)',
+                               array( 'key' => $key, 'value' => $value ),
+                               ConsistencyEnum::CONSISTENCY_LOCAL_ONE
+                       );
+                       return true;
+               } catch ( evseevnn\Cassandra\ConnectionException $e ) {
+                       $this->lastError = self::ERR_UNREACHABLE;
+               } catch ( evseevnn\Cassandra\QueryException $e ) {
+                       $this->lastError = self::ERR_NO_RESPONSE;
+               } catch ( evseevnn\Cassandra\CassandraException $e ) {
+                       $this->lastError = self::ERR_UNEXPECTED;
+               }
+
+               return false;
+       }
+
+       /**
+        * Delete the value of a key from stash
+        *
+        * @param string $key Cache key
+        * @return bool True if the item was deleted or not found, false on 
failure
+        */
+       public function delete( $key ) {
+               try {
+                       $this->connect();
+                       $this->db->query(
+                               'DELETE FROM "objectstash" WHERE "key" = :key',
+                               array( 'key' => $key ),
+                               ConsistencyEnum::CONSISTENCY_LOCAL_ONE
+                       );
+                       return true;
+               } catch ( evseevnn\Cassandra\ConnectionException $e ) {
+                       $this->lastError = self::ERR_UNREACHABLE;
+               } catch ( evseevnn\Cassandra\QueryException $e ) {
+                       $this->lastError = self::ERR_NO_RESPONSE;
+               } catch ( evseevnn\Cassandra\CassandraException $e ) {
+                       $this->lastError = self::ERR_UNEXPECTED;
+               }
+
+               return false;
+       }
+
+       /**
+        * Try to connect to Cassandra, remebering recent failures
+        *
+        * @throws evseevnn\Cassandra\ConnectionException
+        */
+       protected function connect() {
+               if ( $this->lastConnError && ( time() - 
$this->lastConnErrorTime ) < 5 ) {
+                       throw $this->lastConnError;
+               }
+               try {
+                       $this->db->connect();
+                       $this->lastConnError = null;
+                       $this->lastConnErrorTime = null;
+               } catch ( evseevnn\Cassandra\ConnectionException $e ) {
+                       $this->lastConnError = $e;
+                       $this->lastConnErrorTime = time();
+                       throw $e;
+               }
+       }
+
+       /**
+        * Get the "last error" registered; clearLastError() should be called 
manually
+        * @return int ERR_* constant for the "last error" registry
+        */
+       public function getLastError();
+
+       /**
+        * Clear the "last error" registry
+        */
+       public function clearLastError();
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/188476
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I5e020112954e69fec21c11309847cdcd00330597
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to