LockManager.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Search_Lucene
  17. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /** Zend_Search_Lucene_Storage_Directory */
  22. require_once 'Zend/Search/Lucene/Storage/Directory.php';
  23. /** Zend_Search_Lucene_Storage_File */
  24. require_once 'Zend/Search/Lucene/Storage/File.php';
  25. /**
  26. * This is an utility class which provides index locks processing functionality
  27. *
  28. * @category Zend
  29. * @package Zend_Search_Lucene
  30. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  31. * @license http://framework.zend.com/license/new-bsd New BSD License
  32. */
  33. class Zend_Search_Lucene_LockManager
  34. {
  35. /**
  36. * consts for name of file to show lock status
  37. */
  38. const WRITE_LOCK_FILE = 'write.lock.file';
  39. const READ_LOCK_FILE = 'read.lock.file';
  40. const READ_LOCK_PROCESSING_LOCK_FILE = 'read-lock-processing.lock.file';
  41. const OPTIMIZATION_LOCK_FILE = 'optimization.lock.file';
  42. /**
  43. * Obtain exclusive write lock on the index
  44. *
  45. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  46. * @return Zend_Search_Lucene_Storage_File
  47. * @throws Zend_Search_Lucene_Exception
  48. */
  49. public static function obtainWriteLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  50. {
  51. $lock = $lockDirectory->createFile(self::WRITE_LOCK_FILE);
  52. if (!$lock->lock(LOCK_EX)) {
  53. require_once 'Zend/Search/Lucene/Exception.php';
  54. throw new Zend_Search_Lucene_Exception('Can\'t obtain exclusive index lock');
  55. }
  56. return $lock;
  57. }
  58. /**
  59. * Release exclusive write lock
  60. *
  61. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  62. */
  63. public static function releaseWriteLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  64. {
  65. $lock = $lockDirectory->getFileObject(self::WRITE_LOCK_FILE);
  66. $lock->unlock();
  67. }
  68. /**
  69. * Obtain the exclusive "read escalation/de-escalation" lock
  70. *
  71. * Required to protect the escalate/de-escalate read lock process
  72. * on GFS (and potentially other) mounted filesystems.
  73. *
  74. * Why we need this:
  75. * While GFS supports cluster-wide locking via flock(), it's
  76. * implementation isn't quite what it should be. The locking
  77. * semantics that work consistently on a local filesystem tend to
  78. * fail on GFS mounted filesystems. This appears to be a design defect
  79. * in the implementation of GFS. How this manifests itself is that
  80. * conditional promotion of a shared lock to exclusive will always
  81. * fail, lock release requests are honored but not immediately
  82. * processed (causing erratic failures of subsequent conditional
  83. * requests) and the releasing of the exclusive lock before the
  84. * shared lock is set when a lock is demoted (which can open a window
  85. * of opportunity for another process to gain an exclusive lock when
  86. * it shoudln't be allowed to).
  87. *
  88. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  89. * @return Zend_Search_Lucene_Storage_File
  90. * @throws Zend_Search_Lucene_Exception
  91. */
  92. private static function _startReadLockProcessing(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  93. {
  94. $lock = $lockDirectory->createFile(self::READ_LOCK_PROCESSING_LOCK_FILE);
  95. if (!$lock->lock(LOCK_EX)) {
  96. require_once 'Zend/Search/Lucene/Exception.php';
  97. throw new Zend_Search_Lucene_Exception('Can\'t obtain exclusive lock for the read lock processing file');
  98. }
  99. return $lock;
  100. }
  101. /**
  102. * Release the exclusive "read escalation/de-escalation" lock
  103. *
  104. * Required to protect the escalate/de-escalate read lock process
  105. * on GFS (and potentially other) mounted filesystems.
  106. *
  107. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  108. */
  109. private static function _stopReadLockProcessing(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  110. {
  111. $lock = $lockDirectory->getFileObject(self::READ_LOCK_PROCESSING_LOCK_FILE);
  112. $lock->unlock();
  113. }
  114. /**
  115. * Obtain shared read lock on the index
  116. *
  117. * It doesn't block other read or update processes, but prevent index from the premature cleaning-up
  118. *
  119. * @param Zend_Search_Lucene_Storage_Directory $defaultLockDirectory
  120. * @return Zend_Search_Lucene_Storage_File
  121. * @throws Zend_Search_Lucene_Exception
  122. */
  123. public static function obtainReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  124. {
  125. $lock = $lockDirectory->createFile(self::READ_LOCK_FILE);
  126. if (!$lock->lock(LOCK_SH)) {
  127. require_once 'Zend/Search/Lucene/Exception.php';
  128. throw new Zend_Search_Lucene_Exception('Can\'t obtain shared reading index lock');
  129. }
  130. return $lock;
  131. }
  132. /**
  133. * Release shared read lock
  134. *
  135. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  136. */
  137. public static function releaseReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  138. {
  139. $lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
  140. $lock->unlock();
  141. }
  142. /**
  143. * Escalate Read lock to exclusive level
  144. *
  145. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  146. * @return boolean
  147. */
  148. public static function escalateReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  149. {
  150. self::_startReadLockProcessing($lockDirectory);
  151. $lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
  152. // First, release the shared lock for the benefit of GFS since
  153. // it will fail the conditional request to promote the lock to
  154. // "exclusive" while the shared lock is held (even when we are
  155. // the only holder).
  156. $lock->unlock();
  157. // GFS is really poor. While the above "unlock" returns, GFS
  158. // doesn't clean up it's tables right away (which will potentially
  159. // cause the conditional locking for the "exclusive" lock to fail.
  160. // We will retry the conditional lock request several times on a
  161. // failure to get past this. The performance hit is negligible
  162. // in the grand scheme of things and only will occur with GFS
  163. // filesystems or if another local process has the shared lock
  164. // on local filesystems.
  165. for ($retries = 0; $retries < 10; $retries++) {
  166. if ($lock->lock(LOCK_EX, true)) {
  167. // Exclusive lock is obtained!
  168. self::_stopReadLockProcessing($lockDirectory);
  169. return true;
  170. }
  171. // wait 1 microsecond
  172. usleep(1);
  173. }
  174. // Restore lock state
  175. $lock->lock(LOCK_SH);
  176. self::_stopReadLockProcessing($lockDirectory);
  177. return false;
  178. }
  179. /**
  180. * De-escalate Read lock to shared level
  181. *
  182. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  183. */
  184. public static function deEscalateReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  185. {
  186. $lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
  187. $lock->lock(LOCK_SH);
  188. }
  189. /**
  190. * Obtain exclusive optimization lock on the index
  191. *
  192. * Returns lock object on success and false otherwise (doesn't block execution)
  193. *
  194. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  195. * @return mixed
  196. */
  197. public static function obtainOptimizationLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  198. {
  199. $lock = $lockDirectory->createFile(self::OPTIMIZATION_LOCK_FILE);
  200. if (!$lock->lock(LOCK_EX, true)) {
  201. return false;
  202. }
  203. return $lock;
  204. }
  205. /**
  206. * Release exclusive optimization lock
  207. *
  208. * @param Zend_Search_Lucene_Storage_Directory $lockDirectory
  209. */
  210. public static function releaseOptimizationLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
  211. {
  212. $lock = $lockDirectory->getFileObject(self::OPTIMIZATION_LOCK_FILE);
  213. $lock->unlock();
  214. }
  215. }