Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kas): collect metrics #1702

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1f730c0
feat(kas) - collect metrics
sujankota Nov 1, 2024
11123cf
Merge branch 'main' into feat/collect-metric
sujankota Nov 6, 2024
3d74003
missed some files to merge
sujankota Nov 6, 2024
cb445e3
missed some files to merge
sujankota Nov 6, 2024
546da3a
fix lint errors
sujankota Nov 6, 2024
106fd9d
minor fix
sujankota Nov 6, 2024
767ce61
Merge branch 'main' into feat/collect-metric
sujankota Nov 8, 2024
f0f72c0
more cleanup
sujankota Nov 8, 2024
8aae5d9
fix lint errors
sujankota Nov 11, 2024
baeb532
minor cleanup
sujankota Nov 11, 2024
6e584bb
enable benchmark ci
sujankota Nov 12, 2024
b460634
debug benchmark ci
sujankota Nov 12, 2024
bd1e0be
debug benchmark ci
sujankota Nov 12, 2024
1061c3a
debug benchmark ci
sujankota Nov 12, 2024
afbfc3b
debug benchmark ci
sujankota Nov 12, 2024
85d08d9
Merge branch 'main' into feat/collect-metric
sujankota Nov 12, 2024
9c9ae6e
debug benchmark ci
sujankota Nov 12, 2024
128c0b4
debug benchmark ci
sujankota Nov 12, 2024
9da3fc0
minor cleanup
sujankota Nov 12, 2024
7838552
fix the nanotdf sdk performance
sujankota Nov 13, 2024
9e77426
Merge branch 'main' into feat/collect-metric
sujankota Nov 13, 2024
04dc857
fix the build
sujankota Nov 13, 2024
7238368
fix the build
sujankota Nov 13, 2024
0de2afe
Merge branch 'main' into feat/collect-metric
sujankota Nov 14, 2024
04b4a61
Merge branch 'main' into feat/collect-metric
sujankota Nov 15, 2024
f9726e3
Merge branch 'main' into feat/collect-metric
sujankota Nov 15, 2024
3f2144c
code cleanup
sujankota Nov 15, 2024
cdc0a93
fix the build
sujankota Nov 15, 2024
50ffdf2
go.sum changes
sujankota Nov 15, 2024
1021e7a
Merge branch 'main' into feat/collect-metric
sujankota Nov 15, 2024
4afe3df
Merge branch 'main' into feat/collect-metric
sujankota Nov 19, 2024
3a1cda5
Merge branch 'main' into feat/collect-metric
sujankota Nov 22, 2024
2b48524
Add attributes
sujankota Nov 22, 2024
fb681ce
Add attributes
sujankota Nov 22, 2024
ae88f78
Add attributes
sujankota Nov 22, 2024
5f1093a
Merge branch 'main' into feat/collect-metric
dmihalcik-virtru Nov 25, 2024
ff13dc1
lint, testify cleanup
dmihalcik-virtru Nov 25, 2024
02a0399
cleanups, hopefully fixes ci
dmihalcik-virtru Nov 25, 2024
77b1f73
Update examples.go
dmihalcik-virtru Nov 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,55 @@ jobs:
- name: validate custom entity rego policy
run: test/rego/custom-entity.bats

benchmark:
name: benchmark tests
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
with:
go-version-file: 'service/go.mod'
check-latest: false
cache-dependency-path: |
service/go.sum
examples/go.sum
protocol/go/go.sum
sdk/go.sum
- if: env.IS_RELEASE_BRANCH == 'true'
run: ./.github/scripts/work-init.sh
- run: go mod download
- run: go mod verify
- run: |
.github/scripts/init-temp-keys.sh
cp opentdf-dev.yaml opentdf.yaml
- name: Added Trusted Certs
run: |
sudo chmod -R 777 ./keys
sudo apt-get install -y ca-certificates
sudo cp ./keys/localhost.crt /usr/local/share/ca-certificates
sudo update-ca-certificates
- run: docker compose up -d --wait --wait-timeout 240 || (docker compose logs && exit 1)
- run: go run ./service provision keycloak
- run: go run ./service provision fixtures
- uses: JarvusInnovations/background-action@2428e7b970a846423095c79d43f759abf979a635
name: start server in background
with:
run: >
go build -o opentdf -v service/main.go
&& .github/scripts/watch.sh opentdf.yaml ./opentdf start
wait-on: |
tcp:localhost:8080
log-output-if: true
wait-for: 90s
- name: build examples
run: cd examples && go build -o examples .
- name: run tdf3 benchmark tests
run: ./examples/examples benchmark --count 5000 --concurrent 50
- name: run nanotdf benchmark tests
run: ./examples/examples benchmark --tdf nanotdf --count 5000 --concurrent 50
- name: collect the metrics from the benchmark tests
run: ./examples/examples metrics

