Files
releases/plugins/logs/plugin_benchmark_test.go
T
Patrick East 739777cfff plugins/logs: Add custom EventV1 -> AST helper
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>
2020-07-15 12:01:12 -07:00

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")
}
}
}