2021-01-14 15:57:04 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2021-02-19 14:42:10 +00:00
|
|
|
package gracefulexit_test
|
2021-01-14 15:57:04 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"math/rand"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"storj.io/common/storj"
|
|
|
|
"storj.io/common/testcontext"
|
|
|
|
"storj.io/storj/private/testplanet"
|
|
|
|
"storj.io/storj/satellite/gracefulexit"
|
2021-04-21 13:42:57 +01:00
|
|
|
"storj.io/storj/satellite/metabase"
|
2021-01-14 15:57:04 +00:00
|
|
|
"storj.io/storj/satellite/overlay"
|
|
|
|
)
|
|
|
|
|
2021-02-17 01:25:25 +00:00
|
|
|
func TestGracefulexitDB_DeleteFinishedExitProgress(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, StorageNodeCount: 6,
|
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
|
|
satellite := planet.Satellites[0]
|
|
|
|
geDB := planet.Satellites[0].DB.GracefulExit()
|
|
|
|
|
|
|
|
days := 6
|
|
|
|
currentTime := time.Now().UTC()
|
|
|
|
// Set timestamp back by the number of days we want to save
|
|
|
|
timestamp := currentTime.AddDate(0, 0, -days).Truncate(time.Millisecond)
|
|
|
|
|
|
|
|
for i := 0; i < days; i++ {
|
|
|
|
nodeID := planet.StorageNodes[i].ID()
|
|
|
|
err := geDB.IncrementProgress(ctx, nodeID, 100, 100, 100)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
_, err = satellite.Overlay.DB.UpdateExitStatus(ctx, &overlay.ExitStatusRequest{
|
|
|
|
NodeID: nodeID,
|
|
|
|
ExitFinishedAt: timestamp,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Advance time by 24 hours
|
|
|
|
timestamp = timestamp.Add(time.Hour * 24)
|
|
|
|
}
|
|
|
|
threeDays := currentTime.AddDate(0, 0, -days/2).Add(-time.Millisecond)
|
|
|
|
finishedNodes, err := geDB.GetFinishedExitNodes(ctx, threeDays)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, finishedNodes, 3)
|
|
|
|
|
|
|
|
finishedNodes, err = geDB.GetFinishedExitNodes(ctx, currentTime)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, finishedNodes, 6)
|
|
|
|
|
|
|
|
count, err := geDB.DeleteFinishedExitProgress(ctx, threeDays)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.EqualValues(t, 3, count)
|
|
|
|
|
|
|
|
// Check that first three nodes were removed from exit progress table
|
|
|
|
for i, node := range planet.StorageNodes {
|
|
|
|
progress, err := geDB.GetProgress(ctx, node.ID())
|
|
|
|
if i < 3 {
|
|
|
|
require.True(t, gracefulexit.ErrNodeNotFound.Has(err))
|
|
|
|
require.Nil(t, progress)
|
|
|
|
} else {
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-01-14 15:57:04 +00:00
|
|
|
func TestGracefulExit_DeleteAllFinishedTransferQueueItems(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, StorageNodeCount: 7,
|
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
|
|
var (
|
|
|
|
cache = planet.Satellites[0].DB.OverlayCache()
|
2021-04-21 16:40:07 +01:00
|
|
|
currentTime = time.Now().UTC()
|
2021-01-14 15:57:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// mark some of the storagenodes as successful exit
|
|
|
|
nodeSuccessful1 := planet.StorageNodes[1]
|
|
|
|
_, err := cache.UpdateExitStatus(ctx, &overlay.ExitStatusRequest{
|
|
|
|
NodeID: nodeSuccessful1.ID(),
|
|
|
|
ExitInitiatedAt: currentTime.Add(-time.Hour),
|
|
|
|
ExitLoopCompletedAt: currentTime.Add(-30 * time.Minute),
|
|
|
|
ExitFinishedAt: currentTime.Add(-25 * time.Minute),
|
|
|
|
ExitSuccess: true,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
nodeSuccessful2 := planet.StorageNodes[2]
|
|
|
|
_, err = cache.UpdateExitStatus(ctx, &overlay.ExitStatusRequest{
|
|
|
|
NodeID: nodeSuccessful2.ID(),
|
|
|
|
ExitInitiatedAt: currentTime.Add(-time.Hour),
|
|
|
|
ExitLoopCompletedAt: currentTime.Add(-17 * time.Minute),
|
|
|
|
ExitFinishedAt: currentTime.Add(-16 * time.Minute),
|
|
|
|
ExitSuccess: true,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
nodeSuccessful3 := planet.StorageNodes[3]
|
|
|
|
_, err = cache.UpdateExitStatus(ctx, &overlay.ExitStatusRequest{
|
|
|
|
NodeID: nodeSuccessful3.ID(),
|
|
|
|
ExitInitiatedAt: currentTime.Add(-time.Hour),
|
|
|
|
ExitLoopCompletedAt: currentTime.Add(-9 * time.Minute),
|
2021-04-21 16:40:07 +01:00
|
|
|
ExitFinishedAt: currentTime.Add(-6 * time.Minute),
|
2021-01-14 15:57:04 +00:00
|
|
|
ExitSuccess: true,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// mark some of the storagenodes as failed exit
|
|
|
|
nodeFailed1 := planet.StorageNodes[4]
|
|
|
|
_, err = cache.UpdateExitStatus(ctx, &overlay.ExitStatusRequest{
|
|
|
|
NodeID: nodeFailed1.ID(),
|
|
|
|
ExitInitiatedAt: currentTime.Add(-time.Hour),
|
|
|
|
ExitLoopCompletedAt: currentTime.Add(-28 * time.Minute),
|
|
|
|
ExitFinishedAt: currentTime.Add(-20 * time.Minute),
|
|
|
|
ExitSuccess: true,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
nodeFailed2 := planet.StorageNodes[5]
|
|
|
|
_, err = cache.UpdateExitStatus(ctx, &overlay.ExitStatusRequest{
|
|
|
|
NodeID: nodeFailed2.ID(),
|
2021-02-02 12:24:42 +00:00
|
|
|
ExitInitiatedAt: currentTime.Add(-time.Hour),
|
|
|
|
ExitLoopCompletedAt: currentTime.Add(-17 * time.Minute),
|
|
|
|
ExitFinishedAt: currentTime.Add(-15 * time.Minute),
|
2021-01-14 15:57:04 +00:00
|
|
|
ExitSuccess: true,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
nodeWithoutItems := planet.StorageNodes[6]
|
|
|
|
_, err = cache.UpdateExitStatus(ctx, &overlay.ExitStatusRequest{
|
|
|
|
NodeID: nodeWithoutItems.ID(),
|
2021-02-02 12:24:42 +00:00
|
|
|
ExitInitiatedAt: currentTime.Add(-time.Hour),
|
|
|
|
ExitLoopCompletedAt: currentTime.Add(-35 * time.Minute),
|
|
|
|
ExitFinishedAt: currentTime.Add(-32 * time.Minute),
|
2021-01-14 15:57:04 +00:00
|
|
|
ExitSuccess: false,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// add some items to the transfer queue for the exited nodes
|
|
|
|
queueItems, nodesItems := generateTransferQueueItems(t, []*testplanet.StorageNode{
|
|
|
|
nodeSuccessful1, nodeSuccessful2, nodeSuccessful3, nodeFailed1, nodeFailed2,
|
|
|
|
})
|
|
|
|
|
|
|
|
gracefulExitDB := planet.Satellites[0].DB.GracefulExit()
|
|
|
|
err = gracefulExitDB.Enqueue(ctx, queueItems)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// count nodes exited before 15 minutes ago
|
|
|
|
nodes, err := gracefulExitDB.CountFinishedTransferQueueItemsByNode(ctx, currentTime.Add(-15*time.Minute))
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, nodes, 3, "invalid number of nodes which have exited 15 minutes ago")
|
|
|
|
|
|
|
|
for id, n := range nodes {
|
|
|
|
assert.EqualValues(t, nodesItems[id], n, "unexpected number of items")
|
|
|
|
}
|
|
|
|
|
|
|
|
// count nodes exited before 4 minutes ago
|
|
|
|
nodes, err = gracefulExitDB.CountFinishedTransferQueueItemsByNode(ctx, currentTime.Add(-4*time.Minute))
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, nodes, 5, "invalid number of nodes which have exited 4 minutes ago")
|
|
|
|
|
|
|
|
for id, n := range nodes {
|
|
|
|
assert.EqualValues(t, nodesItems[id], n, "unexpected number of items")
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete items of nodes exited before 15 minutes ago
|
|
|
|
count, err := gracefulExitDB.DeleteAllFinishedTransferQueueItems(ctx, currentTime.Add(-15*time.Minute))
|
|
|
|
require.NoError(t, err)
|
|
|
|
expectedNumDeletedItems := nodesItems[nodeSuccessful1.ID()] +
|
|
|
|
nodesItems[nodeSuccessful2.ID()] +
|
|
|
|
nodesItems[nodeFailed1.ID()]
|
|
|
|
require.EqualValues(t, expectedNumDeletedItems, count, "invalid number of delet items")
|
|
|
|
|
|
|
|
// check that only a few nodes have exited are left with items
|
|
|
|
nodes, err = gracefulExitDB.CountFinishedTransferQueueItemsByNode(ctx, currentTime.Add(time.Minute))
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, nodes, 2, "invalid number of exited nodes with items")
|
|
|
|
|
|
|
|
for id, n := range nodes {
|
|
|
|
assert.EqualValues(t, nodesItems[id], n, "unexpected number of items")
|
|
|
|
assert.NotEqual(t, nodeSuccessful1.ID(), id, "node shouldn't have items")
|
|
|
|
assert.NotEqual(t, nodeSuccessful2.ID(), id, "node shouldn't have items")
|
|
|
|
assert.NotEqual(t, nodeFailed1.ID(), id, "node shouldn't have items")
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete items of there rest exited nodes
|
|
|
|
count, err = gracefulExitDB.DeleteAllFinishedTransferQueueItems(ctx, currentTime.Add(time.Minute))
|
|
|
|
require.NoError(t, err)
|
|
|
|
expectedNumDeletedItems = nodesItems[nodeSuccessful3.ID()] + nodesItems[nodeFailed2.ID()]
|
|
|
|
require.EqualValues(t, expectedNumDeletedItems, count, "invalid number of delet items")
|
|
|
|
|
|
|
|
// check that there aren't more exited nodes with items
|
|
|
|
nodes, err = gracefulExitDB.CountFinishedTransferQueueItemsByNode(ctx, currentTime.Add(time.Minute))
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, nodes, 0, "invalid number of exited nodes with items")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateTransferQueueItems(t *testing.T, nodes []*testplanet.StorageNode) ([]gracefulexit.TransferQueueItem, map[storj.NodeID]int64) {
|
|
|
|
getNodeID := func() storj.NodeID {
|
|
|
|
n := rand.Intn(len(nodes))
|
|
|
|
return nodes[n].ID()
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
items = make([]gracefulexit.TransferQueueItem, rand.Intn(100)+10)
|
|
|
|
nodesItems = make(map[storj.NodeID]int64, len(items))
|
|
|
|
)
|
|
|
|
for i, item := range items {
|
|
|
|
item.NodeID = getNodeID()
|
2021-02-19 16:15:22 +00:00
|
|
|
item.Key = metabase.SegmentKey{byte(i)}
|
|
|
|
item.PieceNum = int32(i + 1)
|
2021-01-14 15:57:04 +00:00
|
|
|
items[i] = item
|
|
|
|
nodesItems[item.NodeID]++
|
|
|
|
}
|
|
|
|
|
|
|
|
return items, nodesItems
|
|
|
|
}
|