65 lines
2.0 KiB
Go
65 lines
2.0 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
// DetectionSchedule survives task-history cleanup and is never projected directly
|
|
// to public APIs. Only automatic results advance this channel/kind clock.
|
|
type DetectionSchedule struct {
|
|
ChannelID int `gorm:"primaryKey;autoIncrement:false"`
|
|
Kind string `gorm:"primaryKey;type:varchar(24)"`
|
|
LastValid string `gorm:"type:varchar(16)"`
|
|
IntervalMinutes int
|
|
NextDueAt int64
|
|
LastFinishedAt int64
|
|
LastResultID string `gorm:"type:varchar(64)"`
|
|
}
|
|
|
|
func NextDetectionInterval(lastValid, outcome string, current int) int {
|
|
if outcome != "normal" && outcome != "degraded" && outcome != "incomplete" {
|
|
return 15
|
|
}
|
|
if lastValid == "" {
|
|
return 30
|
|
}
|
|
if lastValid != outcome {
|
|
return 15
|
|
}
|
|
if current == 15 {
|
|
return 30
|
|
}
|
|
return 60
|
|
}
|
|
|
|
func EnsureDetectionSchedule(ctx context.Context, seed DetectionSchedule) error {
|
|
return DB.WithContext(ctx).Clauses(clause.OnConflict{DoNothing: true}).Create(&seed).Error
|
|
}
|
|
|
|
func ApplyDetectionScheduleResult(ctx context.Context, channelID int, kind, resultID string, finishedAt int64, outcome string) error {
|
|
return DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var state DetectionSchedule
|
|
if err := lockForUpdate(tx).Where("channel_id = ? AND kind = ?", channelID, kind).First(&state).Error; err != nil {
|
|
return err
|
|
}
|
|
// A result is committed before its clock advances. Reconciliation can
|
|
// safely replay it after a crash, without advancing the clock twice.
|
|
if finishedAt <= state.LastFinishedAt || resultID == state.LastResultID {
|
|
return nil
|
|
}
|
|
state.IntervalMinutes = NextDetectionInterval(state.LastValid, outcome, state.IntervalMinutes)
|
|
if kind == "ciallo" {
|
|
state.IntervalMinutes = 45
|
|
}
|
|
if outcome == "normal" || outcome == "degraded" || outcome == "incomplete" {
|
|
state.LastValid = outcome
|
|
}
|
|
state.LastFinishedAt, state.LastResultID = finishedAt, resultID
|
|
state.NextDueAt = finishedAt + int64(state.IntervalMinutes)*60
|
|
return tx.Save(&state).Error
|
|
})
|
|
}
|