Skip to content
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

restore shared lock ttl to previous value when releasing #37469

Merged
merged 2 commits into from
Dec 22, 2023

Conversation

icewind1991
Copy link
Member

@icewind1991 icewind1991 commented Mar 29, 2023

With shared locks, each time the lock is acquired, the ttl for the path is reset.

Due to this "ttl extension" when a shared lock isn't freed correctly for any reason
the lock won't expire until no shared locks are required for the path for 1h.
This can lead to a client repeatedly trying to upload a file, and failing forever
because the lock never gets the opportunity to expire.

To help the lock expire in this case, we lower the TTL back to what it was before we
took the shared lock only if nobody else got a shared lock after we did.

This doesn't handle all cases where multiple requests are acquiring shared locks
but it should handle some of the more common ones and not hurt things further.

Ideally each copy of a shared lock will have it's own independent TTL so locks can expire while others are still being help, but we don't have that luxury.

To test:

  • setup an instance with redis file locking without applying this patch
  • set a breakpoint at acquireLock
  • upload a file called test.txt
  • note the $path and $this->memcache->prefix used for the lock for test.txt (check $readablePath to ensure you're look at the right lock)
  • remove the breakpoint
  • delete test.txt
  • open a redis-cli and run
    • set "<$this->memcache->prefix><$path>" 1 (key should be something like bc2af67f80d0445df3c328c2a0f08faf/lockfiles/0ca47d1e59d0cbf07f715297a70f8417
    • expire "<$this->memcache->prefix><$path>" 3600
  • you have now created a "leaked" lock with a TTL of 1 hour
  • wait a few seconds and run expire "<$this->memcache->prefix><$path>" in redis-cli to check that the TTL is counting down
  • try to upload test.txt again, this should fail due to the leaked lock
  • check the TTL of the leaked lock which should have been reset to ~1h
  • apply the patch
  • try uploading `test.txt again
  • check that the TTL of the leaked lock didn't get reset to 1h

@icewind1991 icewind1991 added the 3. to review Waiting for reviews label Mar 29, 2023
@icewind1991 icewind1991 added this to the Nextcloud 27 milestone Mar 29, 2023
@icewind1991 icewind1991 requested review from a team, ArtificialOwl, blizzz and come-nc and removed request for a team March 29, 2023 16:36
lib/private/Lock/MemcacheLockingProvider.php Fixed Show fixed Hide fixed
lib/private/Lock/MemcacheLockingProvider.php Fixed Show fixed Hide fixed
lib/private/Memcache/Redis.php Fixed Show fixed Hide fixed
lib/private/Memcache/Redis.php Fixed Show fixed Hide fixed
lib/private/Memcache/LoggerWrapperCache.php Fixed Show fixed Hide fixed
lib/private/Memcache/LoggerWrapperCache.php Fixed Show fixed Hide fixed
lib/private/Memcache/ProfilerWrapperCache.php Fixed Show fixed Hide fixed
lib/private/Memcache/ProfilerWrapperCache.php Fixed Show fixed Hide fixed
lib/private/Memcache/Redis.php Fixed Show fixed Hide fixed
lib/private/Lock/MemcacheLockingProvider.php Outdated Show resolved Hide resolved
lib/private/Lock/MemcacheLockingProvider.php Show resolved Hide resolved
lib/private/Lock/MemcacheLockingProvider.php Show resolved Hide resolved
lib/private/Memcache/LoggerWrapperCache.php Outdated Show resolved Hide resolved
lib/private/Memcache/LoggerWrapperCache.php Outdated Show resolved Hide resolved
lib/private/Memcache/ProfilerWrapperCache.php Outdated Show resolved Hide resolved
lib/private/Memcache/ProfilerWrapperCache.php Outdated Show resolved Hide resolved
lib/public/IMemcacheTTL.php Outdated Show resolved Hide resolved
@come-nc
Copy link
Contributor

come-nc commented Mar 30, 2023

I am not sure I understand the problem description.
When uploading test.txt fails, it is because of the existing lock? If so, it means it fails to acquire the lock, no?
If it succeed to acquire the lock and extends it, what fails?

@icewind1991
Copy link
Member Author

When uploading test.txt fails, it is because of the existing lock?

Yes

If so, it means it fails to acquire the lock, no?

on upload it first 1) acquires a shared lock, 2) changes it to an exclusive one, 3) changes it back to a shared one
2) fails here, but 1) still extended the TTL

So if a client repeatedly attempts to upload a file that has a leaked shared lock, it will never succeed to change it's shared lock into an exclusive one, but the TTL will never expire.

lib/private/Lock/MemcacheLockingProvider.php Outdated Show resolved Hide resolved
@@ -88,6 +118,12 @@ public function releaseLock(string $path, int $type): void {
$newValue = $this->memcache->dec($path);
}

if ($newValue > 0) {
$this->restoreTTL($path);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the restoreTTL from changeLock not enough, given the scenario you described?
What is the rationale behind this one in releaseLock? For other kind of exceptions/failures?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still want to restore the ttl when a request just acquires + releases.

lib/private/Memcache/Redis.php Fixed Show fixed Hide fixed
lib/private/Memcache/Redis.php Fixed Show fixed Hide fixed
Copy link
Member

@blizzz blizzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logically it looks okay and I can follow your outlines, but I really cannot pretend to be very knowledgeable here. But contributed some bikeshedding ;)

lib/public/IMemcacheTTL.php Show resolved Hide resolved
lib/public/IMemcacheTTL.php Outdated Show resolved Hide resolved
lib/private/Lock/MemcacheLockingProvider.php Outdated Show resolved Hide resolved
lib/private/Lock/MemcacheLockingProvider.php Outdated Show resolved Hide resolved
lib/private/Memcache/Redis.php Outdated Show resolved Hide resolved
lib/private/Memcache/Redis.php Outdated Show resolved Hide resolved
This was referenced May 3, 2023
@skjnldsv skjnldsv added 2. developing Work in progress and removed 3. to review Waiting for reviews labels May 9, 2023
@skjnldsv skjnldsv modified the milestones: Nextcloud 27, Nextcloud 28 May 9, 2023
@skjnldsv skjnldsv added the bug label May 9, 2023
@skjnldsv skjnldsv mentioned this pull request Nov 1, 2023
This was referenced Nov 6, 2023
This was referenced Nov 14, 2023
@icewind1991
Copy link
Member Author

rebased, changed compareSetTTL to use lua scripts and added tests for compareSetTTL

@skjnldsv skjnldsv added 4. to release Ready to be released and/or waiting for tests to finish and removed 2. developing Work in progress labels Dec 21, 2023
Signed-off-by: Robin Appelman <robin@icewind.nl>
Signed-off-by: Robin Appelman <robin@icewind.nl>
@skjnldsv
Copy link
Member

/backport to stable28

@skjnldsv
Copy link
Member

/backport to stable27

@sorbaugh sorbaugh merged commit 7d8741c into master Dec 22, 2023
50 checks passed
@sorbaugh sorbaugh deleted the lock-restore-ttl branch December 22, 2023 09:43
@backportbot-nextcloud
Copy link

The backport to stable27 failed. Please do this backport manually.

# Switch to the target branch and update it
git checkout stable27
git pull origin stable27

# Create the new backport branch
git checkout -b fix/foo-stable27

# Cherry pick the change from the commit sha1 of the change against the default branch
# This might cause conflicts. Resolve them.
git cherry-pick abc123

# Push the cherry pick commit to the remote repository and open a pull request
git push origin fix/foo-stable27

Error: Unknown error

More info at https://docs.nextcloud.com/server/latest/developer_manual/getting_started/development_process.html#manual-backport

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4. to release Ready to be released and/or waiting for tests to finish bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants