This document describes the monitor lifecycle management and capacity management systems that govern how monitors are promoted, demoted, and maintained across the NTP Pool monitoring infrastructure.
- Registration → Monitor created with
pendingstatus - Activation → Manual approval changes status to
testingoractive - Server Assignment → API creates
server_scoresentries ascandidate - Promotion Pipeline →
candidate→testing→activeper server - Maintenance → Ongoing health and constraint evaluation
- Deactivation → Status changes to
pausedordeleted
- Assignment → Monitor assigned to server as
candidate - Testing Phase → Promoted to
testingto collect performance data - Active Phase → Promoted to
activebased on performance - Demotion → Demoted due to constraints, health, or capacity limits
- Removal → Removed from server assignment
Available Pool → Candidate → Testing → Active
↑ ↓ ↓ ↓
└──────────────┴─────────┴───────┘
(Demotion paths)
- Available → Candidate: Assignment policy (handled by API)
- Candidate → Testing: Health check, constraint validation, capacity available
- Testing → Active: Performance metrics meet thresholds, globally active status
- Constraint violations: Network, account, diversity violations
- Health issues: Poor performance metrics, connectivity problems
- Global status changes: Monitor becomes pending, paused, or deleted
- Capacity management: Excess monitors beyond targets
Centralized promotion logic ensures consistent behavior across all rules:
type promotionResult struct {
promoted bool
reason string
}
func attemptPromotion(monitor *Monitor, targetStatus Status,
workingCounts *workingCounts, emergency bool) promotionResult {
// Unified promotion logic with:
// - Emergency override handling
// - Constraint checking
// - Capacity validation
// - Working count updates
}- Default: 7 active monitors per server
- Minimum: Never reduce below 1 (safety mechanism)
- Emergency override: Zero active triggers emergency mode
- Base target: 5 testing monitors per server
- Dynamic adjustment: Increases when active monitors below target
- Formula:
targetTesting = 5 + max(0, targetActive - actualActive)
Example: Server with 3 active monitors (target 7)
- Gap: 7 - 3 = 4 missing active monitors
- Testing target: 5 + 4 = 9 testing monitors
- Allows larger pipeline when rebuilding active pool
- Minimum: 4 globally active monitors in testing pool per server
- Purpose: Ensures promotion pipeline for server-active status
- Bootstrap: Promotes globally active candidates when requirement not met
Demotes excess healthy active monitors when count exceeds target:
Safety Checks:
- Never reduce to 0 active monitors
- Respects emergency override conditions
- Reserves demotion budget for constraint violations
Implementation:
if workingActiveCount > targetActiveMonitors &&
workingActiveCount > 1 &&
!emergencyOverride &&
limits.activeRemovals > demotionsSoFar {
// Calculate available budget after reserving for constraints
constraintDemotionsNeeded := countConstraintViolations()
reservedDemotions := min(constraintDemotionsNeeded, remainingBudget)
availableDemotions := remainingBudget - reservedDemotions
// Demote worst performers
demotionsNeeded := min(excessActive, availableDemotions)
}Manages testing pool size based on dynamic targets:
Dynamic Calculation:
baseTestingTarget := 5
activeGap := max(0, targetActiveMonitors - len(activeMonitors))
dynamicTestingTarget := baseTestingTarget + activeGap
if len(testingMonitors) > dynamicTestingTarget {
// Demote excess testing monitors to candidate
}Promotes candidates to testing using two complementary approaches:
Phase 1: Capacity-Based Promotion (existing logic):
testingCapacity := max(0, dynamicTestingTarget - workingTestingCount)
promotionsNeeded := min(min(changesRemaining, 2), testingCapacity)Phase 2: Performance-Based Replacement (new logic): When testing pool is at capacity, compares candidate performance with existing testing monitors and replaces worse performers:
// Only attempt replacement if we have budget remaining
remainingBudget := promotionLimit - capacityPromotions
if remainingBudget > 0 && len(candidates) > 0 && len(testingMonitors) > 0 {
// Find better-performing candidates that can replace worse testing monitors
// Respects all constraints and account limits
replacementChanges := attemptTestingReplacements(...)
}Performance Comparison Logic:
- Health status takes priority (healthy always beats unhealthy)
- Among monitors with equal health, RTT determines performance (lower is better)
- Only replaces when candidate significantly outperforms testing monitor
Constraint Compliance:
- Tests replacement scenarios with temporary account limits
- Validates both demotion and promotion against all constraints
- Maintains testing pool size (1 out, 1 in)
Independent limits for each transition type prevent competition:
type changeLimits struct {
activeRemovals int // active → testing demotions
testingRemovals int // testing → candidate demotions
promotions int // testing → active, candidate → testing
}func calculateChangeLimits(currentActiveMonitors, blockedMonitors int) changeLimits {
base := 2 // Increased from 1 for better throughput
if blockedMonitors > 1 {
base = 3 // Expedite cleanup when many blocked
}
if currentActiveMonitors == 0 {
base = 4 // Bootstrap mode
}
return changeLimits{
activeRemovals: base,
testingRemovals: base,
promotions: base,
}
}- Independent processing per status group
- Higher throughput (base 2 vs previous 1)
- Dynamic testing pool sizing works correctly
- Maintained safety with per-group limits
The system maintains accurate counts throughout the selection process:
type workingCounts struct {
active int
testing int
}
// Initialize with current counts
working := workingCounts{
active: len(activeMonitors),
testing: len(testingMonitors),
}
// Update after each status change decision
func (wc *workingCounts) applyChange(change statusChange) {
// Decrement from source status
switch change.fromStatus {
case ServerScoresStatusActive:
wc.active--
case ServerScoresStatusTesting:
wc.testing--
}
// Increment to target status
switch change.toStatus {
case ServerScoresStatusActive:
wc.active++
case ServerScoresStatusTesting:
wc.testing++
}
}Update working counts immediately after each change decision, not after database execution. This prevents counting errors that led to monitor target overruns.
The rules execute in a specific order to maximize efficiency:
- Rule 1 (Immediate Blocking): Remove monitors that should be blocked immediately
- Rule 2 (Gradual Constraint Removal): Gradual removal of candidateOut monitors
- Rule 1.5 (Active Excess Demotion): Demote excess healthy active monitors
- Rule 3 (Testing to Active Promotion): Promote from testing to active
- Rule 5 (Candidate to Testing Promotion): Promote candidates to testing and replace worse-performing testing monitors
- Rule 2.5 (Testing Pool Management): Demote excess testing monitors
- Rule 6 (Bootstrap Promotion): Bootstrap case promotions
- Rule 2.5 after Rule 5: Ensures Rule 2.5 sees all pending promotions from Rule 5
- Rule 1.5 before Rule 3: Ensures promotion counts are accurate after active demotions
- Constraint rules first: Clears violations before capacity optimizations
Emergency override activates when len(activeMonitors) == 0, indicating system-critical state.
- Constraint bypassing: Allows promotions despite constraint violations
- Capacity respect: Still respects capacity limits even in emergency
- Unified handling: All promotion functions accept
emergencyOverrideparameter
emergencyOverride := len(activeMonitors) == 0
// Level 1: Emergency safety (allows constraint violations)
if !emergencyOverride && hasConstraintViolation() {
return false // Block only if not emergency
}
// Level 2: Capacity limits (respected even in emergencies)
if workingCount >= targetCount {
return false // Always respect capacity
}- Zero active monitors on server
- All monitors in candidate status
- Need to establish initial monitoring
- Promotes multiple candidates to testing simultaneously
- Respects constraint checking during promotion
- Uses emergency override for faster recovery
- Targets minimum viable monitoring coverage
if len(activeMonitors) == 0 && len(testingMonitors) == 0 {
// Bootstrap mode: promote up to target testing count
promoteCount := min(targetTestingCount, len(healthyCandidates))
// Apply emergency override for constraint bypassing
emergencyOverride := true
}- RTT (Round Trip Time): Network latency to server
- Step: Time accuracy measurement
- Health Score: Composite health calculation
- Availability: Successful monitoring percentage
- Poor performance: Triggers candidateOut (gradual demotion)
- Good performance: Enables testing → active promotion
- No performance data: Prevents promotion until data available
Configurable thresholds determine promotion/demotion decisions:
- Testing monitors need consistent performance for active promotion
- Active monitors with degraded performance face demotion
- Candidates need basic health check for testing promotion
The system never reduces active monitors to zero unless in controlled scenarios:
- Emergency override allows bypass only for recovery
- Rule 1.5 checks
workingActiveCount > 1before demoting - Bootstrap logic ensures rapid recovery from zero state
When multiple changes compete for limited budget:
- Count constraint violations requiring immediate attention
- Reserve budget for constraint-based demotions
- Use remaining budget for capacity optimizations
- Capacity checks prevent over-promotion requiring immediate cleanup
- Dynamic testing targets adjust based on active monitor availability
- Working counts prevent mathematical inconsistencies
selector_monitor_pool_size{status, server_id}: Current pool sizesselector_globally_active_monitors{server_id}: Globally active in testing- Active/testing count ratios and gap measurements
selector_process_duration_seconds{server_id}: Selection algorithm timingselector_changes_applied_total{server_id}: Successful changes per server- Working count accuracy validation
- Promotion/demotion rates by reason
- Constraint violation frequencies
- Emergency override activation tracking
- Monitor activation triggers
InsertMonitorServerScores - Creates candidate entries for all compatible servers
- Selector promotes candidates through testing to active
server_scores.statustracks per-server monitor statemonitors.statusprovides global monitor stateaccounts.flagsconfigures per-account limits
- Dynamic testing target calculation
- Configurable active monitor targets
- Per-account limit customization via flags JSON
This capacity management design ensures efficient monitor utilization while maintaining system stability and operational safety.