Ctrl K
ComunidadGuías…

pkg/cache/cache.go

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package cache

import (
	"context"
	"errors"
	"time"
)

// ErrGetNotFound is returned when a key is absent. A miss is an error here rather than a zero
// value, so a caller cannot mistake "nothing cached" for "cached nothing".
var ErrGetNotFound = errors.New("failed to find the value on the cache")

// Get reads a key and decodes it into T, saving the caller a variable to pass by address. It
// returns ErrGetNotFound when the key is absent.
func Get[T any](ctx context.Context, cache Cache, key string) (*T, error) {
	var t *T

	if err := cache.Get(ctx, key, &t); err != nil {
		return nil, err
	}

	if t == nil {
		return nil, ErrGetNotFound
	}

	return t, nil
}

// Cache is the key/value store the services cache in. Implementations must be safe for concurrent
// use; NewNullCache is the no-op one, which is what an instance without Redis runs.
type Cache interface {
	Get(ctx context.Context, key string, value any) error
	Set(ctx context.Context, key string, value any, ttl time.Duration) error
	Delete(ctx context.Context, key string) error

	// SetNX atomically sets key to value with the given ttl only if the key does
	// not already exist, and reports whether it was set. It is the building block
	// for single-use reservations, such as a one-time token that must be redeemed
	// exactly once under concurrent requests, which Get+Set cannot do race-free.
	SetNX(ctx context.Context, key string, value any, ttl time.Duration) (bool, error)

	// CompareAndDelete atomically deletes key only while it still holds value, and reports whether
	// it did. It releases a reservation taken with SetNX under a value unique to its owner, so an
	// owner whose reservation expired cannot delete the one another caller took after it.
	CompareAndDelete(ctx context.Context, key, value string) (bool, error)

	// HasAccountLockout reports whether the source is currently blocked from attempting to
	// log in to a user with the specified userID. It returns the absolute Unix timestamp
	// in seconds representing the end of the lockout while the cache's clock is before it, or 0
	// once that second is reached or no lockout was found; the attempt number and an error
	// if any.
	HasAccountLockout(ctx context.Context, source, userID string) (lockout int64, attempt int, err error)

	// StoreLoginAttempt stores a login attempt from source to the user with the specified userID.
	// If the attempt number equals or exceeds 3, it sets a lockout for future login attempts.
	//
	// The lockout duration is calculated based on the number of attempts made, increasing exponentially
	// by a factor of 4 after the third attempt. Attempts must last for half of the double lockout duration.
	//
	// This means that a user who was locked out for 4 minutes must have the attempts stored for 10
	// minutes (or 6 minutes after the timeout). Any wrong attempt within this time will increase the
	// lockout once again. After this, the attempts will be reset, and new wrong attempts will start the
	// attempt counter from 0.
	//
	// The following equations are used to calculate both lockout and attempt duration, with 'x' representing
	// the lockout duration and 'y' the attempt duration:
	//
	//	F(x) = min(B * 4^(a - 3), M)
	//	F(y) = max(min(x * 2.5, M), 2 * B)
	//
	// Where:
	//
	//	x is the lockout duration.
	//	y is the attempt duration.
	//	a is the attempt number.
	//	B is the first lockout's duration, specified by the "SHELLHUB_ACCOUNT_LOCKOUT_BASE" environment variable.
	//	M is the maximum duration value, specified by the "SHELLHUB_MAXIMUM_ACCOUNT_LOCKOUT" environment variable.
	//
	// Examples, in minutes, for B = 1 minute, M = 32768 (15 days) and a = n:
	//
	//	n    = 3   | 4  | 5  | 8    | 11
	//	__________________________________
	//	F(x) = 1   | 4  | 16 | 1024 | 32768
	//	F(y) = 2.5 | 10 | 40 | 2560 | 32768
	//
	// It returns the absolute Unix timestamp in seconds representing the end of the lockout, or 0 if no
	// lockout was found; the attempt number and an error if any.
	StoreLoginAttempt(ctx context.Context, source, userID string) (lockout int64, attempt int, err error)

	// ResetLoginAttempts resets the login attempts and associated lockout from the source to
	// the user with the specified userID.
	ResetLoginAttempts(ctx context.Context, source, userID string) error
}