From 7e8c70528b9a569e952753a8147f22e4cc116206 Mon Sep 17 00:00:00 2001 From: MohammadReza Palide Date: Wed, 3 May 2023 15:55:55 +0330 Subject: [PATCH 01/15] remove old monthly logic --- pkg/uptime-tracker/store/memory_store.go | 28 ++--- pkg/uptime-tracker/store/postgres_store.go | 117 ++++++++------------ pkg/uptime-tracker/store/uptime_response.go | 72 +----------- pkg/uptime-tracker/store/visors_response.go | 9 +- 4 files changed, 63 insertions(+), 163 deletions(-) diff --git a/pkg/uptime-tracker/store/memory_store.go b/pkg/uptime-tracker/store/memory_store.go index 34382f3..da8296f 100644 --- a/pkg/uptime-tracker/store/memory_store.go +++ b/pkg/uptime-tracker/store/memory_store.go @@ -78,7 +78,7 @@ func (s *memStore) GetAllUptimes(startYear int, startMonth time.Month, endYear i startDate := time.Date(startYear, startMonth, 1, 0, 0, 0, 0, time.Now().Location()) endDate := time.Date(endYear, endMonth, 1, 0, 0, 0, 0, time.Now().Location()) - var uptimes []map[string]string + var keys []string for ; startDate.Before(endDate) || startDate.Equal(endDate); startDate = startDate.AddDate(0, 1, 0) { if _, ok := s.visors[startDate.Year()]; !ok { continue @@ -90,10 +90,12 @@ func (s *memStore) GetAllUptimes(startYear int, startMonth time.Month, endYear i continue } - uptimes = append(uptimes, monthUptimes) + for pk := range monthUptimes { + keys = append(keys, pk) + } } - return makeUptimeResponse(uptimes, s.lastTS, map[string]string{}, startYear, startMonth, endYear, endMonth, nil) + return makeUptimeResponse(keys, s.lastTS, map[string]string{}, nil) } func (s *memStore) GetUptimes(pubKeys []string, startYear int, startMonth time.Month, endYear int, endMonth time.Month) (UptimeResponse, error) { @@ -103,7 +105,7 @@ func (s *memStore) GetUptimes(pubKeys []string, startYear int, startMonth time.M startDate := time.Date(startYear, startMonth, 1, 0, 0, 0, 0, time.Now().Location()) endDate := time.Date(endYear, endMonth, 1, 0, 0, 0, 0, time.Now().Location()) - var uptimes []map[string]string + var keys []string for ; startDate.Before(endDate) || startDate.Equal(endDate); startDate = startDate.AddDate(0, 1, 0) { if _, ok := s.visors[startDate.Year()]; !ok { continue @@ -114,17 +116,15 @@ func (s *memStore) GetUptimes(pubKeys []string, startYear int, startMonth time.M continue } - monthUptimes := make(map[string]string, len(pubKeys)) for _, pk := range pubKeys { - uptime, ok := s.visors[startDate.Year()][startDate.Month()][pk] + _, ok := s.visors[startDate.Year()][startDate.Month()][pk] if !ok { continue } - monthUptimes[pk] = uptime + keys = append(keys, pk) } - uptimes = append(uptimes, monthUptimes) } lastTSMap := make(map[string]string) @@ -138,7 +138,7 @@ func (s *memStore) GetUptimes(pubKeys []string, startYear int, startMonth time.M lastTSMap[pk] = ts } - return makeUptimeResponse(uptimes, lastTSMap, map[string]string{}, startYear, startMonth, endYear, endMonth, nil) + return makeUptimeResponse(keys, lastTSMap, map[string]string{}, nil) } func (s *memStore) GetAllVisors(locDetails geo.LocationDetails) (VisorsResponse, error) { @@ -151,19 +151,11 @@ func (s *memStore) GetAllVisors(locDetails geo.LocationDetails) (VisorsResponse, startYear, startMonth := now.Year(), now.Month() startDate := time.Date(startYear, startMonth, 1, 0, 0, 0, 0, time.Now().Location()) - currentMonthKeys, ok := s.visors[startDate.Year()][startDate.Month()] - if !ok { - return VisorsResponse{}, fmt.Errorf("error getting current Month Keys") - } for pk, ip := range s.ips[fmt.Sprintf("%d:%d", startDate.Year(), startDate.Month())] { ips[pk] = ip - - if ts, ok := s.lastTS[pk]; ok { - currentMonthKeys[pk] = ts - } } - return makeVisorsResponse(currentMonthKeys, ips, locDetails) + return makeVisorsResponse(ips, locDetails) } func (s *memStore) GetVisorsIPs(month string) (map[string]visorIPsResponse, error) { diff --git a/pkg/uptime-tracker/store/postgres_store.go b/pkg/uptime-tracker/store/postgres_store.go index c888443..63fa833 100644 --- a/pkg/uptime-tracker/store/postgres_store.go +++ b/pkg/uptime-tracker/store/postgres_store.go @@ -24,9 +24,6 @@ type postgresStore struct { // NewPostgresStore creates new uptimes postgres store. func NewPostgresStore(logger *logging.Logger, cl *gorm.DB) (Store, error) { // automigrate - if err := cl.AutoMigrate(Uptime{}); err != nil { - logger.Warn("failed to complete automigrate process") - } if err := cl.AutoMigrate(DailyUptimeHistory{}); err != nil { logger.Warn("failed to complete automigrate process") } @@ -53,16 +50,6 @@ func (s *postgresStore) UpdateUptime(pk, ip, version string) error { return nil } - // get existing data - var uptimeRecord Uptime - startDate := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) - err := s.client. - Where("created_at >= ? AND pub_key = ?", startDate, pk). - First(&uptimeRecord).Error - if err != nil && err != gorm.ErrRecordNotFound { - return err - } - // get existing data of daily record var dailyUptimeRecord DailyUptimeHistory startDailyDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) @@ -73,31 +60,23 @@ func (s *postgresStore) UpdateUptime(pk, ip, version string) error { return dailyErr } - if uptimeRecord.PubKey == "" { - uptimeRecord.PubKey = pk - } if dailyUptimeRecord.PubKey == "" { dailyUptimeRecord.PubKey = pk } - uptimeRecord.Online += seconds + dailyUptimeRecord.DailyOnline += seconds if ip != "" { ips := []string{} - if len(uptimeRecord.IPs) > 0 { - ips = strings.Split(uptimeRecord.IPs, ",") + if len(dailyUptimeRecord.IPs) > 0 { + ips = strings.Split(dailyUptimeRecord.IPs, ",") } ips = append(ips, ip) - uptimeRecord.IPs = uniqueIPs(ips) - uptimeRecord.LastIP = ip - } - uptimeRecord.Version = version - if err := s.client.Save(&uptimeRecord).Error; err != nil { - return fmt.Errorf("failed to create/update uptime record: %w", err) + dailyUptimeRecord.IPs = uniqueIPs(ips) + dailyUptimeRecord.LastIP = ip } - - dailyUptimeRecord.DailyOnline += seconds + dailyUptimeRecord.Version = version if err := s.client.Save(&dailyUptimeRecord).Error; err != nil { - return fmt.Errorf("failed to create/update daily uptime record: %w", err) + return fmt.Errorf("failed to create/update uptime record: %w", err) } // update cache @@ -112,75 +91,75 @@ func (s *postgresStore) GetAllUptimes(startYear int, startMonth time.Month, endY startDate := time.Date(startYear, startMonth, 1, 0, 0, 0, 0, time.Now().Location()) endDate := time.Date(endYear, endMonth, 1, 0, 0, 0, 0, time.Now().Location()) - var uptimes []map[string]string + var keys []string lastTSs := make(map[string]string) versions := make(map[string]string) var murError error - var uptimesRecords []Uptime + var uptimesRecords []DailyUptimeHistory for ; startDate.Before(endDate) || startDate.Equal(endDate); startDate = startDate.AddDate(0, 1, 0) { - monthUptimes := make(map[string]string) - if err := s.client.Where("created_at BETWEEN ? AND ?", startDate, startDate.AddDate(0, 1, 0).Add(-1*time.Second)).Find(&uptimesRecords).Error; err != nil { + if err := s.client.Where("created_at BETWEEN ? AND ?", startDate, startDate.AddDate(0, 1, 0).Add(-1*time.Second)).Order("id DESC").Find(&uptimesRecords).Error; err != nil { murError = errors.New("failed on fetching data from pg store") break } for _, record := range uptimesRecords { - monthUptimes[record.PubKey] = fmt.Sprint(record.Online) - if lastTSs[record.PubKey] <= fmt.Sprint(record.UpdatedAt.Unix()) { - lastTSs[record.PubKey] = fmt.Sprint(record.UpdatedAt.Unix()) + if _, ok := lastTSs[record.PubKey]; !ok { + if lastTSs[record.PubKey] <= fmt.Sprint(record.UpdatedAt.Unix()) { + lastTSs[record.PubKey] = fmt.Sprint(record.UpdatedAt.Unix()) + } + versions[record.PubKey] = record.Version + keys = append(keys, record.PubKey) } - versions[record.PubKey] = record.Version } - uptimes = append(uptimes, monthUptimes) } - return makeUptimeResponse(uptimes, lastTSs, versions, startYear, startMonth, endYear, endMonth, murError) + return makeUptimeResponse(keys, lastTSs, versions, murError) } func (s *postgresStore) GetUptimes(pubKeys []string, startYear int, startMonth time.Month, endYear int, endMonth time.Month) (UptimeResponse, error) { startDate := time.Date(startYear, startMonth, 1, 0, 0, 0, 0, time.Now().Location()) endDate := time.Date(endYear, endMonth, 1, 0, 0, 0, 0, time.Now().Location()) - var uptimes []map[string]string + var keys []string versions := make(map[string]string) - var uptimesRecords []Uptime + var uptimesRecords []DailyUptimeHistory var murError error lastTSs := make(map[string]string) for ; startDate.Before(endDate) || startDate.Equal(endDate); startDate = startDate.AddDate(0, 1, 0) { - monthUptimes := make(map[string]string) - if err := s.client.Where("created_at BETWEEN ? AND ? AND pub_key = ?", startDate, startDate.AddDate(0, 1, 0).Add(-1*time.Second), pubKeys).Find(&uptimesRecords).Error; err != nil { + if err := s.client.Where("created_at BETWEEN ? AND ? AND pub_key = ?", startDate, startDate.AddDate(0, 1, 0).Add(-1*time.Second), pubKeys).Order("id DESC").Find(&uptimesRecords).Error; err != nil { murError = errors.New("failed on fetching data from pg store") } for _, record := range uptimesRecords { - monthUptimes[record.PubKey] = fmt.Sprint(record.Online) - if lastTSs[record.PubKey] <= fmt.Sprint(record.UpdatedAt.Unix()) { - lastTSs[record.PubKey] = fmt.Sprint(record.UpdatedAt.Unix()) + if _, ok := lastTSs[record.PubKey]; !ok { + if lastTSs[record.PubKey] <= fmt.Sprint(record.UpdatedAt.Unix()) { + lastTSs[record.PubKey] = fmt.Sprint(record.UpdatedAt.Unix()) + } + versions[record.PubKey] = record.Version + keys = append(keys, record.PubKey) } - versions[record.PubKey] = record.Version } - uptimes = append(uptimes, monthUptimes) } - return makeUptimeResponse(uptimes, lastTSs, versions, startYear, startMonth, endYear, endMonth, murError) + return makeUptimeResponse(keys, lastTSs, versions, murError) } func (s *postgresStore) GetAllVisors(locDetails geo.LocationDetails) (VisorsResponse, error) { ips := make(map[string]string) - currentMonthData := make(map[string]string) - var uptimesRecords []Uptime + var uptimesRecords []DailyUptimeHistory now := time.Now() startYear, startMonth := now.Year(), now.Month() startDate := time.Date(startYear, startMonth, 1, 0, 0, 0, 0, time.Now().Location()) - if err := s.client.Where("created_at >= ?", startDate).Find(&uptimesRecords).Error; err != nil { + if err := s.client.Where("created_at >= ?", startDate).Order("id DESC").Find(&uptimesRecords).Error; err != nil { return VisorsResponse{}, err } for _, record := range uptimesRecords { - ips[record.PubKey] = record.LastIP - currentMonthData[record.PubKey] = fmt.Sprint(record.Online) + if _, ok := ips[record.PubKey]; !ok { + ips[record.PubKey] = record.LastIP + } } - return makeVisorsResponse(currentMonthData, ips, locDetails) + return makeVisorsResponse(ips, locDetails) } func (s *postgresStore) GetDailyUpdateHistory() (map[string]map[string]string, error) { @@ -248,21 +227,23 @@ func (s *postgresStore) Close() { } func (s *postgresStore) readAllUptimeIPMembers(timeValue time.Time) (map[string]string, error) { - var uptimesRecords []Uptime + var uptimesRecords []DailyUptimeHistory response := make(map[string]string) if timeValue.IsZero() { - if err := s.client.Find(&uptimesRecords).Error; err != nil { + if err := s.client.Order("id DESC").Find(&uptimesRecords).Error; err != nil { return response, err } } else { - if err := s.client.Where("created_at BETWEEN ? AND ?", timeValue, timeValue.AddDate(0, 1, 0).Add(-1*time.Second)).Find(&uptimesRecords).Error; err != nil { + if err := s.client.Where("created_at BETWEEN ? AND ?", timeValue, timeValue.AddDate(0, 1, 0).Add(-1*time.Second)).Order("id DESC").Find(&uptimesRecords).Error; err != nil { return response, err } } for _, record := range uptimesRecords { - response[record.PubKey] = record.LastIP + if _, ok := response[record.PubKey]; !ok { + response[record.PubKey] = record.LastIP + } } if len(response) == 0 { @@ -290,35 +271,27 @@ func (s *postgresStore) setCache(pk string, ts int64) { func (s *postgresStore) GetNumberOfUptimesInCurrentMonth() (int, error) { var counter int64 now := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location()) - err := s.client.Model(&Uptime{}).Where("created_at BETWEEN ? AND ?", now, now.AddDate(0, 1, 0).Add(-1*time.Second)).Count(&counter).Error + err := s.client.Model(&DailyUptimeHistory{}).Where("created_at BETWEEN ? AND ?", now, now.AddDate(0, 1, 0).Add(-1*time.Second)).Group("pub_key").Count(&counter).Error return int(counter), err } func (s *postgresStore) GetNumberOfUptimesByYearAndMonth(year int, month time.Month) (int, error) { var counter int64 timeValue := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.Now().Location()) - err := s.client.Model(&Uptime{}).Where("created_at BETWEEN ? AND ?", timeValue, timeValue.AddDate(0, 1, 0).Add(-1*time.Second)).Count(&counter).Error + err := s.client.Model(&DailyUptimeHistory{}).Where("created_at BETWEEN ? AND ?", timeValue, timeValue.AddDate(0, 1, 0).Add(-1*time.Second)).Group("pub_key").Count(&counter).Error return int(counter), err } -// Uptime is gorm.Model for uptime table -type Uptime struct { - ID uint `gorm:"primarykey" json:"-"` - CreatedAt time.Time - UpdatedAt time.Time - PubKey string - Online int - Version string - IPs string - LastIP string -} - // DailyUptimeHistory is gorm.Model for daily uptime history table type DailyUptimeHistory struct { ID uint `gorm:"primarykey" json:"-"` CreatedAt time.Time + UpdatedAt time.Time PubKey string DailyOnline int + Version string + IPs string + LastIP string } func uniqueIPs(ips []string) string { diff --git a/pkg/uptime-tracker/store/uptime_response.go b/pkg/uptime-tracker/store/uptime_response.go index ca7dc50..777d29f 100644 --- a/pkg/uptime-tracker/store/uptime_response.go +++ b/pkg/uptime-tracker/store/uptime_response.go @@ -1,7 +1,6 @@ package store import ( - "fmt" "sort" "strconv" "time" @@ -37,73 +36,17 @@ type UptimeDefV2 struct { DailyOnlineHistory map[string]string `json:"daily,omitempty"` } -func makeUptimeResponse(uptimes []map[string]string, lastTS map[string]string, versions map[string]string, startYear int, startMonth time.Month, endYear int, endMonth time.Month, callingErr error) (UptimeResponse, error) { +func makeUptimeResponse(keys []string, lastTS map[string]string, versions map[string]string, callingErr error) (UptimeResponse, error) { if callingErr != nil { return UptimeResponse{}, callingErr } - if len(uptimes) == 0 { + if len(keys) == 0 { return UptimeResponse{}, nil } - startDate := time.Date(startYear, startMonth, 1, 0, 0, 0, 0, time.Now().Location()) - endDate := time.Date(endYear, endMonth, 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 1, 0) - - // take just complete seconds - totalPeriodSeconds := float64(int(endDate.Sub(startDate).Seconds())) - - now := time.Now() - currentYear := now.Year() - currentMonth := now.Month() - - var ensureLimit bool - var totalPeriodSecondsPassed float64 - if endYear == currentYear && endMonth == currentMonth { - // interval ends with the current month, we'll need to ensure limits. All intervals which - // ended before the current month are complete, but for the current month, we need to ensure - // that uptime of the node is not more than the passed part of the month, i.e. uptime can't be - // 16 days on the 15th of the month - ensureLimit = true - - currentMonthStart := time.Date(currentYear, currentMonth, 1, 0, 0, 0, 0, now.Location()) - currentMonthEnd := currentMonthStart.AddDate(0, 1, 0) - currentMonthTotalSeconds := float64(int(currentMonthEnd.Sub(currentMonthStart).Seconds())) - - // take just complete seconds - partOfMonthPassed := float64(int(now.Sub(currentMonthStart).Seconds())) - - // initially `totalPeriodSeconds` contains full time interval including full current month, - // so we need to subtract total month seconds and add time of the month which is actually - // passed. And this is the hard cap for our uptimes, we can't overcome this boundary - totalPeriodSecondsPassed = totalPeriodSeconds - currentMonthTotalSeconds + partOfMonthPassed - } - - totalUptimes := make(map[string]int64, len(uptimes[0])) - for _, monthUptimes := range uptimes { - for pk, uptimeStr := range monthUptimes { - uptime, err := strconv.ParseInt(uptimeStr, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing uptime value for visor %s: %w", pk, err) - } - - totalUptimes[pk] += uptime - } - } - response := make(UptimeResponse, 0) - for pk, uptime := range totalUptimes { - uptime64 := float64(uptime) - if ensureLimit && uptime64 > totalPeriodSecondsPassed { - uptime64 = totalPeriodSecondsPassed - } - if !ensureLimit && uptime64 > totalPeriodSeconds { - uptime64 = totalPeriodSeconds - } - - percentage := uptime64 / totalPeriodSeconds * 100 - if percentage > 100 { - percentage = 100 - } + for _, pk := range keys { online := false ts, err := strconv.ParseInt(lastTS[pk], 10, 64) @@ -112,12 +55,9 @@ func makeUptimeResponse(uptimes []map[string]string, lastTS map[string]string, v } entry := UptimeDef{ - Key: pk, - Uptime: uptime64, - Downtime: totalPeriodSeconds - uptime64, - Percentage: percentage, - Online: online, - Version: versions[pk], + Key: pk, + Online: online, + Version: versions[pk], } response = append(response, entry) diff --git a/pkg/uptime-tracker/store/visors_response.go b/pkg/uptime-tracker/store/visors_response.go index 679575b..c162440 100644 --- a/pkg/uptime-tracker/store/visors_response.go +++ b/pkg/uptime-tracker/store/visors_response.go @@ -19,14 +19,9 @@ type VisorDef struct { Lon float64 `json:"lon"` } -func makeVisorsResponse(currentMonthKeys, ips map[string]string, locDetails geo.LocationDetails) (VisorsResponse, error) { +func makeVisorsResponse(ips map[string]string, locDetails geo.LocationDetails) (VisorsResponse, error) { response := VisorsResponse{} - for pk := range currentMonthKeys { - ip, ok := ips[pk] - if !ok { - continue - } - + for pk, ip := range ips { geo, err := locDetails(net.ParseIP(ip)) if err != nil { log.WithError(err). From 2fddde1206d8f8c42b4ffed3156738e255f2711c Mon Sep 17 00:00:00 2001 From: MohammadReza Palide Date: Wed, 3 May 2023 16:00:37 +0330 Subject: [PATCH 02/15] fix api_test --- cmd/uptime-tracker/commands/root.go | 14 ++++++------- pkg/uptime-tracker/api/api.go | 6 +++--- pkg/uptime-tracker/api/api_test.go | 21 +++++-------------- pkg/uptime-tracker/store/memory_store_test.go | 3 ++- pkg/uptime-tracker/store/postgres_store.go | 3 ++- pkg/uptime-tracker/store/store.go | 3 ++- 6 files changed, 21 insertions(+), 29 deletions(-) diff --git a/cmd/uptime-tracker/commands/root.go b/cmd/uptime-tracker/commands/root.go index 0d40b6a..691ae2a 100644 --- a/cmd/uptime-tracker/commands/root.go +++ b/cmd/uptime-tracker/commands/root.go @@ -9,10 +9,17 @@ import ( "os" "strings" + "github.com/SkycoinPro/skywire-services/internal/pg" + "github.com/SkycoinPro/skywire-services/internal/utmetrics" + "github.com/SkycoinPro/skywire-services/pkg/uptime-tracker/api" + "github.com/SkycoinPro/skywire-services/pkg/uptime-tracker/store" logrussyslog "github.com/sirupsen/logrus/hooks/syslog" "github.com/skycoin/dmsg/pkg/direct" "github.com/skycoin/dmsg/pkg/dmsg" "github.com/skycoin/dmsg/pkg/dmsghttp" + "github.com/spf13/cobra" + "gorm.io/gorm" + "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/cmdutil" @@ -22,13 +29,6 @@ import ( "github.com/skycoin/skywire-utilities/pkg/metricsutil" "github.com/skycoin/skywire-utilities/pkg/storeconfig" "github.com/skycoin/skywire-utilities/pkg/tcpproxy" - "github.com/spf13/cobra" - "gorm.io/gorm" - - "github.com/SkycoinPro/skywire-services/internal/pg" - "github.com/SkycoinPro/skywire-services/internal/utmetrics" - "github.com/SkycoinPro/skywire-services/pkg/uptime-tracker/api" - "github.com/SkycoinPro/skywire-services/pkg/uptime-tracker/store" ) const ( diff --git a/pkg/uptime-tracker/api/api.go b/pkg/uptime-tracker/api/api.go index cb1dcca..84fa0fd 100644 --- a/pkg/uptime-tracker/api/api.go +++ b/pkg/uptime-tracker/api/api.go @@ -15,12 +15,15 @@ import ( "sync" "time" + "github.com/SkycoinPro/skywire-services/internal/utmetrics" + "github.com/SkycoinPro/skywire-services/pkg/uptime-tracker/store" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/httprate" "github.com/go-echarts/go-echarts/v2/charts" "github.com/go-echarts/go-echarts/v2/opts" "github.com/sirupsen/logrus" + "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/geo" @@ -29,9 +32,6 @@ import ( "github.com/skycoin/skywire-utilities/pkg/logging" "github.com/skycoin/skywire-utilities/pkg/metricsutil" "github.com/skycoin/skywire-utilities/pkg/netutil" - - "github.com/SkycoinPro/skywire-services/internal/utmetrics" - "github.com/SkycoinPro/skywire-services/pkg/uptime-tracker/store" ) const ( diff --git a/pkg/uptime-tracker/api/api_test.go b/pkg/uptime-tracker/api/api_test.go index 0a0ea4f..b5c99da 100644 --- a/pkg/uptime-tracker/api/api_test.go +++ b/pkg/uptime-tracker/api/api_test.go @@ -9,17 +9,16 @@ import ( "net/http" "net/http/httptest" "testing" - "time" + + "github.com/SkycoinPro/skywire-services/internal/utmetrics" + "github.com/SkycoinPro/skywire-services/pkg/uptime-tracker/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/geo" "github.com/skycoin/skywire-utilities/pkg/httpauth" "github.com/skycoin/skywire-utilities/pkg/storeconfig" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/SkycoinPro/skywire-services/internal/utmetrics" - "github.com/SkycoinPro/skywire-services/pkg/uptime-tracker/store" ) var testPubKey, testSec = cipher.GenerateKeyPair() @@ -63,19 +62,9 @@ func TestHandleUptimes(t *testing.T) { var resp store.UptimeResponse require.NoError(t, json.NewDecoder(bytes.NewBuffer(w.Body.Bytes())).Decode(&resp)) - now := time.Now() - monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) - monthEnd := monthStart.AddDate(0, 1, 0) - - totalMonthSeconds := float64(int(monthEnd.Sub(monthStart).Seconds())) - require.Len(t, resp, 1) assert.Equal(t, pk.String(), resp[0].Key) - iterationsCount := store.UptimeSeconds * float64(iterations) - assert.Equal(t, iterationsCount, resp[0].Uptime) - assert.Equal(t, totalMonthSeconds-iterationsCount, resp[0].Downtime) - assert.Equal(t, iterationsCount/totalMonthSeconds*100, resp[0].Percentage) assert.True(t, resp[0].Online) } diff --git a/pkg/uptime-tracker/store/memory_store_test.go b/pkg/uptime-tracker/store/memory_store_test.go index b1244cb..e29761c 100644 --- a/pkg/uptime-tracker/store/memory_store_test.go +++ b/pkg/uptime-tracker/store/memory_store_test.go @@ -9,9 +9,10 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/geo" - "github.com/stretchr/testify/require" ) func TestMemory(t *testing.T) { diff --git a/pkg/uptime-tracker/store/postgres_store.go b/pkg/uptime-tracker/store/postgres_store.go index 63fa833..6e3c6b6 100644 --- a/pkg/uptime-tracker/store/postgres_store.go +++ b/pkg/uptime-tracker/store/postgres_store.go @@ -8,9 +8,10 @@ import ( "sync" "time" + "gorm.io/gorm" + "github.com/skycoin/skywire-utilities/pkg/geo" "github.com/skycoin/skywire-utilities/pkg/logging" - "gorm.io/gorm" ) type postgresStore struct { diff --git a/pkg/uptime-tracker/store/store.go b/pkg/uptime-tracker/store/store.go index bee6724..bb84d3a 100644 --- a/pkg/uptime-tracker/store/store.go +++ b/pkg/uptime-tracker/store/store.go @@ -3,9 +3,10 @@ package store import ( "time" + "gorm.io/gorm" + "github.com/skycoin/skywire-utilities/pkg/geo" "github.com/skycoin/skywire-utilities/pkg/logging" - "gorm.io/gorm" ) const ( From f95ed2c006a30f50f9f9acb560a6938589026bd0 Mon Sep 17 00:00:00 2001 From: MohammadReza Palide Date: Thu, 4 May 2023 10:11:12 +0330 Subject: [PATCH 03/15] add daily routin for delete old entries and save last day data to json file --- cmd/uptime-tracker/commands/root.go | 4 ++- pkg/uptime-tracker/api/api.go | 32 +++++++++++++++++++++- pkg/uptime-tracker/api/api_test.go | 6 ++-- pkg/uptime-tracker/store/memory_store.go | 8 ++++++ pkg/uptime-tracker/store/postgres_store.go | 15 +++++++++- pkg/uptime-tracker/store/store.go | 2 ++ 6 files changed, 61 insertions(+), 6 deletions(-) diff --git a/cmd/uptime-tracker/commands/root.go b/cmd/uptime-tracker/commands/root.go index 691ae2a..1890119 100644 --- a/cmd/uptime-tracker/commands/root.go +++ b/cmd/uptime-tracker/commands/root.go @@ -53,6 +53,7 @@ var ( testing bool dmsgDisc string sk cipher.SecKey + storeData int ) func init() { @@ -63,6 +64,7 @@ func init() { rootCmd.Flags().IntVar(&redisPoolSize, "redis-pool-size", 10, "redis connection pool size") rootCmd.Flags().StringVar(&pgHost, "pg-host", "localhost", "host of postgres") rootCmd.Flags().StringVar(&pgPort, "pg-port", "5432", "port of postgres") + rootCmd.Flags().IntVar(&storeData, "store-data", 14, "number of days data store in db") rootCmd.Flags().BoolVarP(&logEnabled, "log", "l", true, "enable request logging") rootCmd.Flags().StringVar(&syslogAddr, "syslog", "", "syslog server address. E.g. localhost:514") rootCmd.Flags().StringVar(&tag, "tag", "uptime_tracker", "logging tag") @@ -151,7 +153,7 @@ var rootCmd = &cobra.Command{ } enableMetrics := metricsAddr != "" - utAPI := api.New(logger, s, nonceStore, locDetails, enableLoadTesting, enableMetrics, m) + utAPI := api.New(logger, s, nonceStore, locDetails, enableLoadTesting, enableMetrics, m, storeData) utPAPI := api.NewPrivate(logger, s) diff --git a/pkg/uptime-tracker/api/api.go b/pkg/uptime-tracker/api/api.go index 84fa0fd..276b5e5 100644 --- a/pkg/uptime-tracker/api/api.go +++ b/pkg/uptime-tracker/api/api.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "net/url" + "os" "strconv" "strings" "sync" @@ -55,6 +56,7 @@ type API struct { visorsCacheMu sync.RWMutex dailyUptimeCache map[string]map[string]string dailyUptimeCacheMu sync.RWMutex + cutoffStoreUptimes int } // PrivateAPI register all the PrivateAPI endpoints. @@ -73,7 +75,7 @@ type HealthCheckResponse struct { // New constructs a new API instance. func New(log logrus.FieldLogger, s store.Store, nonceStore httpauth.NonceStore, locDetails geo.LocationDetails, - enableLoadTesting, enableMetrics bool, m utmetrics.Metrics) *API { + enableLoadTesting, enableMetrics bool, m utmetrics.Metrics, cutoffStoreData int) *API { if log == nil { log = logging.MustGetLogger("uptime_tracker") } @@ -84,6 +86,7 @@ func New(log logrus.FieldLogger, s store.Store, nonceStore httpauth.NonceStore, store: s, locDetails: locDetails, startedAt: time.Now(), + cutoffStoreUptimes: cutoffStoreData, } r := chi.NewRouter() @@ -141,6 +144,7 @@ func (api *API) log(r *http.Request) logrus.FieldLogger { // RunBackgroundTasks is function which runs periodic background tasks of API. func (api *API) RunBackgroundTasks(ctx context.Context, logger logrus.FieldLogger) { + date := time.Now().Format("YYYY-MM-DD") cacheTicker := time.NewTicker(time.Minute * 5) defer cacheTicker.Stop() ticker := time.NewTicker(time.Second * 10) @@ -153,6 +157,10 @@ func (api *API) RunBackgroundTasks(ctx context.Context, logger logrus.FieldLogge return case <-cacheTicker.C: api.updateInternalCaches(logger) + if time.Now().Format("YYYY-MM-DD") > date { + api.dailyRoutine(logger) + date = time.Now().Format("YYYY-MM-DD") + } case <-ticker.C: api.updateInternalState(ctx, logger) } @@ -174,6 +182,28 @@ func (api *API) updateInternalCaches(logger logrus.FieldLogger) { } } +func (api *API) dailyRoutine(logger logrus.FieldLogger) { + // api.cutoffStoreUptimes + // delete old data + err := api.store.DeleteOldEntries(api.cutoffStoreUptimes) + if err != nil { + logger.WithError(err).Warn("unable to delete old entries from db") + } + // save yesterday data as YYYY-MM-DD-uptime-data.json file + data, err := api.store.GetLastDayData() + if err != nil { + logger.WithError(err).Warn("unable to fetch last day data from db") + return + } + // save to file + file, _ := json.MarshalIndent(data, "", " ") //nolint + fileName := fmt.Sprintf("%s-uptime-data.json", time.Now().AddDate(0, 0, -1).Format("YYYY-MM-DD")) + err = os.WriteFile(fileName, file, 0644) //nolint + if err != nil { + logger.WithError(err).Warn("unable to save data to json file") + } +} + func (api *API) updateVisorsCache() error { visors, err := api.store.GetAllVisors(api.locDetails) if err != nil { diff --git a/pkg/uptime-tracker/api/api_test.go b/pkg/uptime-tracker/api/api_test.go index b5c99da..0444f33 100644 --- a/pkg/uptime-tracker/api/api_test.go +++ b/pkg/uptime-tracker/api/api_test.go @@ -41,7 +41,7 @@ func TestHandleUptimes(t *testing.T) { nonceMock, err := httpauth.NewNonceStore(ctx, storeconfig.Config{Type: storeconfig.Memory}, "") require.NoError(t, err) api := New(nil, mock, nonceMock, geoFunc, false, false, - utmetrics.NewEmpty()) + utmetrics.NewEmpty(), 0) pk, _ := cipher.GenerateKeyPair() @@ -75,7 +75,7 @@ func TestAPI_handleUpdate(t *testing.T) { nonceMock, err := httpauth.NewNonceStore(ctx, storeconfig.Config{Type: storeconfig.Memory}, "") require.NoError(t, err) api := New(nil, mock, nonceMock, geoFunc, false, false, - utmetrics.NewEmpty()) + utmetrics.NewEmpty(), 0) t.Run("StatusOK", func(t *testing.T) { w := httptest.NewRecorder() @@ -106,7 +106,7 @@ func TestApi_UpdateRemovedMethod(t *testing.T) { nonceMock, err := httpauth.NewNonceStore(ctx, storeconfig.Config{Type: storeconfig.Memory}, "") require.NoError(t, err) api := New(nil, mock, nonceMock, geoFunc, false, false, - utmetrics.NewEmpty()) + utmetrics.NewEmpty(), 0) t.Run("StatusGone", func(t *testing.T) { w := httptest.NewRecorder() diff --git a/pkg/uptime-tracker/store/memory_store.go b/pkg/uptime-tracker/store/memory_store.go index da8296f..f2286ce 100644 --- a/pkg/uptime-tracker/store/memory_store.go +++ b/pkg/uptime-tracker/store/memory_store.go @@ -240,3 +240,11 @@ func (s *memStore) Close() { func (s *memStore) GetDailyUpdateHistory() (map[string]map[string]string, error) { return map[string]map[string]string{}, nil } + +func (s *memStore) DeleteOldEntries(cutoff int) error { + return nil +} + +func (s *memStore) GetLastDayData() ([]DailyUptimeHistory, error) { + return []DailyUptimeHistory{}, nil +} diff --git a/pkg/uptime-tracker/store/postgres_store.go b/pkg/uptime-tracker/store/postgres_store.go index 6e3c6b6..b326408 100644 --- a/pkg/uptime-tracker/store/postgres_store.go +++ b/pkg/uptime-tracker/store/postgres_store.go @@ -166,7 +166,7 @@ func (s *postgresStore) GetAllVisors(locDetails geo.LocationDetails) (VisorsResp func (s *postgresStore) GetDailyUpdateHistory() (map[string]map[string]string, error) { var uptimesRecords []DailyUptimeHistory now := time.Now() - startDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Now().Location()).AddDate(0, 0, -40) + startDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Now().Location()).AddDate(0, 0, -7) if err := s.client.Where("created_at >= ?", startDate).Find(&uptimesRecords).Error; err != nil { return map[string]map[string]string{}, err } @@ -283,6 +283,19 @@ func (s *postgresStore) GetNumberOfUptimesByYearAndMonth(year int, month time.Mo return int(counter), err } +func (s *postgresStore) DeleteOldEntries(cutoff int) error { + deleteDate := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 0, -cutoff) + err := s.client.Where("created_at < ?", deleteDate).Delete(&DailyUptimeHistory{}).Error + return err +} + +func (s *postgresStore) GetLastDayData() ([]DailyUptimeHistory, error) { + var data []DailyUptimeHistory + today := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Now().Location()) + err := s.client.Where("created_at BETWEEN ? AND ?", today.AddDate(0, 0, -1), today.Add(-1*time.Second)).Find(&data).Error + return data, err +} + // DailyUptimeHistory is gorm.Model for daily uptime history table type DailyUptimeHistory struct { ID uint `gorm:"primarykey" json:"-"` diff --git a/pkg/uptime-tracker/store/store.go b/pkg/uptime-tracker/store/store.go index bb84d3a..5e116d5 100644 --- a/pkg/uptime-tracker/store/store.go +++ b/pkg/uptime-tracker/store/store.go @@ -24,6 +24,8 @@ type Store interface { GetNumberOfUptimesByYearAndMonth(year int, month time.Month) (int, error) UpdateUptime(pk, ip, version string) error GetDailyUpdateHistory() (map[string]map[string]string, error) + DeleteOldEntries(cutoff int) error + GetLastDayData() ([]DailyUptimeHistory, error) Close() } From f4894797f0fb6362eac34dd5e13a0bea2b5099a7 Mon Sep 17 00:00:00 2001 From: MohammadReza Palide Date: Thu, 4 May 2023 10:21:35 +0330 Subject: [PATCH 04/15] update location to save data | change cutoff default to 7 --- cmd/uptime-tracker/commands/root.go | 2 +- pkg/uptime-tracker/api/api.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/uptime-tracker/commands/root.go b/cmd/uptime-tracker/commands/root.go index 1890119..002eef1 100644 --- a/cmd/uptime-tracker/commands/root.go +++ b/cmd/uptime-tracker/commands/root.go @@ -64,7 +64,7 @@ func init() { rootCmd.Flags().IntVar(&redisPoolSize, "redis-pool-size", 10, "redis connection pool size") rootCmd.Flags().StringVar(&pgHost, "pg-host", "localhost", "host of postgres") rootCmd.Flags().StringVar(&pgPort, "pg-port", "5432", "port of postgres") - rootCmd.Flags().IntVar(&storeData, "store-data", 14, "number of days data store in db") + rootCmd.Flags().IntVar(&storeData, "cutoff-store-data", 7, "number of days data store in db") rootCmd.Flags().BoolVarP(&logEnabled, "log", "l", true, "enable request logging") rootCmd.Flags().StringVar(&syslogAddr, "syslog", "", "syslog server address. E.g. localhost:514") rootCmd.Flags().StringVar(&tag, "tag", "uptime_tracker", "logging tag") diff --git a/pkg/uptime-tracker/api/api.go b/pkg/uptime-tracker/api/api.go index 276b5e5..bbae9b7 100644 --- a/pkg/uptime-tracker/api/api.go +++ b/pkg/uptime-tracker/api/api.go @@ -197,7 +197,7 @@ func (api *API) dailyRoutine(logger logrus.FieldLogger) { } // save to file file, _ := json.MarshalIndent(data, "", " ") //nolint - fileName := fmt.Sprintf("%s-uptime-data.json", time.Now().AddDate(0, 0, -1).Format("YYYY-MM-DD")) + fileName := fmt.Sprintf("/daily-data/%s-uptime-data.json", time.Now().AddDate(0, 0, -1).Format("YYYY-MM-DD")) err = os.WriteFile(fileName, file, 0644) //nolint if err != nil { logger.WithError(err).Warn("unable to save data to json file") From c33ff5a073cc2fd9d496355c6e8bda2a31f92904 Mon Sep 17 00:00:00 2001 From: MohammadReza Palide Date: Thu, 4 May 2023 10:22:56 +0330 Subject: [PATCH 05/15] change variable name --- cmd/uptime-tracker/commands/root.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/uptime-tracker/commands/root.go b/cmd/uptime-tracker/commands/root.go index 002eef1..c9bef1f 100644 --- a/cmd/uptime-tracker/commands/root.go +++ b/cmd/uptime-tracker/commands/root.go @@ -53,7 +53,7 @@ var ( testing bool dmsgDisc string sk cipher.SecKey - storeData int + cutoffStoreData int ) func init() { @@ -64,7 +64,7 @@ func init() { rootCmd.Flags().IntVar(&redisPoolSize, "redis-pool-size", 10, "redis connection pool size") rootCmd.Flags().StringVar(&pgHost, "pg-host", "localhost", "host of postgres") rootCmd.Flags().StringVar(&pgPort, "pg-port", "5432", "port of postgres") - rootCmd.Flags().IntVar(&storeData, "cutoff-store-data", 7, "number of days data store in db") + rootCmd.Flags().IntVar(&cutoffStoreData, "cutoff-store-data", 7, "number of days data store in db") rootCmd.Flags().BoolVarP(&logEnabled, "log", "l", true, "enable request logging") rootCmd.Flags().StringVar(&syslogAddr, "syslog", "", "syslog server address. E.g. localhost:514") rootCmd.Flags().StringVar(&tag, "tag", "uptime_tracker", "logging tag") @@ -153,7 +153,7 @@ var rootCmd = &cobra.Command{ } enableMetrics := metricsAddr != "" - utAPI := api.New(logger, s, nonceStore, locDetails, enableLoadTesting, enableMetrics, m, storeData) + utAPI := api.New(logger, s, nonceStore, locDetails, enableLoadTesting, enableMetrics, m, cutoffStoreData) utPAPI := api.NewPrivate(logger, s) From 8ca59a74b39e950461729fe9f136aa5879e5c95f Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 9 May 2023 11:00:41 +0330 Subject: [PATCH 06/15] add new flag | fix name of variables --- cmd/uptime-tracker/commands/root.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/uptime-tracker/commands/root.go b/cmd/uptime-tracker/commands/root.go index c9bef1f..ae6a62a 100644 --- a/cmd/uptime-tracker/commands/root.go +++ b/cmd/uptime-tracker/commands/root.go @@ -53,7 +53,8 @@ var ( testing bool dmsgDisc string sk cipher.SecKey - cutoffStoreData int + storeDataCutoff int + storeDataPath string ) func init() { @@ -64,7 +65,8 @@ func init() { rootCmd.Flags().IntVar(&redisPoolSize, "redis-pool-size", 10, "redis connection pool size") rootCmd.Flags().StringVar(&pgHost, "pg-host", "localhost", "host of postgres") rootCmd.Flags().StringVar(&pgPort, "pg-port", "5432", "port of postgres") - rootCmd.Flags().IntVar(&cutoffStoreData, "cutoff-store-data", 7, "number of days data store in db") + rootCmd.Flags().IntVar(&storeDataCutoff, "store-data-cutoff", 7, "number of days data store in db") + rootCmd.Flags().StringVar(&storeDataPath, "store-data-path", "/var/lib/skywire-ut/daily-data", "path of db daily data store") rootCmd.Flags().BoolVarP(&logEnabled, "log", "l", true, "enable request logging") rootCmd.Flags().StringVar(&syslogAddr, "syslog", "", "syslog server address. E.g. localhost:514") rootCmd.Flags().StringVar(&tag, "tag", "uptime_tracker", "logging tag") @@ -153,7 +155,7 @@ var rootCmd = &cobra.Command{ } enableMetrics := metricsAddr != "" - utAPI := api.New(logger, s, nonceStore, locDetails, enableLoadTesting, enableMetrics, m, cutoffStoreData) + utAPI := api.New(logger, s, nonceStore, locDetails, enableLoadTesting, enableMetrics, m, storeDataCutoff, storeDataPath) utPAPI := api.NewPrivate(logger, s) From 9095f05c5536aaed4d40f23d9898f2c546e83390 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 9 May 2023 11:03:05 +0330 Subject: [PATCH 07/15] remove missed fields from response --- pkg/uptime-tracker/api/api.go | 16 +++++++--------- pkg/uptime-tracker/api/api_test.go | 6 +++--- pkg/uptime-tracker/store/memory_store_test.go | 11 ++--------- pkg/uptime-tracker/store/uptime_response.go | 12 +++--------- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/pkg/uptime-tracker/api/api.go b/pkg/uptime-tracker/api/api.go index bbae9b7..4b06c9c 100644 --- a/pkg/uptime-tracker/api/api.go +++ b/pkg/uptime-tracker/api/api.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "math" "net" "net/http" "net/url" @@ -56,7 +55,8 @@ type API struct { visorsCacheMu sync.RWMutex dailyUptimeCache map[string]map[string]string dailyUptimeCacheMu sync.RWMutex - cutoffStoreUptimes int + storeUptimesCutoff int + storeUptimesPath string } // PrivateAPI register all the PrivateAPI endpoints. @@ -75,7 +75,7 @@ type HealthCheckResponse struct { // New constructs a new API instance. func New(log logrus.FieldLogger, s store.Store, nonceStore httpauth.NonceStore, locDetails geo.LocationDetails, - enableLoadTesting, enableMetrics bool, m utmetrics.Metrics, cutoffStoreData int) *API { + enableLoadTesting, enableMetrics bool, m utmetrics.Metrics, storeDataCutoff int, storeDataPath string) *API { if log == nil { log = logging.MustGetLogger("uptime_tracker") } @@ -86,7 +86,8 @@ func New(log logrus.FieldLogger, s store.Store, nonceStore httpauth.NonceStore, store: s, locDetails: locDetails, startedAt: time.Now(), - cutoffStoreUptimes: cutoffStoreData, + storeUptimesCutoff: storeDataCutoff, + storeUptimesPath: storeDataPath, } r := chi.NewRouter() @@ -185,7 +186,7 @@ func (api *API) updateInternalCaches(logger logrus.FieldLogger) { func (api *API) dailyRoutine(logger logrus.FieldLogger) { // api.cutoffStoreUptimes // delete old data - err := api.store.DeleteOldEntries(api.cutoffStoreUptimes) + err := api.store.DeleteOldEntries(api.storeUptimesCutoff) if err != nil { logger.WithError(err).Warn("unable to delete old entries from db") } @@ -197,7 +198,7 @@ func (api *API) dailyRoutine(logger logrus.FieldLogger) { } // save to file file, _ := json.MarshalIndent(data, "", " ") //nolint - fileName := fmt.Sprintf("/daily-data/%s-uptime-data.json", time.Now().AddDate(0, 0, -1).Format("YYYY-MM-DD")) + fileName := fmt.Sprintf("%s/%s-uptime-data.json", api.storeUptimesPath, time.Now().AddDate(0, 0, -1).Format("YYYY-MM-DD")) err = os.WriteFile(fileName, file, 0644) //nolint if err != nil { logger.WithError(err).Warn("unable to save data to json file") @@ -439,11 +440,8 @@ func (api *API) handleUptimes(w http.ResponseWriter, r *http.Request) { var uptimesV2 store.UptimeResponseV2 for _, uptime := range uptimes { var uptimev2 store.UptimeDefV2 - uptimev2.Downtime = uptime.Downtime uptimev2.Key = uptime.Key - uptimev2.Uptime = uptime.Uptime uptimev2.Online = uptime.Online - uptimev2.Percentage = math.Round(uptime.Percentage*100) / 100 uptimev2.DailyOnlineHistory = dailyUptimeHistory[uptime.Key] uptimev2.Version = uptime.Version uptimesV2 = append(uptimesV2, uptimev2) diff --git a/pkg/uptime-tracker/api/api_test.go b/pkg/uptime-tracker/api/api_test.go index 0444f33..f5ebca4 100644 --- a/pkg/uptime-tracker/api/api_test.go +++ b/pkg/uptime-tracker/api/api_test.go @@ -41,7 +41,7 @@ func TestHandleUptimes(t *testing.T) { nonceMock, err := httpauth.NewNonceStore(ctx, storeconfig.Config{Type: storeconfig.Memory}, "") require.NoError(t, err) api := New(nil, mock, nonceMock, geoFunc, false, false, - utmetrics.NewEmpty(), 0) + utmetrics.NewEmpty(), 0, "") pk, _ := cipher.GenerateKeyPair() @@ -75,7 +75,7 @@ func TestAPI_handleUpdate(t *testing.T) { nonceMock, err := httpauth.NewNonceStore(ctx, storeconfig.Config{Type: storeconfig.Memory}, "") require.NoError(t, err) api := New(nil, mock, nonceMock, geoFunc, false, false, - utmetrics.NewEmpty(), 0) + utmetrics.NewEmpty(), 0, "") t.Run("StatusOK", func(t *testing.T) { w := httptest.NewRecorder() @@ -106,7 +106,7 @@ func TestApi_UpdateRemovedMethod(t *testing.T) { nonceMock, err := httpauth.NewNonceStore(ctx, storeconfig.Config{Type: storeconfig.Memory}, "") require.NoError(t, err) api := New(nil, mock, nonceMock, geoFunc, false, false, - utmetrics.NewEmpty(), 0) + utmetrics.NewEmpty(), 0, "") t.Run("StatusGone", func(t *testing.T) { w := httptest.NewRecorder() diff --git a/pkg/uptime-tracker/store/memory_store_test.go b/pkg/uptime-tracker/store/memory_store_test.go index e29761c..7b384c0 100644 --- a/pkg/uptime-tracker/store/memory_store_test.go +++ b/pkg/uptime-tracker/store/memory_store_test.go @@ -30,17 +30,10 @@ func testUptime(t *testing.T, store Store) { } now := time.Now() - monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) - monthEnd := monthStart.AddDate(0, 1, 0) - - totalMonthSeconds := float64(int(monthEnd.Sub(monthStart).Seconds())) wantUptime := UptimeDef{ - Key: pk.String(), - Uptime: iterations, - Downtime: totalMonthSeconds - iterations, - Percentage: iterations / totalMonthSeconds * 100, - Online: true, + Key: pk.String(), + Online: true, } wantVisor := VisorDef{ diff --git a/pkg/uptime-tracker/store/uptime_response.go b/pkg/uptime-tracker/store/uptime_response.go index 777d29f..c1ff225 100644 --- a/pkg/uptime-tracker/store/uptime_response.go +++ b/pkg/uptime-tracker/store/uptime_response.go @@ -14,12 +14,9 @@ type UptimeResponse []UptimeDef // UptimeDef is the item of `UptimeResponse`. type UptimeDef struct { - Key string `json:"key"` - Uptime float64 `json:"uptime"` - Downtime float64 `json:"downtime"` - Percentage float64 `json:"percentage"` - Online bool `json:"online"` - Version string `json:"-"` + Key string `json:"key"` + Online bool `json:"online"` + Version string `json:"-"` } // UptimeResponseV2 is the tracker API response format v2 for `/uptimes`. @@ -28,9 +25,6 @@ type UptimeResponseV2 []UptimeDefV2 // UptimeDefV2 is the item of `UptimeResponseV2`. type UptimeDefV2 struct { Key string `json:"pk"` - Uptime float64 `json:"up"` - Downtime float64 `json:"down"` - Percentage float64 `json:"pct"` Online bool `json:"on"` Version string `json:"version,omitempty"` DailyOnlineHistory map[string]string `json:"daily,omitempty"` From 09955dc305bbf72d13ce22a5033adadeb31d9a0b Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 9 May 2023 11:14:25 +0330 Subject: [PATCH 08/15] update README.md --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index fe27ca6..03c8392 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,28 @@ Run `make build` to build the service and `make install` to install into go bina Refer to the [`cmd`](cmd) subdirectories for setting up the individual service locally. +### DB Setup +`uptime-tracker` needs database for running that we use postgresql here as default database. For setting it up, you just need run pg (by docker or install binary or etc.), make a database with UTF-8 character-set, and pass two credential as flag and save three of them as env variable before running services. + +First of all, you needs run postgres. You can run it by docker: +``` +docker run --name skywire-ut-pg -e POSTGRES_PASSWORD=secret -e POSTGRES_USER=skywire-ut -d postgres +``` + +then, if you want run `uptime-tracker` service, you should pass `--pg-host` and `--pg-port` as flag on running its binary, and also save `PG_USER`, `PG_PASSWORD` and `PG_DATABASE` as env variable. +``` +export PG_USER=skywire-ut +export PG_PASSWORD=secret +export PG_DATABASE=skywire-ut +``` +and run service by + +``` +./uptime-tracker --pg-host localhost --pg-port 5432 +``` + +All tables created automatically and no need handle manually. + ## Deployments TPD From 903e64ef77c0e120a384cd85bd93f4a9e87d850c Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 9 May 2023 11:15:27 +0330 Subject: [PATCH 09/15] revert dockerhub image name from skycoin to skycoinpro --- docker/docker_push.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker_push.sh b/docker/docker_push.sh index 548329a..1adc0e1 100755 --- a/docker/docker_push.sh +++ b/docker/docker_push.sh @@ -6,7 +6,7 @@ tag="$1" registry="$REGISTRY" if [ -z "$registry" ]; then - registry="skycoin" + registry="skycoinpro" fi if [ -z "$tag" ]; then From 37ebf4e591b660ab6e1703f8ce8799104793337a Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 9 May 2023 12:49:56 +0330 Subject: [PATCH 10/15] update README.md | fix save db data issue --- README.md | 9 +++++---- pkg/uptime-tracker/api/api.go | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 03c8392..ba2637e 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,10 @@ Refer to the [`cmd`](cmd) subdirectories for setting up the individual service l ### DB Setup `uptime-tracker` needs database for running that we use postgresql here as default database. For setting it up, you just need run pg (by docker or install binary or etc.), make a database with UTF-8 character-set, and pass two credential as flag and save three of them as env variable before running services. -First of all, you needs run postgres. You can run it by docker: +First of all, you needs run postgres and redis. You can run it by docker: ``` -docker run --name skywire-ut-pg -e POSTGRES_PASSWORD=secret -e POSTGRES_USER=skywire-ut -d postgres +docker run --name skywire-ut-pg -e POSTGRES_PASSWORD=secret -e POSTGRES_USER=skywire-ut -p 5432:5432 -d postgres +docker run --name my-redis -p 6379:6379 -d redis ``` then, if you want run `uptime-tracker` service, you should pass `--pg-host` and `--pg-port` as flag on running its binary, and also save `PG_USER`, `PG_PASSWORD` and `PG_DATABASE` as env variable. @@ -25,10 +26,10 @@ export PG_USER=skywire-ut export PG_PASSWORD=secret export PG_DATABASE=skywire-ut ``` -and run service by +and run it by ``` -./uptime-tracker --pg-host localhost --pg-port 5432 +./bin/uptime-tracker --pg-host localhost --pg-port 5432 --store-data-path skywire-ut/daily-data ``` All tables created automatically and no need handle manually. diff --git a/pkg/uptime-tracker/api/api.go b/pkg/uptime-tracker/api/api.go index 4b06c9c..eff4e97 100644 --- a/pkg/uptime-tracker/api/api.go +++ b/pkg/uptime-tracker/api/api.go @@ -145,7 +145,7 @@ func (api *API) log(r *http.Request) logrus.FieldLogger { // RunBackgroundTasks is function which runs periodic background tasks of API. func (api *API) RunBackgroundTasks(ctx context.Context, logger logrus.FieldLogger) { - date := time.Now().Format("YYYY-MM-DD") + date := time.Now().Format("2006-01-02") cacheTicker := time.NewTicker(time.Minute * 5) defer cacheTicker.Stop() ticker := time.NewTicker(time.Second * 10) @@ -158,9 +158,9 @@ func (api *API) RunBackgroundTasks(ctx context.Context, logger logrus.FieldLogge return case <-cacheTicker.C: api.updateInternalCaches(logger) - if time.Now().Format("YYYY-MM-DD") > date { + if time.Now().Format("2006-01-02") > date { api.dailyRoutine(logger) - date = time.Now().Format("YYYY-MM-DD") + date = time.Now().Format("2006-01-02") } case <-ticker.C: api.updateInternalState(ctx, logger) @@ -184,7 +184,6 @@ func (api *API) updateInternalCaches(logger logrus.FieldLogger) { } func (api *API) dailyRoutine(logger logrus.FieldLogger) { - // api.cutoffStoreUptimes // delete old data err := api.store.DeleteOldEntries(api.storeUptimesCutoff) if err != nil { @@ -196,9 +195,11 @@ func (api *API) dailyRoutine(logger logrus.FieldLogger) { logger.WithError(err).Warn("unable to fetch last day data from db") return } + // check path, make its if not available + os.MkdirAll(api.storeUptimesPath, os.ModePerm) //nolint // save to file file, _ := json.MarshalIndent(data, "", " ") //nolint - fileName := fmt.Sprintf("%s/%s-uptime-data.json", api.storeUptimesPath, time.Now().AddDate(0, 0, -1).Format("YYYY-MM-DD")) + fileName := fmt.Sprintf("%s/%s-uptime-data.json", api.storeUptimesPath, time.Now().AddDate(0, 0, -1).Format("2006-01-02")) err = os.WriteFile(fileName, file, 0644) //nolint if err != nil { logger.WithError(err).Warn("unable to save data to json file") From f125fb7493e59a022964501f2e3f4062cc7c8fcd Mon Sep 17 00:00:00 2001 From: Mohammed Date: Wed, 10 May 2023 16:49:16 +0330 Subject: [PATCH 11/15] new logic on store/delete old entries from db --- pkg/uptime-tracker/api/api.go | 48 ++++++++++++++-------- pkg/uptime-tracker/store/memory_store.go | 8 +++- pkg/uptime-tracker/store/postgres_store.go | 16 +++++--- pkg/uptime-tracker/store/store.go | 5 ++- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/pkg/uptime-tracker/api/api.go b/pkg/uptime-tracker/api/api.go index eff4e97..511bf8f 100644 --- a/pkg/uptime-tracker/api/api.go +++ b/pkg/uptime-tracker/api/api.go @@ -145,7 +145,6 @@ func (api *API) log(r *http.Request) logrus.FieldLogger { // RunBackgroundTasks is function which runs periodic background tasks of API. func (api *API) RunBackgroundTasks(ctx context.Context, logger logrus.FieldLogger) { - date := time.Now().Format("2006-01-02") cacheTicker := time.NewTicker(time.Minute * 5) defer cacheTicker.Stop() ticker := time.NewTicker(time.Second * 10) @@ -158,10 +157,7 @@ func (api *API) RunBackgroundTasks(ctx context.Context, logger logrus.FieldLogge return case <-cacheTicker.C: api.updateInternalCaches(logger) - if time.Now().Format("2006-01-02") > date { - api.dailyRoutine(logger) - date = time.Now().Format("2006-01-02") - } + api.dailyRoutine(logger) case <-ticker.C: api.updateInternalState(ctx, logger) } @@ -184,26 +180,44 @@ func (api *API) updateInternalCaches(logger logrus.FieldLogger) { } func (api *API) dailyRoutine(logger logrus.FieldLogger) { - // delete old data - err := api.store.DeleteOldEntries(api.storeUptimesCutoff) - if err != nil { - logger.WithError(err).Warn("unable to delete old entries from db") - } - // save yesterday data as YYYY-MM-DD-uptime-data.json file - data, err := api.store.GetLastDayData() + oldestEntry, err := api.store.GetOldestEntry() if err != nil { - logger.WithError(err).Warn("unable to fetch last day data from db") + logger.WithError(err).Warn("unable to fetch oldest entry from db") return } + + from := oldestEntry.CreatedAt + to := time.Now().AddDate(0, 0, -(api.storeUptimesCutoff)) + + for to.After(from) { + timeValue := time.Date(from.Year(), from.Month(), from.Day(), 0, 0, 0, 0, time.Now().Location()) + data, err := api.store.GetSpecificDayData(timeValue) + if err != nil { + logger.WithField("date", timeValue.Format("2006-01-02")).WithError(err).Warn("unable to fetch data specific date from db") + return + } + err = api.storeDailyData(data) + if err != nil { + if err != nil { + logger.WithError(err).Warn("unable to save data to json file") + return + } + } + err = api.store.DeleteEntries(data) + if err != nil { + logger.WithError(err).Warn("unable to delete old entries from db") + } + from.AddDate(0, 0, 1) + } +} + +func (api *API) storeDailyData(data []store.DailyUptimeHistory) error { // check path, make its if not available os.MkdirAll(api.storeUptimesPath, os.ModePerm) //nolint // save to file file, _ := json.MarshalIndent(data, "", " ") //nolint fileName := fmt.Sprintf("%s/%s-uptime-data.json", api.storeUptimesPath, time.Now().AddDate(0, 0, -1).Format("2006-01-02")) - err = os.WriteFile(fileName, file, 0644) //nolint - if err != nil { - logger.WithError(err).Warn("unable to save data to json file") - } + return os.WriteFile(fileName, file, 0644) //nolint } func (api *API) updateVisorsCache() error { diff --git a/pkg/uptime-tracker/store/memory_store.go b/pkg/uptime-tracker/store/memory_store.go index f2286ce..28a86a7 100644 --- a/pkg/uptime-tracker/store/memory_store.go +++ b/pkg/uptime-tracker/store/memory_store.go @@ -241,10 +241,14 @@ func (s *memStore) GetDailyUpdateHistory() (map[string]map[string]string, error) return map[string]map[string]string{}, nil } -func (s *memStore) DeleteOldEntries(cutoff int) error { +func (s *memStore) DeleteEntries([]DailyUptimeHistory) error { return nil } -func (s *memStore) GetLastDayData() ([]DailyUptimeHistory, error) { +func (s *memStore) GetOldestEntry() (DailyUptimeHistory, error) { + return DailyUptimeHistory{}, nil +} + +func (s *memStore) GetSpecificDayData(time time.Time) ([]DailyUptimeHistory, error) { return []DailyUptimeHistory{}, nil } diff --git a/pkg/uptime-tracker/store/postgres_store.go b/pkg/uptime-tracker/store/postgres_store.go index b326408..5db2c6d 100644 --- a/pkg/uptime-tracker/store/postgres_store.go +++ b/pkg/uptime-tracker/store/postgres_store.go @@ -283,16 +283,20 @@ func (s *postgresStore) GetNumberOfUptimesByYearAndMonth(year int, month time.Mo return int(counter), err } -func (s *postgresStore) DeleteOldEntries(cutoff int) error { - deleteDate := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 0, -cutoff) - err := s.client.Where("created_at < ?", deleteDate).Delete(&DailyUptimeHistory{}).Error +func (s *postgresStore) DeleteEntries(data []DailyUptimeHistory) error { + err := s.client.Delete(&data).Error return err } -func (s *postgresStore) GetLastDayData() ([]DailyUptimeHistory, error) { +func (s *postgresStore) GetOldestEntry() (DailyUptimeHistory, error) { + var data DailyUptimeHistory + err := s.client.Limit(1).Order("created_at asc").Find(&data).Error + return data, err +} + +func (s *postgresStore) GetSpecificDayData(timeValue time.Time) ([]DailyUptimeHistory, error) { var data []DailyUptimeHistory - today := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Now().Location()) - err := s.client.Where("created_at BETWEEN ? AND ?", today.AddDate(0, 0, -1), today.Add(-1*time.Second)).Find(&data).Error + err := s.client.Where("created_at BETWEEN ? AND ?", timeValue, timeValue.AddDate(0, 0, 1).Add(-1*time.Second)).Find(&data).Error return data, err } diff --git a/pkg/uptime-tracker/store/store.go b/pkg/uptime-tracker/store/store.go index 5e116d5..df251d6 100644 --- a/pkg/uptime-tracker/store/store.go +++ b/pkg/uptime-tracker/store/store.go @@ -24,8 +24,9 @@ type Store interface { GetNumberOfUptimesByYearAndMonth(year int, month time.Month) (int, error) UpdateUptime(pk, ip, version string) error GetDailyUpdateHistory() (map[string]map[string]string, error) - DeleteOldEntries(cutoff int) error - GetLastDayData() ([]DailyUptimeHistory, error) + DeleteEntries([]DailyUptimeHistory) error + GetOldestEntry() (DailyUptimeHistory, error) + GetSpecificDayData(time time.Time) ([]DailyUptimeHistory, error) Close() } From 6dcf7f907c4d94704019daa8eb8a0b89f197f3e1 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Wed, 10 May 2023 16:51:19 +0330 Subject: [PATCH 12/15] add maxOpenConn PR changes here --- cmd/uptime-tracker/commands/root.go | 4 +++- internal/pg/lib.go | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/uptime-tracker/commands/root.go b/cmd/uptime-tracker/commands/root.go index ae6a62a..2cffc26 100644 --- a/cmd/uptime-tracker/commands/root.go +++ b/cmd/uptime-tracker/commands/root.go @@ -45,6 +45,7 @@ var ( redisPoolSize int pgHost string pgPort string + pgMaxOpenConn int logEnabled bool syslogAddr string tag string @@ -65,6 +66,7 @@ func init() { rootCmd.Flags().IntVar(&redisPoolSize, "redis-pool-size", 10, "redis connection pool size") rootCmd.Flags().StringVar(&pgHost, "pg-host", "localhost", "host of postgres") rootCmd.Flags().StringVar(&pgPort, "pg-port", "5432", "port of postgres") + rootCmd.Flags().IntVar(&pgMaxOpenConn, "pg-max-open-conn", 60, "maximum open connection of db") rootCmd.Flags().IntVar(&storeDataCutoff, "store-data-cutoff", 7, "number of days data store in db") rootCmd.Flags().StringVar(&storeDataPath, "store-data-path", "/var/lib/skywire-ut/daily-data", "path of db daily data store") rootCmd.Flags().BoolVarP(&logEnabled, "log", "l", true, "enable request logging") @@ -120,7 +122,7 @@ var rootCmd = &cobra.Command{ pgPassword, pgDatabase) - gormDB, err = pg.Init(dsn) + gormDB, err = pg.Init(dsn, pgMaxOpenConn) if err != nil { logger.Fatalf("Failed to connect to database %v", err) } diff --git a/internal/pg/lib.go b/internal/pg/lib.go index c3b037f..3cf5e96 100644 --- a/internal/pg/lib.go +++ b/internal/pg/lib.go @@ -7,11 +7,12 @@ import ( ) // Init creates a connection to database -func Init(dns string) (*gorm.DB, error) { +func Init(dns string, pgMaxOpenConn int) (*gorm.DB, error) { db, err := gorm.Open(postgres.Open(dns), &gorm.Config{}) if err != nil { return db, err } - + dbConf, _ := db.DB() //nolint + dbConf.SetMaxOpenConns(pgMaxOpenConn) return db, nil } From a2a2778ac2a31a78e2d3e3684c1b493229fd75c2 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Wed, 10 May 2023 17:09:13 +0330 Subject: [PATCH 13/15] fix delete old entries --- pkg/uptime-tracker/store/postgres_store.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/uptime-tracker/store/postgres_store.go b/pkg/uptime-tracker/store/postgres_store.go index 5db2c6d..da1a801 100644 --- a/pkg/uptime-tracker/store/postgres_store.go +++ b/pkg/uptime-tracker/store/postgres_store.go @@ -284,8 +284,11 @@ func (s *postgresStore) GetNumberOfUptimesByYearAndMonth(year int, month time.Mo } func (s *postgresStore) DeleteEntries(data []DailyUptimeHistory) error { - err := s.client.Delete(&data).Error - return err + for _, entry := range data { + err := s.client.Delete(&DailyUptimeHistory{}, "id = ?", entry.ID).Error + return err + } + return nil } func (s *postgresStore) GetOldestEntry() (DailyUptimeHistory, error) { From baeb7ee46ded532ee8ac4221d87240ae5cb3c7c8 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 11 May 2023 12:28:57 +0330 Subject: [PATCH 14/15] fix date issue --- pkg/uptime-tracker/api/api.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/uptime-tracker/api/api.go b/pkg/uptime-tracker/api/api.go index 511bf8f..8d6f873 100644 --- a/pkg/uptime-tracker/api/api.go +++ b/pkg/uptime-tracker/api/api.go @@ -145,6 +145,8 @@ func (api *API) log(r *http.Request) logrus.FieldLogger { // RunBackgroundTasks is function which runs periodic background tasks of API. func (api *API) RunBackgroundTasks(ctx context.Context, logger logrus.FieldLogger) { + + api.dailyRoutine(logger) cacheTicker := time.NewTicker(time.Minute * 5) defer cacheTicker.Stop() ticker := time.NewTicker(time.Second * 10) @@ -196,27 +198,25 @@ func (api *API) dailyRoutine(logger logrus.FieldLogger) { logger.WithField("date", timeValue.Format("2006-01-02")).WithError(err).Warn("unable to fetch data specific date from db") return } - err = api.storeDailyData(data) + err = api.storeDailyData(data, timeValue) if err != nil { - if err != nil { - logger.WithError(err).Warn("unable to save data to json file") - return - } + logger.WithError(err).Warn("unable to save data to json file") + return } err = api.store.DeleteEntries(data) if err != nil { logger.WithError(err).Warn("unable to delete old entries from db") } - from.AddDate(0, 0, 1) + from = from.AddDate(0, 0, 1) } } -func (api *API) storeDailyData(data []store.DailyUptimeHistory) error { +func (api *API) storeDailyData(data []store.DailyUptimeHistory, timeValue time.Time) error { // check path, make its if not available os.MkdirAll(api.storeUptimesPath, os.ModePerm) //nolint // save to file file, _ := json.MarshalIndent(data, "", " ") //nolint - fileName := fmt.Sprintf("%s/%s-uptime-data.json", api.storeUptimesPath, time.Now().AddDate(0, 0, -1).Format("2006-01-02")) + fileName := fmt.Sprintf("%s/%s-uptime-data.json", api.storeUptimesPath, timeValue.Format("2006-01-02")) return os.WriteFile(fileName, file, 0644) //nolint } From c4afad2656a81c6cb1faa775f4ae596b5f1a2d58 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 11 May 2023 13:15:17 +0330 Subject: [PATCH 15/15] fix delete old entries --- pkg/uptime-tracker/api/api.go | 1 - pkg/uptime-tracker/store/postgres_store.go | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/uptime-tracker/api/api.go b/pkg/uptime-tracker/api/api.go index 8d6f873..3ef780f 100644 --- a/pkg/uptime-tracker/api/api.go +++ b/pkg/uptime-tracker/api/api.go @@ -145,7 +145,6 @@ func (api *API) log(r *http.Request) logrus.FieldLogger { // RunBackgroundTasks is function which runs periodic background tasks of API. func (api *API) RunBackgroundTasks(ctx context.Context, logger logrus.FieldLogger) { - api.dailyRoutine(logger) cacheTicker := time.NewTicker(time.Minute * 5) defer cacheTicker.Stop() diff --git a/pkg/uptime-tracker/store/postgres_store.go b/pkg/uptime-tracker/store/postgres_store.go index da1a801..1408f8c 100644 --- a/pkg/uptime-tracker/store/postgres_store.go +++ b/pkg/uptime-tracker/store/postgres_store.go @@ -285,8 +285,10 @@ func (s *postgresStore) GetNumberOfUptimesByYearAndMonth(year int, month time.Mo func (s *postgresStore) DeleteEntries(data []DailyUptimeHistory) error { for _, entry := range data { - err := s.client.Delete(&DailyUptimeHistory{}, "id = ?", entry.ID).Error - return err + err := s.client.Delete(&DailyUptimeHistory{}, entry.ID).Error + if err != nil { + return err + } } return nil }