2020-09-09 20:20:44 +01:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package accounting_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"testing"
|
2023-03-13 08:56:50 +00:00
|
|
|
"time"
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"storj.io/common/testcontext"
|
|
|
|
"storj.io/common/testrand"
|
|
|
|
"storj.io/common/uuid"
|
|
|
|
"storj.io/storj/private/testplanet"
|
|
|
|
"storj.io/storj/satellite"
|
|
|
|
"storj.io/storj/satellite/accounting"
|
|
|
|
"storj.io/storj/satellite/console"
|
|
|
|
"storj.io/storj/satellite/satellitedb/satellitedbtest"
|
|
|
|
)
|
|
|
|
|
|
|
|
type mockDB struct {
|
|
|
|
callCount int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mdb *mockDB) GetProjectLimits(ctx context.Context, projectID uuid.UUID) (accounting.ProjectLimits, error) {
|
|
|
|
mdb.callCount++
|
|
|
|
return accounting.ProjectLimits{}, nil
|
|
|
|
}
|
|
|
|
func TestProjectLimitCacheCallCount(t *testing.T) {
|
|
|
|
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
|
|
|
mdb := mockDB{}
|
2021-12-03 15:06:20 +00:00
|
|
|
projectLimitCache := accounting.NewProjectLimitCache(&mdb, 0, 0, 0, accounting.ProjectLimitConfig{CacheCapacity: 100})
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
testProject, err := db.Console().Projects().Insert(ctx, &console.Project{Name: "test", OwnerID: testrand.UUID()})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
const expectedCallCount = 1
|
|
|
|
|
2023-03-10 09:37:50 +00:00
|
|
|
_, err = projectLimitCache.GetBandwidthLimit(ctx, testProject.ID)
|
2020-09-09 20:20:44 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
// if the data isn't in the cache we call into the database to get it
|
|
|
|
require.Equal(t, expectedCallCount, mdb.callCount)
|
|
|
|
|
2023-03-10 09:37:50 +00:00
|
|
|
_, err = projectLimitCache.GetBandwidthLimit(ctx, testProject.ID)
|
2020-09-09 20:20:44 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
// call count should still be 1 since the data is in the cache and we don't need
|
|
|
|
// to get it from the db
|
|
|
|
require.Equal(t, expectedCallCount, mdb.callCount)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProjectLimitCache(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0,
|
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
|
|
saPeer := planet.Satellites[0]
|
|
|
|
projectUsageSvc := saPeer.Accounting.ProjectUsage
|
2023-03-13 16:55:30 +00:00
|
|
|
projects := saPeer.DB.Console().Projects()
|
2020-09-09 20:20:44 +01:00
|
|
|
accountingDB := saPeer.DB.ProjectAccounting()
|
|
|
|
projectLimitCache := saPeer.ProjectLimits.Cache
|
2021-07-01 00:13:45 +01:00
|
|
|
defaultUsageLimit := saPeer.Config.Console.UsageLimits.Storage.Free.Int64()
|
|
|
|
defaultBandwidthLimit := saPeer.Config.Console.UsageLimits.Bandwidth.Free.Int64()
|
2021-12-03 15:06:20 +00:00
|
|
|
defaultSegmentLimit := int64(1000000)
|
2023-03-13 16:55:30 +00:00
|
|
|
dbDefaultLimits := accounting.ProjectLimits{
|
|
|
|
Usage: &defaultUsageLimit,
|
|
|
|
Bandwidth: &defaultBandwidthLimit,
|
|
|
|
Segments: &defaultSegmentLimit,
|
|
|
|
RateLimit: nil,
|
|
|
|
BurstLimit: nil,
|
|
|
|
}
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
testProject, err := saPeer.DB.Console().Projects().Insert(ctx, &console.Project{Name: "test", OwnerID: testrand.UUID()})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-03-13 16:55:30 +00:00
|
|
|
secondTestProject, err := saPeer.DB.Console().Projects().Insert(ctx, &console.Project{Name: "second project", OwnerID: testrand.UUID()})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-09-09 20:20:44 +01:00
|
|
|
const (
|
|
|
|
errorLimit = 0
|
|
|
|
expectedUsageLimit = 1
|
|
|
|
expectedBandwidthLimit = 2
|
2021-12-03 15:06:20 +00:00
|
|
|
expectedSegmentLimit = 3
|
2023-03-13 16:55:30 +00:00
|
|
|
expectedRateLimit = 4
|
|
|
|
expectedBurstLimit = 5
|
2020-09-09 20:20:44 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
t.Run("project ID doesn't exist", func(t *testing.T) {
|
|
|
|
projectID := testrand.UUID()
|
|
|
|
actualStorageLimitFromDB, err := accountingDB.GetProjectStorageLimit(ctx, projectID)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Nil(t, actualStorageLimitFromDB)
|
|
|
|
|
|
|
|
actualLimitsFromDB, err := accountingDB.GetProjectLimits(ctx, projectID)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Equal(t, accounting.ProjectLimits{}, actualLimitsFromDB)
|
|
|
|
|
2021-12-03 15:06:20 +00:00
|
|
|
// storage
|
2023-03-10 09:37:50 +00:00
|
|
|
_, err = projectLimitCache.GetLimits(ctx, projectID)
|
2020-09-09 20:20:44 +01:00
|
|
|
assert.Error(t, err)
|
|
|
|
|
|
|
|
actualStorageLimitFromSvc, err := projectUsageSvc.GetProjectStorageLimit(ctx, projectID)
|
|
|
|
assert.Error(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
assert.EqualValues(t, errorLimit, actualStorageLimitFromSvc)
|
2021-12-03 15:06:20 +00:00
|
|
|
|
|
|
|
// bandwidth
|
2023-03-10 09:37:50 +00:00
|
|
|
actualBandwidthLimitFromCache, err := projectLimitCache.GetBandwidthLimit(ctx, projectID)
|
2021-12-03 15:06:20 +00:00
|
|
|
assert.Error(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
assert.EqualValues(t, errorLimit, actualBandwidthLimitFromCache)
|
2021-12-03 15:06:20 +00:00
|
|
|
|
|
|
|
actualBandwidthLimitFromSvc, err := projectUsageSvc.GetProjectBandwidthLimit(ctx, projectID)
|
|
|
|
assert.Error(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
assert.EqualValues(t, errorLimit, actualBandwidthLimitFromSvc)
|
2020-09-09 20:20:44 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("default limits", func(t *testing.T) {
|
2020-10-06 13:50:29 +01:00
|
|
|
actualLimitsFromDB, err := accountingDB.GetProjectLimits(ctx, testProject.ID)
|
2020-09-09 20:20:44 +01:00
|
|
|
assert.NoError(t, err)
|
2021-12-03 15:06:20 +00:00
|
|
|
assert.Equal(t, accounting.ProjectLimits{
|
|
|
|
Segments: &defaultSegmentLimit,
|
|
|
|
}, actualLimitsFromDB)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
2023-03-10 09:37:50 +00:00
|
|
|
actualLimitsFromCache, err := projectLimitCache.GetLimits(ctx, testProject.ID)
|
2020-09-09 20:20:44 +01:00
|
|
|
assert.NoError(t, err)
|
2020-10-06 13:50:29 +01:00
|
|
|
assert.Equal(t, dbDefaultLimits, actualLimitsFromCache)
|
|
|
|
|
|
|
|
actualStorageLimitFromDB, err := accountingDB.GetProjectStorageLimit(ctx, testProject.ID)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Nil(t, actualStorageLimitFromDB)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
2023-03-10 09:37:50 +00:00
|
|
|
actualLimitFromCache, err := projectLimitCache.GetLimits(ctx, testProject.ID)
|
2020-09-09 20:20:44 +01:00
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
assert.EqualValues(t, *dbDefaultLimits.Usage, *actualLimitFromCache.Usage)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
actualStorageLimitFromSvc, err := projectUsageSvc.GetProjectStorageLimit(ctx, testProject.ID)
|
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
assert.EqualValues(t, *dbDefaultLimits.Usage, actualStorageLimitFromSvc)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
actualBandwidthLimitFromDB, err := accountingDB.GetProjectBandwidthLimit(ctx, testProject.ID)
|
|
|
|
assert.NoError(t, err)
|
2020-10-06 13:50:29 +01:00
|
|
|
assert.Nil(t, actualBandwidthLimitFromDB)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
2023-03-10 09:37:50 +00:00
|
|
|
actualBandwidthLimitFromCache, err := projectLimitCache.GetBandwidthLimit(ctx, testProject.ID)
|
2020-09-09 20:20:44 +01:00
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
assert.EqualValues(t, *dbDefaultLimits.Bandwidth, actualBandwidthLimitFromCache)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
actualBandwidthLimitFromSvc, err := projectUsageSvc.GetProjectBandwidthLimit(ctx, testProject.ID)
|
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
assert.EqualValues(t, *dbDefaultLimits.Bandwidth, actualBandwidthLimitFromSvc)
|
2020-09-09 20:20:44 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("update limits in the database", func(t *testing.T) {
|
|
|
|
err = accountingDB.UpdateProjectUsageLimit(ctx, testProject.ID, expectedUsageLimit)
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = accountingDB.UpdateProjectBandwidthLimit(ctx, testProject.ID, expectedBandwidthLimit)
|
|
|
|
require.NoError(t, err)
|
2021-12-03 15:06:20 +00:00
|
|
|
err = accountingDB.UpdateProjectSegmentLimit(ctx, testProject.ID, expectedSegmentLimit)
|
|
|
|
require.NoError(t, err)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
actualStorageLimitFromDB, err := accountingDB.GetProjectStorageLimit(ctx, testProject.ID)
|
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
require.EqualValues(t, expectedUsageLimit, *actualStorageLimitFromDB)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
actualLimitsFromDB, err := accountingDB.GetProjectLimits(ctx, testProject.ID)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
usageLimits := int64(expectedUsageLimit)
|
|
|
|
bwLimits := int64(expectedBandwidthLimit)
|
2021-12-03 15:06:20 +00:00
|
|
|
segmentsLimits := int64(expectedSegmentLimit)
|
|
|
|
assert.Equal(t, accounting.ProjectLimits{Usage: &usageLimits, Bandwidth: &bwLimits, Segments: &segmentsLimits}, actualLimitsFromDB)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
2021-12-03 15:06:20 +00:00
|
|
|
// storage
|
2023-03-10 09:37:50 +00:00
|
|
|
actualLimitFromCache, err := projectLimitCache.GetLimits(ctx, testProject.ID)
|
2020-09-09 20:20:44 +01:00
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
require.EqualValues(t, expectedUsageLimit, *actualLimitFromCache.Usage)
|
|
|
|
require.EqualValues(t, expectedSegmentLimit, *actualLimitFromCache.Segments)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
actualStorageLimitFromSvc, err := projectUsageSvc.GetProjectStorageLimit(ctx, testProject.ID)
|
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
require.EqualValues(t, expectedUsageLimit, actualStorageLimitFromSvc)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
2021-12-03 15:06:20 +00:00
|
|
|
// bandwidth
|
2020-09-09 20:20:44 +01:00
|
|
|
actualBandwidthLimitFromDB, err := accountingDB.GetProjectBandwidthLimit(ctx, testProject.ID)
|
|
|
|
require.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
require.EqualValues(t, expectedBandwidthLimit, *actualBandwidthLimitFromDB)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
2023-03-10 09:37:50 +00:00
|
|
|
actualBandwidthLimitFromCache, err := projectLimitCache.GetBandwidthLimit(ctx, testProject.ID)
|
2020-09-09 20:20:44 +01:00
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
require.EqualValues(t, expectedBandwidthLimit, actualBandwidthLimitFromCache)
|
2020-09-09 20:20:44 +01:00
|
|
|
|
|
|
|
actualBandwidthLimitFromSvc, err := projectUsageSvc.GetProjectBandwidthLimit(ctx, testProject.ID)
|
|
|
|
assert.NoError(t, err)
|
2022-09-12 12:54:12 +01:00
|
|
|
require.EqualValues(t, expectedBandwidthLimit, actualBandwidthLimitFromSvc)
|
2021-12-03 15:06:20 +00:00
|
|
|
|
|
|
|
// segments
|
|
|
|
actualSegmentLimitFromDB, err := accountingDB.GetProjectSegmentLimit(ctx, testProject.ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.EqualValues(t, expectedSegmentLimit, *actualSegmentLimitFromDB)
|
2023-03-13 16:55:30 +00:00
|
|
|
|
|
|
|
// rate and burst limit
|
|
|
|
require.NoError(t, projects.UpdateRateLimit(ctx, secondTestProject.ID, expectedRateLimit))
|
|
|
|
require.NoError(t, projects.UpdateBurstLimit(ctx, secondTestProject.ID, expectedBurstLimit))
|
|
|
|
|
|
|
|
limits, err := projectLimitCache.GetLimits(ctx, secondTestProject.ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.EqualValues(t, expectedRateLimit, *limits.RateLimit)
|
|
|
|
require.EqualValues(t, expectedBurstLimit, *limits.BurstLimit)
|
2020-09-09 20:20:44 +01:00
|
|
|
})
|
2023-03-13 08:56:50 +00:00
|
|
|
|
|
|
|
t.Run("cache is used", func(t *testing.T) {
|
|
|
|
require.NoError(t, accountingDB.UpdateProjectUsageLimit(ctx, testProject.ID, 1))
|
|
|
|
require.NoError(t, accountingDB.UpdateProjectBandwidthLimit(ctx, testProject.ID, 2))
|
|
|
|
require.NoError(t, accountingDB.UpdateProjectSegmentLimit(ctx, testProject.ID, 3))
|
|
|
|
|
|
|
|
projectLimitCache := accounting.NewProjectLimitCache(accountingDB, 0, 0, 0, accounting.ProjectLimitConfig{
|
|
|
|
CacheCapacity: 10,
|
|
|
|
CacheExpiration: 60 * time.Second,
|
|
|
|
})
|
|
|
|
|
|
|
|
// fill cache with values from DB
|
|
|
|
beforeCachedLimits, err := projectLimitCache.GetLimits(ctx, testProject.ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// update limits in DB but not in cache
|
|
|
|
require.NoError(t, accountingDB.UpdateProjectUsageLimit(ctx, testProject.ID, 4))
|
|
|
|
require.NoError(t, accountingDB.UpdateProjectBandwidthLimit(ctx, testProject.ID, 5))
|
|
|
|
require.NoError(t, accountingDB.UpdateProjectSegmentLimit(ctx, testProject.ID, 6))
|
|
|
|
|
|
|
|
// verify that old values are still cached because expiration time was not reached yet
|
|
|
|
afterCachedLimits, err := projectLimitCache.GetLimits(ctx, testProject.ID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.Equal(t, beforeCachedLimits, afterCachedLimits)
|
|
|
|
})
|
2020-09-09 20:20:44 +01:00
|
|
|
})
|
|
|
|
}
|