2019-04-09 14:48:35 +01:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package accounting_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2019-09-04 17:05:34 +01:00
|
|
|
"math"
|
2019-04-09 14:48:35 +01:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2019-08-08 14:47:04 +01:00
|
|
|
"github.com/stretchr/testify/assert"
|
2019-04-09 14:48:35 +01:00
|
|
|
"github.com/stretchr/testify/require"
|
2019-06-13 13:46:08 +01:00
|
|
|
|
2019-12-27 11:48:47 +00:00
|
|
|
"storj.io/common/memory"
|
|
|
|
"storj.io/common/storj"
|
|
|
|
"storj.io/common/testcontext"
|
|
|
|
"storj.io/common/testrand"
|
2020-03-30 10:08:50 +01:00
|
|
|
"storj.io/common/uuid"
|
2019-04-09 14:48:35 +01:00
|
|
|
"storj.io/storj/satellite"
|
2019-07-28 06:55:36 +01:00
|
|
|
"storj.io/storj/satellite/accounting"
|
2019-12-10 16:12:49 +00:00
|
|
|
"storj.io/storj/satellite/console"
|
2021-04-21 13:42:57 +01:00
|
|
|
"storj.io/storj/satellite/metabase"
|
2019-04-09 14:48:35 +01:00
|
|
|
"storj.io/storj/satellite/satellitedb/satellitedbtest"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestSaveBucketTallies(t *testing.T) {
|
2020-01-19 16:29:15 +00:00
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
2019-04-09 14:48:35 +01:00
|
|
|
// Setup: create bucket storage tallies
|
2020-04-02 15:18:08 +01:00
|
|
|
projectID := testrand.UUID()
|
2019-06-26 11:38:51 +01:00
|
|
|
|
|
|
|
bucketTallies, expectedTallies, err := createBucketStorageTallies(projectID)
|
2019-04-09 14:48:35 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Execute test: retrieve the save tallies and confirm they contains the expected data
|
|
|
|
intervalStart := time.Now()
|
2019-05-10 20:05:42 +01:00
|
|
|
pdb := db.ProjectAccounting()
|
2019-09-12 18:31:50 +01:00
|
|
|
|
|
|
|
err = pdb.SaveTallies(ctx, intervalStart, bucketTallies)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
tallies, err := pdb.GetTallies(ctx)
|
2019-04-09 14:48:35 +01:00
|
|
|
require.NoError(t, err)
|
2019-09-12 18:31:50 +01:00
|
|
|
for _, tally := range tallies {
|
2019-04-09 14:48:35 +01:00
|
|
|
require.Contains(t, expectedTallies, tally)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-08-08 14:47:04 +01:00
|
|
|
func TestStorageNodeUsage(t *testing.T) {
|
2020-01-19 16:29:15 +00:00
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
2019-10-04 20:09:52 +01:00
|
|
|
const days = 30
|
2019-09-04 17:05:34 +01:00
|
|
|
|
|
|
|
now := time.Now().UTC()
|
|
|
|
|
2019-08-08 14:47:04 +01:00
|
|
|
nodeID := testrand.NodeID()
|
2019-09-04 17:05:34 +01:00
|
|
|
startDate := now.Add(time.Hour * 24 * -days)
|
2019-08-08 14:47:04 +01:00
|
|
|
|
|
|
|
var nodes storj.NodeIDList
|
2019-10-04 20:09:52 +01:00
|
|
|
nodes = append(nodes, nodeID, testrand.NodeID(), testrand.NodeID(), testrand.NodeID())
|
2019-08-08 14:47:04 +01:00
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
rollups, tallies, lastDate := makeRollupsAndStorageNodeStorageTallies(nodes, startDate, days)
|
2019-08-08 14:47:04 +01:00
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
lastRollup := rollups[lastDate]
|
2019-08-08 14:47:04 +01:00
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
accountingDB := db.StoragenodeAccounting()
|
|
|
|
|
|
|
|
// create last rollup timestamp
|
|
|
|
_, err := accountingDB.LastTimestamp(ctx, accounting.LastRollup)
|
2019-08-08 14:47:04 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
// save tallies
|
|
|
|
for latestTally, tallies := range tallies {
|
2023-03-22 18:05:48 +00:00
|
|
|
err = accountingDB.SaveTallies(ctx, latestTally, tallies.nodeIDs, tallies.totals)
|
2019-09-04 17:05:34 +01:00
|
|
|
require.NoError(t, err)
|
2019-08-08 14:47:04 +01:00
|
|
|
}
|
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
// save rollup
|
|
|
|
err = accountingDB.SaveRollup(ctx, lastDate.Add(time.Hour*-24), rollups)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
t.Run("usage with pending tallies", func(t *testing.T) {
|
|
|
|
nodeStorageUsages, err := accountingDB.QueryStorageNodeUsage(ctx, nodeID, time.Time{}, now)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.NotNil(t, nodeStorageUsages)
|
|
|
|
assert.Equal(t, days, len(nodeStorageUsages))
|
2019-08-08 14:47:04 +01:00
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
// check usage from rollups
|
|
|
|
for _, usage := range nodeStorageUsages[:len(nodeStorageUsages)-1] {
|
|
|
|
assert.Equal(t, nodeID, usage.NodeID)
|
2019-10-04 20:09:52 +01:00
|
|
|
dateRollup, ok := rollups[usage.Timestamp.UTC()]
|
|
|
|
if assert.True(t, ok) {
|
|
|
|
nodeRollup, ok := dateRollup[nodeID]
|
|
|
|
if assert.True(t, ok) {
|
|
|
|
assert.Equal(t, nodeRollup.AtRestTotal, usage.StorageUsed)
|
|
|
|
}
|
|
|
|
}
|
2019-09-04 17:05:34 +01:00
|
|
|
}
|
2019-08-08 14:47:04 +01:00
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
// check last usage that calculated from tallies
|
|
|
|
lastUsage := nodeStorageUsages[len(nodeStorageUsages)-1]
|
|
|
|
|
2019-10-04 20:09:52 +01:00
|
|
|
assert.Equal(t, nodeID, lastUsage.NodeID)
|
|
|
|
assert.Equal(t, lastRollup[nodeID].StartTime, lastUsage.Timestamp.UTC())
|
|
|
|
assert.Equal(t, lastRollup[nodeID].AtRestTotal, lastUsage.StorageUsed)
|
2019-09-04 17:05:34 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("usage entirely from rollups", func(t *testing.T) {
|
|
|
|
const (
|
|
|
|
start = 10
|
|
|
|
// should be greater than 2
|
|
|
|
// not to include tallies into result
|
|
|
|
end = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
startDate, endDate := now.Add(time.Hour*24*-start), now.Add(time.Hour*24*-end)
|
|
|
|
|
|
|
|
nodeStorageUsages, err := accountingDB.QueryStorageNodeUsage(ctx, nodeID, startDate, endDate)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.NotNil(t, nodeStorageUsages)
|
|
|
|
assert.Equal(t, start-end, len(nodeStorageUsages))
|
|
|
|
|
|
|
|
for _, usage := range nodeStorageUsages {
|
|
|
|
assert.Equal(t, nodeID, usage.NodeID)
|
2019-10-04 20:09:52 +01:00
|
|
|
dateRollup, ok := rollups[usage.Timestamp.UTC()]
|
|
|
|
if assert.True(t, ok) {
|
|
|
|
nodeRollup, ok := dateRollup[nodeID]
|
|
|
|
if assert.True(t, ok) {
|
|
|
|
assert.Equal(t, nodeRollup.AtRestTotal, usage.StorageUsed)
|
|
|
|
}
|
|
|
|
}
|
2019-09-04 17:05:34 +01:00
|
|
|
}
|
|
|
|
})
|
2019-08-08 14:47:04 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-07-28 15:58:47 +01:00
|
|
|
// There can be more than one rollup in a day. Test that the sums are grouped by day.
|
|
|
|
func TestStorageNodeUsage_TwoRollupsInADay(t *testing.T) {
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
|
|
|
now := time.Now().UTC()
|
|
|
|
|
|
|
|
nodeID := testrand.NodeID()
|
|
|
|
|
|
|
|
accountingDB := db.StoragenodeAccounting()
|
|
|
|
|
|
|
|
// create last rollup timestamp
|
|
|
|
_, err := accountingDB.LastTimestamp(ctx, accounting.LastRollup)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
t1 := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
|
|
|
t2 := time.Date(now.Year(), now.Month(), now.Day(), 12, 0, 0, 0, now.Location())
|
|
|
|
rollups := make(accounting.RollupStats)
|
|
|
|
|
|
|
|
rollups[t1] = make(map[storj.NodeID]*accounting.Rollup)
|
|
|
|
rollups[t2] = make(map[storj.NodeID]*accounting.Rollup)
|
|
|
|
|
|
|
|
rollups[t1][nodeID] = &accounting.Rollup{
|
2022-07-11 21:18:10 +01:00
|
|
|
NodeID: nodeID,
|
|
|
|
AtRestTotal: 1000,
|
|
|
|
StartTime: t1,
|
|
|
|
IntervalEndTime: t1,
|
2020-07-28 15:58:47 +01:00
|
|
|
}
|
|
|
|
rollups[t2][nodeID] = &accounting.Rollup{
|
2022-07-11 21:18:10 +01:00
|
|
|
NodeID: nodeID,
|
|
|
|
AtRestTotal: 500,
|
|
|
|
StartTime: t2,
|
|
|
|
IntervalEndTime: t2,
|
2020-07-28 15:58:47 +01:00
|
|
|
}
|
|
|
|
// save rollup
|
|
|
|
err = accountingDB.SaveRollup(ctx, now.Add(time.Hour*-24), rollups)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-11-04 17:24:11 +00:00
|
|
|
nodeStorageUsages, err := accountingDB.QueryStorageNodeUsage(ctx, nodeID, t1.Add(-24*time.Hour), t2.Add(24*time.Hour))
|
2020-07-28 15:58:47 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, nodeStorageUsages)
|
|
|
|
require.Equal(t, 1, len(nodeStorageUsages))
|
|
|
|
|
|
|
|
require.Equal(t, nodeID, nodeStorageUsages[0].NodeID)
|
|
|
|
require.EqualValues(t, 1500, nodeStorageUsages[0].StorageUsed)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-25 14:18:04 +00:00
|
|
|
func TestProjectLimits(t *testing.T) {
|
2020-01-19 16:29:15 +00:00
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
2020-04-02 15:18:08 +01:00
|
|
|
proj, err := db.Console().Projects().Insert(ctx, &console.Project{Name: "test", OwnerID: testrand.UUID()})
|
2019-11-25 14:18:04 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-12-10 16:12:49 +00:00
|
|
|
err = db.ProjectAccounting().UpdateProjectUsageLimit(ctx, proj.ID, 1)
|
2019-11-25 14:18:04 +00:00
|
|
|
require.NoError(t, err)
|
2020-05-12 14:01:15 +01:00
|
|
|
err = db.ProjectAccounting().UpdateProjectBandwidthLimit(ctx, proj.ID, 2)
|
2019-11-25 14:18:04 +00:00
|
|
|
|
|
|
|
t.Run("get", func(t *testing.T) {
|
2019-12-10 16:12:49 +00:00
|
|
|
storageLimit, err := db.ProjectAccounting().GetProjectStorageLimit(ctx, proj.ID)
|
2019-11-25 14:18:04 +00:00
|
|
|
assert.NoError(t, err)
|
2020-09-06 00:02:12 +01:00
|
|
|
assert.Equal(t, memory.Size(1).Int64(), *storageLimit)
|
2019-11-25 14:18:04 +00:00
|
|
|
|
2019-12-10 16:12:49 +00:00
|
|
|
bandwidthLimit, err := db.ProjectAccounting().GetProjectBandwidthLimit(ctx, proj.ID)
|
2019-11-25 14:18:04 +00:00
|
|
|
assert.NoError(t, err)
|
2020-09-06 00:02:12 +01:00
|
|
|
assert.Equal(t, memory.Size(2).Int64(), *bandwidthLimit)
|
2019-11-25 14:18:04 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("update", func(t *testing.T) {
|
2019-12-10 16:12:49 +00:00
|
|
|
err = db.ProjectAccounting().UpdateProjectUsageLimit(ctx, proj.ID, 4)
|
2019-11-25 14:18:04 +00:00
|
|
|
require.NoError(t, err)
|
2020-05-12 14:01:15 +01:00
|
|
|
err = db.ProjectAccounting().UpdateProjectBandwidthLimit(ctx, proj.ID, 3)
|
2019-11-25 14:18:04 +00:00
|
|
|
|
2019-12-10 16:12:49 +00:00
|
|
|
storageLimit, err := db.ProjectAccounting().GetProjectStorageLimit(ctx, proj.ID)
|
2019-11-25 14:18:04 +00:00
|
|
|
assert.NoError(t, err)
|
2020-09-06 00:02:12 +01:00
|
|
|
assert.Equal(t, memory.Size(4).Int64(), *storageLimit)
|
2019-11-25 14:18:04 +00:00
|
|
|
|
2019-12-10 16:12:49 +00:00
|
|
|
bandwidthLimit, err := db.ProjectAccounting().GetProjectBandwidthLimit(ctx, proj.ID)
|
2019-11-25 14:18:04 +00:00
|
|
|
assert.NoError(t, err)
|
2020-09-06 00:02:12 +01:00
|
|
|
assert.Equal(t, memory.Size(3).Int64(), *bandwidthLimit)
|
2019-11-25 14:18:04 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-08-31 11:14:20 +01:00
|
|
|
func createBucketStorageTallies(projectID uuid.UUID) (map[metabase.BucketLocation]*accounting.BucketTally, []accounting.BucketTally, error) {
|
|
|
|
bucketTallies := make(map[metabase.BucketLocation]*accounting.BucketTally)
|
2019-04-09 14:48:35 +01:00
|
|
|
var expectedTallies []accounting.BucketTally
|
|
|
|
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
bucketName := fmt.Sprintf("%s%d", "testbucket", i)
|
2020-08-31 11:14:20 +01:00
|
|
|
bucketLocation := metabase.BucketLocation{
|
|
|
|
ProjectID: projectID,
|
|
|
|
BucketName: bucketName,
|
|
|
|
}
|
2019-04-09 14:48:35 +01:00
|
|
|
|
|
|
|
// Setup: The data in this tally should match the pointer that the uplink.upload created
|
|
|
|
tally := accounting.BucketTally{
|
2020-08-31 11:14:20 +01:00
|
|
|
BucketLocation: metabase.BucketLocation{
|
|
|
|
ProjectID: projectID,
|
|
|
|
BucketName: bucketName,
|
|
|
|
},
|
2021-07-01 12:29:25 +01:00
|
|
|
ObjectCount: int64(1),
|
|
|
|
TotalSegments: int64(2),
|
|
|
|
TotalBytes: int64(2),
|
|
|
|
MetadataSize: int64(1),
|
2019-04-09 14:48:35 +01:00
|
|
|
}
|
2020-08-31 11:14:20 +01:00
|
|
|
bucketTallies[bucketLocation] = &tally
|
2019-04-09 14:48:35 +01:00
|
|
|
expectedTallies = append(expectedTallies, tally)
|
|
|
|
|
|
|
|
}
|
|
|
|
return bucketTallies, expectedTallies, nil
|
|
|
|
}
|
2019-08-08 14:47:04 +01:00
|
|
|
|
2023-03-22 18:05:48 +00:00
|
|
|
type nodesAndTallies struct {
|
|
|
|
nodeIDs []storj.NodeID
|
|
|
|
totals []float64
|
|
|
|
}
|
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
// make rollups and tallies for specified nodes and date range.
|
2023-03-22 18:05:48 +00:00
|
|
|
func makeRollupsAndStorageNodeStorageTallies(nodes []storj.NodeID, start time.Time, days int) (accounting.RollupStats, map[time.Time]nodesAndTallies, time.Time) {
|
2019-08-08 14:47:04 +01:00
|
|
|
rollups := make(accounting.RollupStats)
|
2023-03-22 18:05:48 +00:00
|
|
|
tallies := make(map[time.Time]nodesAndTallies)
|
2019-09-04 17:05:34 +01:00
|
|
|
|
|
|
|
const (
|
|
|
|
hours = 12
|
|
|
|
)
|
2019-08-08 14:47:04 +01:00
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
for i := 0; i < days; i++ {
|
|
|
|
startDay := time.Date(start.Year(), start.Month(), start.Day()+i, 0, 0, 0, 0, start.Location())
|
|
|
|
if rollups[startDay] == nil {
|
|
|
|
rollups[startDay] = make(map[storj.NodeID]*accounting.Rollup)
|
2019-08-08 14:47:04 +01:00
|
|
|
}
|
|
|
|
|
2023-03-22 18:05:48 +00:00
|
|
|
for h := 0; h < hours; h++ {
|
|
|
|
intervalEndTime := startDay.Add(time.Hour * time.Duration(h))
|
|
|
|
tallies[intervalEndTime] = nodesAndTallies{
|
|
|
|
nodeIDs: make([]storj.NodeID, len(nodes)),
|
|
|
|
totals: make([]float64, len(nodes)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for nodeIndex, node := range nodes {
|
2019-08-08 14:47:04 +01:00
|
|
|
rollup := &accounting.Rollup{
|
2019-09-04 17:05:34 +01:00
|
|
|
NodeID: node,
|
|
|
|
StartTime: startDay,
|
2019-08-08 14:47:04 +01:00
|
|
|
}
|
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
for h := 0; h < hours; h++ {
|
2022-07-11 21:18:10 +01:00
|
|
|
intervalEndTime := startDay.Add(time.Hour * time.Duration(h))
|
2019-09-04 17:05:34 +01:00
|
|
|
|
|
|
|
tallieAtRest := math.Round(testrand.Float64n(1000))
|
2023-03-22 18:05:48 +00:00
|
|
|
tallies[intervalEndTime].nodeIDs[nodeIndex] = node
|
|
|
|
tallies[intervalEndTime].totals[nodeIndex] = tallieAtRest
|
2019-09-04 17:05:34 +01:00
|
|
|
rollup.AtRestTotal += tallieAtRest
|
2022-07-11 21:18:10 +01:00
|
|
|
rollup.IntervalEndTime = intervalEndTime
|
2019-09-04 17:05:34 +01:00
|
|
|
}
|
2019-08-08 14:47:04 +01:00
|
|
|
|
2019-09-04 17:05:34 +01:00
|
|
|
rollups[startDay][node] = rollup
|
|
|
|
}
|
2019-08-08 14:47:04 +01:00
|
|
|
}
|
|
|
|
|
2019-10-04 20:09:52 +01:00
|
|
|
return rollups, tallies, time.Date(start.Year(), start.Month(), start.Day()+days-1, 0, 0, 0, 0, start.Location())
|
2019-08-08 14:47:04 +01:00
|
|
|
}
|