storj/storagenode/storageusage/storageusage_test.go
Clement Sam 3d3f9d133a storagenode: fix B*h to bytes disk usage conversion
The used space graph values are correct when a single satellite is
selected but wrong for 'All satellites'. This is related to the
queries for getting the individual disk usages for all satellites
per day and the summary and average for all satellites per day:

1. dividing the sum of at_rest_total by the total_hours is wrong.
Simply put, we were assuming that, for example (4/2)+(6/3) equals
to (4+6)/(2+3), assuming we had 4 and 6 at_rest_total values with
2 and 3 respective hours.

2. To get the average, we need to first find the sum of the
at_rest_total_bytes for each timestamp across all satellites
before taking the average of the sums instead of just taking the
average from the individual satellite values.

Closes https://github.com/storj/storj/issues/5519

Change-Id: Ib1314e238b695a6c1ecd9f9171ee86dd56bb3b24
2023-02-06 18:50:31 +00:00

202 lines
6.4 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package storageusage_test
import (
"math"
"testing"
"time"
"github.com/stretchr/testify/assert"
"storj.io/common/storj"
"storj.io/common/testcontext"
"storj.io/common/testrand"
"storj.io/storj/storagenode"
"storj.io/storj/storagenode/storagenodedb/storagenodedbtest"
"storj.io/storj/storagenode/storageusage"
)
func TestStorageUsage(t *testing.T) {
const (
satelliteNum = 10
days = 30
)
now := time.Now().UTC()
var satellites []storj.NodeID
satelliteID := testrand.NodeID()
satellites = append(satellites, satelliteID)
for i := 0; i < satelliteNum-1; i++ {
satellites = append(satellites, testrand.NodeID())
}
stamps := storagenodedbtest.MakeStorageUsageStamps(satellites, days, now)
var totalSummary, averageUsage float64
expectedDailyStamps := make(map[storj.NodeID]map[time.Time]storageusage.Stamp)
expectedDailyStampsTotals := make(map[time.Time]float64)
summaryBySatellite := make(map[storj.NodeID]float64)
averageBySatellite := make(map[storj.NodeID]float64)
for _, stamp := range stamps {
if expectedDailyStamps[stamp.SatelliteID] == nil {
expectedDailyStamps[stamp.SatelliteID] = map[time.Time]storageusage.Stamp{}
}
expectedDailyStamps[stamp.SatelliteID][stamp.IntervalStart.UTC()] = stamp
}
totalUsageBytes := float64(0)
totalStamps := float64(0)
for _, satellite := range satellites {
satelliteUsageBytes := float64(0)
for _, stamp := range expectedDailyStamps[satellite] {
intervalStart := stamp.IntervalStart.UTC()
expectedDailyStampsTotals[intervalStart] += stamp.AtRestTotal
summaryBySatellite[satellite] += stamp.AtRestTotal
totalSummary += stamp.AtRestTotal
satelliteUsageBytes += stamp.AtRestTotalBytes
totalUsageBytes += stamp.AtRestTotalBytes
totalStamps++
}
averageBySatellite[satellite] = satelliteUsageBytes / float64(len(expectedDailyStamps[satellite]))
}
averageUsage = totalUsageBytes / float64(len(expectedDailyStampsTotals))
storagenodedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db storagenode.DB) {
storageUsageDB := db.StorageUsage()
t.Run("store", func(t *testing.T) {
err := storageUsageDB.Store(ctx, stamps)
assert.NoError(t, err)
})
t.Run("get daily", func(t *testing.T) {
from := now.AddDate(0, 0, -20)
res, err := storageUsageDB.GetDaily(ctx, satelliteID, from, now)
assert.NoError(t, err)
assert.NotNil(t, res)
for _, stamp := range res {
assert.Equal(t, satelliteID, stamp.SatelliteID)
assert.Equal(t, expectedDailyStamps[satelliteID][stamp.IntervalStart].AtRestTotal, stamp.AtRestTotal)
assert.Equal(t, expectedDailyStamps[satelliteID][stamp.IntervalStart].AtRestTotalBytes, stamp.AtRestTotalBytes)
assert.Equal(t, expectedDailyStamps[satelliteID][stamp.IntervalStart].IntervalInHours, stamp.IntervalInHours)
}
})
t.Run("get daily total", func(t *testing.T) {
res, err := storageUsageDB.GetDailyTotal(ctx, time.Time{}, now)
assert.NoError(t, err)
assert.NotNil(t, res)
assert.Equal(t, days, len(res))
for _, stamp := range res {
// there can be inconsistencies in the values due to rounding off errors
// and can make the test flaky.
// rounding the values to 5 decimal places to avoid flakiness
assert.Equal(t, roundFloat(expectedDailyStampsTotals[stamp.IntervalStart]), roundFloat(stamp.AtRestTotal))
}
})
t.Run("summary satellite", func(t *testing.T) {
summ, averageUsageInBytes, err := storageUsageDB.SatelliteSummary(ctx, satelliteID, time.Time{}, now)
assert.NoError(t, err)
assert.Equal(t, roundFloat(summaryBySatellite[satelliteID]), roundFloat(summ))
assert.Equal(t, roundFloat(averageBySatellite[satelliteID]), roundFloat(averageUsageInBytes))
})
t.Run("summary", func(t *testing.T) {
summ, averageUsageInBytes, err := storageUsageDB.Summary(ctx, time.Time{}, now)
assert.NoError(t, err)
assert.Equal(t, roundFloat(totalSummary), roundFloat(summ))
assert.Equal(t, roundFloat(averageUsage), roundFloat(averageUsageInBytes))
})
})
}
func TestEmptyStorageUsage(t *testing.T) {
storagenodedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db storagenode.DB) {
var emptySummary, zeroHourInterval float64
now := time.Now()
storageUsageDB := db.StorageUsage()
t.Run("get daily", func(t *testing.T) {
res, err := storageUsageDB.GetDaily(ctx, storj.NodeID{}, time.Time{}, now)
assert.NoError(t, err)
assert.Nil(t, res)
})
t.Run("get daily total", func(t *testing.T) {
res, err := storageUsageDB.GetDailyTotal(ctx, time.Time{}, now)
assert.NoError(t, err)
assert.Nil(t, res)
})
t.Run("summary satellite", func(t *testing.T) {
summ, averageUsageInBytes, err := storageUsageDB.SatelliteSummary(ctx, storj.NodeID{}, time.Time{}, now)
assert.NoError(t, err)
assert.Equal(t, emptySummary, summ)
assert.Equal(t, zeroHourInterval, averageUsageInBytes)
})
t.Run("summary", func(t *testing.T) {
summ, averageUsageInBytes, err := storageUsageDB.Summary(ctx, time.Time{}, now)
assert.NoError(t, err)
assert.Equal(t, emptySummary, summ)
assert.Equal(t, zeroHourInterval, averageUsageInBytes)
})
})
}
func TestZeroStorageUsage(t *testing.T) {
storagenodedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db storagenode.DB) {
storageUsageDB := db.StorageUsage()
now := time.Now().UTC()
satelliteID := testrand.NodeID()
stamp := storageusage.Stamp{
SatelliteID: satelliteID,
AtRestTotal: 0,
IntervalStart: time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()),
IntervalEndTime: now,
}
expectedStamp := []storageusage.Stamp{stamp}
t.Run("store", func(t *testing.T) {
err := storageUsageDB.Store(ctx, []storageusage.Stamp{stamp})
assert.NoError(t, err)
})
t.Run("get daily", func(t *testing.T) {
res, err := storageUsageDB.GetDaily(ctx, satelliteID, time.Time{}, now)
assert.NoError(t, err)
assert.Equal(t, len(res), 1)
assert.Equal(t, expectedStamp[0].AtRestTotal, res[0].AtRestTotal)
})
t.Run("get daily total", func(t *testing.T) {
res, err := storageUsageDB.GetDailyTotal(ctx, time.Time{}, now)
assert.NoError(t, err)
assert.Equal(t, len(res), 1)
assert.Equal(t, expectedStamp[0].AtRestTotal, res[0].AtRestTotal)
})
})
}
// RoundFloat rounds float value to 5 decimal places.
func roundFloat(value float64) float64 {
return math.Round(value*100000) / 100000
}