LockManager.php 8.5 KB

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