103 lines
3.4 KiB
Go
103 lines
3.4 KiB
Go
|
// Copyright (C) 2019 Storj Labs, Inc.
|
||
|
// See LICENSE for copying information.
|
||
|
|
||
|
package live
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/skyrings/skyring-common/tools/uuid"
|
||
|
"github.com/zeebo/errs"
|
||
|
"go.uber.org/zap"
|
||
|
)
|
||
|
|
||
|
// Config contains configurable values for the live accounting service.
|
||
|
type Config struct {
|
||
|
StorageBackend string `help:"what to use for storing real-time accounting data"`
|
||
|
}
|
||
|
|
||
|
// Service represents the external interface to the live accounting
|
||
|
// functionality.
|
||
|
type Service interface {
|
||
|
GetProjectStorageUsage(ctx context.Context, projectID uuid.UUID) (int64, int64, error)
|
||
|
AddProjectStorageUsage(ctx context.Context, projectID uuid.UUID, inlineSpaceUsed, remoteSpaceUsed int64) error
|
||
|
ResetTotals()
|
||
|
}
|
||
|
|
||
|
// New creates a new live.Service instance of the type specified in
|
||
|
// the provided config.
|
||
|
func New(log *zap.Logger, config Config) (Service, error) {
|
||
|
parts := strings.SplitN(config.StorageBackend, ":", 2)
|
||
|
var backendType string
|
||
|
if len(parts) == 0 || parts[0] == "" {
|
||
|
backendType = "plainmemory"
|
||
|
} else {
|
||
|
backendType = parts[0]
|
||
|
}
|
||
|
switch backendType {
|
||
|
case "plainmemory":
|
||
|
return newPlainMemoryLiveAccounting(log)
|
||
|
}
|
||
|
return nil, errs.New("unrecognized live accounting backend specifier %q", backendType)
|
||
|
}
|
||
|
|
||
|
// plainMemoryLiveAccounting represents an live.Service-implementing
|
||
|
// instance using plain memory (no coordination with other servers). It can be
|
||
|
// used to coordinate tracking of how much space a project has used.
|
||
|
//
|
||
|
// This should probably only be used at small scale or for testing areas where
|
||
|
// the accounting cache does not matter significantly. For production, an
|
||
|
// implementation that allows multiple servers to participate together would
|
||
|
// be preferable.
|
||
|
type plainMemoryLiveAccounting struct {
|
||
|
log *zap.Logger
|
||
|
|
||
|
spaceMapLock sync.RWMutex
|
||
|
spaceDeltas map[uuid.UUID]spaceUsedAccounting
|
||
|
}
|
||
|
|
||
|
type spaceUsedAccounting struct {
|
||
|
inlineSpace int64
|
||
|
remoteSpace int64
|
||
|
}
|
||
|
|
||
|
func newPlainMemoryLiveAccounting(log *zap.Logger) (*plainMemoryLiveAccounting, error) {
|
||
|
pmac := &plainMemoryLiveAccounting{log: log}
|
||
|
pmac.ResetTotals()
|
||
|
return pmac, nil
|
||
|
}
|
||
|
|
||
|
// GetProjectStorageUsage gets inline and remote storage totals for a given
|
||
|
// project, back to the time of the last accounting tally.
|
||
|
func (pmac *plainMemoryLiveAccounting) GetProjectStorageUsage(ctx context.Context, projectID uuid.UUID) (inlineTotal, remoteTotal int64, err error) {
|
||
|
pmac.spaceMapLock.Lock()
|
||
|
defer pmac.spaceMapLock.Unlock()
|
||
|
curVal := pmac.spaceDeltas[projectID]
|
||
|
return curVal.inlineSpace, curVal.remoteSpace, nil
|
||
|
}
|
||
|
|
||
|
// AddProjectStorageUsage lets the live accounting know that the given
|
||
|
// project has just added inlineSpaceUsed bytes of inline space usage
|
||
|
// and remoteSpaceUsed bytes of remote space usage.
|
||
|
func (pmac *plainMemoryLiveAccounting) AddProjectStorageUsage(ctx context.Context, projectID uuid.UUID, inlineSpaceUsed, remoteSpaceUsed int64) error {
|
||
|
pmac.spaceMapLock.Lock()
|
||
|
defer pmac.spaceMapLock.Unlock()
|
||
|
curVal := pmac.spaceDeltas[projectID]
|
||
|
curVal.inlineSpace += inlineSpaceUsed
|
||
|
curVal.remoteSpace += remoteSpaceUsed
|
||
|
pmac.spaceDeltas[projectID] = curVal
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ResetTotals reset all space-used totals for all projects back to zero. This
|
||
|
// would normally be done in concert with calculating new tally counts in the
|
||
|
// accountingDB.
|
||
|
func (pmac *plainMemoryLiveAccounting) ResetTotals() {
|
||
|
pmac.log.Info("Resetting real-time accounting data")
|
||
|
pmac.spaceMapLock.Lock()
|
||
|
pmac.spaceDeltas = make(map[uuid.UUID]spaceUsedAccounting)
|
||
|
pmac.spaceMapLock.Unlock()
|
||
|
}
|