Skip to content

Commit

Permalink
driver: Cache pipeline config and improve request latency (#348)
Browse files Browse the repository at this point in the history
Closes #346
  • Loading branch information
ecktom authored and aeneasr committed Jan 26, 2020
1 parent 495adcf commit 95673ed
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 139 deletions.
55 changes: 53 additions & 2 deletions driver/configuration/provider_viper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package configuration

import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"hash/crc64"
"net/url"
"strings"
"sync"
"time"

"github.com/imdario/mergo"
Expand Down Expand Up @@ -101,11 +104,16 @@ const (
)

type ViperProvider struct {
l logrus.FieldLogger
l logrus.FieldLogger
configMutex sync.RWMutex
configCachge map[uint64]json.RawMessage
}

func NewViperProvider(l logrus.FieldLogger) *ViperProvider {
return &ViperProvider{l: l}
return &ViperProvider{
l: l,
configCachge: make(map[uint64]json.RawMessage),
}
}

func (v *ViperProvider) AccessRuleRepositories() []url.URL {
Expand Down Expand Up @@ -197,7 +205,46 @@ func (v *ViperProvider) pipelineIsEnabled(prefix, id string) bool {
return viperx.GetBool(v.l, fmt.Sprintf("%s.%s.enabled", prefix, id), false)
}

func (v *ViperProvider) hashPipelineConfig(prefix, id string, override json.RawMessage) (uint64, error) {
ts := viper.ConfigChangeAt().UnixNano()
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(ts))

slices := [][]byte{
[]byte(prefix),
[]byte(id),
[]byte(override),
[]byte(b),
}

var hashSlices []byte
for _, s := range slices {
hashSlices = append(hashSlices, s...)
}

return crc64.Checksum(hashSlices, crc64.MakeTable(crc64.ECMA)), nil
}

func (v *ViperProvider) PipelineConfig(prefix, id string, override json.RawMessage, dest interface{}) error {
hash, err := v.hashPipelineConfig(prefix, id, override)
if err != nil {
return errors.WithStack(err)
}

v.configMutex.RLock()
c, ok := v.configCachge[hash]
v.configMutex.RUnlock()

if ok {
if dest != nil {
if err := json.NewDecoder(bytes.NewBuffer(c)).Decode(dest); err != nil {
return errors.WithStack(err)
}
}

return nil
}

// we need to create a copy for config otherwise we will accidentally override values
config, err := x.Deepcopy(viperx.GetStringMapConfig(stringsx.Splitx(fmt.Sprintf("%s.%s.config", prefix, id), ".")...))
if err != nil {
Expand Down Expand Up @@ -252,6 +299,10 @@ func (v *ViperProvider) PipelineConfig(prefix, id string, override json.RawMessa
return errors.WithStack(result.Errors())
}

v.configMutex.Lock()
v.configCachge[hash] = marshalled
v.configMutex.Unlock()

return nil
}

Expand Down
36 changes: 33 additions & 3 deletions driver/configuration/provider_viper_public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,9 @@ func TestPipelineConfig(t *testing.T) {
require.NoError(t, p.PipelineConfig("authenticators", "oauth2_introspection", nil, &res))
assert.JSONEq(t, `{"introspection_request_headers":{},"introspection_url":"https://override/path","pre_authorization":{"client_id":"some_id","client_secret":"some_secret","enabled":true,"scope":["foo","bar"],"token_url":"https://my-website.com/oauth2/token"},"required_scope":[],"scope_strategy":"exact","target_audience":[],"trusted_issuers":[]}`, string(res), "%s", res)

// Cleanup
require.NoError(t, os.Setenv("AUTHENTICATORS_OAUTH2_INTROSPECTION_CONFIG_INTROSPECTION_URL", ""))

require.NoError(t, p.PipelineConfig("authenticators", "oauth2_introspection", nil, &res))
assert.JSONEq(t, `{"introspection_request_headers":{},"introspection_url":"https://my-website.com/oauth2/introspection","pre_authorization":{"client_id":"some_id","client_secret":"some_secret","enabled":true,"scope":["foo","bar"],"token_url":"https://my-website.com/oauth2/token"},"required_scope":[],"scope_strategy":"exact","target_audience":[],"trusted_issuers":[]}`, string(res), "%s", res)

})

t.Run("case=should fail when invalid value is used in override", func(t *testing.T) {
Expand Down Expand Up @@ -93,6 +91,38 @@ func TestPipelineConfig(t *testing.T) {
})
}

/*
go test ./... -v -bench=. -run BenchmarkPipelineConfig -benchtime=10s
v0.35.1
594 20119202 ns/op
v0.35.2
3048037 3908 ns/op
*/

func BenchmarkPipelineConfig(b *testing.B) {
viper.Reset()
viperx.InitializeConfig(
"oathkeeper",
"./../../docs/",
logrus.New(),
)

err := viperx.Validate(gojsonschema.NewReferenceLoader("file://../../.schemas/config.schema.json"))
if err != nil {
viperx.LoggerWithValidationErrorFields(logrus.New(), err).Error("unable to validate")
}
require.NoError(b, err)

p := NewViperProvider(logrus.New())

for n := 0; n < b.N; n++ {
res := json.RawMessage{}
p.PipelineConfig("authenticators", "oauth2_introspection", nil, &res)
}
}

func TestViperProvider(t *testing.T) {
viper.Reset()
viperx.InitializeConfig(
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ require (
github.com/go-openapi/runtime v0.19.5
github.com/go-openapi/strfmt v0.19.3
github.com/go-openapi/swag v0.19.5
github.com/go-openapi/validate v0.19.3
github.com/go-sql-driver/mysql v1.4.1
github.com/go-swagger/go-swagger v0.21.1-0.20200107003254-1c98855b472d
github.com/gobuffalo/httptest v1.0.2
Expand All @@ -36,7 +35,7 @@ require (
github.com/ory/herodot v0.6.2
github.com/ory/ladon v1.0.1
github.com/ory/sdk/swagutil v0.0.0-20200123152503-0d50960e70bd // indirect
github.com/ory/viper v1.5.6
github.com/ory/viper v1.5.7
github.com/ory/x v0.0.91
github.com/pborman/uuid v1.2.0
github.com/pelletier/go-toml v1.6.0 // indirect
Expand Down
Loading

0 comments on commit 95673ed

Please sign in to comment.