Skip to content

Commit

Permalink
switch to authcontrol
Browse files Browse the repository at this point in the history
  • Loading branch information
klaidliadon committed Oct 23, 2024
1 parent dae13f4 commit a9376fe
Show file tree
Hide file tree
Showing 28 changed files with 336 additions and 806 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ The package offers a Redis implementation for the cache, and a Memory version of

The methods that are used to save/load in a permanent storage the 3 entities are not implemented.
The requests are measure in compute units, if a compute unit is not specified it is assumed that the value it's 1.
A client can specify the amount of compute units by manipulating the request context using the `WithComputeUnits` function.
A client can specify the amount of compute units by manipulating the request context using the `WithCost` function.

# Increment operation

The client method `SpendComputeUnits` takes care of doing an increment operation in the cache. And works as follows:
The client method `SpendCost` takes care of doing an increment operation in the cache. And works as follows:
- It tries to fetch the usage record from the cache
- On a hit it executes the INCR operation.
- On a miss it sets it to `-1` and ask the server to populate it.
Expand Down
20 changes: 10 additions & 10 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ type QuotaCache interface {
}

type UsageCache interface {
SetComputeUnits(ctx context.Context, redisKey string, amount int64) error
ClearComputeUnits(ctx context.Context, redisKey string) (bool, error)
PeekComputeUnits(ctx context.Context, redisKey string) (int64, error)
SpendComputeUnits(ctx context.Context, redisKey string, amount, limit int64) (int64, error)
SetCost(ctx context.Context, redisKey string, amount int64) error
ClearCost(ctx context.Context, redisKey string) (bool, error)
PeekCost(ctx context.Context, redisKey string) (int64, error)
SpendCost(ctx context.Context, redisKey string, amount, limit int64) (int64, error)
}

