2019-05-10 20:05:42 +01:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package satellitedb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/skyrings/skyring-common/tools/uuid"
|
|
|
|
|
2019-05-28 16:36:52 +01:00
|
|
|
"storj.io/storj/internal/memory"
|
2019-05-10 20:05:42 +01:00
|
|
|
"storj.io/storj/pkg/pb"
|
2019-07-28 06:55:36 +01:00
|
|
|
"storj.io/storj/satellite/accounting"
|
2019-05-10 20:05:42 +01:00
|
|
|
dbx "storj.io/storj/satellite/satellitedb/dbx"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ProjectAccounting implements the accounting/db ProjectAccounting interface
|
|
|
|
type ProjectAccounting struct {
|
|
|
|
db *dbx.DB
|
|
|
|
}
|
|
|
|
|
|
|
|
// SaveTallies saves the latest bucket info
|
2019-09-12 18:31:50 +01:00
|
|
|
func (db *ProjectAccounting) SaveTallies(ctx context.Context, intervalStart time.Time, bucketTallies map[string]*accounting.BucketTally) (err error) {
|
2019-06-04 12:55:38 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-09-12 18:31:50 +01:00
|
|
|
|
2019-05-10 20:05:42 +01:00
|
|
|
if len(bucketTallies) == 0 {
|
2019-09-12 18:31:50 +01:00
|
|
|
return nil
|
2019-05-10 20:05:42 +01:00
|
|
|
}
|
|
|
|
|
2019-08-13 23:13:56 +01:00
|
|
|
// TODO: see if we can send all bucket storage tallies to the db in one operation
|
2019-09-12 18:31:50 +01:00
|
|
|
return Error.Wrap(db.db.WithTx(ctx, func(ctx context.Context, tx *dbx.Tx) error {
|
2019-08-13 23:13:56 +01:00
|
|
|
for _, info := range bucketTallies {
|
2019-09-12 18:31:50 +01:00
|
|
|
err := tx.CreateNoReturn_BucketStorageTally(ctx,
|
|
|
|
dbx.BucketStorageTally_BucketName(info.BucketName),
|
2019-09-13 14:51:41 +01:00
|
|
|
dbx.BucketStorageTally_ProjectId(info.ProjectID[:]),
|
2019-09-12 18:31:50 +01:00
|
|
|
dbx.BucketStorageTally_IntervalStart(intervalStart),
|
|
|
|
dbx.BucketStorageTally_Inline(uint64(info.InlineBytes)),
|
|
|
|
dbx.BucketStorageTally_Remote(uint64(info.RemoteBytes)),
|
|
|
|
dbx.BucketStorageTally_RemoteSegmentsCount(uint(info.RemoteSegments)),
|
|
|
|
dbx.BucketStorageTally_InlineSegmentsCount(uint(info.InlineSegments)),
|
2019-09-13 14:51:41 +01:00
|
|
|
dbx.BucketStorageTally_ObjectCount(uint(info.ObjectCount)),
|
2019-09-12 18:31:50 +01:00
|
|
|
dbx.BucketStorageTally_MetadataSize(uint64(info.MetadataSize)),
|
|
|
|
)
|
2019-08-13 23:13:56 +01:00
|
|
|
if err != nil {
|
|
|
|
return Error.Wrap(err)
|
|
|
|
}
|
2019-05-10 20:05:42 +01:00
|
|
|
}
|
2019-08-13 23:13:56 +01:00
|
|
|
return nil
|
2019-09-12 18:31:50 +01:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTallies saves the latest bucket info
|
|
|
|
func (db *ProjectAccounting) GetTallies(ctx context.Context) (tallies []accounting.BucketTally, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
dbxTallies, err := db.db.All_BucketStorageTally(ctx)
|
2019-08-13 23:13:56 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, Error.Wrap(err)
|
2019-05-10 20:05:42 +01:00
|
|
|
}
|
2019-09-12 18:31:50 +01:00
|
|
|
|
|
|
|
for _, dbxTally := range dbxTallies {
|
2019-09-13 14:51:41 +01:00
|
|
|
projectID, err := bytesToUUID(dbxTally.ProjectId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2019-09-12 18:31:50 +01:00
|
|
|
tallies = append(tallies, accounting.BucketTally{
|
|
|
|
BucketName: dbxTally.BucketName,
|
2019-09-13 14:51:41 +01:00
|
|
|
ProjectID: projectID,
|
|
|
|
ObjectCount: int64(dbxTally.ObjectCount),
|
2019-09-12 18:31:50 +01:00
|
|
|
InlineSegments: int64(dbxTally.InlineSegmentsCount),
|
|
|
|
RemoteSegments: int64(dbxTally.RemoteSegmentsCount),
|
|
|
|
InlineBytes: int64(dbxTally.Inline),
|
|
|
|
RemoteBytes: int64(dbxTally.Remote),
|
|
|
|
MetadataSize: int64(dbxTally.MetadataSize),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return tallies, nil
|
2019-05-10 20:05:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateStorageTally creates a record in the bucket_storage_tallies accounting table
|
2019-06-04 12:55:38 +01:00
|
|
|
func (db *ProjectAccounting) CreateStorageTally(ctx context.Context, tally accounting.BucketStorageTally) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-09-12 18:31:50 +01:00
|
|
|
|
|
|
|
return Error.Wrap(db.db.CreateNoReturn_BucketStorageTally(
|
2019-05-10 20:05:42 +01:00
|
|
|
ctx,
|
|
|
|
dbx.BucketStorageTally_BucketName([]byte(tally.BucketName)),
|
|
|
|
dbx.BucketStorageTally_ProjectId(tally.ProjectID[:]),
|
|
|
|
dbx.BucketStorageTally_IntervalStart(tally.IntervalStart),
|
|
|
|
dbx.BucketStorageTally_Inline(uint64(tally.InlineBytes)),
|
|
|
|
dbx.BucketStorageTally_Remote(uint64(tally.RemoteBytes)),
|
|
|
|
dbx.BucketStorageTally_RemoteSegmentsCount(uint(tally.RemoteSegmentCount)),
|
|
|
|
dbx.BucketStorageTally_InlineSegmentsCount(uint(tally.InlineSegmentCount)),
|
|
|
|
dbx.BucketStorageTally_ObjectCount(uint(tally.ObjectCount)),
|
|
|
|
dbx.BucketStorageTally_MetadataSize(uint64(tally.MetadataSize)),
|
2019-09-12 18:31:50 +01:00
|
|
|
))
|
2019-05-10 20:05:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetAllocatedBandwidthTotal returns the sum of GET bandwidth usage allocated for a projectID for a time frame
|
2019-06-25 16:58:42 +01:00
|
|
|
func (db *ProjectAccounting) GetAllocatedBandwidthTotal(ctx context.Context, projectID uuid.UUID, from time.Time) (_ int64, err error) {
|
2019-06-04 12:55:38 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-05-10 20:05:42 +01:00
|
|
|
var sum *int64
|
|
|
|
query := `SELECT SUM(allocated) FROM bucket_bandwidth_rollups WHERE project_id = ? AND action = ? AND interval_start > ?;`
|
2019-06-21 16:38:37 +01:00
|
|
|
err = db.db.QueryRow(db.db.Rebind(query), projectID[:], pb.PieceAction_GET, from).Scan(&sum)
|
2019-05-10 20:05:42 +01:00
|
|
|
if err == sql.ErrNoRows || sum == nil {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return *sum, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetStorageTotals returns the current inline and remote storage usage for a projectID
|
2019-06-04 12:55:38 +01:00
|
|
|
func (db *ProjectAccounting) GetStorageTotals(ctx context.Context, projectID uuid.UUID) (inline int64, remote int64, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-05-10 20:05:42 +01:00
|
|
|
var inlineSum, remoteSum sql.NullInt64
|
|
|
|
var intervalStart time.Time
|
|
|
|
|
|
|
|
// Sum all the inline and remote values for a project that all share the same interval_start.
|
|
|
|
// All records for a project that have the same interval start are part of the same tally run.
|
|
|
|
// This should represent the most recent calculation of a project's total at rest storage.
|
|
|
|
query := `SELECT interval_start, SUM(inline), SUM(remote)
|
|
|
|
FROM bucket_storage_tallies
|
|
|
|
WHERE project_id = ?
|
|
|
|
GROUP BY interval_start
|
|
|
|
ORDER BY interval_start DESC LIMIT 1;`
|
|
|
|
|
2019-06-04 12:55:38 +01:00
|
|
|
err = db.db.QueryRow(db.db.Rebind(query), projectID[:]).Scan(&intervalStart, &inlineSum, &remoteSum)
|
2019-05-10 20:05:42 +01:00
|
|
|
if err != nil || !inlineSum.Valid || !remoteSum.Valid {
|
|
|
|
return 0, 0, nil
|
|
|
|
}
|
|
|
|
return inlineSum.Int64, remoteSum.Int64, err
|
|
|
|
}
|
2019-05-28 16:36:52 +01:00
|
|
|
|
|
|
|
// GetProjectUsageLimits returns project usage limit
|
2019-06-04 12:55:38 +01:00
|
|
|
func (db *ProjectAccounting) GetProjectUsageLimits(ctx context.Context, projectID uuid.UUID) (_ memory.Size, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-05-28 16:36:52 +01:00
|
|
|
project, err := db.db.Get_Project_By_Id(ctx, dbx.Project_Id(projectID[:]))
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return memory.Size(project.UsageLimit), nil
|
|
|
|
}
|