Skip to content

Commit

Permalink
satellite/payments: conditionally add emission impact info to stripe …
Browse files Browse the repository at this point in the history
…invoices footer

Added new invoice generation command flag --emission.
Added logic to calculate emission impact based on invoice storage usage.

Issue:
#6694

Change-Id: I24ad5fb50776a32195d416434319d3bea2b1a9e9
  • Loading branch information
VitaliiShpital authored and Storj Robot committed Feb 16, 2024
1 parent 12ae65d commit 11b4095
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 39 deletions.
2 changes: 2 additions & 0 deletions cmd/satellite/billing.go
Expand Up @@ -15,6 +15,7 @@ import (
"storj.io/common/uuid"
"storj.io/storj/satellite"
"storj.io/storj/satellite/analytics"
"storj.io/storj/satellite/emission"
"storj.io/storj/satellite/payments/stripe"
"storj.io/storj/satellite/satellitedb"
)
Expand Down Expand Up @@ -79,6 +80,7 @@ func setupPayments(log *zap.Logger, db satellite.DB) (*stripe.Service, error) {
pc.PackagePlans.Packages,
pc.BonusRate,
analytics.NewService(log.Named("analytics:service"), runCfg.Analytics, runCfg.Console.SatelliteName),
emission.NewService(runCfg.Emission),
)
}

Expand Down
9 changes: 6 additions & 3 deletions cmd/satellite/main.go
Expand Up @@ -227,7 +227,8 @@ var (
RunE: cmdCreateCustomerBalanceInvoiceItems,
}

aggregate = false
aggregate = false
includeEmissionInfo = false

prepareCustomerInvoiceRecordsCmd = &cobra.Command{
Use: "prepare-invoice-records [period]",
Expand Down Expand Up @@ -436,8 +437,10 @@ func init() {
billingCmd.AddCommand(createCustomerProjectInvoiceItemsCmd)
billingCmd.AddCommand(createCustomerAggregatedProjectInvoiceItemsCmd)
billingCmd.AddCommand(createCustomerInvoicesCmd)
createCustomerInvoicesCmd.Flags().BoolVar(&includeEmissionInfo, "emission", false, "Used to enable CO2 emission impact calculation to be added to invoice footer.")
billingCmd.AddCommand(generateCustomerInvoicesCmd)
generateCustomerInvoicesCmd.Flags().BoolVar(&aggregate, "aggregate", false, "Used to enable invoice items aggregation in case users have many projects (more than 83).")
generateCustomerInvoicesCmd.Flags().BoolVar(&includeEmissionInfo, "emission", false, "Used to enable CO2 emission impact calculation to be added to invoice footer.")
billingCmd.AddCommand(finalizeCustomerInvoicesCmd)
billingCmd.AddCommand(payInvoicesWithTokenCmd)
billingCmd.AddCommand(payAllInvoicesCmd)
Expand Down Expand Up @@ -894,7 +897,7 @@ func cmdCreateCustomerInvoices(cmd *cobra.Command, args []string) (err error) {
}

return runBillingCmd(ctx, func(ctx context.Context, payments *stripe.Service, _ satellite.DB) error {
return payments.CreateInvoices(ctx, periodStart)
return payments.CreateInvoices(ctx, periodStart, includeEmissionInfo)
})
}

Expand All @@ -907,7 +910,7 @@ func cmdGenerateCustomerInvoices(cmd *cobra.Command, args []string) (err error)
}

return runBillingCmd(ctx, func(ctx context.Context, payments *stripe.Service, _ satellite.DB) error {
return payments.GenerateInvoices(ctx, periodStart, aggregate)
return payments.GenerateInvoices(ctx, periodStart, aggregate, includeEmissionInfo)
})
}

Expand Down
2 changes: 2 additions & 0 deletions satellite/admin.go
Expand Up @@ -27,6 +27,7 @@ import (
"storj.io/storj/satellite/buckets"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/restkeys"
"storj.io/storj/satellite/emission"
"storj.io/storj/satellite/metabase"
"storj.io/storj/satellite/payments"
"storj.io/storj/satellite/payments/stripe"
Expand Down Expand Up @@ -209,6 +210,7 @@ func NewAdmin(log *zap.Logger, full *identity.FullIdentity, db DB, metabaseDB *m
pc.PackagePlans.Packages,
pc.BonusRate,
peer.Analytics.Service,
emission.NewService(config.Emission),
)

if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions satellite/api.go
Expand Up @@ -473,6 +473,8 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
}
}

emissionService := emission.NewService(config.Emission)

{ // setup payments
pc := config.Payments

Expand Down Expand Up @@ -516,6 +518,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
pc.PackagePlans.Packages,
pc.BonusRate,
peer.Analytics.Service,
emissionService,
)

if err != nil {
Expand Down Expand Up @@ -570,8 +573,6 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
consoleConfig.AccountFreeze,
)

emissionService := emission.NewService(config.Emission)

