Add GetUptimeByKey to store interface

This commit is contained in:
TwinProduction
2021-08-12 21:54:23 -04:00
committed by Chris
parent 968b960283
commit 0b6fc6b520
18 changed files with 182 additions and 71 deletions

View File

@ -6,7 +6,8 @@ import (
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/paging"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/util"
"github.com/TwinProduction/gocache"
)
@ -69,6 +70,35 @@ func (s *Store) GetServiceStatusByKey(key string, params *paging.ServiceStatusPa
return ShallowCopyServiceStatus(serviceStatus.(*core.ServiceStatus), params)
}
// GetUptimeByKey returns the uptime percentage during a time range
func (s *Store) GetUptimeByKey(key string, from, to time.Time) (float64, error) {
if from.After(to) {
return 0, common.ErrInvalidTimeRange
}
serviceStatus := s.cache.GetValue(key)
if serviceStatus == nil || serviceStatus.(*core.ServiceStatus).Uptime == nil {
return 0, common.ErrServiceNotFound
}
successfulExecutions := uint64(0)
totalExecutions := uint64(0)
current := from
for to.Sub(current) >= 0 {
hourlyUnixTimestamp := current.Truncate(time.Hour).Unix()
hourlyStats := serviceStatus.(*core.ServiceStatus).Uptime.HourlyStatistics[hourlyUnixTimestamp]
if hourlyStats == nil || hourlyStats.TotalExecutions == 0 {
current = current.Add(time.Hour)
continue
}
successfulExecutions += hourlyStats.SuccessfulExecutions
totalExecutions += hourlyStats.TotalExecutions
current = current.Add(time.Hour)
}
if totalExecutions == 0 {
return 0, nil
}
return float64(successfulExecutions) / float64(totalExecutions), nil
}
// Insert adds the observed result for the specified service into the store
func (s *Store) Insert(service *core.Service, result *core.Result) {
key := service.Key()

View File

@ -5,7 +5,7 @@ import (
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/paging"
"github.com/TwinProduction/gatus/storage/store/common/paging"
)
var (

View File

@ -2,7 +2,8 @@ package memory
import (
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/paging"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
)
// ShallowCopyServiceStatus returns a shallow copy of a ServiceStatus with only the results
@ -63,11 +64,11 @@ func AddResult(ss *core.ServiceStatus, result *core.Result) {
// Check if there's any change since the last result
if ss.Results[len(ss.Results)-1].Success != result.Success {
ss.Events = append(ss.Events, core.NewEventFromResult(result))
if len(ss.Events) > core.MaximumNumberOfEvents {
if len(ss.Events) > common.MaximumNumberOfEvents {
// Doing ss.Events[1:] would usually be sufficient, but in the case where for some reason, the slice has
// more than one extra element, we can get rid of all of them at once and thus returning the slice to a
// length of MaximumNumberOfEvents by using ss.Events[len(ss.Events)-MaximumNumberOfEvents:] instead
ss.Events = ss.Events[len(ss.Events)-core.MaximumNumberOfEvents:]
ss.Events = ss.Events[len(ss.Events)-common.MaximumNumberOfEvents:]
}
}
} else {
@ -75,11 +76,11 @@ func AddResult(ss *core.ServiceStatus, result *core.Result) {
ss.Events = append(ss.Events, core.NewEventFromResult(result))
}
ss.Results = append(ss.Results, result)
if len(ss.Results) > core.MaximumNumberOfResults {
if len(ss.Results) > common.MaximumNumberOfResults {
// Doing ss.Results[1:] would usually be sufficient, but in the case where for some reason, the slice has more
// than one extra element, we can get rid of all of them at once and thus returning the slice to a length of
// MaximumNumberOfResults by using ss.Results[len(ss.Results)-MaximumNumberOfResults:] instead
ss.Results = ss.Results[len(ss.Results)-core.MaximumNumberOfResults:]
ss.Results = ss.Results[len(ss.Results)-common.MaximumNumberOfResults:]
}
processUptimeAfterResult(ss.Uptime, result)
}

View File

@ -4,13 +4,14 @@ import (
"testing"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/paging"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
)
func BenchmarkShallowCopyServiceStatus(b *testing.B) {
service := &testService
serviceStatus := core.NewServiceStatus(service.Key(), service.Group, service.Name)
for i := 0; i < core.MaximumNumberOfResults; i++ {
for i := 0; i < common.MaximumNumberOfResults; i++ {
AddResult(serviceStatus, &testSuccessfulResult)
}
for n := 0; n < b.N; n++ {

View File

@ -5,20 +5,21 @@ import (
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/paging"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
)
func TestAddResult(t *testing.T) {
service := &core.Service{Name: "name", Group: "group"}
serviceStatus := core.NewServiceStatus(service.Key(), service.Group, service.Name)
for i := 0; i < (core.MaximumNumberOfResults+core.MaximumNumberOfEvents)*2; i++ {
for i := 0; i < (common.MaximumNumberOfResults+common.MaximumNumberOfEvents)*2; i++ {
AddResult(serviceStatus, &core.Result{Success: i%2 == 0, Timestamp: time.Now()})
}
if len(serviceStatus.Results) != core.MaximumNumberOfResults {
t.Errorf("expected serviceStatus.Results to not exceed a length of %d", core.MaximumNumberOfResults)
if len(serviceStatus.Results) != common.MaximumNumberOfResults {
t.Errorf("expected serviceStatus.Results to not exceed a length of %d", common.MaximumNumberOfResults)
}
if len(serviceStatus.Events) != core.MaximumNumberOfEvents {
t.Errorf("expected serviceStatus.Events to not exceed a length of %d", core.MaximumNumberOfEvents)
if len(serviceStatus.Events) != common.MaximumNumberOfEvents {
t.Errorf("expected serviceStatus.Events to not exceed a length of %d", common.MaximumNumberOfEvents)
}
// Try to add nil serviceStatus
AddResult(nil, &core.Result{Timestamp: time.Now()})