Skip to content

Commit

Permalink
feat: faster SQL queries for checks and strict check mode (#1171)
Browse files Browse the repository at this point in the history
With this change we introduce an experimental strict mode that drastically reduces the number of SQL queries performed during checks. This is experimental to allow adjusting its behavior in a breaking manner, but it is ready for production usage.
Also some of the non-strict queries are optimized.

Co-authored-by: Patrik <zepatrik@users.noreply.github.com>
  • Loading branch information
hperl and zepatrik committed Jan 17, 2023
1 parent 69ef50a commit 8e07890
Show file tree
Hide file tree
Showing 26 changed files with 714 additions and 343 deletions.
4 changes: 2 additions & 2 deletions .schema/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
The schemas in this directory are meant for external and public use.

The config schema is generated from the internal one at
`internal/driver/config/config.schema.json`, so in case of changes to the config
schema, please edit that internal schema instead.
`embedx/config.schema.json`, so in case of changes to the config schema, please
edit that internal schema instead.
7 changes: 7 additions & 0 deletions benchtest.new.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
goos: linux
goarch: amd64
pkg: github.com/ory/keto/internal/check
cpu: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
BenchmarkComputedUsersets/Computed_userset-8 14576 81280 ns/op 1.000 queries/op 17675 B/op 364 allocs/op
PASS
ok github.com/ory/keto/internal/check 2.055s
6 changes: 6 additions & 0 deletions embedx/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,14 @@
"file:///etc/configs/keto_namespaces.ts",
"ws://my.websocket.server/keto_namespaces.ts"
]
},
"experimental_strict_mode": {
"type": "boolean",
"title": "Strict permission checking mode",
"description": "EXPERIMENTAL: If strict mode is enabled, then relation tuples for permits are not checked directly (but the rewrites are applied). Similarly, subject sets are only expanded if they were declared with SubjectSet<...>. These stricter rules result in much faster checks with fewer queries to the underlying database. The behavior of strict mode might change while it is experimental."
}
},
"additionalProperties": false,
"required": ["location"]
}
]
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
github.com/ory/herodot v0.9.13
github.com/ory/jsonschema/v3 v3.0.7
github.com/ory/keto/proto v0.10.0-alpha.0
github.com/ory/x v0.0.524
github.com/ory/x v0.0.531
github.com/pelletier/go-toml v1.9.5
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -77,6 +77,7 @@ require (
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-bindata/go-bindata v3.1.2+incompatible // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
Expand All @@ -94,6 +95,7 @@ require (
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/pprof v0.0.0-20221010195024-131d412537ea // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE=
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
Expand Down Expand Up @@ -278,6 +280,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -643,6 +647,8 @@ github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE=
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
github.com/ory/x v0.0.524 h1:U7JQKiaz+JpWWJvYYqdwVCqXcvI3W9uYO+4v7ew98Vk=
github.com/ory/x v0.0.524/go.mod h1:XBqhPZRppPHTxtsE0l0oI/B2Onf1QJtMRGPh3CpEpA0=
github.com/ory/x v0.0.531 h1:ndkhfzgX7y1ChNnYPS5ioqVuvyRENXKUBrNnkmoPOFQ=
github.com/ory/x v0.0.531/go.mod h1:XBqhPZRppPHTxtsE0l0oI/B2Onf1QJtMRGPh3CpEpA0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
Expand Down Expand Up @@ -1205,6 +1211,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
Expand Down
7 changes: 7 additions & 0 deletions internal/check/2023-01-09-benchtest.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
goos: linux
goarch: amd64
pkg: github.com/ory/keto/internal/check
cpu: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
BenchmarkComputedUsersets/Computed_userset-8 2155 583306 ns/op 9.000 queries/op 109370 B/op 1556 allocs/op
PASS
ok github.com/ory/keto/internal/check 1.351s
56 changes: 55 additions & 1 deletion internal/check/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ package check_test

import (
"context"
_ "embed"
"fmt"
"strings"
"testing"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"

"github.com/ory/keto/internal/check"
"github.com/ory/keto/internal/check/checkgroup"
"github.com/ory/keto/internal/driver"
"github.com/ory/keto/internal/driver/config"
"github.com/ory/keto/internal/namespace"
"github.com/ory/keto/internal/namespace/ast"
Expand Down Expand Up @@ -96,7 +101,6 @@ func BenchmarkCheckEngine(b *testing.B) {
require.NoError(b, reg.Config(ctx).Set(config.KeyLimitMaxReadDepth, 100*maxDepth))
e := check.NewEngine(reg)

b.ResetTimer()
b.Run("case=deep tree", func(b *testing.B) {
for _, depth := range depths {
b.Run(fmt.Sprintf("depth=%03d", depth), func(b *testing.B) {
Expand Down Expand Up @@ -127,3 +131,53 @@ func BenchmarkCheckEngine(b *testing.B) {
}
})
}

//go:embed testfixtures/project_opl.ts
var ProjectOPLConfig string

func BenchmarkComputedUsersets(b *testing.B) {
ctx := context.Background()

spans := tracetest.NewSpanRecorder()
tracer := trace.NewTracerProvider(trace.WithSpanProcessor(spans)).Tracer("")
reg := driver.NewSqliteTestRegistry(b, false,
driver.WithLogLevel("debug"),
driver.WithOPL(ProjectOPLConfig),
driver.WithTracer(tracer),
driver.WithConfig(config.KeyNamespacesExperimentalStrictMode, true))
reg.Logger().Logger.SetLevel(logrus.DebugLevel)

insertFixtures(b, reg.RelationTupleManager(), []string{
"Project:Ory#owner@User:Admin",
"Project:Ory#developer@User:Dev",
})

e := check.NewEngine(reg)

query := tupleFromString(b, "Project:Ory#readProject@User:Dev")

b.Run("Computed userset", func(b *testing.B) {
initialDBSpans := dbSpans(spans)
for i := 0; i < b.N; i++ {
res := e.CheckRelationTuple(ctx, query, 0)
assert.NoError(b, res.Err)
if res.Err != nil {
b.Errorf("got unexpected error: %v", res.Err)
}
if res.Membership != checkgroup.IsMember {
b.Error("check failed")
}
}
b.ReportMetric((float64(dbSpans(spans)-initialDBSpans))/float64(b.N), "queries/op")
})

}

func dbSpans(spans *tracetest.SpanRecorder) (count int) {
for _, s := range spans.Started() {
if strings.HasPrefix(s.Name(), "persistence.sql") {
count++
}
}
return
}
Loading

0 comments on commit 8e07890

Please sign in to comment.