mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-13 03:42:35 -06:00
739777cfff
This pretty substantially improves performance by avoiding to do a JSON round trip and then converting into AST types. There are a couple of fields left that require the round trip, we can iterate on this as needed to supply better options for the results and metrics. This custom ASTer leaves out the rule counts, which also helps to improve the performance. On my local machine the numbers look like: ``` name old time/op new time/op delta MaskingNop-8 129µs ± 1% 110µs ± 1% -15.19% (p=0.000 n=7+10) MaskingRuleCountsNop/1Rules-8 138µs ± 1% 111µs ± 1% -19.34% (p=0.000 n=10+10) MaskingRuleCountsNop/10Rules-8 180µs ± 1% 116µs ± 4% -35.70% (p=0.000 n=10+10) MaskingRuleCountsNop/100Rules-8 614µs ± 1% 113µs ± 1% -81.66% (p=0.000 n=10+8) MaskingRuleCountsNop/1000Rules-8 5.16ms ± 2% 0.11ms ± 1% -97.79% (p=0.000 n=10+10) MaskingErase-8 148µs ± 2% 129µs ± 0% -12.65% (p=0.000 n=10+10) name old alloc/op new alloc/op delta MaskingNop-8 56.7kB ± 0% 48.0kB ± 0% -15.43% (p=0.000 n=10+10) MaskingRuleCountsNop/1Rules-8 59.5kB ± 0% 48.0kB ± 0% -19.41% (p=0.000 n=10+10) MaskingRuleCountsNop/10Rules-8 84.9kB ± 0% 48.0kB ± 0% -43.47% (p=0.000 n=10+10) MaskingRuleCountsNop/100Rules-8 362kB ± 0% 48kB ± 0% -86.73% (p=0.000 n=9+10) MaskingRuleCountsNop/1000Rules-8 3.26MB ± 1% 0.05MB ± 0% -98.52% (p=0.000 n=10+10) MaskingErase-8 65.7kB ± 0% 57.0kB ± 0% -13.33% (p=0.000 n=10+10) name old allocs/op new allocs/op delta MaskingNop-8 1.23k ± 0% 1.12k ± 0% -8.46% (p=0.000 n=10+10) MaskingRuleCountsNop/1Rules-8 1.31k ± 0% 1.12k ± 0% -14.32% (p=0.000 n=10+10) MaskingRuleCountsNop/10Rules-8 1.98k ± 0% 1.12k ± 0% -43.30% (p=0.000 n=10+10) MaskingRuleCountsNop/100Rules-8 8.74k ± 0% 1.12k ± 0% -87.13% (p=0.000 n=10+10) MaskingRuleCountsNop/1000Rules-8 76.3k ± 0% 1.1k ± 0% -98.52% (p=0.000 n=10+10) MaskingErase-8 1.33k ± 0% 1.22k ± 0% -7.84% (p=0.000 n=10+10) ``` So the performance no longer scales with the number of rules hit, and overall the base cost is almost 20% faster. This time directly impacts round trip latency for OPA REST clients performing evaluations. Signed-off-by: Patrick East <east.patrick@gmail.com>
269 lines
5.6 KiB
Go
269 lines
5.6 KiB
Go
package logs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/open-policy-agent/opa/plugins"
|
|
"github.com/open-policy-agent/opa/storage"
|
|
"github.com/open-policy-agent/opa/storage/inmem"
|
|
"github.com/open-policy-agent/opa/util"
|
|
)
|
|
|
|
const largeEvent = `{
|
|
"_id": "15596749567705615560",
|
|
"decision_id": "0e67fda0-170b-454d-9f5e-29691073f97e",
|
|
"input": {
|
|
"apiVersion": "admission.k8s.io/v1beta1",
|
|
"kind": "AdmissionReview",
|
|
"request": {
|
|
"kind": {
|
|
"group": "",
|
|
"kind": "Pod",
|
|
"version": "v1"
|
|
},
|
|
"namespace": "demo",
|
|
"object": {
|
|
"metadata": {
|
|
"creationTimestamp": "2019-06-04T19:02:35Z",
|
|
"labels": {
|
|
"run": "nginx"
|
|
},
|
|
"name": "nginx",
|
|
"namespace": "demo",
|
|
"uid": "507e4c3c-86fb-11e9-b289-42010a8000b2"
|
|
},
|
|
"spec": {
|
|
"containers": [
|
|
{
|
|
"image": "nginx",
|
|
"imagePullPolicy": "Always",
|
|
"name": "nginx",
|
|
"resources": {},
|
|
"terminationMessagePath": "/dev/termination-log",
|
|
"terminationMessagePolicy": "File",
|
|
"volumeMounts": [
|
|
{
|
|
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
|
|
"name": "default-token-5vjbc",
|
|
"readOnly": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"dnsPolicy": "ClusterFirst",
|
|
"priority": 0,
|
|
"restartPolicy": "Never",
|
|
"schedulerName": "default-scheduler",
|
|
"securityContext": {},
|
|
"serviceAccount": "default",
|
|
"serviceAccountName": "default",
|
|
"terminationGracePeriodSeconds": 30,
|
|
"tolerations": [
|
|
{
|
|
"effect": "NoExecute",
|
|
"key": "node.kubernetes.io/not-ready",
|
|
"operator": "Exists",
|
|
"tolerationSeconds": 300
|
|
},
|
|
{
|
|
"effect": "NoExecute",
|
|
"key": "node.kubernetes.io/unreachable",
|
|
"operator": "Exists",
|
|
"tolerationSeconds": 300
|
|
}
|
|
],
|
|
"volumes": [
|
|
{
|
|
"name": "default-token-5vjbc",
|
|
"secret": {
|
|
"secretName": "default-token-5vjbc"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"status": {
|
|
"phase": "Pending",
|
|
"qosClass": "BestEffort"
|
|
}
|
|
},
|
|
"oldObject": null,
|
|
"operation": "CREATE",
|
|
"resource": {
|
|
"group": "",
|
|
"resource": "pods",
|
|
"version": "v1"
|
|
},
|
|
"userInfo": {
|
|
"groups": [
|
|
"system:serviceaccounts",
|
|
"system:serviceaccounts:opa-system",
|
|
"system:authenticated"
|
|
],
|
|
"username": "system:serviceaccount:opa-system:default"
|
|
}
|
|
}
|
|
},
|
|
"labels": {
|
|
"id": "462a43bd-6a5f-4530-9386-30b0f4e0c8af",
|
|
"policy-type": "kubernetes/admission_control",
|
|
"system-type": "kubernetes",
|
|
"version": "0.10.5"
|
|
},
|
|
"metrics": {
|
|
"timer_rego_module_compile_ns": 222,
|
|
"timer_rego_module_parse_ns": 313,
|
|
"timer_rego_query_compile_ns": 121360,
|
|
"timer_rego_query_eval_ns": 923279,
|
|
"timer_rego_query_parse_ns": 287152,
|
|
"timer_server_handler_ns": 2563846
|
|
},
|
|
"path": "admission_control/main",
|
|
"requested_by": "10.52.0.1:53848",
|
|
"result": {
|
|
"apiVersion": "admission.k8s.io/v1beta1",
|
|
"kind": "AdmissionReview",
|
|
"response": {
|
|
"allowed": false,
|
|
"status": {
|
|
"reason": "Resource Pod/demo/nginx includes container image 'nginx' from prohibited registry"
|
|
}
|
|
}
|
|
},
|
|
"revision": "jafsdkjfhaslkdfjlaksdjflaksjdflkajsdlkfjasldkfjlaksdjflkasdjflkasjdflkajsdflkjasdklfjalsdjf",
|
|
"timestamp": "2019-06-04T19:02:35.692Z"
|
|
}`
|
|
|
|
func BenchmarkMaskingNop(b *testing.B) {
|
|
|
|
ctx := context.Background()
|
|
store := inmem.New()
|
|
|
|
manager, err := plugins.New(nil, "test", store)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
} else if err := manager.Start(ctx); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
cfg := &Config{Service: "svc"}
|
|
cfg.validateAndInjectDefaults([]string{"svc"}, nil)
|
|
plugin := New(cfg, manager)
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
b.StopTimer()
|
|
|
|
var event EventV1
|
|
|
|
if err := util.UnmarshalJSON([]byte(largeEvent), &event); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.StartTimer()
|
|
|
|
if err := plugin.maskEvent(ctx, nil, &event); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func BenchmarkMaskingRuleCountsNop(b *testing.B) {
|
|
numRules := []int{1, 10, 100, 1000}
|
|
|
|
ctx := context.Background()
|
|
store := inmem.New()
|
|
|
|
manager, err := plugins.New(nil, "test", store)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
} else if err := manager.Start(ctx); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
cfg := &Config{Service: "svc"}
|
|
cfg.validateAndInjectDefaults([]string{"svc"}, nil)
|
|
plugin := New(cfg, manager)
|
|
|
|
for _, ruleCount := range numRules {
|
|
|
|
b.Run(fmt.Sprintf("%dRules", ruleCount), func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
var event EventV1
|
|
if err := util.UnmarshalJSON([]byte(largeEvent), &event); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.StartTimer()
|
|
|
|
if err := plugin.maskEvent(ctx, nil, &event); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkMaskingErase(b *testing.B) {
|
|
|
|
ctx := context.Background()
|
|
store := inmem.New()
|
|
|
|
err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
|
|
if err := store.UpsertPolicy(ctx, txn, "test.rego", []byte(`
|
|
package system.log
|
|
|
|
mask["/input"] {
|
|
input.input.request.kind.kind == "Pod"
|
|
}
|
|
`)); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
manager, err := plugins.New(nil, "test", store)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
} else if err := manager.Start(ctx); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
cfg := &Config{Service: "svc"}
|
|
cfg.validateAndInjectDefaults([]string{"svc"}, nil)
|
|
plugin := New(cfg, manager)
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
b.StopTimer()
|
|
|
|
var event EventV1
|
|
|
|
if err := util.UnmarshalJSON([]byte(largeEvent), &event); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.StartTimer()
|
|
|
|
if err := plugin.maskEvent(ctx, nil, &event); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
if event.Input != nil {
|
|
b.Fatal("Expected input to be erased")
|
|
}
|
|
}
|
|
|
|
}
|