fetch_ml/tests/benchmarks/kms_benchmark_test.go
Jeremie Fraeys 7cd86fb88a
Some checks failed
Build Pipeline / Build Binaries (push) Failing after 3m39s
Build Pipeline / Build Docker Images (push) Has been skipped
Build Pipeline / Sign HIPAA Config (push) Has been skipped
Build Pipeline / Generate SLSA Provenance (push) Has been skipped
Checkout test / test (push) Successful in 6s
CI Pipeline / Test (ubuntu-latest on self-hosted) (push) Failing after 1s
CI Pipeline / Dev Compose Smoke Test (push) Has been skipped
CI Pipeline / Security Scan (push) Has been skipped
CI Pipeline / Test Scripts (push) Has been skipped
CI Pipeline / Test Native Libraries (push) Has been skipped
CI Pipeline / Native Library Build Matrix (push) Has been skipped
Contract Tests / Spec Drift Detection (push) Failing after 11s
Contract Tests / API Contract Tests (push) Has been skipped
Deploy API Docs / Build API Documentation (push) Failing after 5s
Deploy API Docs / Deploy to GitHub Pages (push) Has been skipped
Documentation / build-and-publish (push) Failing after 40s
Test Matrix / test-native-vs-pure (cgo) (push) Failing after 14s
Test Matrix / test-native-vs-pure (native) (push) Failing after 35s
Test Matrix / test-native-vs-pure (pure) (push) Failing after 18s
CI Pipeline / Trigger Build Workflow (push) Failing after 1s
Build CLI with Embedded SQLite / build (arm64, aarch64-linux) (push) Has been cancelled
Build CLI with Embedded SQLite / build (x86_64, x86_64-linux) (push) Has been cancelled
Build CLI with Embedded SQLite / build-macos (arm64) (push) Has been cancelled
Build CLI with Embedded SQLite / build-macos (x86_64) (push) Has been cancelled
Security Scan / Security Analysis (push) Has been cancelled
Security Scan / Native Library Security (push) Has been cancelled
Verification & Maintenance / V.1 - Schema Drift Detection (push) Has been cancelled
Verification & Maintenance / V.4 - Custom Go Vet Analyzers (push) Has been cancelled
Verification & Maintenance / V.7 - Audit Chain Integrity (push) Has been cancelled
Verification & Maintenance / V.6 - Extended Security Scanning (push) Has been cancelled
Verification & Maintenance / V.10 - OpenSSF Scorecard (push) Has been cancelled
Verification & Maintenance / Verification Summary (push) Has been cancelled
feat: add new API handlers, build scripts, and ADRs
- Introduce audit, plugin, and scheduler API handlers
- Add spec_embed.go for OpenAPI spec embedding
- Create modular build scripts (cli, go, native, cross-platform)
- Add deployment cleanup and health-check utilities
- New ADRs: hot reload, audit store, SSE updates, RBAC, caching, offline mode, KMS regions, tenant offboarding
- Add KMS configuration schema and worker variants
- Include KMS benchmark tests
2026-03-04 13:24:27 -05:00

279 lines
7.1 KiB
Go

