Some checks failed
Build and Release / Create Release (push) Successful in 0s
Trigger Vault Plugin Rebuild / Trigger Vault Rebuild (push) Successful in 0s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 2m48s
Build and Release / Lint (push) Failing after 5m2s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Has been skipped
Build and Release / Build Binaries (amd64, darwin, linux-latest) (push) Has been skipped
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Has been skipped
Build and Release / Build Binaries (arm64, darwin, linux-latest) (push) Has been skipped
Build and Release / Build Binaries (arm64, linux, linux-latest) (push) Has been skipped
Build and Release / Unit Tests (push) Successful in 5m37s
Go's semantic import versioning requires v2+ modules to include the major version in the module path. This enables using proper version tags (v3.x.x) instead of pseudo-versions. Updated module path: code.gitcaddy.com/server/v3
91 lines
2.1 KiB
Go
91 lines
2.1 KiB
Go
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cache
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"code.gitcaddy.com/server/v3/modules/log"
|
|
"code.gitcaddy.com/server/v3/modules/util"
|
|
)
|
|
|
|
// EphemeralCache is a cache that can be used to store data in a request level context
|
|
// This is useful for caching data that is expensive to calculate and is likely to be
|
|
// used multiple times in a request.
|
|
type EphemeralCache struct {
|
|
data map[any]map[any]any
|
|
lock sync.RWMutex
|
|
created time.Time
|
|
checkLifeTime time.Duration
|
|
}
|
|
|
|
var timeNow = time.Now
|
|
|
|
func NewEphemeralCache(checkLifeTime ...time.Duration) *EphemeralCache {
|
|
return &EphemeralCache{
|
|
data: make(map[any]map[any]any),
|
|
created: timeNow(),
|
|
checkLifeTime: util.OptionalArg(checkLifeTime, 0),
|
|
}
|
|
}
|
|
|
|
func (cc *EphemeralCache) checkExceededLifeTime(tp, key any) bool {
|
|
if cc.checkLifeTime > 0 && timeNow().Sub(cc.created) > cc.checkLifeTime {
|
|
log.Warn("EphemeralCache is expired, is highly likely to be abused for long-life tasks: %v, %v", tp, key)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (cc *EphemeralCache) Get(tp, key any) (any, bool) {
|
|
if cc.checkExceededLifeTime(tp, key) {
|
|
return nil, false
|
|
}
|
|
cc.lock.RLock()
|
|
defer cc.lock.RUnlock()
|
|
ret, ok := cc.data[tp][key]
|
|
return ret, ok
|
|
}
|
|
|
|
func (cc *EphemeralCache) Put(tp, key, value any) {
|
|
if cc.checkExceededLifeTime(tp, key) {
|
|
return
|
|
}
|
|
|
|
cc.lock.Lock()
|
|
defer cc.lock.Unlock()
|
|
|
|
d := cc.data[tp]
|
|
if d == nil {
|
|
d = make(map[any]any)
|
|
cc.data[tp] = d
|
|
}
|
|
d[key] = value
|
|
}
|
|
|
|
func (cc *EphemeralCache) Delete(tp, key any) {
|
|
if cc.checkExceededLifeTime(tp, key) {
|
|
return
|
|
}
|
|
|
|
cc.lock.Lock()
|
|
defer cc.lock.Unlock()
|
|
delete(cc.data[tp], key)
|
|
}
|
|
|
|
func GetWithEphemeralCache[T, K any](ctx context.Context, c *EphemeralCache, groupKey string, targetKey K, f func(context.Context, K) (T, error)) (T, error) {
|
|
v, has := c.Get(groupKey, targetKey)
|
|
if vv, ok := v.(T); has && ok {
|
|
return vv, nil
|
|
}
|
|
t, err := f(ctx, targetKey)
|
|
if err != nil {
|
|
return t, err
|
|
}
|
|
c.Put(groupKey, targetKey, t)
|
|
return t, nil
|
|
}
|