Rename Service to Endpoint (#192)

* Add clarifications in comments

* #191: Rename Service to Endpoint
This commit is contained in:
TwiN
2021-10-23 16:47:12 -04:00
committed by GitHub
parent 634123d723
commit 6ed93d4b82
99 changed files with 2136 additions and 2006 deletions

View File

@ -3,6 +3,6 @@ package common
import "errors"
var (
ErrServiceNotFound = errors.New("service not found") // When a service does not exist in the store
ErrEndpointNotFound = errors.New("endpoint not found") // When an endpoint does not exist in the store
ErrInvalidTimeRange = errors.New("'from' cannot be older than 'to'") // When an invalid time range is provided
)

View File

@ -1,9 +1,9 @@
package common
const (
// MaximumNumberOfResults is the maximum number of results that a service can have
// MaximumNumberOfResults is the maximum number of results that an endpoint can have
MaximumNumberOfResults = 100
// MaximumNumberOfEvents is the maximum number of events that a service can have
// MaximumNumberOfEvents is the maximum number of events that an endpoint can have
MaximumNumberOfEvents = 50
)

View File

@ -1,27 +1,27 @@
package paging
// ServiceStatusParams represents all parameters that can be used for paging purposes
type ServiceStatusParams struct {
// EndpointStatusParams represents all parameters that can be used for paging purposes
type EndpointStatusParams struct {
EventsPage int // Number of the event page
EventsPageSize int // Size of the event page
ResultsPage int // Number of the result page
ResultsPageSize int // Size of the result page
}
// NewServiceStatusParams creates a new ServiceStatusParams
func NewServiceStatusParams() *ServiceStatusParams {
return &ServiceStatusParams{}
// NewEndpointStatusParams creates a new EndpointStatusParams
func NewEndpointStatusParams() *EndpointStatusParams {
return &EndpointStatusParams{}
}
// WithEvents sets the values for EventsPage and EventsPageSize
func (params *ServiceStatusParams) WithEvents(page, pageSize int) *ServiceStatusParams {
func (params *EndpointStatusParams) WithEvents(page, pageSize int) *EndpointStatusParams {
params.EventsPage = page
params.EventsPageSize = pageSize
return params
}
// WithResults sets the values for ResultsPage and ResultsPageSize
func (params *ServiceStatusParams) WithResults(page, pageSize int) *ServiceStatusParams {
func (params *EndpointStatusParams) WithResults(page, pageSize int) *EndpointStatusParams {
params.ResultsPage = page
params.ResultsPageSize = pageSize
return params

View File

@ -2,10 +2,10 @@ package paging
import "testing"
func TestNewServiceStatusParams(t *testing.T) {
func TestNewEndpointStatusParams(t *testing.T) {
type Scenario struct {
Name string
Params *ServiceStatusParams
Params *EndpointStatusParams
ExpectedEventsPage int
ExpectedEventsPageSize int
ExpectedResultsPage int
@ -14,7 +14,7 @@ func TestNewServiceStatusParams(t *testing.T) {
scenarios := []Scenario{
{
Name: "empty-params",
Params: NewServiceStatusParams(),
Params: NewEndpointStatusParams(),
ExpectedEventsPage: 0,
ExpectedEventsPageSize: 0,
ExpectedResultsPage: 0,
@ -22,7 +22,7 @@ func TestNewServiceStatusParams(t *testing.T) {
},
{
Name: "with-events-page-2-size-7",
Params: NewServiceStatusParams().WithEvents(2, 7),
Params: NewEndpointStatusParams().WithEvents(2, 7),
ExpectedEventsPage: 2,
ExpectedEventsPageSize: 7,
ExpectedResultsPage: 0,
@ -30,7 +30,7 @@ func TestNewServiceStatusParams(t *testing.T) {
},
{
Name: "with-events-page-4-size-3-uptime",
Params: NewServiceStatusParams().WithEvents(4, 3),
Params: NewEndpointStatusParams().WithEvents(4, 3),
ExpectedEventsPage: 4,
ExpectedEventsPageSize: 3,
ExpectedResultsPage: 0,
@ -38,7 +38,7 @@ func TestNewServiceStatusParams(t *testing.T) {
},
{
Name: "with-results-page-1-size-20-uptime",
Params: NewServiceStatusParams().WithResults(1, 20),
Params: NewEndpointStatusParams().WithResults(1, 20),
ExpectedEventsPage: 0,
ExpectedEventsPageSize: 0,
ExpectedResultsPage: 1,
@ -46,7 +46,7 @@ func TestNewServiceStatusParams(t *testing.T) {
},
{
Name: "with-results-page-2-size-10-events-page-3-size-50",
Params: NewServiceStatusParams().WithResults(2, 10).WithEvents(3, 50),
Params: NewEndpointStatusParams().WithResults(2, 10).WithEvents(3, 50),
ExpectedEventsPage: 3,
ExpectedEventsPageSize: 50,
ExpectedResultsPage: 2,

View File

@ -14,7 +14,7 @@ import (
)
func init() {
gob.Register(&core.ServiceStatus{})
gob.Register(&core.EndpointStatus{})
gob.Register(&core.HourlyUptimeStatistics{})
gob.Register(&core.Uptime{})
gob.Register(&core.Result{})
@ -46,32 +46,32 @@ func NewStore(file string) (*Store, error) {
return store, nil
}
// GetAllServiceStatuses returns all monitored core.ServiceStatus
// GetAllEndpointStatuses returns all monitored core.EndpointStatus
// with a subset of core.Result defined by the page and pageSize parameters
func (s *Store) GetAllServiceStatuses(params *paging.ServiceStatusParams) ([]*core.ServiceStatus, error) {
serviceStatuses := s.cache.GetAll()
pagedServiceStatuses := make([]*core.ServiceStatus, 0, len(serviceStatuses))
for _, v := range serviceStatuses {
pagedServiceStatuses = append(pagedServiceStatuses, ShallowCopyServiceStatus(v.(*core.ServiceStatus), params))
func (s *Store) GetAllEndpointStatuses(params *paging.EndpointStatusParams) ([]*core.EndpointStatus, error) {
endpointStatuses := s.cache.GetAll()
pagedEndpointStatuses := make([]*core.EndpointStatus, 0, len(endpointStatuses))
for _, v := range endpointStatuses {
pagedEndpointStatuses = append(pagedEndpointStatuses, ShallowCopyEndpointStatus(v.(*core.EndpointStatus), params))
}
sort.Slice(pagedServiceStatuses, func(i, j int) bool {
return pagedServiceStatuses[i].Key < pagedServiceStatuses[j].Key
sort.Slice(pagedEndpointStatuses, func(i, j int) bool {
return pagedEndpointStatuses[i].Key < pagedEndpointStatuses[j].Key
})
return pagedServiceStatuses, nil
return pagedEndpointStatuses, nil
}
// GetServiceStatus returns the service status for a given service name in the given group
func (s *Store) GetServiceStatus(groupName, serviceName string, params *paging.ServiceStatusParams) (*core.ServiceStatus, error) {
return s.GetServiceStatusByKey(util.ConvertGroupAndServiceToKey(groupName, serviceName), params)
// GetEndpointStatus returns the endpoint status for a given endpoint name in the given group
func (s *Store) GetEndpointStatus(groupName, endpointName string, params *paging.EndpointStatusParams) (*core.EndpointStatus, error) {
return s.GetEndpointStatusByKey(util.ConvertGroupAndEndpointNameToKey(groupName, endpointName), params)
}
// GetServiceStatusByKey returns the service status for a given key
func (s *Store) GetServiceStatusByKey(key string, params *paging.ServiceStatusParams) (*core.ServiceStatus, error) {
serviceStatus := s.cache.GetValue(key)
if serviceStatus == nil {
return nil, common.ErrServiceNotFound
// GetEndpointStatusByKey returns the endpoint status for a given key
func (s *Store) GetEndpointStatusByKey(key string, params *paging.EndpointStatusParams) (*core.EndpointStatus, error) {
endpointStatus := s.cache.GetValue(key)
if endpointStatus == nil {
return nil, common.ErrEndpointNotFound
}
return ShallowCopyServiceStatus(serviceStatus.(*core.ServiceStatus), params), nil
return ShallowCopyEndpointStatus(endpointStatus.(*core.EndpointStatus), params), nil
}
// GetUptimeByKey returns the uptime percentage during a time range
@ -79,16 +79,16 @@ 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
endpointStatus := s.cache.GetValue(key)
if endpointStatus == nil || endpointStatus.(*core.EndpointStatus).Uptime == nil {
return 0, common.ErrEndpointNotFound
}
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]
hourlyStats := endpointStatus.(*core.EndpointStatus).Uptime.HourlyStatistics[hourlyUnixTimestamp]
if hourlyStats == nil || hourlyStats.TotalExecutions == 0 {
current = current.Add(time.Hour)
continue
@ -108,15 +108,15 @@ func (s *Store) GetAverageResponseTimeByKey(key string, from, to time.Time) (int
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
endpointStatus := s.cache.GetValue(key)
if endpointStatus == nil || endpointStatus.(*core.EndpointStatus).Uptime == nil {
return 0, common.ErrEndpointNotFound
}
current := from
var totalExecutions, totalResponseTime uint64
for to.Sub(current) >= 0 {
hourlyUnixTimestamp := current.Truncate(time.Hour).Unix()
hourlyStats := serviceStatus.(*core.ServiceStatus).Uptime.HourlyStatistics[hourlyUnixTimestamp]
hourlyStats := endpointStatus.(*core.EndpointStatus).Uptime.HourlyStatistics[hourlyUnixTimestamp]
if hourlyStats == nil || hourlyStats.TotalExecutions == 0 {
current = current.Add(time.Hour)
continue
@ -136,15 +136,15 @@ func (s *Store) GetHourlyAverageResponseTimeByKey(key string, from, to time.Time
if from.After(to) {
return nil, common.ErrInvalidTimeRange
}
serviceStatus := s.cache.GetValue(key)
if serviceStatus == nil || serviceStatus.(*core.ServiceStatus).Uptime == nil {
return nil, common.ErrServiceNotFound
endpointStatus := s.cache.GetValue(key)
if endpointStatus == nil || endpointStatus.(*core.EndpointStatus).Uptime == nil {
return nil, common.ErrEndpointNotFound
}
hourlyAverageResponseTimes := make(map[int64]int)
current := from
for to.Sub(current) >= 0 {
hourlyUnixTimestamp := current.Truncate(time.Hour).Unix()
hourlyStats := serviceStatus.(*core.ServiceStatus).Uptime.HourlyStatistics[hourlyUnixTimestamp]
hourlyStats := endpointStatus.(*core.EndpointStatus).Uptime.HourlyStatistics[hourlyUnixTimestamp]
if hourlyStats == nil || hourlyStats.TotalExecutions == 0 {
current = current.Add(time.Hour)
continue
@ -155,26 +155,26 @@ func (s *Store) GetHourlyAverageResponseTimeByKey(key string, from, to time.Time
return hourlyAverageResponseTimes, nil
}
// Insert adds the observed result for the specified service into the store
func (s *Store) Insert(service *core.Service, result *core.Result) error {
key := service.Key()
// Insert adds the observed result for the specified endpoint into the store
func (s *Store) Insert(endpoint *core.Endpoint, result *core.Result) error {
key := endpoint.Key()
s.Lock()
serviceStatus, exists := s.cache.Get(key)
status, exists := s.cache.Get(key)
if !exists {
serviceStatus = core.NewServiceStatus(key, service.Group, service.Name)
serviceStatus.(*core.ServiceStatus).Events = append(serviceStatus.(*core.ServiceStatus).Events, &core.Event{
status = core.NewEndpointStatus(endpoint.Group, endpoint.Name)
status.(*core.EndpointStatus).Events = append(status.(*core.EndpointStatus).Events, &core.Event{
Type: core.EventStart,
Timestamp: time.Now(),
})
}
AddResult(serviceStatus.(*core.ServiceStatus), result)
s.cache.Set(key, serviceStatus)
AddResult(status.(*core.EndpointStatus), result)
s.cache.Set(key, status)
s.Unlock()
return nil
}
// DeleteAllServiceStatusesNotInKeys removes all ServiceStatus that are not within the keys provided
func (s *Store) DeleteAllServiceStatusesNotInKeys(keys []string) int {
// DeleteAllEndpointStatusesNotInKeys removes all EndpointStatus that are not within the keys provided
func (s *Store) DeleteAllEndpointStatusesNotInKeys(keys []string) int {
var keysToDelete []string
for _, existingKey := range s.cache.GetKeysByPattern("*", 0) {
shouldDelete := true

View File

@ -15,7 +15,7 @@ var (
now = time.Now()
testService = core.Service{
testEndpoint = core.Endpoint{
Name: "name",
Group: "group",
URL: "https://example.org/what/ever",
@ -84,39 +84,39 @@ var (
func TestStore_SanityCheck(t *testing.T) {
store, _ := NewStore("")
defer store.Close()
store.Insert(&testService, &testSuccessfulResult)
serviceStatuses, _ := store.GetAllServiceStatuses(paging.NewServiceStatusParams())
if numberOfServiceStatuses := len(serviceStatuses); numberOfServiceStatuses != 1 {
t.Fatalf("expected 1 ServiceStatus, got %d", numberOfServiceStatuses)
store.Insert(&testEndpoint, &testSuccessfulResult)
endpointStatuses, _ := store.GetAllEndpointStatuses(paging.NewEndpointStatusParams())
if numberOfEndpointStatuses := len(endpointStatuses); numberOfEndpointStatuses != 1 {
t.Fatalf("expected 1 EndpointStatus, got %d", numberOfEndpointStatuses)
}
store.Insert(&testService, &testUnsuccessfulResult)
// Both results inserted are for the same service, therefore, the count shouldn't have increased
serviceStatuses, _ = store.GetAllServiceStatuses(paging.NewServiceStatusParams())
if numberOfServiceStatuses := len(serviceStatuses); numberOfServiceStatuses != 1 {
t.Fatalf("expected 1 ServiceStatus, got %d", numberOfServiceStatuses)
store.Insert(&testEndpoint, &testUnsuccessfulResult)
// Both results inserted are for the same endpoint, therefore, the count shouldn't have increased
endpointStatuses, _ = store.GetAllEndpointStatuses(paging.NewEndpointStatusParams())
if numberOfEndpointStatuses := len(endpointStatuses); numberOfEndpointStatuses != 1 {
t.Fatalf("expected 1 EndpointStatus, got %d", numberOfEndpointStatuses)
}
if hourlyAverageResponseTime, err := store.GetHourlyAverageResponseTimeByKey(testService.Key(), time.Now().Add(-24*time.Hour), time.Now()); err != nil {
if hourlyAverageResponseTime, err := store.GetHourlyAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-24*time.Hour), time.Now()); err != nil {
t.Errorf("expected no error, got %v", err)
} else if len(hourlyAverageResponseTime) != 1 {
t.Errorf("expected 1 hour to have had a result in the past 24 hours, got %d", len(hourlyAverageResponseTime))
}
if uptime, _ := store.GetUptimeByKey(testService.Key(), time.Now().Add(-24*time.Hour), time.Now()); uptime != 0.5 {
if uptime, _ := store.GetUptimeByKey(testEndpoint.Key(), time.Now().Add(-24*time.Hour), time.Now()); uptime != 0.5 {
t.Errorf("expected uptime of last 24h to be 0.5, got %f", uptime)
}
if averageResponseTime, _ := store.GetAverageResponseTimeByKey(testService.Key(), time.Now().Add(-24*time.Hour), time.Now()); averageResponseTime != 450 {
if averageResponseTime, _ := store.GetAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-24*time.Hour), time.Now()); averageResponseTime != 450 {
t.Errorf("expected average response time of last 24h to be 450, got %d", averageResponseTime)
}
ss, _ := store.GetServiceStatus(testService.Group, testService.Name, paging.NewServiceStatusParams().WithResults(1, 20).WithEvents(1, 20))
ss, _ := store.GetEndpointStatus(testEndpoint.Group, testEndpoint.Name, paging.NewEndpointStatusParams().WithResults(1, 20).WithEvents(1, 20))
if ss == nil {
t.Fatalf("Store should've had key '%s', but didn't", testService.Key())
t.Fatalf("Store should've had key '%s', but didn't", testEndpoint.Key())
}
if len(ss.Events) != 3 {
t.Errorf("Service '%s' should've had 3 events, got %d", ss.Name, len(ss.Events))
t.Errorf("Endpoint '%s' should've had 3 events, got %d", ss.Name, len(ss.Events))
}
if len(ss.Results) != 2 {
t.Errorf("Service '%s' should've had 2 results, got %d", ss.Name, len(ss.Results))
t.Errorf("Endpoint '%s' should've had 2 results, got %d", ss.Name, len(ss.Results))
}
if deleted := store.DeleteAllServiceStatusesNotInKeys([]string{}); deleted != 1 {
if deleted := store.DeleteAllEndpointStatusesNotInKeys([]string{}); deleted != 1 {
t.Errorf("%d entries should've been deleted, got %d", 1, deleted)
}
}

View File

@ -19,7 +19,7 @@ func BenchmarkProcessUptimeAfterResult(b *testing.B) {
Success: n%15 == 0,
Timestamp: timestamp,
})
// Simulate service with an interval of 3 minutes
// Simulate an endpoint with an interval of 3 minutes
timestamp = timestamp.Add(3 * time.Minute)
}
b.ReportAllocs()

View File

@ -8,9 +8,9 @@ import (
)
func TestProcessUptimeAfterResult(t *testing.T) {
service := &core.Service{Name: "name", Group: "group"}
serviceStatus := core.NewServiceStatus(service.Key(), service.Group, service.Name)
uptime := serviceStatus.Uptime
endpoint := &core.Endpoint{Name: "name", Group: "group"}
status := core.NewEndpointStatus(endpoint.Group, endpoint.Name)
uptime := status.Uptime
now := time.Now()
now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
@ -43,18 +43,18 @@ func TestProcessUptimeAfterResult(t *testing.T) {
}
func TestAddResultUptimeIsCleaningUpAfterItself(t *testing.T) {
service := &core.Service{Name: "name", Group: "group"}
serviceStatus := core.NewServiceStatus(service.Key(), service.Group, service.Name)
endpoint := &core.Endpoint{Name: "name", Group: "group"}
status := core.NewEndpointStatus(endpoint.Group, endpoint.Name)
now := time.Now()
now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
// Start 12 days ago
timestamp := now.Add(-12 * 24 * time.Hour)
for timestamp.Unix() <= now.Unix() {
AddResult(serviceStatus, &core.Result{Timestamp: timestamp, Success: true})
if len(serviceStatus.Uptime.HourlyStatistics) > numberOfHoursInTenDays {
t.Errorf("At no point in time should there be more than %d entries in serviceStatus.SuccessfulExecutionsPerHour, but there are %d", numberOfHoursInTenDays, len(serviceStatus.Uptime.HourlyStatistics))
AddResult(status, &core.Result{Timestamp: timestamp, Success: true})
if len(status.Uptime.HourlyStatistics) > numberOfHoursInTenDays {
t.Errorf("At no point in time should there be more than %d entries in status.SuccessfulExecutionsPerHour, but there are %d", numberOfHoursInTenDays, len(status.Uptime.HourlyStatistics))
}
// Simulate service with an interval of 3 minutes
// Simulate endpoint with an interval of 3 minutes
timestamp = timestamp.Add(3 * time.Minute)
}
}

View File

@ -6,10 +6,10 @@ import (
"github.com/TwiN/gatus/v3/storage/store/common/paging"
)
// ShallowCopyServiceStatus returns a shallow copy of a ServiceStatus with only the results
// ShallowCopyEndpointStatus returns a shallow copy of a EndpointStatus with only the results
// within the range defined by the page and pageSize parameters
func ShallowCopyServiceStatus(ss *core.ServiceStatus, params *paging.ServiceStatusParams) *core.ServiceStatus {
shallowCopy := &core.ServiceStatus{
func ShallowCopyEndpointStatus(ss *core.EndpointStatus, params *paging.EndpointStatusParams) *core.EndpointStatus {
shallowCopy := &core.EndpointStatus{
Name: ss.Name,
Group: ss.Group,
Key: ss.Key,
@ -49,9 +49,9 @@ func getStartAndEndIndex(numberOfResults int, page, pageSize int) (int, int) {
return start, end
}
// AddResult adds a Result to ServiceStatus.Results and makes sure that there are
// AddResult adds a Result to EndpointStatus.Results and makes sure that there are
// no more than MaximumNumberOfResults results in the Results slice
func AddResult(ss *core.ServiceStatus, result *core.Result) {
func AddResult(ss *core.EndpointStatus, result *core.Result) {
if ss == nil {
return
}

View File

@ -8,14 +8,14 @@ import (
"github.com/TwiN/gatus/v3/storage/store/common/paging"
)
func BenchmarkShallowCopyServiceStatus(b *testing.B) {
service := &testService
serviceStatus := core.NewServiceStatus(service.Key(), service.Group, service.Name)
func BenchmarkShallowCopyEndpointStatus(b *testing.B) {
endpoint := &testEndpoint
status := core.NewEndpointStatus(endpoint.Group, endpoint.Name)
for i := 0; i < common.MaximumNumberOfResults; i++ {
AddResult(serviceStatus, &testSuccessfulResult)
AddResult(status, &testSuccessfulResult)
}
for n := 0; n < b.N; n++ {
ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(1, 20))
ShallowCopyEndpointStatus(status, paging.NewEndpointStatusParams().WithResults(1, 20))
}
b.ReportAllocs()
}

View File

@ -10,57 +10,57 @@ import (
)
func TestAddResult(t *testing.T) {
service := &core.Service{Name: "name", Group: "group"}
serviceStatus := core.NewServiceStatus(service.Key(), service.Group, service.Name)
endpoint := &core.Endpoint{Name: "name", Group: "group"}
endpointStatus := core.NewEndpointStatus(endpoint.Group, endpoint.Name)
for i := 0; i < (common.MaximumNumberOfResults+common.MaximumNumberOfEvents)*2; i++ {
AddResult(serviceStatus, &core.Result{Success: i%2 == 0, Timestamp: time.Now()})
AddResult(endpointStatus, &core.Result{Success: i%2 == 0, Timestamp: time.Now()})
}
if len(serviceStatus.Results) != common.MaximumNumberOfResults {
t.Errorf("expected serviceStatus.Results to not exceed a length of %d", common.MaximumNumberOfResults)
if len(endpointStatus.Results) != common.MaximumNumberOfResults {
t.Errorf("expected endpointStatus.Results to not exceed a length of %d", common.MaximumNumberOfResults)
}
if len(serviceStatus.Events) != common.MaximumNumberOfEvents {
t.Errorf("expected serviceStatus.Events to not exceed a length of %d", common.MaximumNumberOfEvents)
if len(endpointStatus.Events) != common.MaximumNumberOfEvents {
t.Errorf("expected endpointStatus.Events to not exceed a length of %d", common.MaximumNumberOfEvents)
}
// Try to add nil serviceStatus
// Try to add nil endpointStatus
AddResult(nil, &core.Result{Timestamp: time.Now()})
}
func TestShallowCopyServiceStatus(t *testing.T) {
service := &core.Service{Name: "name", Group: "group"}
serviceStatus := core.NewServiceStatus(service.Key(), service.Group, service.Name)
func TestShallowCopyEndpointStatus(t *testing.T) {
endpoint := &core.Endpoint{Name: "name", Group: "group"}
endpointStatus := core.NewEndpointStatus(endpoint.Group, endpoint.Name)
ts := time.Now().Add(-25 * time.Hour)
for i := 0; i < 25; i++ {
AddResult(serviceStatus, &core.Result{Success: i%2 == 0, Timestamp: ts})
AddResult(endpointStatus, &core.Result{Success: i%2 == 0, Timestamp: ts})
ts = ts.Add(time.Hour)
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(-1, -1)).Results) != 0 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(-1, -1)).Results) != 0 {
t.Error("expected to have 0 result")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(1, 1)).Results) != 1 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(1, 1)).Results) != 1 {
t.Error("expected to have 1 result")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(5, 0)).Results) != 0 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(5, 0)).Results) != 0 {
t.Error("expected to have 0 results")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(-1, 20)).Results) != 0 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(-1, 20)).Results) != 0 {
t.Error("expected to have 0 result, because the page was invalid")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(1, -1)).Results) != 0 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(1, -1)).Results) != 0 {
t.Error("expected to have 0 result, because the page size was invalid")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(1, 10)).Results) != 10 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(1, 10)).Results) != 10 {
t.Error("expected to have 10 results, because given a page size of 10, page 1 should have 10 elements")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(2, 10)).Results) != 10 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(2, 10)).Results) != 10 {
t.Error("expected to have 10 results, because given a page size of 10, page 2 should have 10 elements")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(3, 10)).Results) != 5 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(3, 10)).Results) != 5 {
t.Error("expected to have 5 results, because given a page size of 10, page 3 should have 5 elements")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(4, 10)).Results) != 0 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(4, 10)).Results) != 0 {
t.Error("expected to have 0 results, because given a page size of 10, page 4 should have 0 elements")
}
if len(ShallowCopyServiceStatus(serviceStatus, paging.NewServiceStatusParams().WithResults(1, 50)).Results) != 25 {
if len(ShallowCopyEndpointStatus(endpointStatus, paging.NewEndpointStatusParams().WithResults(1, 50)).Results) != 25 {
t.Error("expected to have 25 results, because there's only 25 results")
}
}

View File

@ -2,21 +2,21 @@ package sql
func (s *Store) createPostgresSchema() error {
_, err := s.db.Exec(`
CREATE TABLE IF NOT EXISTS service (
service_id BIGSERIAL PRIMARY KEY,
service_key TEXT UNIQUE,
service_name TEXT,
service_group TEXT,
UNIQUE(service_name, service_group)
CREATE TABLE IF NOT EXISTS endpoints (
endpoint_id BIGSERIAL PRIMARY KEY,
endpoint_key TEXT UNIQUE,
endpoint_name TEXT,
endpoint_group TEXT,
UNIQUE(endpoint_name, endpoint_group)
)
`)
if err != nil {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS service_event (
service_event_id BIGSERIAL PRIMARY KEY,
service_id INTEGER REFERENCES service(service_id) ON DELETE CASCADE,
CREATE TABLE IF NOT EXISTS endpoint_events (
endpoint_event_id BIGSERIAL PRIMARY KEY,
endpoint_id INTEGER REFERENCES endpoints(endpoint_id) ON DELETE CASCADE,
event_type TEXT,
event_timestamp TIMESTAMP
)
@ -25,9 +25,9 @@ func (s *Store) createPostgresSchema() error {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS service_result (
service_result_id BIGSERIAL PRIMARY KEY,
service_id BIGINT REFERENCES service(service_id) ON DELETE CASCADE,
CREATE TABLE IF NOT EXISTS endpoint_results (
endpoint_result_id BIGSERIAL PRIMARY KEY,
endpoint_id BIGINT REFERENCES endpoints(endpoint_id) ON DELETE CASCADE,
success BOOLEAN,
errors TEXT,
connected BOOLEAN,
@ -44,25 +44,25 @@ func (s *Store) createPostgresSchema() error {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS service_result_condition (
service_result_condition_id BIGSERIAL PRIMARY KEY,
service_result_id BIGINT REFERENCES service_result(service_result_id) ON DELETE CASCADE,
condition TEXT,
success BOOLEAN
CREATE TABLE IF NOT EXISTS endpoint_result_conditions (
endpoint_result_condition_id BIGSERIAL PRIMARY KEY,
endpoint_result_id BIGINT REFERENCES endpoint_results(endpoint_result_id) ON DELETE CASCADE,
condition TEXT,
success BOOLEAN
)
`)
if err != nil {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS service_uptime (
service_uptime_id BIGSERIAL PRIMARY KEY,
service_id BIGINT REFERENCES service(service_id) ON DELETE CASCADE,
hour_unix_timestamp BIGINT,
total_executions BIGINT,
successful_executions BIGINT,
total_response_time BIGINT,
UNIQUE(service_id, hour_unix_timestamp)
CREATE TABLE IF NOT EXISTS endpoint_uptimes (
endpoint_uptime_id BIGSERIAL PRIMARY KEY,
endpoint_id BIGINT REFERENCES endpoints(endpoint_id) ON DELETE CASCADE,
hour_unix_timestamp BIGINT,
total_executions BIGINT,
successful_executions BIGINT,
total_response_time BIGINT,
UNIQUE(endpoint_id, hour_unix_timestamp)
)
`)
return err

View File

@ -2,21 +2,21 @@ package sql
func (s *Store) createSQLiteSchema() error {
_, err := s.db.Exec(`
CREATE TABLE IF NOT EXISTS service (
service_id INTEGER PRIMARY KEY,
service_key TEXT UNIQUE,
service_name TEXT,
service_group TEXT,
UNIQUE(service_name, service_group)
CREATE TABLE IF NOT EXISTS endpoints (
endpoint_id INTEGER PRIMARY KEY,
endpoint_key TEXT UNIQUE,
endpoint_name TEXT,
endpoint_group TEXT,
UNIQUE(endpoint_name, endpoint_group)
)
`)
if err != nil {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS service_event (
service_event_id INTEGER PRIMARY KEY,
service_id INTEGER REFERENCES service(service_id) ON DELETE CASCADE,
CREATE TABLE IF NOT EXISTS endpoint_events (
endpoint_event_id INTEGER PRIMARY KEY,
endpoint_id INTEGER REFERENCES endpoints(endpoint_id) ON DELETE CASCADE,
event_type TEXT,
event_timestamp TIMESTAMP
)
@ -25,9 +25,9 @@ func (s *Store) createSQLiteSchema() error {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS service_result (
service_result_id INTEGER PRIMARY KEY,
service_id INTEGER REFERENCES service(service_id) ON DELETE CASCADE,
CREATE TABLE IF NOT EXISTS endpoint_results (
endpoint_result_id INTEGER PRIMARY KEY,
endpoint_id INTEGER REFERENCES endpoints(endpoint_id) ON DELETE CASCADE,
success INTEGER,
errors TEXT,
connected INTEGER,
@ -44,25 +44,25 @@ func (s *Store) createSQLiteSchema() error {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS service_result_condition (
service_result_condition_id INTEGER PRIMARY KEY,
service_result_id INTEGER REFERENCES service_result(service_result_id) ON DELETE CASCADE,
condition TEXT,
success INTEGER
CREATE TABLE IF NOT EXISTS endpoint_result_conditions (
endpoint_result_condition_id INTEGER PRIMARY KEY,
endpoint_result_id INTEGER REFERENCES endpoint_results(endpoint_result_id) ON DELETE CASCADE,
condition TEXT,
success INTEGER
)
`)
if err != nil {
return err
}
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS service_uptime (
service_uptime_id INTEGER PRIMARY KEY,
service_id INTEGER REFERENCES service(service_id) ON DELETE CASCADE,
CREATE TABLE IF NOT EXISTS endpoint_uptimes (
endpoint_uptime_id INTEGER PRIMARY KEY,
endpoint_id INTEGER REFERENCES endpoints(endpoint_id) ON DELETE CASCADE,
hour_unix_timestamp INTEGER,
total_executions INTEGER,
successful_executions INTEGER,
total_response_time INTEGER,
UNIQUE(service_id, hour_unix_timestamp)
UNIQUE(endpoint_id, hour_unix_timestamp)
)
`)
return err

View File

@ -89,44 +89,44 @@ func (s *Store) createSchema() error {
return s.createPostgresSchema()
}
// GetAllServiceStatuses returns all monitored core.ServiceStatus
// GetAllEndpointStatuses returns all monitored core.EndpointStatus
// with a subset of core.Result defined by the page and pageSize parameters
func (s *Store) GetAllServiceStatuses(params *paging.ServiceStatusParams) ([]*core.ServiceStatus, error) {
func (s *Store) GetAllEndpointStatuses(params *paging.EndpointStatusParams) ([]*core.EndpointStatus, error) {
tx, err := s.db.Begin()
if err != nil {
return nil, err
}
keys, err := s.getAllServiceKeys(tx)
keys, err := s.getAllEndpointKeys(tx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
serviceStatuses := make([]*core.ServiceStatus, 0, len(keys))
endpointStatuses := make([]*core.EndpointStatus, 0, len(keys))
for _, key := range keys {
serviceStatus, err := s.getServiceStatusByKey(tx, key, params)
endpointStatus, err := s.getEndpointStatusByKey(tx, key, params)
if err != nil {
continue
}
serviceStatuses = append(serviceStatuses, serviceStatus)
endpointStatuses = append(endpointStatuses, endpointStatus)
}
if err = tx.Commit(); err != nil {
_ = tx.Rollback()
}
return serviceStatuses, err
return endpointStatuses, err
}
// GetServiceStatus returns the service status for a given service name in the given group
func (s *Store) GetServiceStatus(groupName, serviceName string, params *paging.ServiceStatusParams) (*core.ServiceStatus, error) {
return s.GetServiceStatusByKey(util.ConvertGroupAndServiceToKey(groupName, serviceName), params)
// GetEndpointStatus returns the endpoint status for a given endpoint name in the given group
func (s *Store) GetEndpointStatus(groupName, endpointName string, params *paging.EndpointStatusParams) (*core.EndpointStatus, error) {
return s.GetEndpointStatusByKey(util.ConvertGroupAndEndpointNameToKey(groupName, endpointName), params)
}
// GetServiceStatusByKey returns the service status for a given key
func (s *Store) GetServiceStatusByKey(key string, params *paging.ServiceStatusParams) (*core.ServiceStatus, error) {
// GetEndpointStatusByKey returns the endpoint status for a given key
func (s *Store) GetEndpointStatusByKey(key string, params *paging.EndpointStatusParams) (*core.EndpointStatus, error) {
tx, err := s.db.Begin()
if err != nil {
return nil, err
}
serviceStatus, err := s.getServiceStatusByKey(tx, key, params)
endpointStatus, err := s.getEndpointStatusByKey(tx, key, params)
if err != nil {
_ = tx.Rollback()
return nil, err
@ -134,7 +134,7 @@ func (s *Store) GetServiceStatusByKey(key string, params *paging.ServiceStatusPa
if err = tx.Commit(); err != nil {
_ = tx.Rollback()
}
return serviceStatus, err
return endpointStatus, err
}
// GetUptimeByKey returns the uptime percentage during a time range
@ -146,12 +146,12 @@ func (s *Store) GetUptimeByKey(key string, from, to time.Time) (float64, error)
if err != nil {
return 0, err
}
serviceID, _, _, err := s.getServiceIDGroupAndNameByKey(tx, key)
endpointID, _, _, err := s.getEndpointIDGroupAndNameByKey(tx, key)
if err != nil {
_ = tx.Rollback()
return 0, err
}
uptime, _, err := s.getServiceUptime(tx, serviceID, from, to)
uptime, _, err := s.getEndpointUptime(tx, endpointID, from, to)
if err != nil {
_ = tx.Rollback()
return 0, err
@ -171,12 +171,12 @@ func (s *Store) GetAverageResponseTimeByKey(key string, from, to time.Time) (int
if err != nil {
return 0, err
}
serviceID, _, _, err := s.getServiceIDGroupAndNameByKey(tx, key)
endpointID, _, _, err := s.getEndpointIDGroupAndNameByKey(tx, key)
if err != nil {
_ = tx.Rollback()
return 0, err
}
averageResponseTime, err := s.getServiceAverageResponseTime(tx, serviceID, from, to)
averageResponseTime, err := s.getEndpointAverageResponseTime(tx, endpointID, from, to)
if err != nil {
_ = tx.Rollback()
return 0, err
@ -196,12 +196,12 @@ func (s *Store) GetHourlyAverageResponseTimeByKey(key string, from, to time.Time
if err != nil {
return nil, err
}
serviceID, _, _, err := s.getServiceIDGroupAndNameByKey(tx, key)
endpointID, _, _, err := s.getEndpointIDGroupAndNameByKey(tx, key)
if err != nil {
_ = tx.Rollback()
return nil, err
}
hourlyAverageResponseTimes, err := s.getServiceHourlyAverageResponseTimes(tx, serviceID, from, to)
hourlyAverageResponseTimes, err := s.getEndpointHourlyAverageResponseTimes(tx, endpointID, from, to)
if err != nil {
_ = tx.Rollback()
return nil, err
@ -212,71 +212,71 @@ func (s *Store) GetHourlyAverageResponseTimeByKey(key string, from, to time.Time
return hourlyAverageResponseTimes, nil
}
// Insert adds the observed result for the specified service into the store
func (s *Store) Insert(service *core.Service, result *core.Result) error {
// Insert adds the observed result for the specified endpoint into the store
func (s *Store) Insert(endpoint *core.Endpoint, result *core.Result) error {
tx, err := s.db.Begin()
if err != nil {
return err
}
serviceID, err := s.getServiceID(tx, service)
endpointID, err := s.getEndpointID(tx, endpoint)
if err != nil {
if err == common.ErrServiceNotFound {
// Service doesn't exist in the database, insert it
if serviceID, err = s.insertService(tx, service); err != nil {
if err == common.ErrEndpointNotFound {
// Endpoint doesn't exist in the database, insert it
if endpointID, err = s.insertEndpoint(tx, endpoint); err != nil {
_ = tx.Rollback()
log.Printf("[sql][Insert] Failed to create service with group=%s; service=%s: %s", service.Group, service.Name, err.Error())
log.Printf("[sql][Insert] Failed to create endpoint with group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
return err
}
} else {
_ = tx.Rollback()
log.Printf("[sql][Insert] Failed to retrieve id of service with group=%s; service=%s: %s", service.Group, service.Name, err.Error())
log.Printf("[sql][Insert] Failed to retrieve id of endpoint with group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
return err
}
}
// First, we need to check if we need to insert a new event.
//
// A new event must be added if either of the following cases happen:
// 1. There is only 1 event. The total number of events for a service can only be 1 if the only existing event is
// 1. There is only 1 event. The total number of events for a endpoint can only be 1 if the only existing event is
// of type EventStart, in which case we will have to create a new event of type EventHealthy or EventUnhealthy
// based on result.Success.
// 2. The lastResult.Success != result.Success. This implies that the service went from healthy to unhealthy or
// 2. The lastResult.Success != result.Success. This implies that the endpoint went from healthy to unhealthy or
// vice-versa, in which case we will have to create a new event of type EventHealthy or EventUnhealthy
// based on result.Success.
numberOfEvents, err := s.getNumberOfEventsByServiceID(tx, serviceID)
numberOfEvents, err := s.getNumberOfEventsByEndpointID(tx, endpointID)
if err != nil {
// Silently fail
log.Printf("[sql][Insert] Failed to retrieve total number of events for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
log.Printf("[sql][Insert] Failed to retrieve total number of events for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
}
if numberOfEvents == 0 {
// There's no events yet, which means we need to add the EventStart and the first healthy/unhealthy event
err = s.insertEvent(tx, serviceID, &core.Event{
err = s.insertEndpointEvent(tx, endpointID, &core.Event{
Type: core.EventStart,
Timestamp: result.Timestamp.Add(-50 * time.Millisecond),
})
if err != nil {
// Silently fail
log.Printf("[sql][Insert] Failed to insert event=%s for group=%s; service=%s: %s", core.EventStart, service.Group, service.Name, err.Error())
log.Printf("[sql][Insert] Failed to insert event=%s for group=%s; endpoint=%s: %s", core.EventStart, endpoint.Group, endpoint.Name, err.Error())
}
event := core.NewEventFromResult(result)
if err = s.insertEvent(tx, serviceID, event); err != nil {
if err = s.insertEndpointEvent(tx, endpointID, event); err != nil {
// Silently fail
log.Printf("[sql][Insert] Failed to insert event=%s for group=%s; service=%s: %s", event.Type, service.Group, service.Name, err.Error())
log.Printf("[sql][Insert] Failed to insert event=%s for group=%s; endpoint=%s: %s", event.Type, endpoint.Group, endpoint.Name, err.Error())
}
} else {
// Get the success value of the previous result
var lastResultSuccess bool
if lastResultSuccess, err = s.getLastServiceResultSuccessValue(tx, serviceID); err != nil {
log.Printf("[sql][Insert] Failed to retrieve outcome of previous result for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
if lastResultSuccess, err = s.getLastEndpointResultSuccessValue(tx, endpointID); err != nil {
log.Printf("[sql][Insert] Failed to retrieve outcome of previous result for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
} else {
// If we managed to retrieve the outcome of the previous result, we'll compare it with the new result.
// If the final outcome (success or failure) of the previous and the new result aren't the same, it means
// that the service either went from Healthy to Unhealthy or Unhealthy -> Healthy, therefore, we'll add
// that the endpoint either went from Healthy to Unhealthy or Unhealthy -> Healthy, therefore, we'll add
// an event to mark the change in state
if lastResultSuccess != result.Success {
event := core.NewEventFromResult(result)
if err = s.insertEvent(tx, serviceID, event); err != nil {
if err = s.insertEndpointEvent(tx, endpointID, event); err != nil {
// Silently fail
log.Printf("[sql][Insert] Failed to insert event=%s for group=%s; service=%s: %s", event.Type, service.Group, service.Name, err.Error())
log.Printf("[sql][Insert] Failed to insert event=%s for group=%s; endpoint=%s: %s", event.Type, endpoint.Group, endpoint.Name, err.Error())
}
}
}
@ -284,41 +284,41 @@ func (s *Store) Insert(service *core.Service, result *core.Result) error {
// This lets us both keep the table clean without impacting performance too much
// (since we're only deleting MaximumNumberOfEvents at a time instead of 1)
if numberOfEvents > eventsCleanUpThreshold {
if err = s.deleteOldServiceEvents(tx, serviceID); err != nil {
log.Printf("[sql][Insert] Failed to delete old events for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
if err = s.deleteOldEndpointEvents(tx, endpointID); err != nil {
log.Printf("[sql][Insert] Failed to delete old events for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
}
}
}
// Second, we need to insert the result.
if err = s.insertResult(tx, serviceID, result); err != nil {
log.Printf("[sql][Insert] Failed to insert result for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
if err = s.insertEndpointResult(tx, endpointID, result); err != nil {
log.Printf("[sql][Insert] Failed to insert result for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
_ = tx.Rollback() // If we can't insert the result, we'll rollback now since there's no point continuing
return err
}
// Clean up old results
numberOfResults, err := s.getNumberOfResultsByServiceID(tx, serviceID)
numberOfResults, err := s.getNumberOfResultsByEndpointID(tx, endpointID)
if err != nil {
log.Printf("[sql][Insert] Failed to retrieve total number of results for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
log.Printf("[sql][Insert] Failed to retrieve total number of results for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
} else {
if numberOfResults > resultsCleanUpThreshold {
if err = s.deleteOldServiceResults(tx, serviceID); err != nil {
log.Printf("[sql][Insert] Failed to delete old results for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
if err = s.deleteOldEndpointResults(tx, endpointID); err != nil {
log.Printf("[sql][Insert] Failed to delete old results for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
}
}
}
// Finally, we need to insert the uptime data.
// Because the uptime data significantly outlives the results, we can't rely on the results for determining the uptime
if err = s.updateServiceUptime(tx, serviceID, result); err != nil {
log.Printf("[sql][Insert] Failed to update uptime for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
if err = s.updateEndpointUptime(tx, endpointID, result); err != nil {
log.Printf("[sql][Insert] Failed to update uptime for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
}
// Clean up old uptime entries
ageOfOldestUptimeEntry, err := s.getAgeOfOldestServiceUptimeEntry(tx, serviceID)
ageOfOldestUptimeEntry, err := s.getAgeOfOldestEndpointUptimeEntry(tx, endpointID)
if err != nil {
log.Printf("[sql][Insert] Failed to retrieve oldest service uptime entry for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
log.Printf("[sql][Insert] Failed to retrieve oldest endpoint uptime entry for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
} else {
if ageOfOldestUptimeEntry > uptimeCleanUpThreshold {
if err = s.deleteOldUptimeEntries(tx, serviceID, time.Now().Add(-(uptimeRetention + time.Hour))); err != nil {
log.Printf("[sql][Insert] Failed to delete old uptime entries for group=%s; service=%s: %s", service.Group, service.Name, err.Error())
if err = s.deleteOldUptimeEntries(tx, endpointID, time.Now().Add(-(uptimeRetention + time.Hour))); err != nil {
log.Printf("[sql][Insert] Failed to delete old uptime entries for group=%s; endpoint=%s: %s", endpoint.Group, endpoint.Name, err.Error())
}
}
}
@ -328,25 +328,25 @@ func (s *Store) Insert(service *core.Service, result *core.Result) error {
return err
}
// DeleteAllServiceStatusesNotInKeys removes all rows owned by a service whose key is not within the keys provided
func (s *Store) DeleteAllServiceStatusesNotInKeys(keys []string) int {
// DeleteAllEndpointStatusesNotInKeys removes all rows owned by an endpoint whose key is not within the keys provided
func (s *Store) DeleteAllEndpointStatusesNotInKeys(keys []string) int {
var err error
var result sql.Result
if len(keys) == 0 {
// Delete everything
result, err = s.db.Exec("DELETE FROM service")
result, err = s.db.Exec("DELETE FROM endpoints")
} else {
args := make([]interface{}, 0, len(keys))
query := "DELETE FROM service WHERE service_key NOT IN ("
query := "DELETE FROM endpoints WHERE endpoint_key NOT IN ("
for i := range keys {
query += fmt.Sprintf("$%d,", i+1)
args = append(args, keys[i])
}
query = query[:len(query)-1] + ")"
query = query[:len(query)-1] + ")" // Remove the last comma and close the parenthesis
result, err = s.db.Exec(query, args...)
}
if err != nil {
log.Printf("[sql][DeleteAllServiceStatusesNotInKeys] Failed to delete rows that do not belong to any of keys=%v: %s", keys, err.Error())
log.Printf("[sql][DeleteAllEndpointStatusesNotInKeys] Failed to delete rows that do not belong to any of keys=%v: %s", keys, err.Error())
return 0
}
rowsAffects, _ := result.RowsAffected()
@ -355,7 +355,7 @@ func (s *Store) DeleteAllServiceStatusesNotInKeys(keys []string) int {
// Clear deletes everything from the store
func (s *Store) Clear() {
_, _ = s.db.Exec("DELETE FROM service")
_, _ = s.db.Exec("DELETE FROM endpoints")
}
// Save does nothing, because this store is immediately persistent.
@ -368,15 +368,15 @@ func (s *Store) Close() {
_ = s.db.Close()
}
// insertService inserts a service in the store and returns the generated id of said service
func (s *Store) insertService(tx *sql.Tx, service *core.Service) (int64, error) {
//log.Printf("[sql][insertService] Inserting service with group=%s and name=%s", service.Group, service.Name)
// insertEndpoint inserts an endpoint in the store and returns the generated id of said endpoint
func (s *Store) insertEndpoint(tx *sql.Tx, endpoint *core.Endpoint) (int64, error) {
//log.Printf("[sql][insertEndpoint] Inserting endpoint with group=%s and name=%s", endpoint.Group, endpoint.Name)
var id int64
err := tx.QueryRow(
"INSERT INTO service (service_key, service_name, service_group) VALUES ($1, $2, $3) RETURNING service_id",
service.Key(),
service.Name,
service.Group,
"INSERT INTO endpoints (endpoint_key, endpoint_name, endpoint_group) VALUES ($1, $2, $3) RETURNING endpoint_id",
endpoint.Key(),
endpoint.Name,
endpoint.Group,
).Scan(&id)
if err != nil {
return 0, err
@ -384,11 +384,11 @@ func (s *Store) insertService(tx *sql.Tx, service *core.Service) (int64, error)
return id, nil
}
// insertEvent inserts a service event in the store
func (s *Store) insertEvent(tx *sql.Tx, serviceID int64, event *core.Event) error {
// insertEndpointEvent inserts en event in the store
func (s *Store) insertEndpointEvent(tx *sql.Tx, endpointID int64, event *core.Event) error {
_, err := tx.Exec(
"INSERT INTO service_event (service_id, event_type, event_timestamp) VALUES ($1, $2, $3)",
serviceID,
"INSERT INTO endpoint_events (endpoint_id, event_type, event_timestamp) VALUES ($1, $2, $3)",
endpointID,
event.Type,
event.Timestamp.UTC(),
)
@ -398,16 +398,16 @@ func (s *Store) insertEvent(tx *sql.Tx, serviceID int64, event *core.Event) erro
return nil
}
// insertResult inserts a result in the store
func (s *Store) insertResult(tx *sql.Tx, serviceID int64, result *core.Result) error {
var serviceResultID int64
// insertEndpointResult inserts a result in the store
func (s *Store) insertEndpointResult(tx *sql.Tx, endpointID int64, result *core.Result) error {
var endpointResultID int64
err := tx.QueryRow(
`
INSERT INTO service_result (service_id, success, errors, connected, status, dns_rcode, certificate_expiration, hostname, ip, duration, timestamp)
INSERT INTO endpoint_results (endpoint_id, success, errors, connected, status, dns_rcode, certificate_expiration, hostname, ip, duration, timestamp)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING service_result_id
RETURNING endpoint_result_id
`,
serviceID,
endpointID,
result.Success,
strings.Join(result.Errors, arraySeparator),
result.Connected,
@ -418,18 +418,18 @@ func (s *Store) insertResult(tx *sql.Tx, serviceID int64, result *core.Result) e
result.IP,
result.Duration,
result.Timestamp.UTC(),
).Scan(&serviceResultID)
).Scan(&endpointResultID)
if err != nil {
return err
}
return s.insertConditionResults(tx, serviceResultID, result.ConditionResults)
return s.insertConditionResults(tx, endpointResultID, result.ConditionResults)
}
func (s *Store) insertConditionResults(tx *sql.Tx, serviceResultID int64, conditionResults []*core.ConditionResult) error {
func (s *Store) insertConditionResults(tx *sql.Tx, endpointResultID int64, conditionResults []*core.ConditionResult) error {
var err error
for _, cr := range conditionResults {
_, err = tx.Exec("INSERT INTO service_result_condition (service_result_id, condition, success) VALUES ($1, $2, $3)",
serviceResultID,
_, err = tx.Exec("INSERT INTO endpoint_result_conditions (endpoint_result_id, condition, success) VALUES ($1, $2, $3)",
endpointResultID,
cr.Condition,
cr.Success,
)
@ -440,7 +440,7 @@ func (s *Store) insertConditionResults(tx *sql.Tx, serviceResultID int64, condit
return nil
}
func (s *Store) updateServiceUptime(tx *sql.Tx, serviceID int64, result *core.Result) error {
func (s *Store) updateEndpointUptime(tx *sql.Tx, endpointID int64, result *core.Result) error {
unixTimestampFlooredAtHour := result.Timestamp.Truncate(time.Hour).Unix()
var successfulExecutions int
if result.Success {
@ -448,14 +448,14 @@ func (s *Store) updateServiceUptime(tx *sql.Tx, serviceID int64, result *core.Re
}
_, err := tx.Exec(
`
INSERT INTO service_uptime (service_id, hour_unix_timestamp, total_executions, successful_executions, total_response_time)
INSERT INTO endpoint_uptimes (endpoint_id, hour_unix_timestamp, total_executions, successful_executions, total_response_time)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT(service_id, hour_unix_timestamp) DO UPDATE SET
total_executions = excluded.total_executions + service_uptime.total_executions,
successful_executions = excluded.successful_executions + service_uptime.successful_executions,
total_response_time = excluded.total_response_time + service_uptime.total_response_time
ON CONFLICT(endpoint_id, hour_unix_timestamp) DO UPDATE SET
total_executions = excluded.total_executions + endpoint_uptimes.total_executions,
successful_executions = excluded.successful_executions + endpoint_uptimes.successful_executions,
total_response_time = excluded.total_response_time + endpoint_uptimes.total_response_time
`,
serviceID,
endpointID,
unixTimestampFlooredAtHour,
1,
successfulExecutions,
@ -464,8 +464,8 @@ func (s *Store) updateServiceUptime(tx *sql.Tx, serviceID int64, result *core.Re
return err
}
func (s *Store) getAllServiceKeys(tx *sql.Tx) (keys []string, err error) {
rows, err := tx.Query("SELECT service_key FROM service ORDER BY service_key")
func (s *Store) getAllEndpointKeys(tx *sql.Tx) (keys []string, err error) {
rows, err := tx.Query("SELECT endpoint_key FROM endpoints ORDER BY endpoint_key")
if err != nil {
return nil, err
}
@ -477,60 +477,60 @@ func (s *Store) getAllServiceKeys(tx *sql.Tx) (keys []string, err error) {
return
}
func (s *Store) getServiceStatusByKey(tx *sql.Tx, key string, parameters *paging.ServiceStatusParams) (*core.ServiceStatus, error) {
serviceID, serviceGroup, serviceName, err := s.getServiceIDGroupAndNameByKey(tx, key)
func (s *Store) getEndpointStatusByKey(tx *sql.Tx, key string, parameters *paging.EndpointStatusParams) (*core.EndpointStatus, error) {
endpointID, group, endpointName, err := s.getEndpointIDGroupAndNameByKey(tx, key)
if err != nil {
return nil, err
}
serviceStatus := core.NewServiceStatus(key, serviceGroup, serviceName)
endpointStatus := core.NewEndpointStatus(group, endpointName)
if parameters.EventsPageSize > 0 {
if serviceStatus.Events, err = s.getEventsByServiceID(tx, serviceID, parameters.EventsPage, parameters.EventsPageSize); err != nil {
log.Printf("[sql][getServiceStatusByKey] Failed to retrieve events for key=%s: %s", key, err.Error())
if endpointStatus.Events, err = s.getEndpointEventsByEndpointID(tx, endpointID, parameters.EventsPage, parameters.EventsPageSize); err != nil {
log.Printf("[sql][getEndpointStatusByKey] Failed to retrieve events for key=%s: %s", key, err.Error())
}
}
if parameters.ResultsPageSize > 0 {
if serviceStatus.Results, err = s.getResultsByServiceID(tx, serviceID, parameters.ResultsPage, parameters.ResultsPageSize); err != nil {
log.Printf("[sql][getServiceStatusByKey] Failed to retrieve results for key=%s: %s", key, err.Error())
if endpointStatus.Results, err = s.getEndpointResultsByEndpointID(tx, endpointID, parameters.ResultsPage, parameters.ResultsPageSize); err != nil {
log.Printf("[sql][getEndpointStatusByKey] Failed to retrieve results for key=%s: %s", key, err.Error())
}
}
//if parameters.IncludeUptime {
// now := time.Now()
// serviceStatus.Uptime.LastHour, _, err = s.getServiceUptime(tx, serviceID, now.Add(-time.Hour), now)
// serviceStatus.Uptime.LastTwentyFourHours, _, err = s.getServiceUptime(tx, serviceID, now.Add(-24*time.Hour), now)
// serviceStatus.Uptime.LastSevenDays, _, err = s.getServiceUptime(tx, serviceID, now.Add(-7*24*time.Hour), now)
// endpointStatus.Uptime.LastHour, _, err = s.getEndpointUptime(tx, endpointID, now.Add(-time.Hour), now)
// endpointStatus.Uptime.LastTwentyFourHours, _, err = s.getEndpointUptime(tx, endpointID, now.Add(-24*time.Hour), now)
// endpointStatus.Uptime.LastSevenDays, _, err = s.getEndpointUptime(tx, endpointID, now.Add(-7*24*time.Hour), now)
//}
return serviceStatus, nil
return endpointStatus, nil
}
func (s *Store) getServiceIDGroupAndNameByKey(tx *sql.Tx, key string) (id int64, group, name string, err error) {
func (s *Store) getEndpointIDGroupAndNameByKey(tx *sql.Tx, key string) (id int64, group, name string, err error) {
err = tx.QueryRow(
`
SELECT service_id, service_group, service_name
FROM service
WHERE service_key = $1
SELECT endpoint_id, endpoint_group, endpoint_name
FROM endpoints
WHERE endpoint_key = $1
LIMIT 1
`,
key,
).Scan(&id, &group, &name)
if err != nil {
if err == sql.ErrNoRows {
return 0, "", "", common.ErrServiceNotFound
return 0, "", "", common.ErrEndpointNotFound
}
return 0, "", "", err
}
return
}
func (s *Store) getEventsByServiceID(tx *sql.Tx, serviceID int64, page, pageSize int) (events []*core.Event, err error) {
func (s *Store) getEndpointEventsByEndpointID(tx *sql.Tx, endpointID int64, page, pageSize int) (events []*core.Event, err error) {
rows, err := tx.Query(
`
SELECT event_type, event_timestamp
FROM service_event
WHERE service_id = $1
ORDER BY service_event_id ASC
FROM endpoint_events
WHERE endpoint_id = $1
ORDER BY endpoint_event_id ASC
LIMIT $2 OFFSET $3
`,
serviceID,
endpointID,
pageSize,
(page-1)*pageSize,
)
@ -545,16 +545,16 @@ func (s *Store) getEventsByServiceID(tx *sql.Tx, serviceID int64, page, pageSize
return
}
func (s *Store) getResultsByServiceID(tx *sql.Tx, serviceID int64, page, pageSize int) (results []*core.Result, err error) {
func (s *Store) getEndpointResultsByEndpointID(tx *sql.Tx, endpointID int64, page, pageSize int) (results []*core.Result, err error) {
rows, err := tx.Query(
`
SELECT service_result_id, success, errors, connected, status, dns_rcode, certificate_expiration, hostname, ip, duration, timestamp
FROM service_result
WHERE service_id = $1
ORDER BY service_result_id DESC -- Normally, we'd sort by timestamp, but sorting by service_result_id is faster
SELECT endpoint_result_id, success, errors, connected, status, dns_rcode, certificate_expiration, hostname, ip, duration, timestamp
FROM endpoint_results
WHERE endpoint_id = $1
ORDER BY endpoint_result_id DESC -- Normally, we'd sort by timestamp, but sorting by endpoint_result_id is faster
LIMIT $2 OFFSET $3
`,
serviceID,
endpointID,
pageSize,
(page-1)*pageSize,
)
@ -576,13 +576,13 @@ func (s *Store) getResultsByServiceID(tx *sql.Tx, serviceID int64, page, pageSiz
}
// Get condition results
args := make([]interface{}, 0, len(idResultMap))
query := `SELECT service_result_id, condition, success
FROM service_result_condition
WHERE service_result_id IN (`
query := `SELECT endpoint_result_id, condition, success
FROM endpoint_result_conditions
WHERE endpoint_result_id IN (`
index := 1
for serviceResultID := range idResultMap {
for endpointResultID := range idResultMap {
query += fmt.Sprintf("$%d,", index)
args = append(args, serviceResultID)
args = append(args, endpointResultID)
index++
}
query = query[:len(query)-1] + ")"
@ -593,25 +593,25 @@ func (s *Store) getResultsByServiceID(tx *sql.Tx, serviceID int64, page, pageSiz
defer rows.Close() // explicitly defer the close in case an error happens during the scan
for rows.Next() {
conditionResult := &core.ConditionResult{}
var serviceResultID int64
if err = rows.Scan(&serviceResultID, &conditionResult.Condition, &conditionResult.Success); err != nil {
var endpointResultID int64
if err = rows.Scan(&endpointResultID, &conditionResult.Condition, &conditionResult.Success); err != nil {
return
}
idResultMap[serviceResultID].ConditionResults = append(idResultMap[serviceResultID].ConditionResults, conditionResult)
idResultMap[endpointResultID].ConditionResults = append(idResultMap[endpointResultID].ConditionResults, conditionResult)
}
return
}
func (s *Store) getServiceUptime(tx *sql.Tx, serviceID int64, from, to time.Time) (uptime float64, avgResponseTime time.Duration, err error) {
func (s *Store) getEndpointUptime(tx *sql.Tx, endpointID int64, from, to time.Time) (uptime float64, avgResponseTime time.Duration, err error) {
rows, err := tx.Query(
`
SELECT SUM(total_executions), SUM(successful_executions), SUM(total_response_time)
FROM service_uptime
WHERE service_id = $1
FROM endpoint_uptimes
WHERE endpoint_id = $1
AND hour_unix_timestamp >= $2
AND hour_unix_timestamp <= $3
`,
serviceID,
endpointID,
from.Unix(),
to.Unix(),
)
@ -629,17 +629,17 @@ func (s *Store) getServiceUptime(tx *sql.Tx, serviceID int64, from, to time.Time
return
}
func (s *Store) getServiceAverageResponseTime(tx *sql.Tx, serviceID int64, from, to time.Time) (int, error) {
func (s *Store) getEndpointAverageResponseTime(tx *sql.Tx, endpointID int64, from, to time.Time) (int, error) {
rows, err := tx.Query(
`
SELECT SUM(total_executions), SUM(total_response_time)
FROM service_uptime
WHERE service_id = $1
FROM endpoint_uptimes
WHERE endpoint_id = $1
AND total_executions > 0
AND hour_unix_timestamp >= $2
AND hour_unix_timestamp <= $3
`,
serviceID,
endpointID,
from.Unix(),
to.Unix(),
)
@ -656,17 +656,17 @@ func (s *Store) getServiceAverageResponseTime(tx *sql.Tx, serviceID int64, from,
return int(float64(totalResponseTime) / float64(totalExecutions)), nil
}
func (s *Store) getServiceHourlyAverageResponseTimes(tx *sql.Tx, serviceID int64, from, to time.Time) (map[int64]int, error) {
func (s *Store) getEndpointHourlyAverageResponseTimes(tx *sql.Tx, endpointID int64, from, to time.Time) (map[int64]int, error) {
rows, err := tx.Query(
`
SELECT hour_unix_timestamp, total_executions, total_response_time
FROM service_uptime
WHERE service_id = $1
FROM endpoint_uptimes
WHERE endpoint_id = $1
AND total_executions > 0
AND hour_unix_timestamp >= $2
AND hour_unix_timestamp <= $3
`,
serviceID,
endpointID,
from.Unix(),
to.Unix(),
)
@ -683,59 +683,59 @@ func (s *Store) getServiceHourlyAverageResponseTimes(tx *sql.Tx, serviceID int64
return hourlyAverageResponseTimes, nil
}
func (s *Store) getServiceID(tx *sql.Tx, service *core.Service) (int64, error) {
func (s *Store) getEndpointID(tx *sql.Tx, endpoint *core.Endpoint) (int64, error) {
var id int64
err := tx.QueryRow("SELECT service_id FROM service WHERE service_key = $1", service.Key()).Scan(&id)
err := tx.QueryRow("SELECT endpoint_id FROM endpoints WHERE endpoint_key = $1", endpoint.Key()).Scan(&id)
if err != nil {
if err == sql.ErrNoRows {
return 0, common.ErrServiceNotFound
return 0, common.ErrEndpointNotFound
}
return 0, err
}
return id, nil
}
func (s *Store) getNumberOfEventsByServiceID(tx *sql.Tx, serviceID int64) (int64, error) {
func (s *Store) getNumberOfEventsByEndpointID(tx *sql.Tx, endpointID int64) (int64, error) {
var numberOfEvents int64
err := tx.QueryRow("SELECT COUNT(1) FROM service_event WHERE service_id = $1", serviceID).Scan(&numberOfEvents)
err := tx.QueryRow("SELECT COUNT(1) FROM endpoint_events WHERE endpoint_id = $1", endpointID).Scan(&numberOfEvents)
return numberOfEvents, err
}
func (s *Store) getNumberOfResultsByServiceID(tx *sql.Tx, serviceID int64) (int64, error) {
func (s *Store) getNumberOfResultsByEndpointID(tx *sql.Tx, endpointID int64) (int64, error) {
var numberOfResults int64
err := tx.QueryRow("SELECT COUNT(1) FROM service_result WHERE service_id = $1", serviceID).Scan(&numberOfResults)
err := tx.QueryRow("SELECT COUNT(1) FROM endpoint_results WHERE endpoint_id = $1", endpointID).Scan(&numberOfResults)
return numberOfResults, err
}
func (s *Store) getAgeOfOldestServiceUptimeEntry(tx *sql.Tx, serviceID int64) (time.Duration, error) {
func (s *Store) getAgeOfOldestEndpointUptimeEntry(tx *sql.Tx, endpointID int64) (time.Duration, error) {
rows, err := tx.Query(
`
SELECT hour_unix_timestamp
FROM service_uptime
WHERE service_id = $1
FROM endpoint_uptimes
WHERE endpoint_id = $1
ORDER BY hour_unix_timestamp
LIMIT 1
`,
serviceID,
endpointID,
)
if err != nil {
return 0, err
}
var oldestServiceUptimeUnixTimestamp int64
var oldestEndpointUptimeUnixTimestamp int64
var found bool
for rows.Next() {
_ = rows.Scan(&oldestServiceUptimeUnixTimestamp)
_ = rows.Scan(&oldestEndpointUptimeUnixTimestamp)
found = true
}
if !found {
return 0, errNoRowsReturned
}
return time.Since(time.Unix(oldestServiceUptimeUnixTimestamp, 0)), nil
return time.Since(time.Unix(oldestEndpointUptimeUnixTimestamp, 0)), nil
}
func (s *Store) getLastServiceResultSuccessValue(tx *sql.Tx, serviceID int64) (bool, error) {
func (s *Store) getLastEndpointResultSuccessValue(tx *sql.Tx, endpointID int64) (bool, error) {
var success bool
err := tx.QueryRow("SELECT success FROM service_result WHERE service_id = $1 ORDER BY service_result_id DESC LIMIT 1", serviceID).Scan(&success)
err := tx.QueryRow("SELECT success FROM endpoint_results WHERE endpoint_id = $1 ORDER BY endpoint_result_id DESC LIMIT 1", endpointID).Scan(&success)
if err != nil {
if err == sql.ErrNoRows {
return false, errNoRowsReturned
@ -745,47 +745,47 @@ func (s *Store) getLastServiceResultSuccessValue(tx *sql.Tx, serviceID int64) (b
return success, nil
}
// deleteOldServiceEvents deletes old service events that are no longer needed
func (s *Store) deleteOldServiceEvents(tx *sql.Tx, serviceID int64) error {
// deleteOldEndpointEvents deletes endpoint events that are no longer needed
func (s *Store) deleteOldEndpointEvents(tx *sql.Tx, endpointID int64) error {
_, err := tx.Exec(
`
DELETE FROM service_event
WHERE service_id = $1
AND service_event_id NOT IN (
SELECT service_event_id
FROM service_event
WHERE service_id = $1
ORDER BY service_event_id DESC
DELETE FROM endpoint_events
WHERE endpoint_id = $1
AND endpoint_event_id NOT IN (
SELECT endpoint_event_id
FROM endpoint_events
WHERE endpoint_id = $1
ORDER BY endpoint_event_id DESC
LIMIT $2
)
`,
serviceID,
endpointID,
common.MaximumNumberOfEvents,
)
return err
}
// deleteOldServiceResults deletes old service results that are no longer needed
func (s *Store) deleteOldServiceResults(tx *sql.Tx, serviceID int64) error {
// deleteOldEndpointResults deletes endpoint results that are no longer needed
func (s *Store) deleteOldEndpointResults(tx *sql.Tx, endpointID int64) error {
_, err := tx.Exec(
`
DELETE FROM service_result
WHERE service_id = $1
AND service_result_id NOT IN (
SELECT service_result_id
FROM service_result
WHERE service_id = $1
ORDER BY service_result_id DESC
DELETE FROM endpoint_results
WHERE endpoint_id = $1
AND endpoint_result_id NOT IN (
SELECT endpoint_result_id
FROM endpoint_results
WHERE endpoint_id = $1
ORDER BY endpoint_result_id DESC
LIMIT $2
)
`,
serviceID,
endpointID,
common.MaximumNumberOfResults,
)
return err
}
func (s *Store) deleteOldUptimeEntries(tx *sql.Tx, serviceID int64, maxAge time.Time) error {
_, err := tx.Exec("DELETE FROM service_uptime WHERE service_id = $1 AND hour_unix_timestamp < $2", serviceID, maxAge.Unix())
func (s *Store) deleteOldUptimeEntries(tx *sql.Tx, endpointID int64, maxAge time.Time) error {
_, err := tx.Exec("DELETE FROM endpoint_uptimes WHERE endpoint_id = $1 AND hour_unix_timestamp < $2", endpointID, maxAge.Unix())
return err
}

View File

@ -16,7 +16,7 @@ var (
now = time.Now()
testService = core.Service{
testEndpoint = core.Endpoint{
Name: "name",
Group: "group",
URL: "https://example.org/what/ever",
@ -100,54 +100,54 @@ func TestStore_InsertCleansUpOldUptimeEntriesProperly(t *testing.T) {
now := time.Now().Round(time.Minute)
now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
store.Insert(&testService, &core.Result{Timestamp: now.Add(-5 * time.Hour), Success: true})
store.Insert(&testEndpoint, &core.Result{Timestamp: now.Add(-5 * time.Hour), Success: true})
tx, _ := store.db.Begin()
oldest, _ := store.getAgeOfOldestServiceUptimeEntry(tx, 1)
oldest, _ := store.getAgeOfOldestEndpointUptimeEntry(tx, 1)
_ = tx.Commit()
if oldest.Truncate(time.Hour) != 5*time.Hour {
t.Errorf("oldest service uptime entry should've been ~5 hours old, was %s", oldest)
t.Errorf("oldest endpoint uptime entry should've been ~5 hours old, was %s", oldest)
}
// The oldest cache entry should remain at ~5 hours old, because this entry is more recent
store.Insert(&testService, &core.Result{Timestamp: now.Add(-3 * time.Hour), Success: true})
store.Insert(&testEndpoint, &core.Result{Timestamp: now.Add(-3 * time.Hour), Success: true})
tx, _ = store.db.Begin()
oldest, _ = store.getAgeOfOldestServiceUptimeEntry(tx, 1)
oldest, _ = store.getAgeOfOldestEndpointUptimeEntry(tx, 1)
_ = tx.Commit()
if oldest.Truncate(time.Hour) != 5*time.Hour {
t.Errorf("oldest service uptime entry should've been ~5 hours old, was %s", oldest)
t.Errorf("oldest endpoint uptime entry should've been ~5 hours old, was %s", oldest)
}
// The oldest cache entry should now become at ~8 hours old, because this entry is older
store.Insert(&testService, &core.Result{Timestamp: now.Add(-8 * time.Hour), Success: true})
store.Insert(&testEndpoint, &core.Result{Timestamp: now.Add(-8 * time.Hour), Success: true})
tx, _ = store.db.Begin()
oldest, _ = store.getAgeOfOldestServiceUptimeEntry(tx, 1)
oldest, _ = store.getAgeOfOldestEndpointUptimeEntry(tx, 1)
_ = tx.Commit()
if oldest.Truncate(time.Hour) != 8*time.Hour {
t.Errorf("oldest service uptime entry should've been ~8 hours old, was %s", oldest)
t.Errorf("oldest endpoint uptime entry should've been ~8 hours old, was %s", oldest)
}
// Since this is one hour before reaching the clean up threshold, the oldest entry should now be this one
store.Insert(&testService, &core.Result{Timestamp: now.Add(-(uptimeCleanUpThreshold - time.Hour)), Success: true})
store.Insert(&testEndpoint, &core.Result{Timestamp: now.Add(-(uptimeCleanUpThreshold - time.Hour)), Success: true})
tx, _ = store.db.Begin()
oldest, _ = store.getAgeOfOldestServiceUptimeEntry(tx, 1)
oldest, _ = store.getAgeOfOldestEndpointUptimeEntry(tx, 1)
_ = tx.Commit()
if oldest.Truncate(time.Hour) != uptimeCleanUpThreshold-time.Hour {
t.Errorf("oldest service uptime entry should've been ~%s hours old, was %s", uptimeCleanUpThreshold-time.Hour, oldest)
t.Errorf("oldest endpoint uptime entry should've been ~%s hours old, was %s", uptimeCleanUpThreshold-time.Hour, oldest)
}
// Since this entry is after the uptimeCleanUpThreshold, both this entry as well as the previous
// one should be deleted since they both surpass uptimeRetention
store.Insert(&testService, &core.Result{Timestamp: now.Add(-(uptimeCleanUpThreshold + time.Hour)), Success: true})
store.Insert(&testEndpoint, &core.Result{Timestamp: now.Add(-(uptimeCleanUpThreshold + time.Hour)), Success: true})
tx, _ = store.db.Begin()
oldest, _ = store.getAgeOfOldestServiceUptimeEntry(tx, 1)
oldest, _ = store.getAgeOfOldestEndpointUptimeEntry(tx, 1)
_ = tx.Commit()
if oldest.Truncate(time.Hour) != 8*time.Hour {
t.Errorf("oldest service uptime entry should've been ~8 hours old, was %s", oldest)
t.Errorf("oldest endpoint uptime entry should've been ~8 hours old, was %s", oldest)
}
}
@ -155,9 +155,9 @@ func TestStore_InsertCleansUpEventsAndResultsProperly(t *testing.T) {
store, _ := NewStore("sqlite", t.TempDir()+"/TestStore_InsertCleansUpEventsAndResultsProperly.db")
defer store.Close()
for i := 0; i < resultsCleanUpThreshold+eventsCleanUpThreshold; i++ {
store.Insert(&testService, &testSuccessfulResult)
store.Insert(&testService, &testUnsuccessfulResult)
ss, _ := store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithResults(1, common.MaximumNumberOfResults*5).WithEvents(1, common.MaximumNumberOfEvents*5))
store.Insert(&testEndpoint, &testSuccessfulResult)
store.Insert(&testEndpoint, &testUnsuccessfulResult)
ss, _ := store.GetEndpointStatusByKey(testEndpoint.Key(), paging.NewEndpointStatusParams().WithResults(1, common.MaximumNumberOfResults*5).WithEvents(1, common.MaximumNumberOfEvents*5))
if len(ss.Results) > resultsCleanUpThreshold+1 {
t.Errorf("number of results shouldn't have exceeded %d, reached %d", resultsCleanUpThreshold, len(ss.Results))
}
@ -171,18 +171,18 @@ func TestStore_InsertCleansUpEventsAndResultsProperly(t *testing.T) {
func TestStore_Persistence(t *testing.T) {
file := t.TempDir() + "/TestStore_Persistence.db"
store, _ := NewStore("sqlite", file)
store.Insert(&testService, &testSuccessfulResult)
store.Insert(&testService, &testUnsuccessfulResult)
if uptime, _ := store.GetUptimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); uptime != 0.5 {
store.Insert(&testEndpoint, &testSuccessfulResult)
store.Insert(&testEndpoint, &testUnsuccessfulResult)
if uptime, _ := store.GetUptimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); uptime != 0.5 {
t.Errorf("the uptime over the past 1h should've been 0.5, got %f", uptime)
}
if uptime, _ := store.GetUptimeByKey(testService.Key(), time.Now().Add(-time.Hour*24), time.Now()); uptime != 0.5 {
if uptime, _ := store.GetUptimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour*24), time.Now()); uptime != 0.5 {
t.Errorf("the uptime over the past 24h should've been 0.5, got %f", uptime)
}
if uptime, _ := store.GetUptimeByKey(testService.Key(), time.Now().Add(-time.Hour*24*7), time.Now()); uptime != 0.5 {
if uptime, _ := store.GetUptimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour*24*7), time.Now()); uptime != 0.5 {
t.Errorf("the uptime over the past 7d should've been 0.5, got %f", uptime)
}
ssFromOldStore, _ := store.GetServiceStatus(testService.Group, testService.Name, paging.NewServiceStatusParams().WithResults(1, common.MaximumNumberOfResults).WithEvents(1, common.MaximumNumberOfEvents))
ssFromOldStore, _ := store.GetEndpointStatus(testEndpoint.Group, testEndpoint.Name, paging.NewEndpointStatusParams().WithResults(1, common.MaximumNumberOfResults).WithEvents(1, common.MaximumNumberOfEvents))
if ssFromOldStore == nil || ssFromOldStore.Group != "group" || ssFromOldStore.Name != "name" || len(ssFromOldStore.Events) != 3 || len(ssFromOldStore.Results) != 2 {
store.Close()
t.Fatal("sanity check failed")
@ -190,7 +190,7 @@ func TestStore_Persistence(t *testing.T) {
store.Close()
store, _ = NewStore("sqlite", file)
defer store.Close()
ssFromNewStore, _ := store.GetServiceStatus(testService.Group, testService.Name, paging.NewServiceStatusParams().WithResults(1, common.MaximumNumberOfResults).WithEvents(1, common.MaximumNumberOfEvents))
ssFromNewStore, _ := store.GetEndpointStatus(testEndpoint.Group, testEndpoint.Name, paging.NewEndpointStatusParams().WithResults(1, common.MaximumNumberOfResults).WithEvents(1, common.MaximumNumberOfEvents))
if ssFromNewStore == nil || ssFromNewStore.Group != "group" || ssFromNewStore.Name != "name" || len(ssFromNewStore.Events) != 3 || len(ssFromNewStore.Results) != 2 {
t.Fatal("failed sanity check")
}
@ -264,42 +264,42 @@ func TestStore_Save(t *testing.T) {
func TestStore_SanityCheck(t *testing.T) {
store, _ := NewStore("sqlite", t.TempDir()+"/TestStore_SanityCheck.db")
defer store.Close()
store.Insert(&testService, &testSuccessfulResult)
serviceStatuses, _ := store.GetAllServiceStatuses(paging.NewServiceStatusParams())
if numberOfServiceStatuses := len(serviceStatuses); numberOfServiceStatuses != 1 {
t.Fatalf("expected 1 ServiceStatus, got %d", numberOfServiceStatuses)
store.Insert(&testEndpoint, &testSuccessfulResult)
endpointStatuses, _ := store.GetAllEndpointStatuses(paging.NewEndpointStatusParams())
if numberOfEndpointStatuses := len(endpointStatuses); numberOfEndpointStatuses != 1 {
t.Fatalf("expected 1 EndpointStatus, got %d", numberOfEndpointStatuses)
}
store.Insert(&testService, &testUnsuccessfulResult)
// Both results inserted are for the same service, therefore, the count shouldn't have increased
serviceStatuses, _ = store.GetAllServiceStatuses(paging.NewServiceStatusParams())
if numberOfServiceStatuses := len(serviceStatuses); numberOfServiceStatuses != 1 {
t.Fatalf("expected 1 ServiceStatus, got %d", numberOfServiceStatuses)
store.Insert(&testEndpoint, &testUnsuccessfulResult)
// Both results inserted are for the same endpoint, therefore, the count shouldn't have increased
endpointStatuses, _ = store.GetAllEndpointStatuses(paging.NewEndpointStatusParams())
if numberOfEndpointStatuses := len(endpointStatuses); numberOfEndpointStatuses != 1 {
t.Fatalf("expected 1 EndpointStatus, got %d", numberOfEndpointStatuses)
}
if hourlyAverageResponseTime, err := store.GetHourlyAverageResponseTimeByKey(testService.Key(), time.Now().Add(-24*time.Hour), time.Now()); err != nil {
if hourlyAverageResponseTime, err := store.GetHourlyAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-24*time.Hour), time.Now()); err != nil {
t.Errorf("expected no error, got %v", err)
} else if len(hourlyAverageResponseTime) != 1 {
t.Errorf("expected 1 hour to have had a result in the past 24 hours, got %d", len(hourlyAverageResponseTime))
}
if uptime, _ := store.GetUptimeByKey(testService.Key(), time.Now().Add(-24*time.Hour), time.Now()); uptime != 0.5 {
if uptime, _ := store.GetUptimeByKey(testEndpoint.Key(), time.Now().Add(-24*time.Hour), time.Now()); uptime != 0.5 {
t.Errorf("expected uptime of last 24h to be 0.5, got %f", uptime)
}
if averageResponseTime, _ := store.GetAverageResponseTimeByKey(testService.Key(), time.Now().Add(-24*time.Hour), time.Now()); averageResponseTime != 450 {
if averageResponseTime, _ := store.GetAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-24*time.Hour), time.Now()); averageResponseTime != 450 {
t.Errorf("expected average response time of last 24h to be 450, got %d", averageResponseTime)
}
ss, _ := store.GetServiceStatus(testService.Group, testService.Name, paging.NewServiceStatusParams().WithResults(1, 20).WithEvents(1, 20))
ss, _ := store.GetEndpointStatus(testEndpoint.Group, testEndpoint.Name, paging.NewEndpointStatusParams().WithResults(1, 20).WithEvents(1, 20))
if ss == nil {
t.Fatalf("Store should've had key '%s', but didn't", testService.Key())
t.Fatalf("Store should've had key '%s', but didn't", testEndpoint.Key())
}
if len(ss.Events) != 3 {
t.Errorf("Service '%s' should've had 3 events, got %d", ss.Name, len(ss.Events))
t.Errorf("Endpoint '%s' should've had 3 events, got %d", ss.Name, len(ss.Events))
}
if len(ss.Results) != 2 {
t.Errorf("Service '%s' should've had 2 results, got %d", ss.Name, len(ss.Results))
t.Errorf("Endpoint '%s' should've had 2 results, got %d", ss.Name, len(ss.Results))
}
if deleted := store.DeleteAllServiceStatusesNotInKeys([]string{"invalid-key-which-means-everything-should-get-deleted"}); deleted != 1 {
if deleted := store.DeleteAllEndpointStatusesNotInKeys([]string{"invalid-key-which-means-everything-should-get-deleted"}); deleted != 1 {
t.Errorf("%d entries should've been deleted, got %d", 1, deleted)
}
if deleted := store.DeleteAllServiceStatusesNotInKeys([]string{}); deleted != 0 {
if deleted := store.DeleteAllEndpointStatusesNotInKeys([]string{}); deleted != 0 {
t.Errorf("There should've been no entries left to delete, got %d", deleted)
}
}
@ -310,55 +310,55 @@ func TestStore_InvalidTransaction(t *testing.T) {
defer store.Close()
tx, _ := store.db.Begin()
tx.Commit()
if _, err := store.insertService(tx, &testService); err == nil {
if _, err := store.insertEndpoint(tx, &testEndpoint); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if err := store.insertEvent(tx, 1, core.NewEventFromResult(&testSuccessfulResult)); err == nil {
if err := store.insertEndpointEvent(tx, 1, core.NewEventFromResult(&testSuccessfulResult)); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if err := store.insertResult(tx, 1, &testSuccessfulResult); err == nil {
if err := store.insertEndpointResult(tx, 1, &testSuccessfulResult); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if err := store.insertConditionResults(tx, 1, testSuccessfulResult.ConditionResults); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if err := store.updateServiceUptime(tx, 1, &testSuccessfulResult); err == nil {
if err := store.updateEndpointUptime(tx, 1, &testSuccessfulResult); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getAllServiceKeys(tx); err == nil {
if _, err := store.getAllEndpointKeys(tx); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getServiceStatusByKey(tx, testService.Key(), paging.NewServiceStatusParams().WithResults(1, 20)); err == nil {
if _, err := store.getEndpointStatusByKey(tx, testEndpoint.Key(), paging.NewEndpointStatusParams().WithResults(1, 20)); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getEventsByServiceID(tx, 1, 1, 50); err == nil {
if _, err := store.getEndpointEventsByEndpointID(tx, 1, 1, 50); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getResultsByServiceID(tx, 1, 1, 50); err == nil {
if _, err := store.getEndpointResultsByEndpointID(tx, 1, 1, 50); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if err := store.deleteOldServiceEvents(tx, 1); err == nil {
if err := store.deleteOldEndpointEvents(tx, 1); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if err := store.deleteOldServiceResults(tx, 1); err == nil {
if err := store.deleteOldEndpointResults(tx, 1); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, _, err := store.getServiceUptime(tx, 1, time.Now(), time.Now()); err == nil {
if _, _, err := store.getEndpointUptime(tx, 1, time.Now(), time.Now()); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getServiceID(tx, &testService); err == nil {
if _, err := store.getEndpointID(tx, &testEndpoint); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getNumberOfEventsByServiceID(tx, 1); err == nil {
if _, err := store.getNumberOfEventsByEndpointID(tx, 1); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getNumberOfResultsByServiceID(tx, 1); err == nil {
if _, err := store.getNumberOfResultsByEndpointID(tx, 1); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getAgeOfOldestServiceUptimeEntry(tx, 1); err == nil {
if _, err := store.getAgeOfOldestEndpointUptimeEntry(tx, 1); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
if _, err := store.getLastServiceResultSuccessValue(tx, 1); err == nil {
if _, err := store.getLastEndpointResultSuccessValue(tx, 1); err == nil {
t.Error("should've returned an error, because the transaction was already committed")
}
}
@ -368,10 +368,10 @@ func TestStore_NoRows(t *testing.T) {
defer store.Close()
tx, _ := store.db.Begin()
defer tx.Rollback()
if _, err := store.getLastServiceResultSuccessValue(tx, 1); err != errNoRowsReturned {
if _, err := store.getLastEndpointResultSuccessValue(tx, 1); err != errNoRowsReturned {
t.Errorf("should've %v, got %v", errNoRowsReturned, err)
}
if _, err := store.getAgeOfOldestServiceUptimeEntry(tx, 1); err != errNoRowsReturned {
if _, err := store.getAgeOfOldestEndpointUptimeEntry(tx, 1); err != errNoRowsReturned {
t.Errorf("should've %v, got %v", errNoRowsReturned, err)
}
}
@ -380,33 +380,33 @@ func TestStore_NoRows(t *testing.T) {
func TestStore_BrokenSchema(t *testing.T) {
store, _ := NewStore("sqlite", t.TempDir()+"/TestStore_BrokenSchema.db")
defer store.Close()
if err := store.Insert(&testService, &testSuccessfulResult); err != nil {
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err != nil {
t.Fatal("expected no error, got", err.Error())
}
if _, err := store.GetAverageResponseTimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); err != nil {
if _, err := store.GetAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); err != nil {
t.Fatal("expected no error, got", err.Error())
}
if _, err := store.GetAllServiceStatuses(paging.NewServiceStatusParams()); err != nil {
if _, err := store.GetAllEndpointStatuses(paging.NewEndpointStatusParams()); err != nil {
t.Fatal("expected no error, got", err.Error())
}
// Break
_, _ = store.db.Exec("DROP TABLE service")
if err := store.Insert(&testService, &testSuccessfulResult); err == nil {
_, _ = store.db.Exec("DROP TABLE endpoints")
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err == nil {
t.Fatal("expected an error")
}
if _, err := store.GetAverageResponseTimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
if _, err := store.GetAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
t.Fatal("expected an error")
}
if _, err := store.GetHourlyAverageResponseTimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
if _, err := store.GetHourlyAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
t.Fatal("expected an error")
}
if _, err := store.GetAllServiceStatuses(paging.NewServiceStatusParams()); err == nil {
if _, err := store.GetAllEndpointStatuses(paging.NewEndpointStatusParams()); err == nil {
t.Fatal("expected an error")
}
if _, err := store.GetUptimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
if _, err := store.GetUptimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
t.Fatal("expected an error")
}
if _, err := store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams()); err == nil {
if _, err := store.GetEndpointStatusByKey(testEndpoint.Key(), paging.NewEndpointStatusParams()); err == nil {
t.Fatal("expected an error")
}
// Repair
@ -414,15 +414,15 @@ func TestStore_BrokenSchema(t *testing.T) {
t.Fatal("schema should've been repaired")
}
store.Clear()
if err := store.Insert(&testService, &testSuccessfulResult); err != nil {
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err != nil {
t.Fatal("expected no error, got", err.Error())
}
// Break
_, _ = store.db.Exec("DROP TABLE service_event")
if err := store.Insert(&testService, &testSuccessfulResult); err != nil {
_, _ = store.db.Exec("DROP TABLE endpoint_events")
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err != nil {
t.Fatal("expected no error, because this should silently fails, got", err.Error())
}
if _, err := store.GetAllServiceStatuses(paging.NewServiceStatusParams().WithResults(1, 1).WithEvents(1, 1)); err != nil {
if _, err := store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 1).WithEvents(1, 1)); err != nil {
t.Fatal("expected no error, because this should silently fail, got", err.Error())
}
// Repair
@ -430,15 +430,15 @@ func TestStore_BrokenSchema(t *testing.T) {
t.Fatal("schema should've been repaired")
}
store.Clear()
if err := store.Insert(&testService, &testSuccessfulResult); err != nil {
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err != nil {
t.Fatal("expected no error, got", err.Error())
}
// Break
_, _ = store.db.Exec("DROP TABLE service_result")
if err := store.Insert(&testService, &testSuccessfulResult); err == nil {
_, _ = store.db.Exec("DROP TABLE endpoint_results")
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err == nil {
t.Fatal("expected an error")
}
if _, err := store.GetAllServiceStatuses(paging.NewServiceStatusParams().WithResults(1, 1).WithEvents(1, 1)); err != nil {
if _, err := store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 1).WithEvents(1, 1)); err != nil {
t.Fatal("expected no error, because this should silently fail, got", err.Error())
}
// Repair
@ -446,12 +446,12 @@ func TestStore_BrokenSchema(t *testing.T) {
t.Fatal("schema should've been repaired")
}
store.Clear()
if err := store.Insert(&testService, &testSuccessfulResult); err != nil {
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err != nil {
t.Fatal("expected no error, got", err.Error())
}
// Break
_, _ = store.db.Exec("DROP TABLE service_result_condition")
if err := store.Insert(&testService, &testSuccessfulResult); err == nil {
_, _ = store.db.Exec("DROP TABLE endpoint_result_conditions")
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err == nil {
t.Fatal("expected an error")
}
// Repair
@ -459,21 +459,21 @@ func TestStore_BrokenSchema(t *testing.T) {
t.Fatal("schema should've been repaired")
}
store.Clear()
if err := store.Insert(&testService, &testSuccessfulResult); err != nil {
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err != nil {
t.Fatal("expected no error, got", err.Error())
}
// Break
_, _ = store.db.Exec("DROP TABLE service_uptime")
if err := store.Insert(&testService, &testSuccessfulResult); err != nil {
_, _ = store.db.Exec("DROP TABLE endpoint_uptimes")
if err := store.Insert(&testEndpoint, &testSuccessfulResult); err != nil {
t.Fatal("expected no error, because this should silently fails, got", err.Error())
}
if _, err := store.GetAverageResponseTimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
if _, err := store.GetAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
t.Fatal("expected an error")
}
if _, err := store.GetHourlyAverageResponseTimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
if _, err := store.GetHourlyAverageResponseTimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
t.Fatal("expected an error")
}
if _, err := store.GetUptimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
if _, err := store.GetUptimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); err == nil {
t.Fatal("expected an error")
}
}

View File

@ -11,15 +11,15 @@ import (
// Store is the interface that each stores should implement
type Store interface {
// GetAllServiceStatuses returns the JSON encoding of all monitored core.ServiceStatus
// GetAllEndpointStatuses returns the JSON encoding of all monitored core.EndpointStatus
// with a subset of core.Result defined by the page and pageSize parameters
GetAllServiceStatuses(params *paging.ServiceStatusParams) ([]*core.ServiceStatus, error)
GetAllEndpointStatuses(params *paging.EndpointStatusParams) ([]*core.EndpointStatus, error)
// GetServiceStatus returns the service status for a given service name in the given group
GetServiceStatus(groupName, serviceName string, params *paging.ServiceStatusParams) (*core.ServiceStatus, error)
// GetEndpointStatus returns the endpoint status for a given endpoint name in the given group
GetEndpointStatus(groupName, endpointName string, params *paging.EndpointStatusParams) (*core.EndpointStatus, error)
// GetServiceStatusByKey returns the service status for a given key
GetServiceStatusByKey(key string, params *paging.ServiceStatusParams) (*core.ServiceStatus, error)
// GetEndpointStatusByKey returns the endpoint status for a given key
GetEndpointStatusByKey(key string, params *paging.EndpointStatusParams) (*core.EndpointStatus, error)
// GetUptimeByKey returns the uptime percentage during a time range
GetUptimeByKey(key string, from, to time.Time) (float64, error)
@ -30,13 +30,13 @@ type Store interface {
// GetHourlyAverageResponseTimeByKey returns a map of hourly (key) average response time in milliseconds (value) during a time range
GetHourlyAverageResponseTimeByKey(key string, from, to time.Time) (map[int64]int, error)
// Insert adds the observed result for the specified service into the store
Insert(service *core.Service, result *core.Result) error
// Insert adds the observed result for the specified endpoint into the store
Insert(endpoint *core.Endpoint, result *core.Result) error
// DeleteAllServiceStatusesNotInKeys removes all ServiceStatus that are not within the keys provided
// DeleteAllEndpointStatusesNotInKeys removes all EndpointStatus that are not within the keys provided
//
// Used to delete services that have been persisted but are no longer part of the configured services
DeleteAllServiceStatusesNotInKeys(keys []string) int
// Used to delete endpoints that have been persisted but are no longer part of the configured endpoints
DeleteAllEndpointStatusesNotInKeys(keys []string) int
// Clear deletes everything from the store
Clear()

View File

@ -10,12 +10,12 @@ import (
"github.com/TwiN/gatus/v3/storage/store/sql"
)
func BenchmarkStore_GetAllServiceStatuses(b *testing.B) {
func BenchmarkStore_GetAllEndpointStatuses(b *testing.B) {
memoryStore, err := memory.NewStore("")
if err != nil {
b.Fatal("failed to create store:", err.Error())
}
sqliteStore, err := sql.NewStore("sqlite", b.TempDir()+"/BenchmarkStore_GetAllServiceStatuses.db")
sqliteStore, err := sql.NewStore("sqlite", b.TempDir()+"/BenchmarkStore_GetAllEndpointStatuses.db")
if err != nil {
b.Fatal("failed to create store:", err.Error())
}
@ -48,18 +48,18 @@ func BenchmarkStore_GetAllServiceStatuses(b *testing.B) {
},
}
for _, scenario := range scenarios {
scenario.Store.Insert(&testService, &testSuccessfulResult)
scenario.Store.Insert(&testService, &testUnsuccessfulResult)
scenario.Store.Insert(&testEndpoint, &testSuccessfulResult)
scenario.Store.Insert(&testEndpoint, &testUnsuccessfulResult)
b.Run(scenario.Name, func(b *testing.B) {
if scenario.Parallel {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
scenario.Store.GetAllServiceStatuses(paging.NewServiceStatusParams().WithResults(1, 20))
scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
}
})
} else {
for n := 0; n < b.N; n++ {
scenario.Store.GetAllServiceStatuses(paging.NewServiceStatusParams().WithResults(1, 20))
scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
}
}
b.ReportAllocs()
@ -118,7 +118,7 @@ func BenchmarkStore_Insert(b *testing.B) {
result = testSuccessfulResult
}
result.Timestamp = time.Now()
scenario.Store.Insert(&testService, &result)
scenario.Store.Insert(&testEndpoint, &result)
n++
}
})
@ -131,7 +131,7 @@ func BenchmarkStore_Insert(b *testing.B) {
result = testSuccessfulResult
}
result.Timestamp = time.Now()
scenario.Store.Insert(&testService, &result)
scenario.Store.Insert(&testEndpoint, &result)
}
}
b.ReportAllocs()
@ -140,12 +140,12 @@ func BenchmarkStore_Insert(b *testing.B) {
}
}
func BenchmarkStore_GetServiceStatusByKey(b *testing.B) {
func BenchmarkStore_GetEndpointStatusByKey(b *testing.B) {
memoryStore, err := memory.NewStore("")
if err != nil {
b.Fatal("failed to create store:", err.Error())
}
sqliteStore, err := sql.NewStore("sqlite", b.TempDir()+"/BenchmarkStore_GetServiceStatusByKey.db")
sqliteStore, err := sql.NewStore("sqlite", b.TempDir()+"/BenchmarkStore_GetEndpointStatusByKey.db")
if err != nil {
b.Fatal("failed to create store:", err.Error())
}
@ -179,19 +179,19 @@ func BenchmarkStore_GetServiceStatusByKey(b *testing.B) {
}
for _, scenario := range scenarios {
for i := 0; i < 50; i++ {
scenario.Store.Insert(&testService, &testSuccessfulResult)
scenario.Store.Insert(&testService, &testUnsuccessfulResult)
scenario.Store.Insert(&testEndpoint, &testSuccessfulResult)
scenario.Store.Insert(&testEndpoint, &testUnsuccessfulResult)
}
b.Run(scenario.Name, func(b *testing.B) {
if scenario.Parallel {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithResults(1, 20))
scenario.Store.GetEndpointStatusByKey(testEndpoint.Key(), paging.NewEndpointStatusParams().WithResults(1, 20))
}
})
} else {
for n := 0; n < b.N; n++ {
scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithResults(1, 20))
scenario.Store.GetEndpointStatusByKey(testEndpoint.Key(), paging.NewEndpointStatusParams().WithResults(1, 20))
}
}
b.ReportAllocs()

View File

@ -18,7 +18,7 @@ var (
now = time.Now().Truncate(time.Hour)
testService = core.Service{
testEndpoint = core.Endpoint{
Name: "name",
Group: "group",
URL: "https://example.org/what/ever",
@ -114,8 +114,8 @@ func cleanUp(scenarios []*Scenario) {
}
}
func TestStore_GetServiceStatusByKey(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetServiceStatusByKey")
func TestStore_GetEndpointStatusByKey(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetEndpointStatusByKey")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
firstResult.Timestamp = now.Add(-time.Minute)
@ -123,25 +123,25 @@ func TestStore_GetServiceStatusByKey(t *testing.T) {
secondResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
serviceStatus, err := scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
scenario.Store.Insert(&testEndpoint, &firstResult)
scenario.Store.Insert(&testEndpoint, &secondResult)
endpointStatus, err := scenario.Store.GetEndpointStatusByKey(testEndpoint.Key(), paging.NewEndpointStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
if err != nil {
t.Fatal("shouldn't have returned an error, got", err.Error())
}
if serviceStatus == nil {
t.Fatalf("serviceStatus shouldn't have been nil")
if endpointStatus == nil {
t.Fatalf("endpointStatus shouldn't have been nil")
}
if serviceStatus.Name != testService.Name {
t.Fatalf("serviceStatus.Name should've been %s, got %s", testService.Name, serviceStatus.Name)
if endpointStatus.Name != testEndpoint.Name {
t.Fatalf("endpointStatus.Name should've been %s, got %s", testEndpoint.Name, endpointStatus.Name)
}
if serviceStatus.Group != testService.Group {
t.Fatalf("serviceStatus.Group should've been %s, got %s", testService.Group, serviceStatus.Group)
if endpointStatus.Group != testEndpoint.Group {
t.Fatalf("endpointStatus.Group should've been %s, got %s", testEndpoint.Group, endpointStatus.Group)
}
if len(serviceStatus.Results) != 2 {
t.Fatalf("serviceStatus.Results should've had 2 entries")
if len(endpointStatus.Results) != 2 {
t.Fatalf("endpointStatus.Results should've had 2 entries")
}
if serviceStatus.Results[0].Timestamp.After(serviceStatus.Results[1].Timestamp) {
if endpointStatus.Results[0].Timestamp.After(endpointStatus.Results[1].Timestamp) {
t.Error("The result at index 0 should've been older than the result at index 1")
}
scenario.Store.Clear()
@ -149,57 +149,57 @@ func TestStore_GetServiceStatusByKey(t *testing.T) {
}
}
func TestStore_GetServiceStatusForMissingStatusReturnsNil(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetServiceStatusForMissingStatusReturnsNil")
func TestStore_GetEndpointStatusForMissingStatusReturnsNil(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetEndpointStatusForMissingStatusReturnsNil")
defer cleanUp(scenarios)
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &testSuccessfulResult)
serviceStatus, err := scenario.Store.GetServiceStatus("nonexistantgroup", "nonexistantname", paging.NewServiceStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
if err != common.ErrServiceNotFound {
t.Error("should've returned ErrServiceNotFound, got", err)
scenario.Store.Insert(&testEndpoint, &testSuccessfulResult)
endpointStatus, err := scenario.Store.GetEndpointStatus("nonexistantgroup", "nonexistantname", paging.NewEndpointStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
if err != common.ErrEndpointNotFound {
t.Error("should've returned ErrEndpointNotFound, got", err)
}
if serviceStatus != nil {
t.Errorf("Returned service status for group '%s' and name '%s' not nil after inserting the service into the store", testService.Group, testService.Name)
if endpointStatus != nil {
t.Errorf("Returned endpoint status for group '%s' and name '%s' not nil after inserting the endpoint into the store", testEndpoint.Group, testEndpoint.Name)
}
serviceStatus, err = scenario.Store.GetServiceStatus(testService.Group, "nonexistantname", paging.NewServiceStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
if err != common.ErrServiceNotFound {
t.Error("should've returned ErrServiceNotFound, got", err)
endpointStatus, err = scenario.Store.GetEndpointStatus(testEndpoint.Group, "nonexistantname", paging.NewEndpointStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
if err != common.ErrEndpointNotFound {
t.Error("should've returned ErrEndpointNotFound, got", err)
}
if serviceStatus != nil {
t.Errorf("Returned service status for group '%s' and name '%s' not nil after inserting the service into the store", testService.Group, "nonexistantname")
if endpointStatus != nil {
t.Errorf("Returned endpoint status for group '%s' and name '%s' not nil after inserting the endpoint into the store", testEndpoint.Group, "nonexistantname")
}
serviceStatus, err = scenario.Store.GetServiceStatus("nonexistantgroup", testService.Name, paging.NewServiceStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
if err != common.ErrServiceNotFound {
t.Error("should've returned ErrServiceNotFound, got", err)
endpointStatus, err = scenario.Store.GetEndpointStatus("nonexistantgroup", testEndpoint.Name, paging.NewEndpointStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
if err != common.ErrEndpointNotFound {
t.Error("should've returned ErrEndpointNotFound, got", err)
}
if serviceStatus != nil {
t.Errorf("Returned service status for group '%s' and name '%s' not nil after inserting the service into the store", "nonexistantgroup", testService.Name)
if endpointStatus != nil {
t.Errorf("Returned endpoint status for group '%s' and name '%s' not nil after inserting the endpoint into the store", "nonexistantgroup", testEndpoint.Name)
}
})
}
}
func TestStore_GetAllServiceStatuses(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetAllServiceStatuses")
func TestStore_GetAllEndpointStatuses(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetAllEndpointStatuses")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
secondResult := testUnsuccessfulResult
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
scenario.Store.Insert(&testEndpoint, &firstResult)
scenario.Store.Insert(&testEndpoint, &secondResult)
// Can't be bothered dealing with timezone issues on the worker that runs the automated tests
serviceStatuses, err := scenario.Store.GetAllServiceStatuses(paging.NewServiceStatusParams().WithResults(1, 20))
endpointStatuses, err := scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20))
if err != nil {
t.Error("shouldn't have returned an error, got", err.Error())
}
if len(serviceStatuses) != 1 {
t.Fatal("expected 1 service status")
if len(endpointStatuses) != 1 {
t.Fatal("expected 1 endpoint status")
}
actual := serviceStatuses[0]
actual := endpointStatuses[0]
if actual == nil {
t.Fatal("expected service status to exist")
t.Fatal("expected endpoint status to exist")
}
if len(actual.Results) != 2 {
t.Error("expected 2 results, got", len(actual.Results))
@ -212,26 +212,26 @@ func TestStore_GetAllServiceStatuses(t *testing.T) {
}
}
func TestStore_GetAllServiceStatusesWithResultsAndEvents(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetAllServiceStatusesWithResultsAndEvents")
func TestStore_GetAllEndpointStatusesWithResultsAndEvents(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetAllEndpointStatusesWithResultsAndEvents")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
secondResult := testUnsuccessfulResult
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
scenario.Store.Insert(&testEndpoint, &firstResult)
scenario.Store.Insert(&testEndpoint, &secondResult)
// Can't be bothered dealing with timezone issues on the worker that runs the automated tests
serviceStatuses, err := scenario.Store.GetAllServiceStatuses(paging.NewServiceStatusParams().WithResults(1, 20).WithEvents(1, 50))
endpointStatuses, err := scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams().WithResults(1, 20).WithEvents(1, 50))
if err != nil {
t.Error("shouldn't have returned an error, got", err.Error())
}
if len(serviceStatuses) != 1 {
t.Fatal("expected 1 service status")
if len(endpointStatuses) != 1 {
t.Fatal("expected 1 endpoint status")
}
actual := serviceStatuses[0]
actual := endpointStatuses[0]
if actual == nil {
t.Fatal("expected service status to exist")
t.Fatal("expected endpoint status to exist")
}
if len(actual.Results) != 2 {
t.Error("expected 2 results, got", len(actual.Results))
@ -244,8 +244,8 @@ func TestStore_GetAllServiceStatusesWithResultsAndEvents(t *testing.T) {
}
}
func TestStore_GetServiceStatusPage1IsHasMoreRecentResultsThanPage2(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetServiceStatusPage1IsHasMoreRecentResultsThanPage2")
func TestStore_GetEndpointStatusPage1IsHasMoreRecentResultsThanPage2(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetEndpointStatusPage1IsHasMoreRecentResultsThanPage2")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
firstResult.Timestamp = now.Add(-time.Minute)
@ -253,30 +253,30 @@ func TestStore_GetServiceStatusPage1IsHasMoreRecentResultsThanPage2(t *testing.T
secondResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
serviceStatusPage1, err := scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithResults(1, 1))
scenario.Store.Insert(&testEndpoint, &firstResult)
scenario.Store.Insert(&testEndpoint, &secondResult)
endpointStatusPage1, err := scenario.Store.GetEndpointStatusByKey(testEndpoint.Key(), paging.NewEndpointStatusParams().WithResults(1, 1))
if err != nil {
t.Error("shouldn't have returned an error, got", err.Error())
}
if serviceStatusPage1 == nil {
t.Fatalf("serviceStatusPage1 shouldn't have been nil")
if endpointStatusPage1 == nil {
t.Fatalf("endpointStatusPage1 shouldn't have been nil")
}
if len(serviceStatusPage1.Results) != 1 {
t.Fatalf("serviceStatusPage1 should've had 1 result")
if len(endpointStatusPage1.Results) != 1 {
t.Fatalf("endpointStatusPage1 should've had 1 result")
}
serviceStatusPage2, err := scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithResults(2, 1))
endpointStatusPage2, err := scenario.Store.GetEndpointStatusByKey(testEndpoint.Key(), paging.NewEndpointStatusParams().WithResults(2, 1))
if err != nil {
t.Error("shouldn't have returned an error, got", err.Error())
}
if serviceStatusPage2 == nil {
t.Fatalf("serviceStatusPage2 shouldn't have been nil")
if endpointStatusPage2 == nil {
t.Fatalf("endpointStatusPage2 shouldn't have been nil")
}
if len(serviceStatusPage2.Results) != 1 {
t.Fatalf("serviceStatusPage2 should've had 1 result")
if len(endpointStatusPage2.Results) != 1 {
t.Fatalf("endpointStatusPage2 should've had 1 result")
}
// Compare the timestamp of both pages
if !serviceStatusPage1.Results[0].Timestamp.After(serviceStatusPage2.Results[0].Timestamp) {
if !endpointStatusPage1.Results[0].Timestamp.After(endpointStatusPage2.Results[0].Timestamp) {
t.Errorf("The result from the first page should've been more recent than the results from the second page")
}
scenario.Store.Clear()
@ -293,21 +293,21 @@ func TestStore_GetUptimeByKey(t *testing.T) {
secondResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
if _, err := scenario.Store.GetUptimeByKey(testService.Key(), time.Now().Add(-time.Hour), time.Now()); err != common.ErrServiceNotFound {
if _, err := scenario.Store.GetUptimeByKey(testEndpoint.Key(), time.Now().Add(-time.Hour), time.Now()); err != common.ErrEndpointNotFound {
t.Errorf("should've returned not found because there's nothing yet, got %v", err)
}
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
if uptime, _ := scenario.Store.GetUptimeByKey(testService.Key(), now.Add(-time.Hour), time.Now()); uptime != 0.5 {
scenario.Store.Insert(&testEndpoint, &firstResult)
scenario.Store.Insert(&testEndpoint, &secondResult)
if uptime, _ := scenario.Store.GetUptimeByKey(testEndpoint.Key(), now.Add(-time.Hour), time.Now()); uptime != 0.5 {
t.Errorf("the uptime over the past 1h should've been 0.5, got %f", uptime)
}
if uptime, _ := scenario.Store.GetUptimeByKey(testService.Key(), now.Add(-time.Hour*24), time.Now()); uptime != 0.5 {
if uptime, _ := scenario.Store.GetUptimeByKey(testEndpoint.Key(), now.Add(-time.Hour*24), time.Now()); uptime != 0.5 {
t.Errorf("the uptime over the past 24h should've been 0.5, got %f", uptime)
}
if uptime, _ := scenario.Store.GetUptimeByKey(testService.Key(), now.Add(-time.Hour*24*7), time.Now()); uptime != 0.5 {
if uptime, _ := scenario.Store.GetUptimeByKey(testEndpoint.Key(), now.Add(-time.Hour*24*7), time.Now()); uptime != 0.5 {
t.Errorf("the uptime over the past 7d should've been 0.5, got %f", uptime)
}
if _, err := scenario.Store.GetUptimeByKey(testService.Key(), now, time.Now().Add(-time.Hour)); err == nil {
if _, err := scenario.Store.GetUptimeByKey(testEndpoint.Key(), now, time.Now().Add(-time.Hour)); err == nil {
t.Error("should've returned an error because the parameter 'from' cannot be older than 'to'")
}
})
@ -331,39 +331,39 @@ func TestStore_GetAverageResponseTimeByKey(t *testing.T) {
fourthResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
scenario.Store.Insert(&testService, &thirdResult)
scenario.Store.Insert(&testService, &fourthResult)
if averageResponseTime, err := scenario.Store.GetAverageResponseTimeByKey(testService.Key(), now.Add(-48*time.Hour), now.Add(-24*time.Hour)); err == nil {
scenario.Store.Insert(&testEndpoint, &firstResult)
scenario.Store.Insert(&testEndpoint, &secondResult)
scenario.Store.Insert(&testEndpoint, &thirdResult)
scenario.Store.Insert(&testEndpoint, &fourthResult)
if averageResponseTime, err := scenario.Store.GetAverageResponseTimeByKey(testEndpoint.Key(), now.Add(-48*time.Hour), now.Add(-24*time.Hour)); err == nil {
if averageResponseTime != 0 {
t.Errorf("expected average response time to be 0ms, got %v", averageResponseTime)
}
} else {
t.Error("shouldn't have returned an error, got", err)
}
if averageResponseTime, err := scenario.Store.GetAverageResponseTimeByKey(testService.Key(), now.Add(-24*time.Hour), now); err == nil {
if averageResponseTime, err := scenario.Store.GetAverageResponseTimeByKey(testEndpoint.Key(), now.Add(-24*time.Hour), now); err == nil {
if averageResponseTime != 287 {
t.Errorf("expected average response time to be 287ms, got %v", averageResponseTime)
}
} else {
t.Error("shouldn't have returned an error, got", err)
}
if averageResponseTime, err := scenario.Store.GetAverageResponseTimeByKey(testService.Key(), now.Add(-time.Hour), now); err == nil {
if averageResponseTime, err := scenario.Store.GetAverageResponseTimeByKey(testEndpoint.Key(), now.Add(-time.Hour), now); err == nil {
if averageResponseTime != 350 {
t.Errorf("expected average response time to be 350ms, got %v", averageResponseTime)
}
} else {
t.Error("shouldn't have returned an error, got", err)
}
if averageResponseTime, err := scenario.Store.GetAverageResponseTimeByKey(testService.Key(), now.Add(-2*time.Hour), now.Add(-time.Hour)); err == nil {
if averageResponseTime, err := scenario.Store.GetAverageResponseTimeByKey(testEndpoint.Key(), now.Add(-2*time.Hour), now.Add(-time.Hour)); err == nil {
if averageResponseTime != 216 {
t.Errorf("expected average response time to be 216ms, got %v", averageResponseTime)
}
} else {
t.Error("shouldn't have returned an error, got", err)
}
if _, err := scenario.Store.GetAverageResponseTimeByKey(testService.Key(), now, now.Add(-2*time.Hour)); err == nil {
if _, err := scenario.Store.GetAverageResponseTimeByKey(testEndpoint.Key(), now, now.Add(-2*time.Hour)); err == nil {
t.Error("expected an error because from > to, got nil")
}
scenario.Store.Clear()
@ -388,11 +388,11 @@ func TestStore_GetHourlyAverageResponseTimeByKey(t *testing.T) {
fourthResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
scenario.Store.Insert(&testService, &thirdResult)
scenario.Store.Insert(&testService, &fourthResult)
hourlyAverageResponseTime, err := scenario.Store.GetHourlyAverageResponseTimeByKey(testService.Key(), now.Add(-24*time.Hour), now)
scenario.Store.Insert(&testEndpoint, &firstResult)
scenario.Store.Insert(&testEndpoint, &secondResult)
scenario.Store.Insert(&testEndpoint, &thirdResult)
scenario.Store.Insert(&testEndpoint, &fourthResult)
hourlyAverageResponseTime, err := scenario.Store.GetHourlyAverageResponseTimeByKey(testEndpoint.Key(), now.Add(-24*time.Hour), now)
if err != nil {
t.Error("shouldn't have returned an error, got", err)
}
@ -419,20 +419,20 @@ func TestStore_Insert(t *testing.T) {
secondResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &testSuccessfulResult)
scenario.Store.Insert(&testService, &testUnsuccessfulResult)
ss, err := scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
scenario.Store.Insert(&testEndpoint, &testSuccessfulResult)
scenario.Store.Insert(&testEndpoint, &testUnsuccessfulResult)
ss, err := scenario.Store.GetEndpointStatusByKey(testEndpoint.Key(), paging.NewEndpointStatusParams().WithEvents(1, common.MaximumNumberOfEvents).WithResults(1, common.MaximumNumberOfResults))
if err != nil {
t.Error("shouldn't have returned an error, got", err)
}
if ss == nil {
t.Fatalf("Store should've had key '%s', but didn't", testService.Key())
t.Fatalf("Store should've had key '%s', but didn't", testEndpoint.Key())
}
if len(ss.Events) != 3 {
t.Fatalf("Service '%s' should've had 3 events, got %d", ss.Name, len(ss.Events))
t.Fatalf("Endpoint '%s' should've had 3 events, got %d", ss.Name, len(ss.Events))
}
if len(ss.Results) != 2 {
t.Fatalf("Service '%s' should've had 2 results, got %d", ss.Name, len(ss.Results))
t.Fatalf("Endpoint '%s' should've had 2 results, got %d", ss.Name, len(ss.Results))
}
for i, expectedResult := range []core.Result{testSuccessfulResult, testUnsuccessfulResult} {
if expectedResult.HTTPStatus != ss.Results[i].HTTPStatus {
@ -488,33 +488,33 @@ func TestStore_Insert(t *testing.T) {
}
}
func TestStore_DeleteAllServiceStatusesNotInKeys(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_DeleteAllServiceStatusesNotInKeys")
func TestStore_DeleteAllEndpointStatusesNotInKeys(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_DeleteAllEndpointStatusesNotInKeys")
defer cleanUp(scenarios)
firstService := core.Service{Name: "service-1", Group: "group"}
secondService := core.Service{Name: "service-2", Group: "group"}
firstEndpoint := core.Endpoint{Name: "endpoint-1", Group: "group"}
secondEndpoint := core.Endpoint{Name: "endpoint-2", Group: "group"}
result := &testSuccessfulResult
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&firstService, result)
scenario.Store.Insert(&secondService, result)
if ss, _ := scenario.Store.GetServiceStatusByKey(firstService.Key(), paging.NewServiceStatusParams()); ss == nil {
t.Fatal("firstService should exist")
scenario.Store.Insert(&firstEndpoint, result)
scenario.Store.Insert(&secondEndpoint, result)
if ss, _ := scenario.Store.GetEndpointStatusByKey(firstEndpoint.Key(), paging.NewEndpointStatusParams()); ss == nil {
t.Fatal("firstEndpoint should exist")
}
if ss, _ := scenario.Store.GetServiceStatusByKey(secondService.Key(), paging.NewServiceStatusParams()); ss == nil {
t.Fatal("secondService should exist")
if ss, _ := scenario.Store.GetEndpointStatusByKey(secondEndpoint.Key(), paging.NewEndpointStatusParams()); ss == nil {
t.Fatal("secondEndpoint should exist")
}
scenario.Store.DeleteAllServiceStatusesNotInKeys([]string{firstService.Key()})
if ss, _ := scenario.Store.GetServiceStatusByKey(firstService.Key(), paging.NewServiceStatusParams()); ss == nil {
t.Error("secondService should've been deleted")
scenario.Store.DeleteAllEndpointStatusesNotInKeys([]string{firstEndpoint.Key()})
if ss, _ := scenario.Store.GetEndpointStatusByKey(firstEndpoint.Key(), paging.NewEndpointStatusParams()); ss == nil {
t.Error("secondEndpoint should've been deleted")
}
if ss, _ := scenario.Store.GetServiceStatusByKey(secondService.Key(), paging.NewServiceStatusParams()); ss != nil {
t.Error("firstService should still exist")
if ss, _ := scenario.Store.GetEndpointStatusByKey(secondEndpoint.Key(), paging.NewEndpointStatusParams()); ss != nil {
t.Error("firstEndpoint should still exist")
}
// Delete everything
scenario.Store.DeleteAllServiceStatusesNotInKeys([]string{})
serviceStatuses, _ := scenario.Store.GetAllServiceStatuses(paging.NewServiceStatusParams())
if len(serviceStatuses) != 0 {
scenario.Store.DeleteAllEndpointStatusesNotInKeys([]string{})
endpointStatuses, _ := scenario.Store.GetAllEndpointStatuses(paging.NewEndpointStatusParams())
if len(endpointStatuses) != 0 {
t.Errorf("everything should've been deleted")
}
})