🚨 Summary
The current PostgreSQL storage implementation has critical performance bottlenecks and design issues
📊 Impact
- Kyverno policy enforcement is delayed (waiting on report storage)
- API server experiences high load due to slow queries
🐛 Critical Issues
Issue #1: Global Mutex Locks Serialize All Operations 🔴
Location: pkg/storage/db/polr.go:19-23, cpolr.go, ephr.go, cephr.go, report.go, clusterreport.go
Problem:
type polrdb struct {
sync.Mutex // ← ONE LOCK FOR ALL POLICY REPORTS
MultiDB *MultiDB
}
func (p *polrdb) Create(ctx context.Context, report *PolicyReport) error {
p.Lock() // ← Blocks ALL other operations
defer p.Unlock()
// ... database work ...
}
Issue #2: Code Duplication (969 Lines × 6 Times) 🔴
Location: All files in pkg/storage/db/
Problem:
polr.go - 181 lines (PolicyReport CRUD)
cpolr.go - 170 lines (ClusterPolicyReport CRUD) - 95% identical
ephr.go - 172 lines (EphemeralReport CRUD) - 95% identical
cephr.go - 172 lines (ClusterEphemeralReport CRUD) - 95% identical
report.go - 137 lines (Report CRUD) - 95% identical
clusterreport.go - 137 lines (ClusterReport CRUD) - 95% identical
Impact:
- Bug fixes must be applied in 6 places
- Already has inconsistent error handling across files
- Adding new features requires changing 6 files
- High maintenance burden
Evidence of Problems:
- Migration watchers have
return instead of continue (bug in all 6 types)
- Error handling differs between implementations
- Some have nil checks, others don't
Issue #3: Postgres should support read replica 🟠
Location: pkg/storage/db/multiDB.go:26-47
Problem:
Every query is diverged to primaryDB, however read queries must be handled via readReplica
Issue #4: Migration Watchers Exit After First Event 🔴
Location: pkg/server/config.go:180-227 (and 3 other watch loops)
Problem:
go func() {
for event := range cpolrWatchInterface.ResultChan() {
switch event.Type {
case watch.Added:
cpolr := event.Object.(*ClusterPolicyReport)
if _, ok := cpolr.Annotations[ServedByAnnotation]; ok {
return // ← BUG: Should be 'continue'!
}
store.Create(ctx, cpolr)
}
}
}()
Impact:
- Watcher exits after processing FIRST report with annotation
- Subsequent reports are NOT synced to database
- Silent failure (no error logged)
- Data inconsistency between etcd and PostgreSQL
Fix: Change return to continue
Issue #5: No Pagination in List Operations 🔴
Location: All List() methods in pkg/storage/db/
Problem:
func (p *polrdb) List(ctx context.Context, namespace string) ([]*PolicyReport, error) {
// Fetches ALL reports in namespace
rows, err := p.MultiDB.ReadQuery(ctx, "SELECT report FROM policyreports WHERE namespace = $1")
for rows.Next() {
// Deserializes ALL reports
json.Unmarshal([]byte(jsonb), &report)
res = append(res, &report)
}
return res // Returns ALL reports
}
Impact:
- With 10,000 reports @ 100KB each = 1GB transferred and deserialized per LIST
- Guaranteed OOM (out of memory) in large clusters
- No way to paginate through results
- API server times out on large responses
Issue #7: Missing Database Indexes 🟠
Current indexes:
CREATE INDEX policyreportnamespace ON policyreports(namespace)
CREATE INDEX policyreportcluster ON policyreports(clusterId)
Missing:
- Composite index on
(clusterId, namespace, name) for Get queries
- No index on resource_version for Watch
- No JSONB GIN indexes for label queries
Impact:
- Get queries may do full table scans
- List queries are slow
- Can't efficiently query by labels
🏗️ Architecture Issues
No Separation of Concerns
- Storage layer knows about Kubernetes (should be generic)
- Manual SQL string building (error-prone)
- No query builder or repository pattern
- Metrics mixed with business logic
No Abstraction
- Can't swap storage backends (PostgreSQL, MySQL, etc.)
- Storage code duplicated for each resource type
- No generic interface
📚 References
🚨 Summary
The current PostgreSQL storage implementation has critical performance bottlenecks and design issues
📊 Impact
🐛 Critical Issues
Issue #1: Global Mutex Locks Serialize All Operations 🔴
Location:
pkg/storage/db/polr.go:19-23,cpolr.go,ephr.go,cephr.go,report.go,clusterreport.goProblem:
Issue #2: Code Duplication (969 Lines × 6 Times) 🔴
Location: All files in
pkg/storage/db/Problem:
Impact:
Evidence of Problems:
returninstead ofcontinue(bug in all 6 types)Issue #3: Postgres should support read replica 🟠
Location:
pkg/storage/db/multiDB.go:26-47Problem:
Every query is diverged to primaryDB, however read queries must be handled via readReplica
Issue #4: Migration Watchers Exit After First Event 🔴
Location:
pkg/server/config.go:180-227(and 3 other watch loops)Problem:
Impact:
Fix: Change
returntocontinueIssue #5: No Pagination in List Operations 🔴
Location: All
List()methods inpkg/storage/db/Problem:
Impact:
Issue #7: Missing Database Indexes 🟠
Current indexes:
Missing:
(clusterId, namespace, name)for Get queriesImpact:
🏗️ Architecture Issues
No Separation of Concerns
No Abstraction
📚 References