storagenode/payouts: fix CurrentMonthExpectations timezone handling. Estimations based on node's join date.

On servers with non-UTC it would have calculated a different month boundary.
If node joined in current month calculations will be related on amount of days node've been working.

Change-Id: Ie572b197f50c6cdff5a044a53dfb5b9138f82f24
This commit is contained in:
Qweder93 2021-01-22 15:06:59 +02:00
parent 49c8e94480
commit c139cbd76b
7 changed files with 80 additions and 13 deletions

1
go.mod
View File

@ -20,7 +20,6 @@ require (
github.com/jackc/pgconn v1.7.0 github.com/jackc/pgconn v1.7.0
github.com/jackc/pgtype v1.5.0 github.com/jackc/pgtype v1.5.0
github.com/jackc/pgx/v4 v4.9.0 github.com/jackc/pgx/v4 v4.9.0
github.com/jinzhu/now v1.1.1
github.com/jtolds/monkit-hw/v2 v2.0.0-20191108235325-141a0da276b3 github.com/jtolds/monkit-hw/v2 v2.0.0-20191108235325-141a0da276b3
github.com/lucas-clemente/quic-go v0.19.3 github.com/lucas-clemente/quic-go v0.19.3
github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/mattn/go-sqlite3 v2.0.3+incompatible

2
go.sum
View File

@ -337,8 +337,6 @@ github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=

View File

@ -51,3 +51,10 @@ func MonthsBetweenDates(from time.Time, to time.Time) int {
func TruncateToHourInNano(t time.Time) int64 { func TruncateToHourInNano(t time.Time) int64 {
return t.Truncate(1 * time.Hour).UnixNano() return t.Truncate(1 * time.Hour).UnixNano()
} }
// UTCEndOfMonth returns utc end of month (f.e. to get last day in month).
func UTCEndOfMonth(now time.Time) time.Time {
now = now.UTC()
y, m, _ := now.Date()
return time.Date(y, m+1, 1, 0, 0, 0, 0, &time.Location{}).Add(-time.Nanosecond)
}

View File

@ -5,6 +5,9 @@ package estimatedpayouts
import ( import (
"math" "math"
"time"
"storj.io/storj/private/date"
) )
// EstimatedPayout contains usage and estimated payouts data for current and previous months. // EstimatedPayout contains usage and estimated payouts data for current and previous months.
@ -61,3 +64,10 @@ func (pm *PayoutMonthly) SetPayout() {
func RoundFloat(value float64) float64 { func RoundFloat(value float64) float64 {
return math.Round(value*100) / 100 return math.Round(value*100) / 100
} }
// SetExpectedMonth set current month expectations.
func (estimatedPayout *EstimatedPayout) SetExpectedMonth(now time.Time) {
daysPaste := float64(now.Day() - 1)
timeInMonth := date.UTCEndOfMonth(now)
estimatedPayout.CurrentMonthExpectations = (estimatedPayout.CurrentMonth.Payout / daysPaste) * float64(timeInMonth.Day())
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package estimatedpayouts_test
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"storj.io/common/testcontext"
"storj.io/storj/private/testplanet"
"storj.io/storj/storagenode/payouts/estimatedpayouts"
)
func TestCurrentMonthExpectations(t *testing.T) {
testplanet.Run(t, testplanet.Config{
StorageNodeCount: 1,
SatelliteCount: 2,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
estimatedPayout := estimatedpayouts.EstimatedPayout{
CurrentMonth: estimatedpayouts.PayoutMonthly{
Payout: 100,
},
}
currentDay := time.Now().Day() - 1
now := time.Now().UTC()
y, m, _ := now.Date()
daysInMonth := time.Date(y, m+1, 1, 0, 0, 0, -1, &time.Location{}).Day()
expectations := (estimatedPayout.CurrentMonth.Payout / float64(currentDay)) * float64(daysInMonth)
estimatedPayout.SetExpectedMonth(now)
require.Equal(t, estimatedPayout.CurrentMonthExpectations, expectations)
})
}

View File

@ -7,7 +7,6 @@ import (
"context" "context"
"time" "time"
"github.com/jinzhu/now"
"github.com/spacemonkeygo/monkit/v3" "github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs" "github.com/zeebo/errs"
@ -57,6 +56,7 @@ func NewService(bandwidthDB bandwidth.DB, reputationDB reputation.DB, storageUsa
func (s *Service) GetSatelliteEstimatedPayout(ctx context.Context, satelliteID storj.NodeID) (payout EstimatedPayout, err error) { func (s *Service) GetSatelliteEstimatedPayout(ctx context.Context, satelliteID storj.NodeID) (payout EstimatedPayout, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
now := time.Now()
currentMonthPayout, previousMonthPayout, err := s.estimatedPayout(ctx, satelliteID) currentMonthPayout, previousMonthPayout, err := s.estimatedPayout(ctx, satelliteID)
if err != nil { if err != nil {
return EstimatedPayout{}, EstimationServiceErr.Wrap(err) return EstimatedPayout{}, EstimationServiceErr.Wrap(err)
@ -64,14 +64,27 @@ func (s *Service) GetSatelliteEstimatedPayout(ctx context.Context, satelliteID s
payout.CurrentMonth = currentMonthPayout payout.CurrentMonth = currentMonthPayout
payout.PreviousMonth = previousMonthPayout payout.PreviousMonth = previousMonthPayout
payout.setExpectations(ctx)
stats, err := s.reputationDB.Get(ctx, satelliteID)
if err != nil {
return EstimatedPayout{}, EstimationServiceErr.Wrap(err)
}
daysSinceJoined := time.Since(stats.JoinedAt).Hours() / 24
if daysSinceJoined >= float64(now.Day()) {
payout.SetExpectedMonth(now)
return payout, nil
}
payout.CurrentMonthExpectations = (payout.CurrentMonth.Payout / daysSinceJoined) * float64(date.UTCEndOfMonth(now).Day())
return payout, nil return payout, nil
} }
// GetAllSatellitesEstimatedPayout returns estimated payouts for current and previous months from all satellites with current level of load. // GetAllSatellitesEstimatedPayout returns estimated payouts for current and previous months from all satellites with current level of load.
func (s *Service) GetAllSatellitesEstimatedPayout(ctx context.Context) (payout EstimatedPayout, err error) { func (s *Service) GetAllSatellitesEstimatedPayout(ctx context.Context) (payout EstimatedPayout, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
now := time.Now()
satelliteIDs := s.trust.GetSatellites(ctx) satelliteIDs := s.trust.GetSatellites(ctx)
for i := 0; i < len(satelliteIDs); i++ { for i := 0; i < len(satelliteIDs); i++ {
@ -97,18 +110,12 @@ func (s *Service) GetAllSatellitesEstimatedPayout(ctx context.Context) (payout E
payout.PreviousMonth.EgressRepairAudit += previous.EgressRepairAudit payout.PreviousMonth.EgressRepairAudit += previous.EgressRepairAudit
payout.PreviousMonth.Held += previous.Held payout.PreviousMonth.Held += previous.Held
} }
payout.setExpectations(ctx)
payout.SetExpectedMonth(now)
return payout, nil return payout, nil
} }
// setExpectations set current month expectations.
func (estimatedPayout *EstimatedPayout) setExpectations(ctx context.Context) {
daysPaste := float64(time.Now().Day() - 1)
DaysInMonth := float64(now.EndOfMonth().Day())
estimatedPayout.CurrentMonthExpectations = (estimatedPayout.CurrentMonth.Payout / daysPaste) * DaysInMonth
}
// estimatedPayout returns estimated payouts data for current and previous months from specific satellite. // estimatedPayout returns estimated payouts data for current and previous months from specific satellite.
func (s *Service) estimatedPayout(ctx context.Context, satelliteID storj.NodeID) (currentMonthPayout PayoutMonthly, previousMonthPayout PayoutMonthly, err error) { func (s *Service) estimatedPayout(ctx context.Context, satelliteID storj.NodeID) (currentMonthPayout PayoutMonthly, previousMonthPayout PayoutMonthly, err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)

View File

@ -286,6 +286,7 @@ type Peer struct {
Storage *multinode.StorageEndpoint Storage *multinode.StorageEndpoint
Bandwidth *multinode.BandwidthEndpoint Bandwidth *multinode.BandwidthEndpoint
Node *multinode.NodeEndpoint Node *multinode.NodeEndpoint
Payout *multinode.PayoutEndpoint
} }
} }
@ -799,6 +800,11 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, revocationDB exten
peer.DB.Reputation(), peer.DB.Reputation(),
peer.Storage2.Trust) peer.Storage2.Trust)
peer.Multinode.Payout = multinode.NewPayoutEndpoint(
peer.Log.Named("multinode:payout-endpoint"),
apiKeys,
peer.DB.Payout())
if err = multinodepb.DRPCRegisterStorage(peer.Server.DRPC(), peer.Multinode.Storage); err != nil { if err = multinodepb.DRPCRegisterStorage(peer.Server.DRPC(), peer.Multinode.Storage); err != nil {
return nil, errs.Combine(err, peer.Close()) return nil, errs.Combine(err, peer.Close())
} }
@ -808,6 +814,9 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, revocationDB exten
if err = multinodepb.DRPCRegisterNode(peer.Server.DRPC(), peer.Multinode.Node); err != nil { if err = multinodepb.DRPCRegisterNode(peer.Server.DRPC(), peer.Multinode.Node); err != nil {
return nil, errs.Combine(err, peer.Close()) return nil, errs.Combine(err, peer.Close())
} }
if err = multinodepb.DRPCRegisterPayout(peer.Server.DRPC(), peer.Multinode.Payout); err != nil {
return nil, errs.Combine(err, peer.Close())
}
} }
return peer, nil return peer, nil