storj/storagenode/storagenodedb/bandwidthdb.go
Jeff Wendling b9d8ddaad1
storagenode: remove datetime calls in favor of UTC (#2557)
* storagenode: remove datetime calls in favor of UTC

datetime only has second level granularity whereas string
comparisons don't. Since we're wiping everything anyway, it's
easier to just use UTC everywhere rather than migrate to
datetime calls.

* add utcdb to check that arguments are utc

* storagenodedb: add trivial tests to ensure calls work

This at least tests that all of the timestamps passed in are
in the UTC timezone.

* fix truncated comment and change migrations to be UTC
2019-07-15 13:38:08 -04:00

160 lines
4.2 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package storagenodedb
import (
"context"
"database/sql"
"sync"
"time"
"github.com/zeebo/errs"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/storj"
"storj.io/storj/storagenode/bandwidth"
)
type bandwidthdb struct {
*InfoDB
bandwidth bandwidthUsed
}
type bandwidthUsed struct {
// Moved to top of struct to resolve alignment issue with atomic operations on ARM
used int64
mu sync.RWMutex
usedSince time.Time
}
// Bandwidth returns table for storing bandwidth usage.
func (db *DB) Bandwidth() bandwidth.DB { return db.info.Bandwidth() }
// Bandwidth returns table for storing bandwidth usage.
func (db *InfoDB) Bandwidth() bandwidth.DB { return &db.bandwidthdb }
// Add adds bandwidth usage to the table
func (db *bandwidthdb) Add(ctx context.Context, satelliteID storj.NodeID, action pb.PieceAction, amount int64, created time.Time) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = db.db.Exec(`
INSERT INTO
bandwidth_usage(satellite_id, action, amount, created_at)
VALUES(?, ?, ?, ?)`, satelliteID, action, amount, created.UTC())
if err == nil {
db.bandwidth.mu.Lock()
defer db.bandwidth.mu.Unlock()
beginningOfMonth := getBeginningOfMonth(created.UTC())
if beginningOfMonth.Equal(db.bandwidth.usedSince) {
db.bandwidth.used += amount
} else if beginningOfMonth.After(db.bandwidth.usedSince) {
usage, err := db.Summary(ctx, beginningOfMonth, time.Now().UTC())
if err != nil {
return err
}
db.bandwidth.usedSince = beginningOfMonth
db.bandwidth.used = usage.Total()
}
}
return ErrInfo.Wrap(err)
}
// MonthSummary returns summary of the current months bandwidth usages
func (db *bandwidthdb) MonthSummary(ctx context.Context) (_ int64, err error) {
defer mon.Task()(&ctx)(&err)
db.bandwidth.mu.RLock()
beginningOfMonth := getBeginningOfMonth(time.Now().UTC())
if beginningOfMonth.Equal(db.bandwidth.usedSince) {
defer db.bandwidth.mu.RUnlock()
return db.bandwidth.used, nil
}
db.bandwidth.mu.RUnlock()
usage, err := db.Summary(ctx, beginningOfMonth, time.Now())
if err != nil {
return 0, err
}
// Just return the usage, don't update the cache. Let add handle updates
return usage.Total(), nil
}
// Summary returns summary of bandwidth usages
func (db *bandwidthdb) Summary(ctx context.Context, from, to time.Time) (_ *bandwidth.Usage, err error) {
defer mon.Task()(&ctx)(&err)
usage := &bandwidth.Usage{}
rows, err := db.db.Query(`
SELECT action, sum(amount)
FROM bandwidth_usage
WHERE ? <= created_at AND created_at <= ?
GROUP BY action`, from.UTC(), to.UTC())
if err != nil {
if err == sql.ErrNoRows {
return usage, nil
}
return nil, ErrInfo.Wrap(err)
}
defer func() { err = errs.Combine(err, rows.Close()) }()
for rows.Next() {
var action pb.PieceAction
var amount int64
err := rows.Scan(&action, &amount)
if err != nil {
return nil, ErrInfo.Wrap(err)
}
usage.Include(action, amount)
}
return usage, ErrInfo.Wrap(rows.Err())
}
// SummaryBySatellite returns summary of bandwidth usage grouping by satellite.
func (db *bandwidthdb) SummaryBySatellite(ctx context.Context, from, to time.Time) (_ map[storj.NodeID]*bandwidth.Usage, err error) {
defer mon.Task()(&ctx)(&err)
entries := map[storj.NodeID]*bandwidth.Usage{}
rows, err := db.db.Query(`
SELECT satellite_id, action, sum(amount)
FROM bandwidth_usage
WHERE ? <= created_at AND created_at <= ?
GROUP BY satellite_id, action`, from.UTC(), to.UTC())
if err != nil {
if err == sql.ErrNoRows {
return entries, nil
}
return nil, ErrInfo.Wrap(err)
}
defer func() { err = errs.Combine(err, rows.Close()) }()
for rows.Next() {
var satelliteID storj.NodeID
var action pb.PieceAction
var amount int64
err := rows.Scan(&satelliteID, &action, &amount)
if err != nil {
return nil, ErrInfo.Wrap(err)
}
entry, ok := entries[satelliteID]
if !ok {
entry = &bandwidth.Usage{}
entries[satelliteID] = entry
}
entry.Include(action, amount)
}
return entries, ErrInfo.Wrap(rows.Err())
}
func getBeginningOfMonth(now time.Time) time.Time {
y, m, _ := now.Date()
return time.Date(y, m, 1, 0, 0, 0, 0, time.Now().UTC().Location())
}