2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-11-14 01:22:18 +00:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2019-01-23 19:58:44 +00:00
|
|
|
package rollup_test
|
2019-01-18 16:53:23 +00:00
|
|
|
|
|
|
|
import (
|
2019-04-04 16:20:59 +01:00
|
|
|
"context"
|
2019-01-18 16:53:23 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
2019-04-03 04:55:24 +01:00
|
|
|
"github.com/stretchr/testify/require"
|
2019-04-23 20:21:30 +01:00
|
|
|
"go.uber.org/zap"
|
2019-01-18 16:53:23 +00:00
|
|
|
|
2019-12-27 11:48:47 +00:00
|
|
|
"storj.io/common/pb"
|
|
|
|
"storj.io/common/storj"
|
|
|
|
"storj.io/common/testcontext"
|
2019-11-14 19:46:15 +00:00
|
|
|
"storj.io/storj/private/testplanet"
|
2019-04-23 20:21:30 +01:00
|
|
|
"storj.io/storj/satellite"
|
2019-07-28 06:55:36 +01:00
|
|
|
"storj.io/storj/satellite/overlay"
|
2019-01-18 16:53:23 +00:00
|
|
|
)
|
|
|
|
|
2019-04-03 04:55:24 +01:00
|
|
|
type testData struct {
|
|
|
|
nodeData map[storj.NodeID]float64
|
|
|
|
bwTotals map[storj.NodeID][]int64
|
|
|
|
}
|
|
|
|
|
2019-04-23 20:21:30 +01:00
|
|
|
func TestRollupNoDeletes(t *testing.T) {
|
2019-02-07 04:16:24 +00:00
|
|
|
testplanet.Run(t, testplanet.Config{
|
2019-06-21 15:21:15 +01:00
|
|
|
SatelliteCount: 1, StorageNodeCount: 10, UplinkCount: 0,
|
|
|
|
Reconfigure: testplanet.Reconfigure{
|
|
|
|
Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
|
|
|
|
// 0 so that we can disqualify a node immediately by triggering a failed audit
|
|
|
|
config.Overlay.Node.AuditReputationLambda = 0
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-04-23 20:21:30 +01:00
|
|
|
func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
2019-09-25 17:54:34 +01:00
|
|
|
planet.Satellites[0].Accounting.Rollup.Loop.Pause()
|
|
|
|
planet.Satellites[0].Accounting.Tally.Loop.Pause()
|
|
|
|
|
2019-06-21 15:21:15 +01:00
|
|
|
dqedNodes, err := dqNodes(ctx, planet)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotEmpty(t, dqedNodes)
|
|
|
|
|
2019-04-23 20:21:30 +01:00
|
|
|
days := 5
|
|
|
|
testData := createData(planet, days)
|
|
|
|
|
|
|
|
// Set timestamp back by the number of days we want to save
|
|
|
|
timestamp := time.Now().UTC().AddDate(0, 0, -1*days)
|
|
|
|
start := timestamp
|
|
|
|
|
|
|
|
for i := 0; i < days; i++ {
|
2019-05-10 20:05:42 +01:00
|
|
|
err := planet.Satellites[0].DB.StoragenodeAccounting().SaveTallies(ctx, timestamp, testData[i].nodeData)
|
2019-04-23 20:21:30 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
err = saveBW(ctx, planet, testData[i].bwTotals, timestamp)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = planet.Satellites[0].Accounting.Rollup.Rollup(ctx)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Advance time by 24 hours
|
|
|
|
timestamp = timestamp.Add(time.Hour * 24)
|
|
|
|
end := timestamp
|
|
|
|
|
|
|
|
// rollup.RollupRaws cuts off the hr/min/sec before saving, we need to do the same when querying
|
|
|
|
start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, start.Location())
|
|
|
|
end = time.Date(end.Year(), end.Month(), end.Day(), 0, 0, 0, 0, end.Location())
|
|
|
|
|
2019-05-10 20:05:42 +01:00
|
|
|
rows, err := planet.Satellites[0].DB.StoragenodeAccounting().QueryPaymentInfo(ctx, start, end)
|
2019-04-23 20:21:30 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
if i == 0 { // we need at least two days for rollup to work
|
|
|
|
assert.Equal(t, 0, len(rows))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// the number of rows should be number of nodes
|
|
|
|
|
|
|
|
assert.Equal(t, len(planet.StorageNodes), len(rows))
|
|
|
|
|
|
|
|
// verify data is correct
|
|
|
|
for _, r := range rows {
|
|
|
|
totals := expectedTotals(testData, r.NodeID, i)
|
|
|
|
assert.Equal(t, int64(totals[0]), r.PutTotal)
|
|
|
|
assert.Equal(t, int64(totals[1]), r.GetTotal)
|
|
|
|
assert.Equal(t, int64(totals[2]), r.GetAuditTotal)
|
|
|
|
assert.Equal(t, int64(totals[3]), r.GetRepairTotal)
|
|
|
|
assert.Equal(t, totals[4], r.AtRestTotal)
|
|
|
|
assert.NotEmpty(t, r.Wallet)
|
2019-06-21 15:21:15 +01:00
|
|
|
if dqedNodes[r.NodeID] {
|
|
|
|
assert.NotNil(t, r.Disqualified)
|
|
|
|
} else {
|
|
|
|
assert.Nil(t, r.Disqualified)
|
|
|
|
}
|
2019-04-23 20:21:30 +01:00
|
|
|
}
|
|
|
|
}
|
2019-05-10 20:05:42 +01:00
|
|
|
raw, err := planet.Satellites[0].DB.StoragenodeAccounting().GetTallies(ctx)
|
2019-04-03 04:55:24 +01:00
|
|
|
require.NoError(t, err)
|
2019-04-23 20:21:30 +01:00
|
|
|
assert.Equal(t, days*len(planet.StorageNodes), len(raw))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
func TestRollupDeletes(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
2019-04-25 20:30:37 +01:00
|
|
|
SatelliteCount: 1, StorageNodeCount: 10, UplinkCount: 0,
|
|
|
|
Reconfigure: testplanet.Reconfigure{
|
|
|
|
Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
|
|
|
|
config.Rollup.DeleteTallies = true
|
2019-06-21 15:21:15 +01:00
|
|
|
// 0 so that we can disqualify a node immediately by triggering a failed audit
|
|
|
|
config.Overlay.Node.AuditReputationLambda = 0
|
2019-04-25 20:30:37 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-04-23 20:21:30 +01:00
|
|
|
func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
2019-09-25 17:54:34 +01:00
|
|
|
planet.Satellites[0].Accounting.Rollup.Loop.Pause()
|
|
|
|
planet.Satellites[0].Accounting.Tally.Loop.Pause()
|
|
|
|
|
2019-06-21 15:21:15 +01:00
|
|
|
dqedNodes, err := dqNodes(ctx, planet)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotEmpty(t, dqedNodes)
|
|
|
|
|
2019-04-23 20:21:30 +01:00
|
|
|
days := 5
|
|
|
|
testData := createData(planet, days)
|
|
|
|
|
|
|
|
// Set timestamp back by the number of days we want to save
|
|
|
|
timestamp := time.Now().UTC().AddDate(0, 0, -1*days)
|
|
|
|
start := timestamp
|
|
|
|
|
|
|
|
for i := 0; i < days; i++ {
|
2019-05-10 20:05:42 +01:00
|
|
|
err := planet.Satellites[0].DB.StoragenodeAccounting().SaveTallies(ctx, timestamp, testData[i].nodeData)
|
2019-04-23 20:21:30 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
err = saveBW(ctx, planet, testData[i].bwTotals, timestamp)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = planet.Satellites[0].Accounting.Rollup.Rollup(ctx)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Assert that RollupStorage deleted all raws except for today's
|
2019-05-10 20:05:42 +01:00
|
|
|
raw, err := planet.Satellites[0].DB.StoragenodeAccounting().GetTallies(ctx)
|
2019-04-23 20:21:30 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
for _, r := range raw {
|
2020-01-21 17:43:53 +00:00
|
|
|
assert.WithinDuration(t, timestamp, r.IntervalEndTime, time.Second)
|
2019-05-10 20:05:42 +01:00
|
|
|
assert.Equal(t, testData[i].nodeData[r.NodeID], r.DataTotal)
|
2019-04-03 04:55:24 +01:00
|
|
|
}
|
2019-02-07 04:16:24 +00:00
|
|
|
|
2019-04-23 20:21:30 +01:00
|
|
|
// Advance time by 24 hours
|
|
|
|
timestamp = timestamp.Add(time.Hour * 24)
|
|
|
|
end := timestamp
|
2019-02-07 04:16:24 +00:00
|
|
|
|
2019-04-23 20:21:30 +01:00
|
|
|
// rollup.RollupRaws cuts off the hr/min/sec before saving, we need to do the same when querying
|
|
|
|
start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, start.Location())
|
|
|
|
end = time.Date(end.Year(), end.Month(), end.Day(), 0, 0, 0, 0, end.Location())
|
2019-02-07 04:16:24 +00:00
|
|
|
|
2019-05-10 20:05:42 +01:00
|
|
|
rows, err := planet.Satellites[0].DB.StoragenodeAccounting().QueryPaymentInfo(ctx, start, end)
|
2019-04-23 20:21:30 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
if i == 0 { // we need at least two days for rollup to work
|
|
|
|
assert.Equal(t, 0, len(rows))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// the number of rows should be number of nodes
|
|
|
|
|
|
|
|
assert.Equal(t, len(planet.StorageNodes), len(rows))
|
|
|
|
|
|
|
|
// verify data is correct
|
|
|
|
for _, r := range rows {
|
|
|
|
totals := expectedTotals(testData, r.NodeID, i)
|
|
|
|
assert.Equal(t, int64(totals[0]), r.PutTotal)
|
|
|
|
assert.Equal(t, int64(totals[1]), r.GetTotal)
|
|
|
|
assert.Equal(t, int64(totals[2]), r.GetAuditTotal)
|
|
|
|
assert.Equal(t, int64(totals[3]), r.GetRepairTotal)
|
|
|
|
assert.Equal(t, totals[4], r.AtRestTotal)
|
|
|
|
assert.NotEmpty(t, r.Wallet)
|
2019-06-21 15:21:15 +01:00
|
|
|
if dqedNodes[r.NodeID] {
|
|
|
|
assert.NotNil(t, r.Disqualified)
|
|
|
|
} else {
|
|
|
|
assert.Nil(t, r.Disqualified)
|
|
|
|
}
|
2019-04-23 20:21:30 +01:00
|
|
|
}
|
2019-02-07 04:16:24 +00:00
|
|
|
}
|
2019-04-23 20:21:30 +01:00
|
|
|
})
|
2019-01-18 16:53:23 +00:00
|
|
|
}
|
|
|
|
|
2020-07-16 15:18:02 +01:00
|
|
|
// expectedTotals sums test data up to, but not including the current day's.
|
2019-04-03 04:55:24 +01:00
|
|
|
func expectedTotals(data []testData, id storj.NodeID, currentDay int) []float64 {
|
|
|
|
totals := make([]float64, 5)
|
|
|
|
for i := 0; i < currentDay; i++ {
|
|
|
|
totals[0] += float64(data[i].bwTotals[id][0])
|
|
|
|
totals[1] += float64(data[i].bwTotals[id][1])
|
|
|
|
totals[2] += float64(data[i].bwTotals[id][2])
|
|
|
|
totals[3] += float64(data[i].bwTotals[id][3])
|
|
|
|
totals[4] += data[i].nodeData[id]
|
|
|
|
}
|
|
|
|
return totals
|
|
|
|
}
|
|
|
|
|
|
|
|
func createData(planet *testplanet.Planet, days int) []testData {
|
|
|
|
data := make([]testData, days)
|
|
|
|
for i := 0; i < days; i++ {
|
|
|
|
i := int64(i)
|
|
|
|
data[i].nodeData = make(map[storj.NodeID]float64)
|
|
|
|
data[i].bwTotals = make(map[storj.NodeID][]int64)
|
|
|
|
for _, n := range planet.StorageNodes {
|
|
|
|
id := n.Identity.ID
|
|
|
|
data[i].nodeData[id] = float64(i * 5000)
|
|
|
|
data[i].bwTotals[id] = []int64{i * 1000, i * 2000, i * 3000, i * 4000}
|
|
|
|
}
|
2019-01-18 16:53:23 +00:00
|
|
|
}
|
2019-04-03 04:55:24 +01:00
|
|
|
return data
|
2019-01-18 16:53:23 +00:00
|
|
|
}
|
2019-04-04 16:20:59 +01:00
|
|
|
|
2020-07-16 15:18:02 +01:00
|
|
|
// dqNodes disqualifies half the nodes in the testplanet and returns a map of dqed nodes.
|
2019-06-21 15:21:15 +01:00
|
|
|
func dqNodes(ctx *testcontext.Context, planet *testplanet.Planet) (map[storj.NodeID]bool, error) {
|
|
|
|
dqed := make(map[storj.NodeID]bool)
|
|
|
|
|
2019-07-31 18:21:06 +01:00
|
|
|
var updateRequests []*overlay.UpdateRequest
|
2019-06-21 15:21:15 +01:00
|
|
|
for i, n := range planet.StorageNodes {
|
|
|
|
if i%2 == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2019-07-31 18:21:06 +01:00
|
|
|
updateRequests = append(updateRequests, &overlay.UpdateRequest{
|
2019-06-21 15:21:15 +01:00
|
|
|
NodeID: n.ID(),
|
|
|
|
IsUp: true,
|
2020-03-09 15:35:54 +00:00
|
|
|
AuditOutcome: overlay.AuditFailure,
|
2019-06-21 15:21:15 +01:00
|
|
|
})
|
2019-07-31 18:21:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_, err := planet.Satellites[0].Overlay.Service.BatchUpdateStats(ctx, updateRequests)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, request := range updateRequests {
|
|
|
|
dqed[request.NodeID] = true
|
2019-06-21 15:21:15 +01:00
|
|
|
}
|
|
|
|
return dqed, nil
|
|
|
|
}
|
|
|
|
|
2019-04-04 16:20:59 +01:00
|
|
|
func saveBW(ctx context.Context, planet *testplanet.Planet, bwTotals map[storj.NodeID][]int64, intervalStart time.Time) error {
|
|
|
|
pieceActions := []pb.PieceAction{pb.PieceAction_PUT, pb.PieceAction_GET, pb.PieceAction_GET_AUDIT, pb.PieceAction_GET_REPAIR}
|
|
|
|
for nodeID, actions := range bwTotals {
|
|
|
|
for actionType, amount := range actions {
|
|
|
|
err := planet.Satellites[0].DB.Orders().UpdateStoragenodeBandwidthSettle(ctx, nodeID, pieceActions[actionType], amount, intervalStart)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|