-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Lock] Check TTL expiration in lock acquisition #22542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
7bd2873
to
3b0db33
Compare
Can this wait 3.4? We'd like to close 3.3 asap :) |
From the 3 pending PR related to the components Lock, this one is the most important. It avoid to acquire an expired Lock (due to network latency for instance). Cf discussion in the issue. That's said, with an adequate warning in the documentation, this PR is not an absolute requirement. |
PR rebased and fixed add messages in Exceptions. |
|
||
public function testAbortAfterExpiration() | ||
{ | ||
$this->markTestIncomplete('Memcached expecte a TTL greater than 1 sec. Simulating slow network is too complex'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be marked as skipped not incomplete.
What's the status of this PR? I'd like to merge/close all PRs about the lock component ASAP to avoid having to revert the component for 3.4. |
@fabpot waiting for approvals. As saids in #22542 (comment) this PR is the most important IMO related to lock component. |
|
||
foreach ($this->stores as $store) { | ||
try { | ||
$store->putOffExpiration($key, $ttl); | ||
if (0.0 >= $adjustedTtl = $expireAt - microtime(true)) { | ||
$this->logger->warning('TTL expires during the put off the expiration of the "{resource}" lock.', array('resource' => $key, 'store' => $store, 'ttl' => $ttl)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Message is not clear to me. Can you explain what you are trying to say?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are trying to put off the expiration of the lock (to extend it life duration) but the method take more time to perform that task than the asked TTL.
For instance, when calling putOffExpiration($key, 31)
takes 31 seconds.
@@ -117,6 +124,10 @@ public function putOffExpiration(Key $key, $ttl) | |||
} | |||
} | |||
|
|||
if (microtime(true) >= $expireAt) { | |||
throw new LockExpiredException(sprintf('Failed to put of the expiration the "%s" lock in less than "%f".', $key, $ttl)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If put off
is the right verb, then there is a typo here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing of
after expiration
also
return; | ||
$expireAt = microtime(true) + $this->initialTtl; | ||
if (!$this->memcached->add((string) $key, $token, (int) ceil($this->initialTtl))) { | ||
// the lock is already acquire. It could be us. Let's try to put off. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
acquired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
// the lock is already acquire. It could be us. Let's try to put off. | ||
$this->putOffExpiration($key, $this->initialTtl); | ||
if (microtime(true) >= $expireAt) { | ||
throw new LockExpiredException(sprintf('Failed to store the "%s" lock in less than "%f".', $key, $this->initialTtl)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove in less than
. I would also add the unit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed (same for others usage)
@@ -105,6 +111,10 @@ public function putOffExpiration(Key $key, $ttl) | |||
if (!$this->memcached->cas($cas, (string) $key, $token, $ttl)) { | |||
throw new LockConflictedException(); | |||
} | |||
|
|||
if (microtime(true) >= $expireAt) { | |||
throw new LockExpiredException(sprintf('Failed to put of the expiration the "%s" lock in less than "%f".', $key, $ttl)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
put off?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
throw new LockConflictedException(); | ||
} | ||
|
||
if (microtime(true) >= $expireAt) { | ||
throw new LockExpiredException(sprintf('Failed to put of the expiration the "%s" lock in less than "%f".', $key, $ttl)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
|
||
public function testAbortAfterExpiration() | ||
{ | ||
$this->markTestSkipped('Memcached expecte a TTL greater than 1 sec. Simulating slow network is too complex'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo expects
Simulating a slow network is too hard
76587d0
to
0bd266a
Compare
54e28aa
to
aa6c353
Compare
PR rebased with few changes
|
aa6c353
to
1522312
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with minor comment
|
||
public function testAbortAfterExpiration() | ||
{ | ||
$this->markTestSkipped('Memcached expecte a TTL greater than 1 sec. Simulating a slow network is too hard'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo expects
a7bc204
to
2cd518e
Compare
81162a6
to
2e4f434
Compare
2e4f434
to
9743bfb
Compare
failling test not related to this PR |
|
||
if (null === $this->expiringDate || $newExpiringDate < $this->expiringDate) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switch to float comparison instead of datetime, due to bug in PHP => https://3v4l.org/bSt3d
src/Symfony/Component/Lock/Key.php
Outdated
if (null === $this->expiringDate || $newExpiringDate < $this->expiringDate) { | ||
$this->expiringDate = $newExpiringDate; | ||
if (null === $this->expiringTime || $this->expiringTime > $newTime) { | ||
$this->expiringTime = $newTime; | ||
} | ||
} | ||
|
||
public function resetExpiringDate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be resetExpiringTime()
now, right?
src/Symfony/Component/Lock/Key.php
Outdated
} | ||
} | ||
|
||
public function resetExpiringDate() | ||
{ | ||
$this->expiringDate = null; | ||
$this->expiringTime = null; | ||
} | ||
|
||
/** | ||
* @return \DateTimeImmutable | ||
*/ | ||
public function getExpiringDate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using microtime is a internal implementation to fix issue with \Datetime comparison. IMHO there is no reason to change the public interface of this class, returning a \Datetime here is more usefull than returning a microtime.
If I have to change it, I would rather stop using ExpiringThing
and replace them by lifetime
with resetLifetime()
, reduceLifetime(float $ttl)
, isExpired(): bool
and getTimeToLive(): float
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We now have a public method that takes a float
and the rest of the API dealing with DateTime
, that feels slightly inconsistent to me as well. I'm fine with float
only.
Would getTimeToLive()
return the remaining ttl at the moment? Or just the original (full) ttl?
Do we really need it? (just wondering)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getTimeToLive
should returns the raimaining TTL. Returning the initial TTL without knowing when this TTL had been sets is useless.
The purpose is to let the locker knowing if he has time to perform his job.
For instance you iterate over thousands of items. You know that it takes between 1 and 3 seconds to treat 1 item. You can use the the getTimeToLive
after each item and when the TTL is below 5 seconds you call the refresh()
method to extends the life of your lock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me
1d60277
to
3b6be16
Compare
{ | ||
$this->expiringDate = null; | ||
return null === $this->expiringTime ? null : $this->expiringTime - microtime(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be null
or (float) PHP_MAX_INT
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd keep null with a comment explaining its meaning in the return annotation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
3b6be16
to
cb7b16c
Compare
cb7b16c
to
b9de71b
Compare
👍 |
Thank you @jderusse. |
…sse) This PR was squashed before being merged into the 3.4 branch (closes #22542). Discussion ---------- [Lock] Check TTL expiration in lock acquisition | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #22452 | License | MIT | Doc PR | NA This PR improve lock acquisition by throwing exception when the TTL is expired during the lock process. Commits ------- f74ef35 [Lock] Check TTL expiration in lock acquisition
This PR improve lock acquisition by throwing exception when the TTL is expired during the lock process.