storj/satellite/satellitedb/nodeevents_test.go
Cameron 08c9d745f1 satellite/nodeevents: take last_attempted into account when selecting
Previously, the node events chore would select based on the earliest
created_at. However, if for some reason this batch fails, it would still
be the next item to select. If there is a consistent error, the chore
would be stuck retrying the same batch over and over. Now instead
GetNextBatch orders by `last_attempted NULLS FIRST ASC, created_at ASC`.
If a batch fails during Notify, last_attempted is updated so we can move
on to a new batch if one exists.

Change-Id: Ia8458e05ac358d85b2f2c6d690f3d607d631be61
2022-12-01 20:08:38 +00:00

244 lines
8.1 KiB
Go

// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package satellitedb_test
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"storj.io/common/testcontext"
"storj.io/common/uuid"
"storj.io/storj/private/teststorj"
"storj.io/storj/satellite"
"storj.io/storj/satellite/nodeevents"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
)
func TestNodeEvents(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
testID := teststorj.NodeIDFromString("test")
testEmail := "test@storj.test"
eventType := nodeevents.Disqualified
neFromInsert, err := db.NodeEvents().Insert(ctx, testEmail, testID, eventType)
require.NoError(t, err)
require.NotNil(t, neFromInsert.ID)
require.Equal(t, testID, neFromInsert.NodeID)
require.Equal(t, testEmail, neFromInsert.Email)
require.Equal(t, eventType, neFromInsert.Event)
require.NotNil(t, neFromInsert.CreatedAt)
require.Nil(t, neFromInsert.EmailSent)
neFromGet, err := db.NodeEvents().GetLatestByEmailAndEvent(ctx, neFromInsert.Email, neFromInsert.Event)
require.NoError(t, err)
require.Equal(t, neFromInsert, neFromGet)
})
}
func TestNodeEventsUpdateEmailSent(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
testID1 := teststorj.NodeIDFromString("test1")
testID2 := teststorj.NodeIDFromString("test2")
testEmail1 := "test1@storj.test"
eventType := nodeevents.Disqualified
// Insert into node events
event1, err := db.NodeEvents().Insert(ctx, testEmail1, testID1, eventType)
require.NoError(t, err)
event2, err := db.NodeEvents().Insert(ctx, testEmail1, testID2, eventType)
require.NoError(t, err)
// GetNextBatch should get them.
events, err := db.NodeEvents().GetNextBatch(ctx, time.Now())
require.NoError(t, err)
var foundEvent1, foundEvent2 bool
for _, ne := range events {
switch ne.NodeID {
case event1.NodeID:
foundEvent1 = true
case event2.NodeID:
foundEvent2 = true
default:
}
}
require.True(t, foundEvent1)
require.True(t, foundEvent2)
// Update email sent
require.NoError(t, db.NodeEvents().UpdateEmailSent(ctx, []uuid.UUID{event1.ID, event2.ID}, time.Now()))
// They shouldn't be found since email_sent should have been updated.
// It's an indirect way of checking. Not the best. We would need to add a new Read method
// to get specific rows by ID.
events, err = db.NodeEvents().GetNextBatch(ctx, time.Now())
require.NoError(t, err)
require.Len(t, events, 0)
})
}
func TestNodeEventsUpdateLastAttempted(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
testID1 := teststorj.NodeIDFromString("test1")
testID2 := teststorj.NodeIDFromString("test2")
testEmail1 := "test1@storj.test"
eventType := nodeevents.Disqualified
event1, err := db.NodeEvents().Insert(ctx, testEmail1, testID1, eventType)
require.NoError(t, err)
event1, err = db.NodeEvents().GetByID(ctx, event1.ID)
require.NoError(t, err)
require.Nil(t, event1.LastAttempted)
event2, err := db.NodeEvents().Insert(ctx, testEmail1, testID2, eventType)
require.NoError(t, err)
event2, err = db.NodeEvents().GetByID(ctx, event2.ID)
require.NoError(t, err)
require.Nil(t, event2.LastAttempted)
require.NoError(t, db.NodeEvents().UpdateLastAttempted(ctx, []uuid.UUID{event1.ID, event2.ID}, time.Now()))
event1, err = db.NodeEvents().GetByID(ctx, event1.ID)
require.NoError(t, err)
require.NotNil(t, event1.LastAttempted)
event2, err = db.NodeEvents().GetByID(ctx, event2.ID)
require.NoError(t, err)
require.NotNil(t, event2.LastAttempted)
})
}
func TestNodeEventsGetByID(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
testID1 := teststorj.NodeIDFromString("test1")
testID2 := teststorj.NodeIDFromString("test2")
testEmail1 := "test1@storj.test"
testEmail2 := "test2@storj.test"
eventType := nodeevents.Disqualified
event1, err := db.NodeEvents().Insert(ctx, testEmail1, testID1, eventType)
require.NoError(t, err)
event2, err := db.NodeEvents().Insert(ctx, testEmail2, testID2, eventType)
require.NoError(t, err)
res, err := db.NodeEvents().GetByID(ctx, event1.ID)
require.NoError(t, err)
require.Equal(t, event1.Email, res.Email)
require.Equal(t, event1.CreatedAt, res.CreatedAt)
require.Equal(t, event1.Event, res.Event)
res, err = db.NodeEvents().GetByID(ctx, event2.ID)
require.NoError(t, err)
require.Equal(t, event2.Email, res.Email)
require.Equal(t, event2.CreatedAt, res.CreatedAt)
require.Equal(t, event2.Event, res.Event)
})
}
func TestNodeEventsGetNextBatch(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
testID1 := teststorj.NodeIDFromString("test1")
testID2 := teststorj.NodeIDFromString("test2")
testEmail1 := "test1@storj.test"
testEmail2 := "test2@storj.test"
eventType := nodeevents.Disqualified
event1, err := db.NodeEvents().Insert(ctx, testEmail1, testID1, eventType)
require.NoError(t, err)
// insert one event with same email and event type, but with different node ID. It should be selected.
event2, err := db.NodeEvents().Insert(ctx, testEmail1, testID2, eventType)
require.NoError(t, err)
// insert one event with same email and event type, but email_sent is not null. Should not be selected.
event3, err := db.NodeEvents().Insert(ctx, testEmail1, testID1, eventType)
require.NoError(t, err)
require.NoError(t, db.NodeEvents().UpdateEmailSent(ctx, []uuid.UUID{event3.ID}, time.Now()))
// insert one event with same email, but different type. Should not be selected.
_, err = db.NodeEvents().Insert(ctx, testEmail1, testID1, nodeevents.BelowMinVersion)
require.NoError(t, err)
// insert one event with same event type, but different email. Should not be selected.
_, err = db.NodeEvents().Insert(ctx, testEmail2, testID1, eventType)
require.NoError(t, err)
batch, err := db.NodeEvents().GetNextBatch(ctx, time.Now())
require.NoError(t, err)
require.Len(t, batch, 2)
var foundEvent1, foundEvent2 bool
for _, ne := range batch {
switch ne.NodeID {
case event1.NodeID:
foundEvent1 = true
case event2.NodeID:
foundEvent2 = true
default:
}
}
require.True(t, foundEvent1)
require.True(t, foundEvent2)
})
}
func TestNodeEventsGetNextBatchSelectionOrder(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
id0 := teststorj.NodeIDFromString("test0")
id1 := teststorj.NodeIDFromString("test1")
id2 := teststorj.NodeIDFromString("test2")
id3 := teststorj.NodeIDFromString("test3")
email0 := "test0@storj.test"
email1 := "test1@storj.test"
email2 := "test2@storj.test"
email3 := "test3@storj.test"
eventType := nodeevents.Disqualified
// GetNextBatch orders by last_attempted, created_at asc
// expected selection order:
// 1. insert0: last_attempted = nil, created_at = earliest
// 2. insert3: last_attempted = nil, created_at = 3rd earliest
// 3. insert2: last_attempted != nil, created_at = 4th earliest
// 4. insert1: last_attempted later than insert2, created_at = 2nd earliest
expectedOrder := []string{
email0, email3, email2, email1,
}
_, err := db.NodeEvents().Insert(ctx, email0, id0, eventType)
require.NoError(t, err)
insert1, err := db.NodeEvents().Insert(ctx, email1, id1, eventType)
require.NoError(t, err)
require.NoError(t, err)
require.NoError(t, db.NodeEvents().UpdateLastAttempted(ctx, []uuid.UUID{insert1.ID}, time.Now().Add(5*time.Minute)))
insert2, err := db.NodeEvents().Insert(ctx, email2, id2, eventType)
require.NoError(t, err)
require.NoError(t, err)
require.NoError(t, db.NodeEvents().UpdateLastAttempted(ctx, []uuid.UUID{insert2.ID}, time.Now()))
_, err = db.NodeEvents().Insert(ctx, email3, id3, eventType)
require.NoError(t, err)
for i := 0; i < 4; i++ {
e, err := db.NodeEvents().GetNextBatch(ctx, time.Now())
require.NoError(t, err)
require.Equal(t, expectedOrder[i], e[0].Email)
require.NoError(t, db.NodeEvents().UpdateEmailSent(ctx, []uuid.UUID{e[0].ID}, time.Now()))
}
})
}