17 require_once SQ_CORE_PACKAGE_PATH.
'/system/locking/locking_method/locking_method.inc';
36 public static $memcache = NULL;
50 parent::__construct($assetid);
64 assert_true(extension_loaded(
'memcache'),
'Cannot use Memcache Locking Method; it requires the memcache PECL extension installed within PHP, which is not installed');
65 assert_true(file_exists(SQ_DATA_PATH.
'/private/conf/memcache.inc'),
'Cannot use Memcache Locking Method; the Memcache configuration file is not set');
67 $memcache_conf = require(SQ_DATA_PATH.
'/private/conf/memcache.inc');
68 $hosts =& $memcache_conf[
'hosts'];
69 $services =& $memcache_conf[
'services'];
71 assert_true(count($hosts) > 0,
'Cannot use Memcache Locking Method; no hosts are defined in the Memcache configuration file');
72 assert_true(array_key_exists(
'locking', $services) === TRUE,
'Cannot use Memcache Locking Method; no Memcache hosts are assigned to locking');
73 assert_true(count($services[
'locking']) > 0,
'Cannot use Memcache Locking Method; no Memcache hosts are assigned to locking');
76 self::$memcache =
new Memcache;
78 foreach ($services[
'locking'] as $host_key => $weight) {
79 assert_true(array_key_exists($host_key, $hosts) === TRUE,
'Cannot use Memcache Locking Method; host key "'.$host_key.
'" assigned for use for locking but not defined as a host');
80 $host = $hosts[$host_key];
81 self::$memcache->addServer($host[
'host'], $host[
'port'], $host[
'persistent'], $weight, $host[
'timeout'], $host[
'retry_interval'], $host[
'status'], Array(
'Locking_Method_Memcache',
'failureCallback'));
84 self::$memcache->setCompressThreshold($memcache_conf[
'compression_threshold'], $memcache_conf[
'compression_min_saving']);
115 public static function acquireLock($lockid, $source_lockid=
'', $expires=0)
117 if (self::$memcache === NULL) {
118 self::_initMemcache();
121 $current_lock = $GLOBALS[
'SQ_SYSTEM']->getLockInfo($lockid);
122 $current_time = time();
125 if (!empty($current_lock)) {
128 if ($current_lock[
'userid'] == $GLOBALS[
'SQ_SYSTEM']->currentUserid()) {
129 return self::updateLock($lockid, $expires);
131 $user = $GLOBALS[
'SQ_SYSTEM']->am->getAsset($current_lock[
'userid']);
132 throw new Exception(
'Lock already held by "'.$user->name.
'"');
137 if (empty($source_lockid)) $source_lockid = $lockid;
139 if (!is_null($expires)) {
140 $expires = (empty($expires)) ? ($current_time + SQ_CONF_LOCK_LENGTH) : (int) $expires;
147 if ($expires !== NULL) {
148 $expire_seconds = $expires - $current_time;
149 if ($expire_seconds < 1) $expire_seconds = 1;
158 'source_lockid' => $source_lockid,
159 'userid' => $GLOBALS[
'SQ_SYSTEM']->currentUserId(),
160 'expires' => ts_iso8601($expires),
163 self::$memcache->set(
'lock:'.$lockid, $lock_data, 0, $expire_seconds);
165 $chain_lockids = self::$memcache->get(
'lockchain:'.$source_lockid);
166 if ($chain_lockids !== FALSE) {
168 if (array_search($lockid, $chain_lockids) === FALSE) {
169 $chain_lockids[] = $lockid;
173 $chain_lockids = Array($lockid);
176 self::$memcache->set(
'lockchain:'.$source_lockid, $chain_lockids, 0, $expire_seconds);
205 if (self::$memcache === NULL) {
206 self::_initMemcache();
209 $current_time = time();
210 $current_lock = self::getLockInfo($lockid);
214 if (empty($current_lock) || empty($current_lock[
'expires'])) {
218 if (!is_null($expires)) {
219 $expires = (!$expires) ? (time() + SQ_CONF_LOCK_LENGTH) : (
int) $expires;
225 if ($expires !== NULL) {
226 $expire_seconds = $expires - $current_time;
227 if ($expire_seconds < 1) $expire_seconds = 1;
234 $chain_lockids = self::$memcache->get(
'lockchain:'.$current_lock[
'source_lockid']);
235 if ($chain_lockids !== FALSE) {
237 'source_lockid' => $current_lock[
'source_lockid'],
238 'userid' => $current_lock[
'userid'],
239 'expires' => ts_iso8601($expires),
241 foreach ($chain_lockids as $chain_lockid) {
242 $lock_data[
'lockid'] = $chain_lockid;
243 self::$memcache->set(
'lock:'.$chain_lockid, $lock_data, 0, $expire_seconds);
246 self::$memcache->set(
'lockchain:'.$current_lock[
'source_lockid'], $chain_lockids, 0, $expire_seconds);
268 if (self::$memcache === NULL) {
269 self::_initMemcache();
272 $current_lock = self::getLockInfo($lockid, FALSE, FALSE);
273 if (empty($current_lock))
return TRUE;
276 $chain_lockids = self::$memcache->get(
'lockchain:'.$current_lock[
'source_lockid']);
277 if ($chain_lockids !== FALSE) {
278 foreach ($chain_lockids as $chain_lockid) {
279 self::$memcache->delete(
'lock:'.$chain_lockid);
281 self::$memcache->delete(
'lockchain:'.$current_lock[
'source_lockid']);
308 public static function getLockInfo($lockid, $full_chain=FALSE, $check_expires=TRUE, $allow_only_one=TRUE)
310 if (self::$memcache === NULL) {
311 self::_initMemcache();
315 if (is_array($lockid) === FALSE) $lockid = Array($lockid);
317 foreach ($lockid as $this_lockid) {
318 $main_lock_data = self::$memcache->get(
'lock:'.$this_lockid);
319 if ($main_lock_data !== FALSE) {
320 $main_lock_data[
'expires'] = empty($main_lock_data[
'expires']) ? NULL : iso8601_ts($main_lock_data[
'expires']);
321 $results[$this_lockid] = $main_lock_data;
324 if ($full_chain === TRUE) {
325 $results[$this_lockid][
'chained_assets'] = Array();
326 $chain_lockids = self::$memcache->get(
'lockchain:'.$main_lock_data[
'source_lockid']);
327 if ($chain_lockids !== FALSE) {
329 foreach ($chain_lockids as $chain_lockid) {
330 if ($chain_lockid !== $this_lockid) {
331 $chain_lock_data = self::$memcache->get(
'lock:'.$chain_lockid);
332 if ($chain_lock_data !== FALSE) {
333 $chain_lock_data[
'expires'] = empty($chain_lock_data[
'expires']) ? NULL : iso8601_ts($chain_lock_data[
'expires']);
334 $results[$this_lockid][
'chained_assets'][$chain_lockid] = $chain_lock_data;
347 if (!empty($results) && $allow_only_one) {
348 $results = reset($results);
410 log_error(get_class().
" failure communicating with $hostname:$port", E_USER_WARNING);