[Database] Added support for lock/unlock in Postgres.
authorAaron Schulz <aschulz@wikimedia.org>
Fri, 1 Feb 2013 19:42:36 +0000 (11:42 -0800)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 15 Feb 2013 02:47:56 +0000 (02:47 +0000)
Change-Id: Ie422de58c36f195597eabdd51a27d3ed2c2880cf

includes/db/DatabasePostgres.php

index 86b8e8a..5049ecf 100644 (file)
@@ -1436,4 +1436,65 @@ SQL;
                }
                return parent::streamStatementEnd( $sql, $newLine );
        }
+
+       /**
+        * Check to see if a named lock is available. This is non-blocking.
+        * See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
+        *
+        * @param $lockName String: name of lock to poll
+        * @param $method String: name of method calling us
+        * @return Boolean
+        * @since 1.20
+        */
+       public function lockIsFree( $lockName, $method ) {
+               $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
+               $result = $this->query( "SELECT (CASE(pg_try_advisory_lock($key))
+                       WHEN 'f' THEN 'f' ELSE pg_advisory_unlock($key) END) AS lockstatus", $method );
+               $row = $this->fetchObject( $result );
+               return ( $row->lockstatus === 't' );
+       }
+
+       /**
+        * See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
+        * @param $lockName string
+        * @param $method string
+        * @param $timeout int
+        * @return bool
+        */
+       public function lock( $lockName, $method, $timeout = 5 ) {
+               $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
+               for ( $attempts=1; $attempts <= $timeout; ++$attempts ) {
+                       $result = $this->query(
+                               "SELECT pg_try_advisory_lock($key) AS lockstatus", $method );
+                       $row = $this->fetchObject( $result );
+                       if ( $row->lockstatus === 't' ) {
+                               return true;
+                       } else {
+                               sleep( 1 );
+                       }
+               }
+               wfDebug( __METHOD__." failed to acquire lock\n" );
+               return false;
+       }
+
+       /**
+        * See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKSFROM PG DOCS: http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
+        * @param $lockName string
+        * @param $method string
+        * @return bool
+        */
+       public function unlock( $lockName, $method ) {
+               $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
+               $result = $this->query( "SELECT pg_advisory_unlock($key) as lockstatus", $method );
+               $row = $this->fetchObject( $result );
+               return ( $row->lockstatus === 't' );
+       }
+
+       /**
+        * @param string $lockName
+        * @return string Integer
+        */
+       private function bigintFromLockName( $lockName ) {
+               return wfBaseConvert( substr( sha1( $lockName ), 0, 15 ), 16, 10 );
+       }
 } // end DatabasePostgres class