authorize: hot path identity provider lookup optimizations

This commit is contained in:
Joe Kralicky 2024-06-18 21:40:03 -04:00
parent 7eca911292
commit e18c04216e
No known key found for this signature in database
GPG key ID: 75C4875F34A9FB79
29 changed files with 387 additions and 284 deletions

View file

@ -0,0 +1,184 @@
package config_test
import (
"context"
"encoding/base64"
"fmt"
"strings"
"testing"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/pkg/cryptutil"
"github.com/stretchr/testify/require"
)
func BenchmarkGetIdentityProviderForRequestURL_Old(b *testing.B) {
runBench := func(numPolicies int) func(b *testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
options := config.NewDefaultOptions()
sharedKey := cryptutil.NewKey()
options.SharedKey = base64.StdEncoding.EncodeToString(sharedKey)
options.Provider = "oidc"
options.ProviderURL = "https://oidc.example.com"
options.ClientID = "client_id"
options.ClientSecret = "client_secret"
urlFormat := "https://*.foo.bar.test-%d.example.com"
for i := range numPolicies {
options.Policies = append(options.Policies,
config.Policy{
From: fmt.Sprintf(urlFormat, i),
To: mustParseWeightedURLs(b, fmt.Sprintf("https://p2-%d", i)),
IDPClientID: fmt.Sprintf("client_id_%d", i),
IDPClientSecret: fmt.Sprintf("client_secret_%d", i),
},
)
}
require.NoError(b, options.Validate())
b.ResetTimer()
for range b.N {
idp, err := options.GetIdentityProviderForRequestURL(context.Background(), fmt.Sprintf(urlFormat, numPolicies-1))
require.NoError(b, err)
require.Equal(b, fmt.Sprintf("client_id_%d", numPolicies-1), idp.ClientId)
require.Equal(b, fmt.Sprintf("client_secret_%d", numPolicies-1), idp.ClientSecret)
}
}
}
b.Run("5 policies", runBench(5))
b.Run("50 policies", runBench(50))
b.Run("500 policies", runBench(500))
b.Run("5000 policies", runBench(5000))
}
var bench = func(fill func(i int, p *config.Policy) string, numPolicies int) func(b *testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
options := config.NewDefaultOptions()
sharedKey := cryptutil.NewKey()
options.SharedKey = base64.StdEncoding.EncodeToString(sharedKey)
options.Provider = "oidc"
options.ProviderURL = "https://oidc.example.com"
options.ClientID = "client_id"
options.ClientSecret = "client_secret"
var allUrls []string
for i := range numPolicies {
p := config.Policy{
To: mustParseWeightedURLs(b, fmt.Sprintf("https://p2-%d", i)),
IDPClientID: fmt.Sprintf("client_id_%d", i),
IDPClientSecret: fmt.Sprintf("client_secret_%d", i),
}
allUrls = append(allUrls, fill(i, &p))
options.Policies = append(options.Policies, p)
}
require.NoError(b, options.Validate())
cache, err := config.NewPolicyCache(options)
require.NoError(b, err)
b.ResetTimer()
for i := range b.N {
reqUrl := strings.Replace(allUrls[i%numPolicies], "*", fmt.Sprint(i), 1)
idp, err := cache.GetIdentityProviderForRequestURL(context.Background(), options, reqUrl)
require.NoError(b, err)
require.Equal(b, fmt.Sprintf("client_id_%d", i%numPolicies), idp.ClientId)
require.Equal(b, fmt.Sprintf("client_secret_%d", i%numPolicies), idp.ClientSecret)
}
}
}
func BenchmarkGetIdentityProviderForRequestURL_New_DomainMatchOnly(b *testing.B) {
domainMatchingOnly := func(i int, p *config.Policy) string {
p.From = fmt.Sprintf("https://*.foo.bar.test-%d.example.com", i)
return p.From
}
b.Run("5 policies (domain matching only)", bench(domainMatchingOnly, 5))
b.Run("50 policies (domain matching only)", bench(domainMatchingOnly, 50))
b.Run("500 policies (domain matching only)", bench(domainMatchingOnly, 500))
b.Run("5000 policies (domain matching only)", bench(domainMatchingOnly, 5000))
}
func BenchmarkGetIdentityProviderForRequestURL_New_PathMatchOnly(b *testing.B) {
pathMatchingOnly := func(i int, p *config.Policy) string {
p.From = "https://example.com"
p.Path = fmt.Sprintf("/foo/bar/path%d", i)
return p.From + p.Path
}
b.Run("5 policies (path matching only)", bench(pathMatchingOnly, 5))
b.Run("50 policies (path matching only)", bench(pathMatchingOnly, 50))
b.Run("500 policies (path matching only)", bench(pathMatchingOnly, 500))
b.Run("5000 policies (path matching only)", bench(pathMatchingOnly, 5000))
}
func BenchmarkGetIdentityProviderForRequestURL_New_DomainAndPathMatching(b *testing.B) {
combinedMatching := func(numPathsPerDomain int) func(i int, p *config.Policy) string {
// returns a sequence of policies (ex: numPathsPerDomain=3)
// https://*.foo.bar.test-0.example.com
// https://*.foo.bar.test-0.example.com/foo/bar/path1
// https://*.foo.bar.test-0.example.com/foo/bar/path2
// https://*.foo.bar.test-1.example.com
// https://*.foo.bar.test-1.example.com/foo/bar/path1
// https://*.foo.bar.test-1.example.com/foo/bar/path2
return func(i int, p *config.Policy) string {
domain := fmt.Sprintf("https://*.foo.bar.test-%d.example.com", i/numPathsPerDomain)
pathIdx := i % numPathsPerDomain
var path string
if pathIdx == 0 {
path = ""
} else {
path = fmt.Sprintf("/foo/bar/path%d", pathIdx)
}
p.From = domain
p.Path = path
return domain + path
}
}
b.Run("25 policies (5 domains, 5 paths per domain)", bench(combinedMatching(5), 25))
b.Run("500 policies (50 domains, 10 paths per domain)", bench(combinedMatching(10), 500))
b.Run("500 policies (10 domains, 50 paths per domain)", bench(combinedMatching(50), 500))
b.Run("2500 policies (50 domains, 50 paths per domain)", bench(combinedMatching(50), 2500))
b.Run("5000 policies (100 domains, 50 paths per domain)", bench(combinedMatching(50), 5000))
b.Run("5000 policies (50 domains, 100 paths per domain)", bench(combinedMatching(100), 5000))
b.Run("10000 policies (100 domains, 100 paths per domain)", bench(combinedMatching(100), 10000))
}
func BenchmarkGetIdentityProviderForRequestURL_New_DomainAndPrefixMatching(b *testing.B) {
combinedMatching := func(numPathsPerDomain int) func(i int, p *config.Policy) string {
// returns a sequence of policies (ex: numPathsPerDomain=3)
// https://*.foo.bar.test-0.example.com/0
// https://*.foo.bar.test-0.example.com/0/1
// https://*.foo.bar.test-0.example.com/0/1/2
// https://*.foo.bar.test-1.example.com/0
// https://*.foo.bar.test-1.example.com/0/1
// https://*.foo.bar.test-1.example.com/0/1/2
return func(i int, p *config.Policy) string {
domain := fmt.Sprintf("https://*.foo.bar.test-%d.example.com", i/numPathsPerDomain)
pathIdx := i % numPathsPerDomain
var prefix strings.Builder
for i := range pathIdx + 1 {
fmt.Fprintf(&prefix, "/%d", i)
}
p.From = domain
p.Prefix = prefix.String()
return domain + p.Prefix
}
}
b.Run("25 policies (5 domains, 5 paths per domain)", bench(combinedMatching(5), 25))
b.Run("500 policies (50 domains, 10 paths per domain)", bench(combinedMatching(10), 500))
b.Run("500 policies (10 domains, 50 paths per domain)", bench(combinedMatching(50), 500))
b.Run("2500 policies (50 domains, 50 paths per domain)", bench(combinedMatching(50), 2500))
b.Run("5000 policies (100 domains, 50 paths per domain)", bench(combinedMatching(50), 5000))
b.Run("5000 policies (50 domains, 100 paths per domain)", bench(combinedMatching(100), 5000))
b.Run("10000 policies (100 domains, 100 paths per domain)", bench(combinedMatching(100), 10000))
}
func mustParseWeightedURLs(t testing.TB, urls ...string) []config.WeightedURL {
wu, err := config.ParseWeightedUrls(urls...)
require.NoError(t, err)
return wu
}