chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:33 +08:00
commit e071084ebe
982 changed files with 160368 additions and 0 deletions
+160
View File
@@ -0,0 +1,160 @@
# Etcd Registry Performance Improvements
This document describes the improvements made to address etcd authentication performance issues and cache penetration problems.
## Problem Statement
### Background
When etcd server authentication is enabled, a serious performance bottleneck can occur at scale. This was observed in production environments with 4000+ service pods.
### Issues Identified
#### 1. High Authentication QPS
- **Root Cause**: The etcd registry used `KeepAliveOnce` for lease renewal, which requires a new authentication request for each call
- **Impact**: With 4000+ pods registering every 30s (default RegisterInterval), this creates ~110 QPS of authentication requests
- **Limitation**: A typical 3-node etcd cluster (64C 256G HDD) can only handle ~100 QPS for authentication
- **Result**: Authentication requests overwhelm etcd, causing KeepAlive failures and service deregistrations
#### 2. Cache Penetration
- **Trigger**: When KeepAlive fails, services deregister from etcd
- **Chain Reaction**:
1. Registry watcher detects deletions
2. Cache is cleared based on delete events
3. All subsequent service lookups hit etcd directly (cache miss)
4. Etcd is already overloaded, causing more failures
- **Result**: Cascading failure where all gRPC requests fail
## Solution
### 1. Use Long-Lived KeepAlive Channels
**Change**: Replaced `KeepAliveOnce` with `KeepAlive`
**Implementation**:
- Added keepalive channel management to `etcdRegistry` struct
- Created `startKeepAlive()` method that establishes a long-lived keepalive stream
- Modified `registerNode()` to reuse existing keepalive channels
- Added `stopKeepAlive()` for proper cleanup on deregistration
**Benefits**:
- **97% reduction in auth requests**: From ~110 QPS to ~3-4 QPS (4000 pods / TTL period)
- **Single authentication per lease**: KeepAlive authenticates once when establishing the stream
- **Automatic renewal**: Etcd sends keepalive responses automatically through the channel
**Code Changes**:
```go
// Before: New auth request every heartbeat
if _, err := e.client.KeepAliveOnce(context.TODO(), leaseID); err != nil {
// handle error
}
// After: Single auth request, reused channel
if err := e.startKeepAlive(s.Name+node.Id, leaseID); err != nil {
// handle error
}
```
### 2. Verify Cache Penetration Protection
**Existing Protection**: The registry cache already uses `singleflight` pattern to prevent stampede
**How it Works**:
- When cache expires/is empty, first request triggers etcd query
- Concurrent requests for same service wait for the first request to complete
- All waiting requests receive the same result
- Only ONE etcd query happens regardless of concurrent request count
**Additional Safety**:
- Stale cache is returned when etcd fails (if cache data exists)
- Prevents cascading failures by avoiding repeated failed requests to etcd
**Verification**:
Added comprehensive tests to confirm this behavior works correctly under load.
## Performance Impact
### Authentication Load Reduction
- **Before**: 4000 pods × (1 auth / 30s) = ~133 auth/sec
- **After**: 4000 pods × (1 auth / lease_ttl) ≈ 3-4 auth/sec (assuming 15min lease TTL)
- **Reduction**: ~97%
### Cache Penetration Prevention
- **Before**: When cache clears, 1000s of concurrent requests → 1000s of etcd queries
- **After**: When cache clears, 1000s of concurrent requests → 1 etcd query (singleflight)
- **Reduction**: ~99.9%
## Testing
### Unit Tests
1. **TestKeepAliveManagement**: Validates keepalive lifecycle
- Verifies channels are created on registration
- Confirms channels are cleaned up on deregistration
2. **TestKeepAliveReducesAuthRequests**: Confirms channel reuse
- Multiple re-registrations use the same keepalive channel
- Validates auth request reduction
3. **TestKeepAliveChannelReconnection**: Tests error handling
- Verifies proper cleanup when keepalive channel closes
4. **TestSingleflightPreventsStampede**: Validates cache behavior
- 10 concurrent requests → 1 etcd query
5. **TestStaleCacheOnError**: Confirms graceful degradation
- Returns stale cache when etcd fails
6. **TestCachePenetrationPrevention**: End-to-end validation
- 50 concurrent requests during etcd failure → 1 etcd query
- All requests receive stale cache
### Integration Tests
- CI workflow runs tests against real etcd instance
- Validates behavior with actual etcd keepalive channels
- Tests run with race detector enabled
## Migration Guide
### For Library Users
No code changes required! The improvements are transparent:
- Existing applications automatically benefit from reduced auth load
- No API changes to `registry.Registry` interface
### For Plugin Developers
If you maintain a custom registry plugin:
- Consider implementing long-lived keepalive channels
- Ensure your cache implementation uses singleflight pattern
- Add tests for concurrent access patterns
## Monitoring Recommendations
### Key Metrics to Track
1. **Etcd Authentication Rate**: Should drop by ~97%
2. **Etcd Query Rate**: Monitor for stampede prevention
3. **Service Registration Success Rate**: Should improve under load
4. **Cache Hit Rate**: Should remain high even during etcd issues
### Expected Behavior
- **Normal Operation**: Low auth QPS, high cache hit rate
- **During Etcd Issues**: Stale cache served, limited etcd queries
- **After Recovery**: Cache refreshes gradually, no stampede
## Related Issues
- Original Issue: [BUG] etcd authentication performance issue and registry cache penetration
- Etcd Documentation: https://etcd.io/docs/latest/learning/api/#lease-keepalive
- Singleflight Pattern: https://pkg.go.dev/golang.org/x/sync/singleflight
## Security Considerations
- **No Authentication Bypass**: Changes only reduce frequency, not security
- **Proper Cleanup**: Keepalive channels properly closed on deregistration
- **Race Condition Free**: All map operations properly synchronized
- **No Resource Leaks**: Goroutines terminate when channels close
## Future Enhancements
Potential improvements for consideration:
1. **Adaptive TTL**: Adjust keepalive frequency based on load
2. **Circuit Breaker**: Temporarily stop queries when etcd is degraded
3. **Metrics**: Expose keepalive channel count, auth rate, etc.
4. **Backoff**: Exponential backoff on keepalive failures