Skip to content

Use ristretto to improve find performance#849

Closed
aeneasr wants to merge 1 commit intospf13:masterfrom
ory:get-cache
Closed

Use ristretto to improve find performance#849
aeneasr wants to merge 1 commit intospf13:masterfrom
ory:get-cache

Conversation

@aeneasr
Copy link
Contributor

@aeneasr aeneasr commented Feb 20, 2020

As discussed in another PR, v.find performance deteriorates as the config map grows and more config sources come in. We can significantly improve the performance of v.find operations (and thus all Get* operations) with a simple cache strategy.

As you can see in the benchmark below, not using a cache needs 4060 ns/op for reading all keys of a large config file, while using a cache reduces that time to 171ns/op on average.

Ristretto was chosen because it is a very performant, thread-safe
cache that does not suffer form congestion or long GC pauses.

% go test -bench=. .
goos: darwin
goarch: amd64
pkg: github.com/spf13/viper
BenchmarkFind/cache=false-8       289495              4060 ns/op
BenchmarkFind/cache=true-8       6980976               171 ns/op
PASS

This patch significantly improves `v.find` performance by using
ristretto cache.

Ristretto was chosen because it is a very performant, thread-safe
cache that does not suffer form congestion or long GC pauses.

```
% go test -bench=. .
goos: darwin
goarch: amd64
pkg: github.com/spf13/viper
BenchmarkGetBool-8               5431267               211 ns/op
BenchmarkFind/cache=false-8       289495              4060 ns/op
BenchmarkFind/cache=true-8       6980976               171 ns/op
BenchmarkGet-8                   6332091               193 ns/op
BenchmarkGetBoolFromMap-8       235071828                5.09 ns/op
PASS
```
@sagikazarmark
Copy link
Collaborator

Wow, this is super cool. I've been meaning to try ristretto for some time, but didn't have a chance.

I'll take a look at the PR soon! Thanks @aeneasr

@aeneasr
Copy link
Contributor Author

aeneasr commented Feb 20, 2020

Cool! We're running race tests on our fork and apparently there is one introduced by the cache. I'll look into what that's caused by, please hold the merge until then. :)

@sagikazarmark
Copy link
Collaborator

Sure!

How did you find the race? Because the race detector is enabled in Viper too:

https://github.com/spf13/viper/blob/master/Makefile#L45

@aeneasr
Copy link
Contributor Author

aeneasr commented Feb 20, 2020

With a simple go test -race -v ./.... Maybe it has to do something with the fork, I'll investigate. But it seems to be related to cache.Clear and cache.Get getting racy.

@aeneasr
Copy link
Contributor Author

aeneasr commented Feb 20, 2020

Ah, I know why this isn't caught here. Enabling -race doesn't do anything really unless there are actual tests with concurrency. We have a test for that in our fork as viper was (still is?) quite racy when it comes to set/get. You can find the test here: https://github.com/ory/viper/blob/get-cache-ory/viper_test.go#L2340

I think viper will have a race condition if that test was added without this patch as well, as there are concurrent read/writes to the map.

@sagikazarmark
Copy link
Collaborator

Well, for now this is what it is, but for the long term I really want to separate reading and writing in Viper.

IMHO fetching config from various sources is what's Viper's strong suite, writing/setting config is just there, but not really the primary use case for Viper (or at least I think it shouldn't be).

So for the long term I plan to keep the current API for compatibility reasons, at least for one more major version, but after that I'd like to see writing either gone from the core, or moved a different interface entirely. Writing and Get-ing are usually done by totally different consumers, so it should be okay.

@aeneasr
Copy link
Contributor Author

aeneasr commented Feb 20, 2020

IMHO fetching config from various sources is what's Viper's strong suite, writing/setting config is just there, but not really the primary use case for Viper (or at least I think it shouldn't be).

All of this works well until there's actually reloading (e.g. on config file changed) involved, which is where viper just breaks all over the place. We're using a RWMutex to prevent stuff like that but it obviously takes away some of the benefits you get from e.g. ristretto, although RLocks are kinda fine I guess.

setting config is just there, but not really the primary use case for Viper (or at least I think it shouldn't be).

As long as setting values for testing is supported I agree :)

@sagikazarmark
Copy link
Collaborator

All of this works well until there's actually reloading

Yeah, Viper pretty much sucks at that at the moment. I'm looking at changing that in a next version.

As long as setting values for testing is supported I agree :)

Obviously. Manually setting values should be possible, but that's a different consumer of Viper.

Ideally "getters" should only be able to use the getter API and not set values. That would probably get rid of lots of races.

@aeneasr
Copy link
Contributor Author

aeneasr commented Feb 23, 2020

Sounds good :) Let me know if there’s anything else I can do in this patch to get it merged :)

Copy link
Collaborator

@sagikazarmark sagikazarmark left a comment

Choose a reason for hiding this comment

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

@aeneasr can you please resolve the conflict? I'd like to get this merged.

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.

2 participants