mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-21 02:33:32 +00:00
c70431caaf
An availability target's configured poll interval only seeded the adaptive scheduler: BuildPlan derived every instance's cadence from the global adaptive bounds, and a failing probe raised the staleness score and error penalty, collapsing the probe interval toward the global 5-second minimum. With interval 120s and failure threshold 4 the alert was promised after ~8 minutes of downtime but fired within the first minute because the four consecutive failures accumulated at the collapsed cadence (#1582). Availability checks promise pollInterval x failureThreshold as the detection window, so the cadence is a user contract, not a scheduling hint. Add a FixedIntervalPollProvider extension that pins an instance to its configured interval, implement it for availability targets, and bypass adaptive selection wherever the next run is computed (plan building, rescheduling, and the non-adaptive fallback paths).
100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
package monitoring
|
|
|
|
import (
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
func (m *Monitor) describeInstancesForScheduler() []InstanceDescriptor {
|
|
providers := m.pollProviderSnapshotWithBuiltins()
|
|
if len(providers) == 0 {
|
|
return nil
|
|
}
|
|
|
|
total := 0
|
|
for _, provider := range providers {
|
|
if provider == nil {
|
|
continue
|
|
}
|
|
total += len(provider.ListInstances(m))
|
|
}
|
|
if total == 0 {
|
|
return nil
|
|
}
|
|
|
|
descriptors := make([]InstanceDescriptor, 0, total)
|
|
for _, provider := range providers {
|
|
if provider == nil {
|
|
continue
|
|
}
|
|
|
|
names := append([]string(nil), provider.ListInstances(m)...)
|
|
if len(names) == 0 {
|
|
continue
|
|
}
|
|
sort.Strings(names)
|
|
|
|
providerType := provider.Type()
|
|
fixedProvider, _ := provider.(FixedIntervalPollProvider)
|
|
for _, name := range names {
|
|
desc := InstanceDescriptor{
|
|
Name: name,
|
|
Type: providerType,
|
|
}
|
|
if fixedProvider != nil {
|
|
desc.FixedInterval = fixedProvider.FixedInstanceInterval(m, name)
|
|
}
|
|
if m.scheduler != nil {
|
|
if last, ok := m.scheduler.LastScheduled(providerType, name); ok {
|
|
desc.LastScheduled = last.NextRun
|
|
desc.LastInterval = last.Interval
|
|
}
|
|
}
|
|
if m.stalenessTracker != nil {
|
|
if snap, ok := m.stalenessTracker.snapshot(providerType, name); ok {
|
|
desc.LastSuccess = snap.LastSuccess
|
|
desc.LastFailure = snap.LastError
|
|
desc.Metadata = TaskMetadata{ChangeHash: snap.ChangeHash}
|
|
}
|
|
}
|
|
descriptors = append(descriptors, desc)
|
|
}
|
|
}
|
|
|
|
return descriptors
|
|
}
|
|
|
|
func (m *Monitor) buildScheduledTasks(now time.Time) []ScheduledTask {
|
|
descriptors := m.describeInstancesForScheduler()
|
|
if len(descriptors) == 0 {
|
|
return nil
|
|
}
|
|
|
|
queueDepth := 0
|
|
if m.taskQueue != nil {
|
|
queueDepth = m.taskQueue.Size()
|
|
}
|
|
|
|
if m.scheduler == nil {
|
|
tasks := make([]ScheduledTask, 0, len(descriptors))
|
|
for _, desc := range descriptors {
|
|
interval := desc.FixedInterval
|
|
if interval <= 0 {
|
|
interval = m.baseIntervalForInstanceType(desc.Type)
|
|
}
|
|
if interval <= 0 {
|
|
interval = DefaultSchedulerConfig().BaseInterval
|
|
}
|
|
tasks = append(tasks, ScheduledTask{
|
|
InstanceName: desc.Name,
|
|
InstanceType: desc.Type,
|
|
NextRun: now,
|
|
Interval: interval,
|
|
})
|
|
}
|
|
return tasks
|
|
}
|
|
|
|
return m.scheduler.BuildPlan(now, descriptors, queueDepth)
|
|
}
|