package benchmarks
import (
"testing"
"time"
"github.com/jfraeys/fetch_ml/internal/crypto"
"github.com/jfraeys/fetch_ml/internal/crypto/kms"
)
// BenchmarkEncryptArtifact measures the full encryption pipeline performance.
// Per ADR-012: Total overhead should be <10ms for MemoryProvider.
func BenchmarkEncryptArtifact(b *testing.B) {
tkm := crypto.NewTestTenantKeyManager(nil)
// Provision a test tenant
hierarchy, err := tkm.ProvisionTenant("bench-tenant")
if err != nil {
b.Fatalf("Failed to provision tenant: %v", err)
}
// Test data - 1KB payload (typical model weights chunk)
plaintext := make([]byte, 1024)
for i := range plaintext {
plaintext[i] = byte(i % 256)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_, err := tkm.EncryptArtifact("bench-tenant", "artifact-1", hierarchy.KMSKeyID, plaintext)
if err != nil {
b.Fatalf("Encrypt failed: %v", err)
}
}
}
// BenchmarkDecryptArtifact measures the full decryption pipeline performance.
// Per ADR-012: Total overhead should be <10ms for MemoryProvider.
func BenchmarkDecryptArtifact(b *testing.B) {
tkm := crypto.NewTestTenantKeyManager(nil)
hierarchy, err := tkm.ProvisionTenant("bench-tenant")
if err != nil {
b.Fatalf("Failed to provision tenant: %v", err)
}
plaintext := make([]byte, 1024)
for i := range plaintext {
plaintext[i] = byte(i % 256)
}
// Pre-encrypt data
encrypted, err := tkm.EncryptArtifact("bench-tenant", "artifact-1", hierarchy.KMSKeyID, plaintext)
if err != nil {
b.Fatalf("Pre-encryption failed: %v", err)
}
b.ReportAllocs()
for b.Loop() {
_, err := tkm.DecryptArtifact(encrypted, hierarchy.KMSKeyID)
if err != nil {
b.Fatalf("Decrypt failed: %v", err)
}
}
}
// BenchmarkMemoryProvider_Encrypt measures baseline encryption without network overhead.
// This establishes the theoretical minimum for KMS operations.
func BenchmarkMemoryProvider_Encrypt(b *testing.B) {
provider := kms.NewMemoryProvider()
defer provider.Close()
cache := kms.NewDEKCache(kms.DefaultCacheConfig())
defer cache.Clear()
config := kms.Config{
Provider: kms.ProviderTypeMemory,
Cache: kms.DefaultCacheConfig(),
}
tkm := crypto.NewTenantKeyManager(provider, cache, config, nil)
hierarchy, err := tkm.ProvisionTenant("bench-tenant")
if err != nil {
b.Fatalf("Failed to provision tenant: %v", err)
}
plaintext := make([]byte, 1024)
b.ReportAllocs()
for b.Loop() {
_, err := tkm.EncryptArtifact("bench-tenant", "artifact-1", hierarchy.KMSKeyID, plaintext)
if err != nil {
b.Fatalf("Encrypt failed: %v", err)
}
}
}
// BenchmarkCacheHit verifies cached DEKs provide <10ms overhead.
func BenchmarkCacheHit(b *testing.B) {
tkm := crypto.NewTestTenantKeyManager(nil)
hierarchy, err := tkm.ProvisionTenant("bench-tenant")
if err != nil {
b.Fatalf("Failed to provision tenant: %v", err)
}
plaintext := make([]byte, 1024)
// First encrypt to populate cache
encrypted, err := tkm.EncryptArtifact("bench-tenant", "cached-artifact", hierarchy.KMSKeyID, plaintext)
if err != nil {
b.Fatalf("Pre-encryption failed: %v", err)
}
// First decrypt to populate DEK cache
_, err = tkm.DecryptArtifact(encrypted, hierarchy.KMSKeyID)
if err != nil {
b.Fatalf("First decrypt failed: %v", err)
}
b.ReportAllocs()
for b.Loop() {
_, err := tkm.DecryptArtifact(encrypted, hierarchy.KMSKeyID)
if err != nil {
b.Fatalf("Decrypt failed: %v", err)
}
}
}
// BenchmarkKeyRotation measures key rotation overhead.
func BenchmarkKeyRotation(b *testing.B) {
tkm := crypto.NewTestTenantKeyManager(nil)
hierarchy, err := tkm.ProvisionTenant("bench-tenant")
if err != nil {
b.Fatalf("Failed to provision tenant: %v", err)
}
b.ReportAllocs()
for b.Loop() {
// Rotate key
newHierarchy, err := tkm.RotateTenantKey("bench-tenant", hierarchy)
if err != nil {
b.Fatalf("Rotation failed: %v", err)
}
hierarchy = newHierarchy
}
}
// BenchmarkEncryptArtifact_LargePayload measures encryption with larger payloads.
func BenchmarkEncryptArtifact_LargePayload(b *testing.B) {
tkm := crypto.NewTestTenantKeyManager(nil)
hierarchy, err := tkm.ProvisionTenant("bench-tenant")
if err != nil {
b.Fatalf("Failed to provision tenant: %v", err)
}
// 1MB payload
plaintext := make([]byte, 1024*1024)
b.ReportAllocs()
for b.Loop() {
_, err := tkm.EncryptArtifact("bench-tenant", "large-artifact", hierarchy.KMSKeyID, plaintext)
if err != nil {
b.Fatalf("Encrypt failed: %v", err)
}
}
}
// BenchmarkParallelEncrypt measures concurrent encryption performance.
func BenchmarkParallelEncrypt(b *testing.B) {
tkm := crypto.NewTestTenantKeyManager(nil)
hierarchy, err := tkm.ProvisionTenant("bench-tenant")
if err != nil {
b.Fatalf("Failed to provision tenant: %v", err)
}
plaintext := make([]byte, 1024)
b.ResetTimer()
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
artifactID := "parallel-artifact-" + string(rune('0'+i%10))
_, err := tkm.EncryptArtifact("bench-tenant", artifactID, hierarchy.KMSKeyID, plaintext)
if err != nil {
b.Fatalf("Encrypt failed: %v", err)
}
i++
}
})
}
// VerifyPerformanceRequirement runs a quick sanity check for the <10ms requirement.
// This is not a benchmark but a verification that typical operations complete within limits.
func TestEncryptPerformance_10msRequirement(t *testing.T) {
tkm := crypto.NewTestTenantKeyManager(nil)
hierarchy, err := tkm.ProvisionTenant("perf-test-tenant")
if err != nil {
t.Fatalf("Failed to provision tenant: %v", err)
}
plaintext := make([]byte, 1024)
// Warm up
for range 10 {
_, _ = tkm.EncryptArtifact("perf-test-tenant", "warmup", hierarchy.KMSKeyID, plaintext)
}
// Measure 100 operations
start := time.Now()
for i := 0; i < 100; i++ {
_, err := tkm.EncryptArtifact("perf-test-tenant", "perf-test", hierarchy.KMSKeyID, plaintext)
if err != nil {
t.Fatalf("Encrypt failed: %v", err)
}
}
elapsed := time.Since(start)
avgPerOp := elapsed / 100
if avgPerOp > 10*time.Millisecond {
t.Errorf("Average encrypt time %v exceeds 10ms requirement", avgPerOp)
}
t.Logf("Average encrypt time: %v (requirement: <10ms)", avgPerOp)
}
// TestDecryptPerformance_10msRequirement verifies decrypt completes within 10ms.
func TestDecryptPerformance_10msRequirement(t *testing.T) {
tkm := crypto.NewTestTenantKeyManager(nil)
hierarchy, err := tkm.ProvisionTenant("perf-test-tenant")
if err != nil {
t.Fatalf("Failed to provision tenant: %v", err)
}
plaintext := make([]byte, 1024)
// Pre-encrypt
encrypted, err := tkm.EncryptArtifact("perf-test-tenant", "perf-test", hierarchy.KMSKeyID, plaintext)
if err != nil {
t.Fatalf("Pre-encryption failed: %v", err)
}
// Warm up cache
for range 10 {
_, _ = tkm.DecryptArtifact(encrypted, hierarchy.KMSKeyID)
}
// Measure 100 operations with cache
start := time.Now()
for range 10 {
_, err := tkm.DecryptArtifact(encrypted, hierarchy.KMSKeyID)
if err != nil {
t.Fatalf("Decrypt failed: %v", err)
}
}
elapsed := time.Since(start)
avgPerOp := elapsed / 100
if avgPerOp > 10*time.Millisecond {
t.Errorf("Average decrypt time %v exceeds 10ms requirement", avgPerOp)
}
t.Logf("Average decrypt time: %v (requirement: <10ms)", avgPerOp)
}