image:
name: image build
runs-on: ubuntu-22.04
Expand Down Expand Up @@ -232,6 +281,7 @@ jobs:
- go
- image
- integration
- benchmark
- license
- platform-xtest
- otdfctl-test
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ sensitive.txt.tdf
keys/
/examples/sensitive.txt.ntdf
sensitive.txt.ntdf
traces/
226 changes: 226 additions & 0 deletions examples/cmd/benchmark.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package cmd

import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"sync"
"time"

"github.com/opentdf/platform/sdk"
"github.com/spf13/cobra"
)

type TDFFormat string

const (
TDF3 TDFFormat = "tdf3"
NanoTDF TDFFormat = "nanotdf"
)

func (f *TDFFormat) String() string {
return string(*f)
}

func (f *TDFFormat) Set(value string) error {
switch value {
case "tdf3", "nanotdf":
*f = TDFFormat(value)
return nil
default:
return errors.New("invalid TDF format")
}
}

func (f *TDFFormat) Type() string {
return "TDFFormat"
}

type BenchmarkConfig struct {
TDFFormat TDFFormat
ConcurrentRequests int
RequestCount int
RequestsPerSecond int
TimeoutSeconds int
}

var config BenchmarkConfig

func init() {
benchmarkCmd := &cobra.Command{
Use: "benchmark",
Short: "OpenTDF benchmark tool",
Long: `A OpenTDF benchmark tool to measure throughput and latency with configurable concurrency.`,
RunE: runBenchmark,
}

benchmarkCmd.Flags().IntVar(&config.ConcurrentRequests, "concurrent", 10, "Number of concurrent requests")
benchmarkCmd.Flags().IntVar(&config.RequestCount, "count", 100, "Total number of requests")
benchmarkCmd.Flags().IntVar(&config.RequestsPerSecond, "rps", 50, "Requests per second limit")
benchmarkCmd.Flags().IntVar(&config.TimeoutSeconds, "timeout", 30, "Timeout in seconds")
benchmarkCmd.Flags().Var(&config.TDFFormat, "tdf", "TDF format (tdf3 or nanotdf)")
ExamplesCmd.AddCommand(benchmarkCmd)
}

