Use ristretto to improve find performance

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
```
This commit is contained in:
aeneasr 2020-02-20 11:59:41 +01:00 committed by Bogdan Drutu
parent d9d7dcdc63
commit fc6df51032
No known key found for this signature in database
GPG key ID: 44D691C6C11748F1
5 changed files with 382 additions and 21 deletions

6
go.mod
View file

@ -7,10 +7,12 @@ require (
github.com/coreos/bbolt v1.3.2 // indirect
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/dgraph-io/ristretto v0.0.3
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/fsnotify/fsnotify v1.4.7
github.com/gogo/protobuf v1.2.1 // indirect
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.3.3 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
@ -27,7 +29,7 @@ require (
github.com/spf13/cast v1.3.0
github.com/spf13/jwalterweatherman v1.0.0
github.com/spf13/pflag v1.0.3
github.com/stretchr/testify v1.3.0
github.com/stretchr/testify v1.4.0
github.com/subosito/gotenv v1.2.0
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect

27
go.sum
View file

@ -20,6 +20,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -33,6 +34,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
@ -48,8 +50,12 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI=
github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
@ -66,16 +72,16 @@ github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -181,7 +187,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -214,6 +219,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
@ -225,10 +231,10 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
@ -248,7 +254,6 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
@ -304,7 +309,6 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -313,7 +317,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@ -328,7 +331,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@ -352,7 +354,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -368,7 +369,6 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -378,6 +378,7 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

248
stub/benchmark.json Normal file
View file

@ -0,0 +1,248 @@
{
"log": {
"level": "debug",
"format": "json"
},
"profiling": "cpu",
"serve": {
"proxy": {
"port": 1234,
"host": "127.0.0.1",
"timeout": {
"read": "1s",
"write": "2s",
"idle": "3s"
},
"cors": {
"enabled": true,
"allowed_origins": [
"https://example.com",
"https://*.example.com"
],
"allowed_methods": [
"POST",
"GET",
"PUT",
"PATCH",
"DELETE"
],
"allowed_headers": [
"Authorization",
"Content-Type"
],
"exposed_headers": [
"Content-Type"
],
"allow_credentials": true,
"max_age": 10,
"debug": true
},
"tls": {
"key": {
"path": "/path/to/key.pem",
"base64": "LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3..."
},
"cert": {
"path": "/path/to/cert.pem",
"base64": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr..."
}
}
},
"api": {
"port": 1235,
"host": "127.0.0.2",
"cors": {
"enabled": true,
"allowed_origins": [
"https://example.org",
"https://*.example.org"
],
"allowed_methods": [
"GET",
"PUT",
"PATCH",
"DELETE"
],
"allowed_headers": [
"Authorization",
"Content-Type"
],
"exposed_headers": [
"Content-Type"
],
"allow_credentials": true,
"max_age": 10,
"debug": true
},
"tls": {
"key": {
"path": "/path/to/key.pem",
"base64": "LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3..."
},
"cert": {
"path": "/path/to/cert.pem",
"base64": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr..."
}
}
}
},
"access_rules": {
"repositories": [
"file://path/to/rules.json",
"inline://W3siaWQiOiJmb28tcnVsZSIsImF1dGhlbnRpY2F0b3JzIjpbXX1d",
"https://path-to-my-rules/rules.json"
],
"matching_strategy": "glob"
},
"errors": {
"fallback": [
"json"
],
"handlers": {
"redirect": {
"enabled": true,
"config": {
"to": "http://path-to/redirect"
}
},
"json": {
"enabled": true,
"config": {
"verbose": true,
"when": [
{
"error": [
"unauthorized",
"forbidden",
"internal_server_error"
],
"request": {
"header": {
"content_type": [
"application/json"
],
"accept": [
"application/json"
]
},
"cidr": [
"127.0.0.0/24"
]
}
}
]
}
}
}
},
"authenticators": {
"anonymous": {
"enabled": true,
"config": {
"subject": "guest"
}
},
"cookie_session": {
"enabled": true,
"config": {
"check_session_url": "https://session-store-host",
"only": [
"sessionid"
]
}
},
"jwt": {
"enabled": true,
"config": {
"jwks_urls": [
"https://my-website.com/.well-known/jwks.json",
"https://my-other-website.com/.well-known/jwks.json",
"file://path/to/local/jwks.json"
],
"scope_strategy": "wildcard"
}
},
"noop": {
"enabled": true
},
"oauth2_client_credentials": {
"enabled": true,
"config": {
"token_url": "https://my-website.com/oauth2/token"
}
},
"oauth2_introspection": {
"enabled": true,
"config": {
"introspection_url": "https://my-website.com/oauth2/introspection",
"scope_strategy": "exact",
"pre_authorization": {
"enabled": true,
"client_id": "some_id",
"client_secret": "some_secret",
"scope": [
"foo",
"bar"
],
"token_url": "https://my-website.com/oauth2/token"
}
}
},
"unauthorized": {
"enabled": true
}
},
"authorizers": {
"allow": {
"enabled": true
},
"deny": {
"enabled": true
},
"keto_engine_acp_ory": {
"enabled": true,
"config": {
"base_url": "http://my-keto/",
"required_action": "unknown",
"required_resource": "unknown"
}
}
},
"mutators": {
"header": {
"enabled": false,
"config": {
"headers": {
"foo": "bar"
}
}
},
"cookie": {
"enabled": true,
"config": {
"cookies": {
"foo": "bar"
}
}
},
"hydrator": {
"enabled": true,
"config": {
"api": {
"url": "https://some-url/"
}
}
},
"id_token": {
"enabled": true,
"config": {
"issuer_url": "https://my-oathkeeper/",
"jwks_url": "https://fetch-keys/from/this/location.json",
"ttl": "1h"
}
},
"noop": {
"enabled": true
}
}
}

View file

@ -34,6 +34,7 @@ import (
"sync"
"time"
"github.com/dgraph-io/ristretto"
"github.com/fsnotify/fsnotify"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/printer"
@ -214,6 +215,8 @@ type Viper struct {
properties *properties.Properties
onConfigChange func(fsnotify.Event)
cache *ristretto.Cache
}
// New returns an initialized Viper instance.
@ -231,7 +234,6 @@ func New() *Viper {
v.env = make(map[string]string)
v.aliases = make(map[string]string)
v.typeByDefValue = false
return v
}
@ -270,6 +272,15 @@ func EnvKeyReplacer(r StringReplacer) Option {
})
}
// Cache sets Viper's cache (*ristretto.Cache).
// Using a cache may cause updates to external sources after the value is cached to be ignored,
// e.g. updating a flag value after the key was initially retrieve does not update the cached value.
func Cache(c *ristretto.Cache) Option {
return optionFunc(func(v *Viper) {
v.cache = c
})
}
// NewWithOptions creates a new Viper instance.
func NewWithOptions(opts ...Option) *Viper {
v := New()
@ -422,6 +433,7 @@ func (v *Viper) SetConfigFile(in string) {
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
func (v *Viper) SetEnvPrefix(in string) {
v.cache.Clear()
if in != "" {
v.envPrefix = in
}
@ -441,6 +453,7 @@ func (v *Viper) mergeWithEnvPrefix(in string) string {
func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }
func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
v.cache.Clear()
v.allowEmptyEnv = allowEmptyEnv
}
@ -470,6 +483,7 @@ func (v *Viper) ConfigFileUsed() string { return v.configFile }
func AddConfigPath(in string) { v.AddConfigPath(in) }
func (v *Viper) AddConfigPath(in string) {
v.cache.Clear()
if in != "" {
absin := absPathify(in)
jww.INFO.Println("adding", absin, "to paths to search")
@ -492,6 +506,7 @@ func AddRemoteProvider(provider, endpoint, path string) error {
}
func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
v.cache.Clear()
if !stringInSlice(provider, SupportedRemoteProviders) {
return UnsupportedRemoteProviderError(provider)
}
@ -524,6 +539,7 @@ func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) err
}
func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
v.cache.Clear()
if !stringInSlice(provider, SupportedRemoteProviders) {
return UnsupportedRemoteProviderError(provider)
}
@ -713,6 +729,7 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
func (v *Viper) SetTypeByDefaultValue(enable bool) {
v.cache.Clear()
v.typeByDefValue = enable
}
@ -732,7 +749,8 @@ func Get(key string) interface{} { return v.Get(key) }
func (v *Viper) Get(key string) interface{} {
lcaseKey := strings.ToLower(key)
val := v.find(lcaseKey, true)
val := v.cachedFind(lcaseKey, true)
if val == nil {
return nil
}
@ -748,7 +766,7 @@ func (v *Viper) Get(key string) interface{} {
switch valType.(type) {
case bool:
return cast.ToBool(val)
val = cast.ToBool(val)
case string:
return cast.ToString(val)
case int32, int16, int8, int:
@ -980,6 +998,7 @@ func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption)
func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }
func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
v.cache.Clear()
return v.BindFlagValues(pflagValueSet{flags})
}
@ -992,6 +1011,7 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
v.cache.Clear()
return v.BindFlagValue(key, pflagValue{flag})
}
@ -1000,6 +1020,7 @@ func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) }
func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
v.cache.Clear()
flags.VisitAll(func(flag FlagValue) {
if err = v.BindFlagValue(flag.Name(), flag); err != nil {
return
@ -1012,6 +1033,7 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }
func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
v.cache.Clear()
if flag == nil {
return fmt.Errorf("flag for %q is nil", key)
}
@ -1026,6 +1048,7 @@ func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
func BindEnv(input ...string) error { return v.BindEnv(input...) }
func (v *Viper) BindEnv(input ...string) error {
v.cache.Clear()
var key, envkey string
if len(input) == 0 {
return fmt.Errorf("missing key to bind to")
@ -1044,6 +1067,19 @@ func (v *Viper) BindEnv(input ...string) error {
return nil
}
// cachedFind uses Viper's cache to find a key's value and `v.find` if it is not available
// in the cache.
func (v *Viper) cachedFind(lcaseKey string, flagDefault bool) interface{} {
realKey := v.realKey(lcaseKey)
if value, found := v.cache.Get(realKey); found {
return value
}
value := v.find(lcaseKey, flagDefault)
v.cache.Set(realKey, value, 0)
return value
}
// Given a key, find the value.
//
// Viper will check to see if an alias exists first.
@ -1226,7 +1262,7 @@ func IsSet(key string) bool { return v.IsSet(key) }
func (v *Viper) IsSet(key string) bool {
lcaseKey := strings.ToLower(key)
val := v.find(lcaseKey, false)
val := v.cachedFind(lcaseKey, false)
return val != nil
}
@ -1235,6 +1271,7 @@ func (v *Viper) IsSet(key string) bool {
func AutomaticEnv() { v.AutomaticEnv() }
func (v *Viper) AutomaticEnv() {
v.cache.Clear()
v.automaticEnvApplied = true
}
@ -1244,6 +1281,7 @@ func (v *Viper) AutomaticEnv() {
func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
v.cache.Clear()
v.envKeyReplacer = r
}
@ -1313,6 +1351,9 @@ func (v *Viper) InConfig(key string) bool {
func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
func (v *Viper) SetDefault(key string, value interface{}) {
// We're clearing the whole cache because nested keys may cause issues if only the key is evicted.
v.cache.Clear()
// If alias passed in, then set the proper default
key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value)
@ -1332,6 +1373,9 @@ func (v *Viper) SetDefault(key string, value interface{}) {
func Set(key string, value interface{}) { v.Set(key, value) }
func (v *Viper) Set(key string, value interface{}) {
// We're clearing the whole cache because nested keys may cause issues if only the key is evicted.
v.cache.Clear()
// If alias passed in, then set the proper override
key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value)
@ -1349,6 +1393,7 @@ func (v *Viper) Set(key string, value interface{}) {
func ReadInConfig() error { return v.ReadInConfig() }
func (v *Viper) ReadInConfig() error {
v.cache.Clear()
jww.INFO.Println("Attempting to read in config file")
filename, err := v.getConfigFile()
if err != nil {
@ -1380,6 +1425,7 @@ func (v *Viper) ReadInConfig() error {
func MergeInConfig() error { return v.MergeInConfig() }
func (v *Viper) MergeInConfig() error {
v.cache.Clear()
jww.INFO.Println("Attempting to merge in config file")
filename, err := v.getConfigFile()
if err != nil {
@ -1403,6 +1449,7 @@ func (v *Viper) MergeInConfig() error {
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
func (v *Viper) ReadConfig(in io.Reader) error {
v.cache.Clear()
v.config = make(map[string]interface{})
return v.unmarshalReader(in, v.config)
}
@ -1411,6 +1458,7 @@ func (v *Viper) ReadConfig(in io.Reader) error {
func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
func (v *Viper) MergeConfig(in io.Reader) error {
v.cache.Clear()
cfg := make(map[string]interface{})
if err := v.unmarshalReader(in, cfg); err != nil {
return err
@ -1423,6 +1471,7 @@ func (v *Viper) MergeConfig(in io.Reader) error {
func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) }
func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
v.cache.Clear()
if v.config == nil {
v.config = make(map[string]interface{})
}
@ -1978,6 +2027,7 @@ func (v *Viper) AllSettings() map[string]interface{} {
func SetFs(fs afero.Fs) { v.SetFs(fs) }
func (v *Viper) SetFs(fs afero.Fs) {
v.cache.Clear()
v.fs = fs
}
@ -1986,6 +2036,7 @@ func (v *Viper) SetFs(fs afero.Fs) {
func SetConfigName(in string) { v.SetConfigName(in) }
func (v *Viper) SetConfigName(in string) {
v.cache.Clear()
if in != "" {
v.configName = in
v.configFile = ""
@ -1997,6 +2048,7 @@ func (v *Viper) SetConfigName(in string) {
func SetConfigType(in string) { v.SetConfigType(in) }
func (v *Viper) SetConfigType(in string) {
v.cache.Clear()
if in != "" {
v.configType = in
}

View file

@ -22,6 +22,7 @@ import (
"testing"
"time"
"github.com/dgraph-io/ristretto"
"github.com/fsnotify/fsnotify"
"github.com/mitchellh/mapstructure"
"github.com/spf13/afero"
@ -244,13 +245,16 @@ func initDirs(t *testing.T) (string, string, func()) {
testDirs = append(testDirs, `d\d`)
}
wd, err := os.Getwd()
require.NoError(t, err, "Unable to get working directory")
root, err := ioutil.TempDir("", "")
require.NoError(t, err, "Failed to create temporary directory")
cleanup := true
defer func() {
if cleanup {
os.Chdir("..")
os.Chdir(wd)
os.RemoveAll(root)
}
}()
@ -273,7 +277,7 @@ func initDirs(t *testing.T) (string, string, func()) {
cleanup = false
return root, config, func() {
os.Chdir("..")
os.Chdir(wd)
os.RemoveAll(root)
}
}
@ -2270,6 +2274,60 @@ func BenchmarkGetBool(b *testing.B) {
}
}
func BenchmarkFindCacheDisabled(b *testing.B) {
os.Setenv("MUTATORS_HEADER_ENABLED", "true")
v = New()
v.SetConfigFile("stub/benchmark.json")
if err := v.ReadInConfig(); err != nil {
b.Fatal(err)
}
v.AutomaticEnv()
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
keys := v.AllKeys()
numKeys := len(keys)
var key string
for i := 0; i < b.N; i++ {
key = keys[i%numKeys]
if v.find(key, true) == nil {
b.Fatalf("cachedFind returned a nil value for key: %s", key)
}
}
}
func BenchmarkFindCacheEnabled(b *testing.B) {
os.Setenv("MUTATORS_HEADER_ENABLED", "true")
cache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: 1000,
MaxCost: 1 << 20, // 1MB max cache
BufferItems: 64,
})
require.NoError(b, err)
v = NewWithOptions(Cache(cache))
v.SetConfigFile("stub/benchmark.json")
if err := v.ReadInConfig(); err != nil {
b.Fatal(err)
}
v.AutomaticEnv()
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
keys := v.AllKeys()
numKeys := len(keys)
var key string
for i := 0; i < b.N; i++ {
key = keys[i%numKeys]
if v.cachedFind(key, true) == nil {
b.Fatalf("cachedFind returned a nil value for key: %s", key)
}
}
}
func BenchmarkGet(b *testing.B) {
key := "BenchmarkGet"
v = New()