storj/satellite/satellitedb/compensation.go
Jeff Wendling 376547c33c satellite/compensation: smaller txns for RecordPeriod
cockroach is having problems with huge transactions and
having them complete before timeouts or whatever, so
do smaller transactions.

because we can have partial recording of payments which
are not unique, we have to do a thing where we read and
check if it already exists before writing. this is not
concurrency safe.

Change-Id: Ia7d59499a43ce6d70cb2a23754edbdd1b643ef1a
2021-03-02 20:14:25 +00:00

138 lines
4.6 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package satellitedb
import (
"context"
"storj.io/common/storj"
"storj.io/storj/private/currency"
"storj.io/storj/satellite/compensation"
"storj.io/storj/satellite/satellitedb/dbx"
)
type compensationDB struct {
db *satelliteDB
}
// QueryTotalAmounts returns withheld data for the given node.
func (comp *compensationDB) QueryTotalAmounts(ctx context.Context, nodeID storj.NodeID) (_ compensation.TotalAmounts, err error) {
defer mon.Task()(&ctx)(&err)
stmt := comp.db.Rebind(`
SELECT
coalesce(SUM(held), 0) AS total_held,
coalesce(SUM(disposed), 0) AS total_disposed,
coalesce(SUM(paid), 0) AS total_paid,
coalesce(SUM(distributed), 0) AS total_distributed
FROM
storagenode_paystubs
WHERE
node_id = ?
`)
var totalHeld, totalDisposed, totalPaid, totalDistributed int64
if err := comp.db.DB.QueryRow(ctx, stmt, nodeID).Scan(&totalHeld, &totalDisposed, &totalPaid, &totalDistributed); err != nil {
return compensation.TotalAmounts{}, Error.Wrap(err)
}
return compensation.TotalAmounts{
TotalHeld: currency.NewMicroUnit(totalHeld),
TotalDisposed: currency.NewMicroUnit(totalDisposed),
TotalPaid: currency.NewMicroUnit(totalPaid),
TotalDistributed: currency.NewMicroUnit(totalDistributed),
}, nil
}
func (comp *compensationDB) RecordPeriod(ctx context.Context, paystubs []compensation.Paystub, payments []compensation.Payment) (err error) {
defer mon.Task()(&ctx)(&err)
if err := comp.RecordPaystubs(ctx, paystubs); err != nil {
return err
}
if err := comp.RecordPayments(ctx, payments); err != nil {
return err
}
return nil
}
func stringPointersEqual(a, b *string) bool {
if a == nil || b == nil {
return a == b
}
return *a == *b
}
func (comp *compensationDB) RecordPayments(ctx context.Context, payments []compensation.Payment) (err error) {
defer mon.Task()(&ctx)(&err)
for _, payment := range payments {
payment := payment // to satisfy linting
err := comp.db.WithTx(ctx, func(ctx context.Context, tx *dbx.Tx) error {
existingPayments, err := tx.All_StoragenodePayment_By_NodeId_And_Period(ctx,
dbx.StoragenodePayment_NodeId(payment.NodeID.Bytes()),
dbx.StoragenodePayment_Period(payment.Period.String()))
if err != nil {
return Error.Wrap(err)
}
// check if the payment already exists. we know period and node id already match.
for _, existingPayment := range existingPayments {
if existingPayment.Amount == payment.Amount.Value() &&
stringPointersEqual(existingPayment.Receipt, payment.Receipt) &&
stringPointersEqual(existingPayment.Notes, payment.Notes) {
return nil
}
}
return Error.Wrap(tx.CreateNoReturn_StoragenodePayment(ctx,
dbx.StoragenodePayment_NodeId(payment.NodeID.Bytes()),
dbx.StoragenodePayment_Period(payment.Period.String()),
dbx.StoragenodePayment_Amount(payment.Amount.Value()),
dbx.StoragenodePayment_Create_Fields{
Receipt: dbx.StoragenodePayment_Receipt_Raw(payment.Receipt),
Notes: dbx.StoragenodePayment_Notes_Raw(payment.Notes),
},
))
})
if err != nil {
return err
}
}
return nil
}
func (comp *compensationDB) RecordPaystubs(ctx context.Context, paystubs []compensation.Paystub) error {
for _, paystub := range paystubs {
err := comp.db.ReplaceNoReturn_StoragenodePaystub(ctx,
dbx.StoragenodePaystub_Period(paystub.Period.String()),
dbx.StoragenodePaystub_NodeId(paystub.NodeID.Bytes()),
dbx.StoragenodePaystub_Codes(paystub.Codes.String()),
dbx.StoragenodePaystub_UsageAtRest(paystub.UsageAtRest),
dbx.StoragenodePaystub_UsageGet(paystub.UsageGet),
dbx.StoragenodePaystub_UsagePut(paystub.UsagePut),
dbx.StoragenodePaystub_UsageGetRepair(paystub.UsageGetRepair),
dbx.StoragenodePaystub_UsagePutRepair(paystub.UsagePutRepair),
dbx.StoragenodePaystub_UsageGetAudit(paystub.UsageGetAudit),
dbx.StoragenodePaystub_CompAtRest(paystub.CompAtRest.Value()),
dbx.StoragenodePaystub_CompGet(paystub.CompGet.Value()),
dbx.StoragenodePaystub_CompPut(paystub.CompPut.Value()),
dbx.StoragenodePaystub_CompGetRepair(paystub.CompGetRepair.Value()),
dbx.StoragenodePaystub_CompPutRepair(paystub.CompPutRepair.Value()),
dbx.StoragenodePaystub_CompGetAudit(paystub.CompGetAudit.Value()),
dbx.StoragenodePaystub_SurgePercent(paystub.SurgePercent),
dbx.StoragenodePaystub_Held(paystub.Held.Value()),
dbx.StoragenodePaystub_Owed(paystub.Owed.Value()),
dbx.StoragenodePaystub_Disposed(paystub.Disposed.Value()),
dbx.StoragenodePaystub_Paid(paystub.Paid.Value()),
dbx.StoragenodePaystub_Distributed(paystub.Distributed.Value()),
)
if err != nil {
return err
}
}
return nil
}