mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
91 lines
2.1 KiB
Go
91 lines
2.1 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cache
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/hanzoai/git/modules/log"
|
|
"github.com/hanzoai/git/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
|
|
}
|