func runBenchmark(cmd *cobra.Command, args []string) error {
in := strings.NewReader("Hello, World!")

// Create new offline client
client, err := newSDK()
if err != nil {
return err
}

out := os.Stdout
if outputName != "-" {
out, err = os.Create("sensitive.txt.tdf")
if err != nil {
return err
}
}
defer func() {
if outputName != "-" {
out.Close()
}
}()

var dataAttributes []string
if config.TDFFormat == NanoTDF {
nanoTDFConfig, err := client.NewNanoTDFConfig()
if err != nil {
return err
}
nanoTDFConfig.SetAttributes(dataAttributes)
nanoTDFConfig.EnableECDSAPolicyBinding()
err = nanoTDFConfig.SetKasURL(fmt.Sprintf("http://%s/kas", "localhost:8080"))
if err != nil {
return err
}

_, err = client.CreateNanoTDF(out, in, *nanoTDFConfig)
if err != nil {
return err
}

if outputName != "-" {
err = cat(cmd, outputName)
if err != nil {
return err
}
}
} else {
opts := []sdk.TDFOption{sdk.WithDataAttributes(dataAttributes...)}
opts = append(opts, sdk.WithAutoconfigure(autoconfigure))
opts = append(opts, sdk.WithKasInformation(
sdk.KASInfo{
URL: fmt.Sprintf("http://%s", "localhost:8080"),
PublicKey: "",
}))
tdf, err := client.CreateTDF(out, in, opts...)
if err != nil {
return err
}

manifestJSON, err := json.MarshalIndent(tdf.Manifest(), "", " ")
if err != nil {
return err
}
cmd.Println(string(manifestJSON))
}

var wg sync.WaitGroup
requests := make(chan struct{}, config.ConcurrentRequests)
results := make(chan time.Duration, config.RequestCount)
errors := make(chan error, config.RequestCount)

// Function to perform the operation
operation := func() {
defer wg.Done()
start := time.Now()

file, err := os.Open("sensitive.txt.tdf")
if err != nil {
errors <- fmt.Errorf("file open error: %v", err)
return
}
defer file.Close()

if config.TDFFormat == NanoTDF {
_, err = client.ReadNanoTDF(io.Discard, file)
if err != nil {
errors <- fmt.Errorf("ReadNanoTDF error: %v", err)
return
}
} else {
tdfreader, err := client.LoadTDF(file)
if err != nil {
errors <- fmt.Errorf("LoadTDF error: %v", err)
return
}

_, err = io.Copy(io.Discard, tdfreader)
if err != nil && err != io.EOF {
errors <- fmt.Errorf("read error: %v", err)
return
}
}

results <- time.Since(start)
}

// Start the benchmark
startTime := time.Now()
for i := 0; i < config.RequestCount; i++ {
wg.Add(1)
requests <- struct{}{}
go func() {
defer func() { <-requests }()
operation()
}()
}

wg.Wait()
close(results)
close(errors)

// Count errors and collect error messages
errorCount := 0
errorMsgs := make(map[string]int)
for err := range errors {
errorCount++
errorMsgs[err.Error()]++
}

successCount := 0
var totalDuration time.Duration
for result := range results {
successCount++
totalDuration += result
}

totalTime := time.Since(startTime)
averageLatency := totalDuration / time.Duration(successCount)
throughput := float64(successCount) / totalTime.Seconds()

// Print results
cmd.Printf("\nBenchmark Results:\n")
cmd.Printf("Total Requests: %d\n", config.RequestCount)
cmd.Printf("Successful Requests: %d\n", successCount)
cmd.Printf("Failed Requests: %d\n", errorCount)
cmd.Printf("Concurrent Requests: %d\n", config.ConcurrentRequests)
cmd.Printf("Total Time: %s\n", totalTime)
cmd.Printf("Average Latency: %s\n", averageLatency)
cmd.Printf("Throughput: %.2f requests/second\n", throughput)

if errorCount > 0 {
cmd.Printf("\nError Summary:\n")
for errMsg, count := range errorMsgs {
cmd.Printf("%s: %d occurrences\n", errMsg, count)
}
}

return nil
}
3 changes: 2 additions & 1 deletion examples/cmd/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"bytes"
"errors"
"fmt"
"github.com/spf13/cobra"
"io"
"os"
"path/filepath"

"github.com/spf13/cobra"
)

func init() {
Expand Down
2 changes: 1 addition & 1 deletion examples/cmd/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func init() {

func newSDK() (*sdk.SDK, error) {
resolver.SetDefaultScheme("passthrough")
opts := []sdk.Option{sdk.WithStoreCollectionHeaders()}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this causing failures in integration test?

opts := []sdk.Option{sdk.WithStoreCollectionHeaders(), sdk.WithInsecurePlaintextConn()}
Copy link
Contributor

@imdominicreed imdominicreed Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you probably want to drop sdk.WithStoreCollectionHeaders().

There is no way to determine if a nanoTDF is part of a collection or not, with this option on, it will always assume its part of a collection.

Since you are decrypting the same nanoTDF, it will store the key with the header and future decrypts will skip the rewrap call.

It's probably best that you remove this option for benchmarking.

if clientCredentials != "" {
i := strings.Index(clientCredentials, ":")
if i < 0 {
Expand Down
Loading
Loading