peer.Console.Service, err = console.NewService(
peer.Log.Named("console:service"),
peer.DB.Console(),
Expand Down
6 changes: 5 additions & 1 deletion satellite/console/service.go
Expand Up @@ -1750,7 +1750,11 @@ func (s *Service) GetEmissionImpact(ctx context.Context, projectID uuid.UUID) (i
period := now.Sub(isMember.project.CreatedAt)
dataInTB := memory.Size(storageUsed).TB()

impact, err = s.emission.CalculateImpact(dataInTB, period)
impact, err = s.emission.CalculateImpact(&emission.CalculationInput{
AmountOfDataInTB: dataInTB,
Duration: period,
IsTBDuration: false,
})
if err != nil {
return nil, Error.Wrap(err)
}
Expand Down
2 changes: 2 additions & 0 deletions satellite/core.go
Expand Up @@ -34,6 +34,7 @@ import (
"storj.io/storj/satellite/console/consoleauth"
"storj.io/storj/satellite/console/dbcleanup"
"storj.io/storj/satellite/console/emailreminders"
"storj.io/storj/satellite/emission"
"storj.io/storj/satellite/gc/sender"
"storj.io/storj/satellite/mailservice"
"storj.io/storj/satellite/metabase"
Expand Down Expand Up @@ -492,6 +493,7 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB,
pc.PackagePlans.Packages,
pc.BonusRate,
peer.Analytics.Service,
emission.NewService(config.Emission),
)
if err != nil {
return nil, errs.Combine(err, peer.Close())
Expand Down
26 changes: 19 additions & 7 deletions satellite/emission/service.go
Expand Up @@ -82,8 +82,15 @@ type Impact struct {
// Row holds data row of predefined number of values.
type Row [modalityCount]*Val

// CalculationInput holds input data needed to perform emission impact calculations.
type CalculationInput struct {
AmountOfDataInTB float64 // The amount of data in terabytes or terabyte-duration.
Duration time.Duration // The Duration over which the data is measured.
IsTBDuration bool // true if AmountOfDataInTB is in terabytes-duration, false if in terabytes.
}

// CalculateImpact calculates emission impact coming from different sources e.g. Storj, hyperscaler or corporateDC.
func (sv *Service) CalculateImpact(amountOfDataInTB float64, duration time.Duration) (*Impact, error) {
func (sv *Service) CalculateImpact(input *CalculationInput) (*Impact, error) {
// Define a data row of services expansion factors.
expansionFactor := sv.prepareExpansionFactorRow()

Expand Down Expand Up @@ -111,7 +118,7 @@ func (sv *Service) CalculateImpact(amountOfDataInTB float64, duration time.Durat
// Define a data row of services carbon emission from powering hard drives.
carbonFromPower := sv.prepareCarbonFromDrivePoweringRow()

timeStored := H(duration.Seconds() / (60 * 60))
timeStored := H(input.Duration.Seconds() / (60 * 60))

// Define a data row of services carbon emission from write and repair actions.
carbonFromWritesAndRepairs, err := sv.prepareCarbonFromWritesAndRepairsRow(timeStored)
Expand All @@ -135,7 +142,7 @@ func (sv *Service) CalculateImpact(amountOfDataInTB float64, duration time.Durat
effectiveCarbonPerByte := prepareEffectiveCarbonPerByteRow(carbonTotalPerByte, modalityUtilization)

// Define a data row of services total carbon emission.
totalCarbon := prepareTotalCarbonRow(amountOfDataInTB, effectiveCarbonPerByte, expansionFactor, regionCount, timeStored)
totalCarbon := prepareTotalCarbonRow(input, effectiveCarbonPerByte, expansionFactor, regionCount, timeStored)

// Calculate Storj blended value.
storjBlended, err := calculateStorjBlended(networkWeighting, totalCarbon)
Expand Down Expand Up @@ -350,17 +357,22 @@ func (sv *Service) prepareCarbonPerByteMetadataOverheadRow() (*Row, error) {
return row, nil
}

func prepareEffectiveCarbonPerByteRow(carbonTotalOerByteRow, utilizationRow *Row) *Row {
func prepareEffectiveCarbonPerByteRow(carbonTotalPerByteRow, utilizationRow *Row) *Row {
row := new(Row)
for modality := 0; modality < modalityCount; modality++ {
row[modality] = carbonTotalOerByteRow[modality].Div(utilizationRow[modality])
row[modality] = carbonTotalPerByteRow[modality].Div(utilizationRow[modality])
}

return row
}

func prepareTotalCarbonRow(amountOfDataInTB float64, effectiveCarbonPerByteRow, expansionFactorRow, regionCountRow *Row, timeStored *Val) *Row {
amountOfData := TB(amountOfDataInTB)
func prepareTotalCarbonRow(input *CalculationInput, effectiveCarbonPerByteRow, expansionFactorRow, regionCountRow *Row, timeStored *Val) *Row {
amountOfData := TB(input.AmountOfDataInTB)

// We don't include timeStored amount value if data type is already TB-duration.
if input.IsTBDuration {
timeStored = H(1)
}

row := new(Row)
for modality := 0; modality < modalityCount; modality++ {
Expand Down
1 change: 1 addition & 0 deletions satellite/payments/stripe/accounts_test.go
Expand Up @@ -74,6 +74,7 @@ func TestSignupCouponCodes(t *testing.T) {
pc.PackagePlans.Packages,
pc.BonusRate,
nil,
nil,
)
require.NoError(t, err)

Expand Down

0 comments on commit 11b4095

Please sign in to comment.