type PermissionCache interface {
Expand Down Expand Up @@ -116,12 +116,12 @@ func (s *RedisCache) setQuota(ctx context.Context, key string, quota *proto.Acce
return nil
}

func (s *RedisCache) SetComputeUnits(ctx context.Context, redisKey string, amount int64) error {
func (s *RedisCache) SetCost(ctx context.Context, redisKey string, amount int64) error {
cacheKey := fmt.Sprintf("%s%s", redisKeyPrefix, redisKey)
return s.client.Set(ctx, cacheKey, amount, s.ttl).Err()
}

func (s *RedisCache) ClearComputeUnits(ctx context.Context, redisKey string) (bool, error) {
func (s *RedisCache) ClearCost(ctx context.Context, redisKey string) (bool, error) {
cacheKey := fmt.Sprintf("%s%s", redisKeyPrefix, redisKey)
count, err := s.client.Del(ctx, cacheKey).Result()
if err != nil {
Expand All @@ -130,7 +130,7 @@ func (s *RedisCache) ClearComputeUnits(ctx context.Context, redisKey string) (bo
return count != 0, nil
}

func (s *RedisCache) PeekComputeUnits(ctx context.Context, redisKey string) (int64, error) {
func (s *RedisCache) PeekCost(ctx context.Context, redisKey string) (int64, error) {
const SpecialValue = -1
cacheKey := fmt.Sprintf("%s%s", redisKeyPrefix, redisKey)
v, err := s.client.Get(ctx, cacheKey).Int64()
Expand All @@ -153,9 +153,9 @@ func (s *RedisCache) PeekComputeUnits(ctx context.Context, redisKey string) (int
return 0, ErrCachePing
}

func (s *RedisCache) SpendComputeUnits(ctx context.Context, redisKey string, amount, limit int64) (int64, error) {
// NOTE: skip redisKeyPrefix as it's already in PeekComputeUnits
v, err := s.PeekComputeUnits(ctx, redisKey)
func (s *RedisCache) SpendCost(ctx context.Context, redisKey string, amount, limit int64) (int64, error) {
// NOTE: skip redisKeyPrefix as it's already in PeekCost
v, err := s.PeekCost(ctx, redisKey)
if err != nil {
return 0, err
}
Expand Down
21 changes: 11 additions & 10 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"sync/atomic"
"time"

"github.com/0xsequence/authcontrol"
"github.com/0xsequence/quotacontrol/middleware"
"github.com/0xsequence/quotacontrol/proto"
"github.com/goware/logger"
Expand Down Expand Up @@ -166,13 +167,13 @@ func (c *Client) FetchUsage(ctx context.Context, quota *proto.AccessQuota, now t
)

for i := range 3 {
usage, err := c.usageCache.PeekComputeUnits(ctx, key)
usage, err := c.usageCache.PeekCost(ctx, key)
if err != nil {
// ping the server to prepare usage
if errors.Is(err, ErrCachePing) {
if _, err := c.quotaClient.PrepareUsage(ctx, quota.AccessKey.ProjectID, quota.Cycle, now); err != nil {
logger.Error("unexpected client error", slog.Any("error", err))
if _, err := c.usageCache.ClearComputeUnits(ctx, key); err != nil {
if _, err := c.usageCache.ClearCost(ctx, key); err != nil {
logger.Error("unexpected cache error", slog.Any("error", err))
}
return 0, nil
Expand All @@ -197,7 +198,7 @@ func (c *Client) FetchUsage(ctx context.Context, quota *proto.AccessQuota, now t
}

func (c *Client) CheckPermission(ctx context.Context, projectID uint64, minPermission proto.UserPermission) (bool, error) {
if sessionType, _ := middleware.GetSessionType(ctx); sessionType >= proto.SessionType_Admin {
if sessionType, _ := authcontrol.GetSessionType(ctx); sessionType >= proto.SessionType_Admin {
return true, nil
}
perm, _, err := c.FetchPermission(ctx, projectID)
Expand All @@ -210,7 +211,7 @@ func (c *Client) CheckPermission(ctx context.Context, projectID uint64, minPermi
// FetchPermission fetches the user permission from cache or from the quota server.
// If an error occurs, it returns nil.
func (c *Client) FetchPermission(ctx context.Context, projectID uint64) (proto.UserPermission, *proto.ResourceAccess, error) {
userID, _ := middleware.GetAccount(ctx)
userID, _ := authcontrol.GetAccount(ctx)
logger := c.logger.With(
slog.String("op", "fetch_permission"),
slog.Uint64("project_id", projectID),
Expand All @@ -235,9 +236,9 @@ func (c *Client) FetchPermission(ctx context.Context, projectID uint64) (proto.U
return perm, access, nil
}

func (c *Client) SpendQuota(ctx context.Context, quota *proto.AccessQuota, computeUnits int64, now time.Time) (spent bool, total int64, err error) {
func (c *Client) SpendQuota(ctx context.Context, quota *proto.AccessQuota, cost int64, now time.Time) (spent bool, total int64, err error) {
// quota is nil only on unexpected errors from quota fetch
if quota == nil || computeUnits == 0 {
if quota == nil || cost == 0 {
return false, 0, nil
}

Expand All @@ -254,18 +255,18 @@ func (c *Client) SpendQuota(ctx context.Context, quota *proto.AccessQuota, compu
key := getQuotaKey(quota.AccessKey.ProjectID, quota.Cycle, now)

for i := range 3 {
total, err := c.usageCache.SpendComputeUnits(ctx, key, computeUnits, cfg.OverMax)
total, err := c.usageCache.SpendCost(ctx, key, cost, cfg.OverMax)
if err != nil {
// limit exceeded
if errors.Is(err, proto.ErrLimitExceeded) {
c.usage.AddKeyUsage(accessKey, now, proto.AccessUsage{LimitedCompute: computeUnits})
c.usage.AddKeyUsage(accessKey, now, proto.AccessUsage{LimitedCompute: cost})
return false, total, proto.ErrLimitExceeded
}
// ping the server to prepare usage
if errors.Is(err, ErrCachePing) {
if _, err := c.quotaClient.PrepareUsage(ctx, quota.AccessKey.ProjectID, quota.Cycle, now); err != nil {
logger.Error("unexpected client error", slog.Any("error", err))
if _, err := c.usageCache.ClearComputeUnits(ctx, key); err != nil {
if _, err := c.usageCache.ClearCost(ctx, key); err != nil {
logger.Error("unexpected cache error", slog.Any("error", err))
}
return false, 0, nil
Expand All @@ -284,7 +285,7 @@ func (c *Client) SpendQuota(ctx context.Context, quota *proto.AccessQuota, compu

}

usage, event := cfg.GetSpendResult(computeUnits, total)
usage, event := cfg.GetSpendResult(cost, total)
if quota.AccessKey.AccessKey == "" {
c.usage.AddProjectUsage(quota.AccessKey.ProjectID, now, usage)
} else {
Expand Down
8 changes: 4 additions & 4 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (c *spendingCounter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}

func mustJWT(t *testing.T, auth *jwtauth.JWTAuth, claims map[string]interface{}) string {
func mustJWT(t *testing.T, auth *jwtauth.JWTAuth, claims map[string]any) string {
t.Helper()
if claims == nil {
return ""
Expand Down Expand Up @@ -98,10 +98,10 @@ func executeRequest(ctx context.Context, handler http.Handler, path, accessKey,
return true, rr.Header(), nil
}

type addCredits int64
type addCost int64

func (i addCredits) Middleware(h http.Handler) http.Handler {
func (i addCost) Middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r.WithContext(middleware.AddComputeUnits(r.Context(), int64(i))))
h.ServeHTTP(w, r.WithContext(middleware.AddCost(r.Context(), int64(i))))
})
}
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
module github.com/0xsequence/quotacontrol

go 1.22.1
go 1.23.2

require (
github.com/0xsequence/authcontrol v0.0.0-20241023152638-fd04748438df
github.com/0xsequence/go-sequence v0.43.0
github.com/alicebob/miniredis/v2 v2.33.0
github.com/go-chi/chi/v5 v5.0.10
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/httprate v0.14.1
github.com/go-chi/httprate-redis v0.5.2
github.com/go-chi/jwtauth/v5 v5.3.1
Expand All @@ -15,7 +16,6 @@ require (
github.com/goware/validation v0.1.3
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/jxskiss/base62 v1.1.0
github.com/lestrrat-go/jwx/v2 v2.1.1
github.com/redis/go-redis/v9 v9.7.0
github.com/stretchr/testify v1.9.0
)
Expand All @@ -34,6 +34,7 @@ require (
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.6 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.1.1 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/0xsequence/authcontrol v0.0.0-20241023152638-fd04748438df h1:yCtCVbfXPmal2Kygm+KxnIw+xZ1a+YjTumAuQtkl418=
github.com/0xsequence/authcontrol v0.0.0-20241023152638-fd04748438df/go.mod h1:K1IlgXPaMZp7f+ZalS9vGax7f74Pwkd3wJjYN4QhyYA=
github.com/0xsequence/ethkit v1.28.0 h1:11p4UXXvYnixQk01+qmAcOF71N9DlSeMcEMbaCPtjaY=
github.com/0xsequence/ethkit v1.28.0/go.mod h1:rv0FAIyEyN0hhwGefbduAz4ujmyjyJXhCd6a0/yF3tk=
github.com/0xsequence/go-sequence v0.43.0 h1:PErMuTg4PeaamJutEJ6tAjrFBA8z0t6lvT9LOVC5RMs=
Expand All @@ -20,8 +22,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnN
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs=
github.com/go-chi/httprate v0.14.1/go.mod h1:TUepLXaz/pCjmCtf/obgOQJ2Sz6rC8fSf5cAt5cnTt0=
github.com/go-chi/httprate-redis v0.5.2 h1:Guel5o38pKaQVjtpGHYhyl2I3Pi62mVUyUji5YTYXxI=
Expand Down
2 changes: 1 addition & 1 deletion go.work
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
go 1.22.1
go 1.23.2

use (
.
Expand Down
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gibson042/canonicaljson-go v1.0.3/go.mod h1:DsLpJTThXyGNO+KZlI85C1/KDcImpP67k/RKVjcaEqo=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/httpvcr v0.2.0/go.mod h1:tGX6IOmSd8LEvItVrT4z7I4BdhjHFU5RPTmvsKudD+Q=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
Expand Down
8 changes: 4 additions & 4 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (h handler) PrepareUsage(ctx context.Context, projectID uint64, cycle *prot
return false, err
}
key := getQuotaKey(projectID, cycle, now)
if err := h.cache.UsageCache.SetComputeUnits(ctx, key, usage.GetTotalUsage()); err != nil {
if err := h.cache.UsageCache.SetCost(ctx, key, usage.GetTotalUsage()); err != nil {
return false, err
}
return true, nil
Expand All @@ -160,7 +160,7 @@ func (h handler) ClearUsage(ctx context.Context, projectID uint64, now time.Time
}

key := getQuotaKey(projectID, cycle, now)
ok, err := h.cache.UsageCache.ClearComputeUnits(ctx, key)
ok, err := h.cache.UsageCache.ClearCost(ctx, key)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -519,15 +519,15 @@ func (h handler) GetProjectStatus(ctx context.Context, projectID uint64) (*proto

key := getQuotaKey(projectID, cycle, now)

usage, err := h.cache.UsageCache.PeekComputeUnits(ctx, key)
usage, err := h.cache.UsageCache.PeekCost(ctx, key)
if err != nil {
if !errors.Is(err, ErrCachePing) {
return nil, err
}
if _, err := h.PrepareUsage(ctx, projectID, cycle, now); err != nil {
return nil, err
}
if usage, err = h.cache.UsageCache.PeekComputeUnits(ctx, key); err != nil {
if usage, err = h.cache.UsageCache.PeekCost(ctx, key); err != nil {
return nil, err
}
}
Expand Down
Loading

0 comments on commit a9376fe

Please sign in to comment.