Skip to content

Commit

Permalink
Update RecordContractSpending (#1417)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisSchinnerl authored Aug 7, 2024
2 parents b2cf836 + 2910735 commit 8ec1f6b
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 35 deletions.
46 changes: 18 additions & 28 deletions stores/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ var (
pruneDirsAlertID = frand.Entropy256()
)

var (
objectDeleteBatchSizes = []int64{10, 50, 100, 200, 500, 1000, 5000, 10000, 50000, 100000}
)
var objectDeleteBatchSizes = []int64{10, 50, 100, 200, 500, 1000, 5000, 10000, 50000, 100000}

type (
contractState uint8
Expand Down Expand Up @@ -715,16 +713,11 @@ func (s *SQLStore) RecordContractSpending(ctx context.Context, records []api.Con
}
metrics := make([]api.ContractMetric, 0, len(squashedRecords))
for fcid, newSpending := range squashedRecords {
err := s.retryTransaction(ctx, func(tx *gorm.DB) error {
var contract dbContract
err := tx.Model(&dbContract{}).
Where("fcid", fileContractID(fcid)).
Joins("Host").
Take(&contract).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil // contract not found, continue with next one
err := s.db.Transaction(ctx, func(tx sql.DatabaseTx) error {
contract, err := tx.Contract(ctx, fcid)
if errors.Is(err, api.ErrContractNotFound) {
} else if err != nil {
return err
return fmt.Errorf("failed to fetch contract: %w", err)
}

remainingCollateral := types.ZeroCurrency
Expand All @@ -734,38 +727,35 @@ func (s *SQLStore) RecordContractSpending(ctx context.Context, records []api.Con
m := api.ContractMetric{
Timestamp: api.TimeNow(),
ContractID: fcid,
HostKey: types.PublicKey(contract.Host.PublicKey),
HostKey: contract.HostKey,
RemainingCollateral: remainingCollateral,
RemainingFunds: latestValues[fcid].validRenterPayout,
RevisionNumber: latestValues[fcid].revision,
UploadSpending: types.Currency(contract.UploadSpending).Add(newSpending.Uploads),
DownloadSpending: types.Currency(contract.DownloadSpending).Add(newSpending.Downloads),
FundAccountSpending: types.Currency(contract.FundAccountSpending).Add(newSpending.FundAccount),
DeleteSpending: types.Currency(contract.DeleteSpending).Add(newSpending.Deletions),
ListSpending: types.Currency(contract.ListSpending).Add(newSpending.SectorRoots),
UploadSpending: contract.Spending.Uploads.Add(newSpending.Uploads),
DownloadSpending: contract.Spending.Downloads.Add(newSpending.Downloads),
FundAccountSpending: contract.Spending.FundAccount.Add(newSpending.FundAccount),
DeleteSpending: contract.Spending.Deletions.Add(newSpending.Deletions),
ListSpending: contract.Spending.SectorRoots.Add(newSpending.SectorRoots),
}
metrics = append(metrics, m)

updates := make(map[string]interface{})
var updates api.ContractSpending
if !newSpending.Uploads.IsZero() {
updates["upload_spending"] = currency(m.UploadSpending)
updates.Uploads = m.UploadSpending
}
if !newSpending.Downloads.IsZero() {
updates["download_spending"] = currency(m.DownloadSpending)
updates.Downloads = m.DownloadSpending
}
if !newSpending.FundAccount.IsZero() {
updates["fund_account_spending"] = currency(m.FundAccountSpending)
updates.FundAccount = m.FundAccountSpending
}
if !newSpending.Deletions.IsZero() {
updates["delete_spending"] = currency(m.DeleteSpending)
updates.Deletions = m.DeleteSpending
}
if !newSpending.SectorRoots.IsZero() {
updates["list_spending"] = currency(m.ListSpending)
updates.SectorRoots = m.ListSpending
}
updates["revision_number"] = latestValues[fcid].revision
updates["size"] = latestValues[fcid].size
err = tx.Model(&contract).Updates(updates).Error
return err
return tx.RecordContractSpending(ctx, fcid, latestValues[fcid].revision, latestValues[fcid].size, updates)
})
if err != nil {
return err
Expand Down
22 changes: 15 additions & 7 deletions stores/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2328,9 +2328,10 @@ func TestRecordContractSpending(t *testing.T) {
cm, err := ss.addTestContract(fcid, hk)
if err != nil {
t.Fatal(err)
}
if cm.Spending != (api.ContractSpending{}) {
} else if cm.Spending != (api.ContractSpending{}) {
t.Fatal("spending should be all 0")
} else if cm.Size != 0 && cm.RevisionNumber != 0 {
t.Fatalf("unexpected size or revision number, %v %v", cm.Size, cm.RevisionNumber)
}

// Record some spending.
Expand All @@ -2350,6 +2351,8 @@ func TestRecordContractSpending(t *testing.T) {
{
ContractID: fcid,
ContractSpending: expectedSpending,
RevisionNumber: 100,
Size: 200,
},
})
if err != nil {
Expand All @@ -2358,16 +2361,20 @@ func TestRecordContractSpending(t *testing.T) {
cm2, err := ss.Contract(context.Background(), fcid)
if err != nil {
t.Fatal(err)
}
if cm2.Spending != expectedSpending {
} else if cm2.Spending != expectedSpending {
t.Fatal("invalid spending", cm2.Spending, expectedSpending)
} else if cm2.Size != 200 && cm2.RevisionNumber != 100 {
t.Fatalf("unexpected size or revision number, %v %v", cm2.Size, cm2.RevisionNumber)
}

// Record the same spending again.
// Record the same spending again but with a lower revision number. This
// shouldn't update the size.
err = ss.RecordContractSpending(context.Background(), []api.ContractSpendingRecord{
{
ContractID: fcid,
ContractSpending: expectedSpending,
RevisionNumber: 100,
Size: 200,
},
})
if err != nil {
Expand All @@ -2377,9 +2384,10 @@ func TestRecordContractSpending(t *testing.T) {
cm3, err := ss.Contract(context.Background(), fcid)
if err != nil {
t.Fatal(err)
}
if cm3.Spending != expectedSpending {
} else if cm3.Spending != expectedSpending {
t.Fatal("invalid spending")
} else if cm2.Size != 200 && cm2.RevisionNumber != 100 {
t.Fatalf("unexpected size or revision number, %v %v", cm2.Size, cm2.RevisionNumber)
}
}

Expand Down
3 changes: 3 additions & 0 deletions stores/sql/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ type (
// or slab buffer.
PruneSlabs(ctx context.Context, limit int64) (int64, error)

// RecordContractSpending records new spending for a contract
RecordContractSpending(ctx context.Context, fcid types.FileContractID, revisionNumber, size uint64, newSpending api.ContractSpending) error

// RecordHostScans records the results of host scans in the database
// such as recording the settings and price table of a host in case of
// success and updating the uptime and downtime of a host.
Expand Down
39 changes: 39 additions & 0 deletions stores/sql/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2763,3 +2763,42 @@ func MarkPackedSlabUploaded(ctx context.Context, tx Tx, slab api.UploadedPackedS
}
return bufferFileName, nil
}

func RecordContractSpending(ctx context.Context, tx Tx, fcid types.FileContractID, revisionNumber, size uint64, newSpending api.ContractSpending) error {
var updateKeys []string
var updateValues []interface{}

if !newSpending.Uploads.IsZero() {
updateKeys = append(updateKeys, "upload_spending = ?")
updateValues = append(updateValues, Currency(newSpending.Uploads))
}
if !newSpending.Downloads.IsZero() {
updateKeys = append(updateKeys, "download_spending = ?")
updateValues = append(updateValues, Currency(newSpending.Downloads))
}
if !newSpending.FundAccount.IsZero() {
updateKeys = append(updateKeys, "fund_account_spending = ?")
updateValues = append(updateValues, Currency(newSpending.FundAccount))
}
if !newSpending.Deletions.IsZero() {
updateKeys = append(updateKeys, "delete_spending = ?")
updateValues = append(updateValues, Currency(newSpending.Deletions))
}
if !newSpending.SectorRoots.IsZero() {
updateKeys = append(updateKeys, "list_spending = ?")
updateValues = append(updateValues, Currency(newSpending.SectorRoots))
}
updateKeys = append(updateKeys, "revision_number = ?", "size = ?")
updateValues = append(updateValues, revisionNumber, size)

updateValues = append(updateValues, FileContractID(fcid))
_, err := tx.Exec(ctx, fmt.Sprintf(`
UPDATE contracts
SET %s
WHERE fcid = ?
`, strings.Join(updateKeys, ",")), updateValues...)
if err != nil {
return fmt.Errorf("failed to record contract spending: %w", err)
}
return nil
}
4 changes: 4 additions & 0 deletions stores/sql/mysql/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,10 @@ func (tx *MainDatabaseTx) PruneSlabs(ctx context.Context, limit int64) (int64, e
return res.RowsAffected()
}

func (tx *MainDatabaseTx) RecordContractSpending(ctx context.Context, fcid types.FileContractID, revisionNumber, size uint64, newSpending api.ContractSpending) error {
return ssql.RecordContractSpending(ctx, tx, fcid, revisionNumber, size, newSpending)
}

func (tx *MainDatabaseTx) RecordHostScans(ctx context.Context, scans []api.HostScan) error {
return ssql.RecordHostScans(ctx, tx, scans)
}
Expand Down
4 changes: 4 additions & 0 deletions stores/sql/sqlite/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,10 @@ func (tx *MainDatabaseTx) PruneSlabs(ctx context.Context, limit int64) (int64, e
return res.RowsAffected()
}

func (tx *MainDatabaseTx) RecordContractSpending(ctx context.Context, fcid types.FileContractID, revisionNumber, size uint64, newSpending api.ContractSpending) error {
return ssql.RecordContractSpending(ctx, tx, fcid, revisionNumber, size, newSpending)
}

func (tx *MainDatabaseTx) RecordHostScans(ctx context.Context, scans []api.HostScan) error {
return ssql.RecordHostScans(ctx, tx, scans)
}
Expand Down

0 comments on commit 8ec1f6b

Please sign in to comment.