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

[6.x] Introduce a locking mechanism for the array cache driver #30253

Merged
merged 1 commit into from
Oct 14, 2019
Merged

[6.x] Introduce a locking mechanism for the array cache driver #30253

merged 1 commit into from
Oct 14, 2019

Conversation

timacdonald
Copy link
Member

@timacdonald timacdonald commented Oct 12, 2019

This PR introduces a locking mechanism to the array cache driver, for testing.

It is currently not possible to substitute the array cache driver in tests for any driver that supports locking if your test suite / app relies on the locking functionality.

This PR changes that and is something I've wanted for on projects I work on.

This allows me to run the test suite without having to have a lock supporting cache driver running / setup on my machine - which is nice coming into a project and working on parts of a project that don't interact with the cache locking mechanism because it means I can setup and digging into code faster - and it also means a test environment can be setup without requiring a "real" driver like Redis / memcached to be setup and configured.

It's also nice that once you do go down the path of introducing cache locking into your project, you don't have to also change you testing driver. It just continues to work as expected. Related: #29161

I'm happy to package this up if it isn't something you want to add / maintain in the framework.

I'd love if someone could triple check the expiration tests to make sure my logic is sold there, and I don't need to shift the boundary forward a microsecond

Not that is really matters, because the in memory cache drivers are so fast...but this is faster than, say, memcached. But, like, both are so quick...it isn't going to have a speed impact because the difference is negligible. I just thought I'd mention it.

@taylorotwell taylorotwell merged commit cde2375 into laravel:6.x Oct 14, 2019
@taylorotwell
Copy link
Member

Nice work 👍

@driesvints
Copy link
Member

@timacdonald nice work dude!

@timacdonald
Copy link
Member Author

Thanks! Hopefully it'll be useful to others as well :)

@barryvdh
Copy link
Contributor

Nice! It always bothered me that I couldn't test it locally also.

Could we add locking to other drivers as well, even though they might not be 100% atomic? To make it possible to use the same codepath locally, when not using redis for example.

@Spartaques
Copy link

I spent a lot of time for understanding what's going on and when i understood that it's all because array driver not support locking i was very hungry. But for now it's not a problem. Good work)

@Spartaques
Copy link

@timacdonald

@timacdonald
Copy link
Member Author

@Spartaques great to hear it'll be handy for you!

@paulandroshchuk
Copy link
Contributor

@timacdonald thanks for this amaizing PR, that's something I've been wanting to for some time.

I just faced a small issue when releasing a released lock. Seems like right now it just simply unsets it from the store which makes it throw an Undefined index {index} exception if the lock's key is missing in the store.

However if we try to release a released/incorrect lock using Redis, it won't throw any exception because Redis won't throw it. (not checked whether it throws with other drivers such as DynamoDB, etc.)

Also, I'm thinking that it will also throw an exception if we're trying to release a lock aquired by another parallel process.

So, what do you think if we release the lock only if it exists within the array store?

public function forceRelease()
{
    if ($this->exists()) {
        unset($this->store->locks[$this->name]);
    }
}

@paulandroshchuk
Copy link
Contributor

@timacdonald I can make a PR for that, just wanted to validate the idea before I do it.

@GrahamCampbell
Copy link
Member

Please target your PR at 7.x, or if there are breaking changes that can't be reviewed tomorrow morning, master. Laravel 7.0.0 will be released tomorrow.

@timacdonald
Copy link
Member Author

@paulandroshchuk i’d love to see the exact error from your test suite. It was my understanding that unset() doesn’t care if the array key exists or not. So maybe it is something else causing the issue. Eithe way, a stacktrace would be great to help you debug this.

See: https://3v4l.org/IhaC2

@timacdonald
Copy link
Member Author

Re: parallel process - I don’t think that is a concern as the driver is only meant for testing. I also thought that parallel test runners all create their own instance of the container and it’s bound service classes, so there shouldn’t be any clashes between threads

@paulandroshchuk
Copy link
Contributor

@paulandroshchuk i’d love to see the exact error from your test suite. It was my understanding that unset() doesn’t care if the array key exists or not. So maybe it is something else causing the issue. Eithe way, a stacktrace would be great to help you debug this.

See: https://3v4l.org/IhaC2

@timacdonald

My bad, unset() shouldn't really throw any exceptions.

I just revisited it and let's take a look at ArrayLock#70. When checking if the lock is owned by the current process it just checks the lock's ['owner'], and if the store doesn't have any lock (which might have been previously released) with the provided name, it will throw an "Undefined index" exception.

So, I think that we'll need to check whether the lock exists in the release() method:

if (! $this->exists()) {
    return false;
}

if (! $this->isOwnedByCurrentProcess()) {
...

@timacdonald
Copy link
Member Author

Ahh, nice yea I can see how we might run into that. I think that makes sense but would be good to have a failing test that we can PR with the fix. Did u wanna put one together or would u like me to help you write one? More than happy to help out.

I can probably help with providing a failing test to you this afternoon and you can add it to the fix PR.

@paulandroshchuk
Copy link
Contributor

I can probably help with providing a failing test to you this afternoon and you can add it to the fix PR.

That would be awesome! Thanks @timacdonald

@timacdonald
Copy link
Member Author

Cool. I didn’t see ya response last night but I’ll whip one up this arvo and ping ya with it.

@timacdonald
Copy link
Member Author

@paulandroshchuk here is a failing test for your PR: timacdonald@226ea07?diff=unified

You can pull that commit into your PR or just copy and paste the code into your PR. Whatever works best for you is totally cool with me. Let me know if I can help in any other way with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants