File indexing completed on 2024-05-26 06:03:23

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Search_Lucene
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /** Zend_Search_Lucene_Storage_Directory */
0023 // require_once 'Zend/Search/Lucene/Storage/Directory.php';
0024 
0025 /** Zend_Search_Lucene_Storage_File */
0026 // require_once 'Zend/Search/Lucene/Storage/File.php';
0027 
0028 /**
0029  * This is an utility class which provides index locks processing functionality
0030  *
0031  * @category   Zend
0032  * @package    Zend_Search_Lucene
0033  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0034  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0035  */
0036 class Zend_Search_Lucene_LockManager
0037 {
0038     /**
0039      * consts for name of file to show lock status
0040      */
0041     const WRITE_LOCK_FILE                = 'write.lock.file';
0042     const READ_LOCK_FILE                 = 'read.lock.file';
0043     const READ_LOCK_PROCESSING_LOCK_FILE = 'read-lock-processing.lock.file';
0044     const OPTIMIZATION_LOCK_FILE         = 'optimization.lock.file';
0045 
0046     /**
0047      * Obtain exclusive write lock on the index
0048      *
0049      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0050      * @return Zend_Search_Lucene_Storage_File
0051      * @throws Zend_Search_Lucene_Exception
0052      */
0053     public static function obtainWriteLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0054     {
0055         $lock = $lockDirectory->createFile(self::WRITE_LOCK_FILE);
0056         if (!$lock->lock(LOCK_EX)) {
0057             // require_once 'Zend/Search/Lucene/Exception.php';
0058             throw new Zend_Search_Lucene_Exception('Can\'t obtain exclusive index lock');
0059         }
0060         return $lock;
0061     }
0062 
0063     /**
0064      * Release exclusive write lock
0065      *
0066      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0067      */
0068     public static function releaseWriteLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0069     {
0070         $lock = $lockDirectory->getFileObject(self::WRITE_LOCK_FILE);
0071         $lock->unlock();
0072     }
0073 
0074     /**
0075      * Obtain the exclusive "read escalation/de-escalation" lock
0076      *
0077      * Required to protect the escalate/de-escalate read lock process
0078      * on GFS (and potentially other) mounted filesystems.
0079      *
0080      * Why we need this:
0081      *  While GFS supports cluster-wide locking via flock(), it's
0082      *  implementation isn't quite what it should be.  The locking
0083      *  semantics that work consistently on a local filesystem tend to
0084      *  fail on GFS mounted filesystems.  This appears to be a design defect
0085      *  in the implementation of GFS.  How this manifests itself is that
0086      *  conditional promotion of a shared lock to exclusive will always
0087      *  fail, lock release requests are honored but not immediately
0088      *  processed (causing erratic failures of subsequent conditional
0089      *  requests) and the releasing of the exclusive lock before the
0090      *  shared lock is set when a lock is demoted (which can open a window
0091      *  of opportunity for another process to gain an exclusive lock when
0092      *  it shoudln't be allowed to).
0093      *
0094      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0095      * @return Zend_Search_Lucene_Storage_File
0096      * @throws Zend_Search_Lucene_Exception
0097      */
0098     private static function _startReadLockProcessing(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0099     {
0100         $lock = $lockDirectory->createFile(self::READ_LOCK_PROCESSING_LOCK_FILE);
0101         if (!$lock->lock(LOCK_EX)) {
0102             // require_once 'Zend/Search/Lucene/Exception.php';
0103             throw new Zend_Search_Lucene_Exception('Can\'t obtain exclusive lock for the read lock processing file');
0104         }
0105         return $lock;
0106     }
0107 
0108     /**
0109      * Release the exclusive "read escalation/de-escalation" lock
0110      *
0111      * Required to protect the escalate/de-escalate read lock process
0112      * on GFS (and potentially other) mounted filesystems.
0113      *
0114      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0115      */
0116     private static function _stopReadLockProcessing(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0117     {
0118         $lock = $lockDirectory->getFileObject(self::READ_LOCK_PROCESSING_LOCK_FILE);
0119         $lock->unlock();
0120     }
0121 
0122 
0123     /**
0124      * Obtain shared read lock on the index
0125      *
0126      * It doesn't block other read or update processes, but prevent index from the premature cleaning-up
0127      *
0128      * @param Zend_Search_Lucene_Storage_Directory $defaultLockDirectory
0129      * @return Zend_Search_Lucene_Storage_File
0130      * @throws Zend_Search_Lucene_Exception
0131      */
0132     public static function obtainReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0133     {
0134         $lock = $lockDirectory->createFile(self::READ_LOCK_FILE);
0135         if (!$lock->lock(LOCK_SH)) {
0136             // require_once 'Zend/Search/Lucene/Exception.php';
0137             throw new Zend_Search_Lucene_Exception('Can\'t obtain shared reading index lock');
0138         }
0139         return $lock;
0140     }
0141 
0142     /**
0143      * Release shared read lock
0144      *
0145      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0146      */
0147     public static function releaseReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0148     {
0149         $lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
0150         $lock->unlock();
0151     }
0152 
0153     /**
0154      * Escalate Read lock to exclusive level
0155      *
0156      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0157      * @return boolean
0158      */
0159     public static function escalateReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0160     {
0161         self::_startReadLockProcessing($lockDirectory);
0162 
0163         $lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
0164 
0165         // First, release the shared lock for the benefit of GFS since
0166         // it will fail the conditional request to promote the lock to
0167         // "exclusive" while the shared lock is held (even when we are
0168         // the only holder).
0169         $lock->unlock();
0170 
0171         // GFS is really poor.  While the above "unlock" returns, GFS
0172         // doesn't clean up it's tables right away (which will potentially
0173         // cause the conditional locking for the "exclusive" lock to fail.
0174         // We will retry the conditional lock request several times on a
0175         // failure to get past this.  The performance hit is negligible
0176         // in the grand scheme of things and only will occur with GFS
0177         // filesystems or if another local process has the shared lock
0178         // on local filesystems.
0179         for ($retries = 0; $retries < 10; $retries++) {
0180             if ($lock->lock(LOCK_EX, true)) {
0181                 // Exclusive lock is obtained!
0182                 self::_stopReadLockProcessing($lockDirectory);
0183                 return true;
0184             }
0185 
0186             // wait 1 microsecond
0187             usleep(1);
0188         }
0189 
0190         // Restore lock state
0191         $lock->lock(LOCK_SH);
0192 
0193         self::_stopReadLockProcessing($lockDirectory);
0194         return false;
0195     }
0196 
0197     /**
0198      * De-escalate Read lock to shared level
0199      *
0200      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0201      */
0202     public static function deEscalateReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0203     {
0204         $lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
0205         $lock->lock(LOCK_SH);
0206     }
0207 
0208     /**
0209      * Obtain exclusive optimization lock on the index
0210      *
0211      * Returns lock object on success and false otherwise (doesn't block execution)
0212      *
0213      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0214      * @return mixed
0215      */
0216     public static function obtainOptimizationLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0217     {
0218         $lock = $lockDirectory->createFile(self::OPTIMIZATION_LOCK_FILE);
0219         if (!$lock->lock(LOCK_EX, true)) {
0220             return false;
0221         }
0222         return $lock;
0223     }
0224 
0225     /**
0226      * Release exclusive optimization lock
0227      *
0228      * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
0229      */
0230     public static function releaseOptimizationLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
0231     {
0232         $lock = $lockDirectory->getFileObject(self::OPTIMIZATION_LOCK_FILE);
0233         $lock->unlock();
0234     }
0235 
0236 }