-
Notifications
You must be signed in to change notification settings - Fork 397
/
projectlimitcache.go
130 lines (114 loc) · 4.34 KB
/
projectlimitcache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package accounting
import (
"context"
"time"
"github.com/zeebo/errs"
"storj.io/common/lrucache"
"storj.io/common/memory"
"storj.io/common/uuid"
)
var (
// ErrProjectLimitType error for project limit type.
ErrProjectLimitType = errs.Class("project limit type")
// ErrGetProjectLimit error for getting project limits from database.
ErrGetProjectLimit = errs.Class("get project limits")
// ErrGetProjectLimitCache error for getting project limits from cache.
ErrGetProjectLimitCache = errs.Class("get project limits from cache")
)
// ProjectLimitDB stores information about projects limits for storage and bandwidth limits.
//
// architecture: Database
type ProjectLimitDB interface {
// GetProjectLimits returns current project limit for both storage and bandwidth.
GetProjectLimits(ctx context.Context, projectID uuid.UUID) (ProjectLimits, error)
}
// ProjectLimitConfig is a configuration struct for project limit.
type ProjectLimitConfig struct {
CacheCapacity int `help:"number of projects to cache." releaseDefault:"20000" devDefault:"100"`
CacheExpiration time.Duration `help:"how long to cache the project limits." releaseDefault:"10m" devDefault:"30s"`
}
// ProjectLimitCache stores the values for both storage usage limit and bandwidth limit for
// each project ID if they differ from the default limits.
type ProjectLimitCache struct {
projectLimitDB ProjectLimitDB
defaultMaxUsage memory.Size
defaultMaxBandwidth memory.Size
defaultMaxSegments int64
state *lrucache.ExpiringLRUOf[ProjectLimits]
}
// NewProjectLimitCache creates a new project limit cache to store the project limits for each project ID.
func NewProjectLimitCache(db ProjectLimitDB, defaultMaxUsage, defaultMaxBandwidth memory.Size, defaultMaxSegments int64, config ProjectLimitConfig) *ProjectLimitCache {
return &ProjectLimitCache{
projectLimitDB: db,
defaultMaxUsage: defaultMaxUsage,
defaultMaxBandwidth: defaultMaxBandwidth,
defaultMaxSegments: defaultMaxSegments,
state: lrucache.NewOf[ProjectLimits](lrucache.Options{
Capacity: config.CacheCapacity,
Expiration: config.CacheExpiration,
Name: "accounting-projectlimit",
}),
}
}
// GetLimits returns the project limits from cache.
func (c *ProjectLimitCache) GetLimits(ctx context.Context, projectID uuid.UUID) (ProjectLimits, error) {
limits, err := c.state.Get(ctx, projectID.String(),
func() (ProjectLimits, error) {
return c.getProjectLimits(ctx, projectID)
})
if err != nil {
return ProjectLimits{}, ErrGetProjectLimitCache.Wrap(err)
}
return limits, nil
}
// GetBandwidthLimit return the bandwidth usage limit for a project ID.
func (c *ProjectLimitCache) GetBandwidthLimit(ctx context.Context, projectID uuid.UUID) (_ memory.Size, err error) {
defer mon.Task()(&ctx)(&err)
projectLimits, err := c.GetLimits(ctx, projectID)
if err != nil {
return 0, err
}
if projectLimits.Bandwidth == nil {
return c.defaultMaxBandwidth, nil
}
return memory.Size(*projectLimits.Bandwidth), nil
}
// GetSegmentLimit return the segment limit for a project ID.
func (c *ProjectLimitCache) GetSegmentLimit(ctx context.Context, projectID uuid.UUID) (_ memory.Size, err error) {
defer mon.Task()(&ctx)(&err)
projectLimits, err := c.GetLimits(ctx, projectID)
if err != nil {
return 0, err
}
if projectLimits.Segments == nil {
return memory.Size(c.defaultMaxSegments), nil
}
return memory.Size(*projectLimits.Segments), nil
}
// getProjectLimits returns project limits from DB.
func (c *ProjectLimitCache) getProjectLimits(ctx context.Context, projectID uuid.UUID) (_ ProjectLimits, err error) {
defer mon.Task()(&ctx, projectID)(&err)
projectLimits, err := c.projectLimitDB.GetProjectLimits(ctx, projectID)
if err != nil {
return ProjectLimits{}, ErrGetProjectLimit.Wrap(err)
}
if projectLimits.Bandwidth == nil {
defaultBandwidth := c.defaultMaxBandwidth.Int64()
projectLimits.Bandwidth = &defaultBandwidth
}
if projectLimits.Usage == nil {
defaultUsage := c.defaultMaxUsage.Int64()
projectLimits.Usage = &defaultUsage
}
if projectLimits.Segments == nil {
defaultSegments := c.defaultMaxSegments
projectLimits.Segments = &defaultSegments
}
if projectLimits.Segments == nil {
defaultSegments := c.defaultMaxSegments
projectLimits.Segments = &defaultSegments
}
return projectLimits, nil
}