code.gitea.io/gitea@v1.19.3/modules/cache/context.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package cache
     5  
     6  import (
     7  	"context"
     8  	"sync"
     9  
    10  	"code.gitea.io/gitea/modules/log"
    11  )
    12  
    13  // cacheContext is a context that can be used to cache data in a request level context
    14  // This is useful for caching data that is expensive to calculate and is likely to be
    15  // used multiple times in a request.
    16  type cacheContext struct {
    17  	ctx  context.Context
    18  	data map[any]map[any]any
    19  	lock sync.RWMutex
    20  }
    21  
    22  func (cc *cacheContext) Get(tp, key any) any {
    23  	cc.lock.RLock()
    24  	defer cc.lock.RUnlock()
    25  	if cc.data[tp] == nil {
    26  		return nil
    27  	}
    28  	return cc.data[tp][key]
    29  }
    30  
    31  func (cc *cacheContext) Put(tp, key, value any) {
    32  	cc.lock.Lock()
    33  	defer cc.lock.Unlock()
    34  	if cc.data[tp] == nil {
    35  		cc.data[tp] = make(map[any]any)
    36  	}
    37  	cc.data[tp][key] = value
    38  }
    39  
    40  func (cc *cacheContext) Delete(tp, key any) {
    41  	cc.lock.Lock()
    42  	defer cc.lock.Unlock()
    43  	if cc.data[tp] == nil {
    44  		return
    45  	}
    46  	delete(cc.data[tp], key)
    47  }
    48  
    49  var cacheContextKey = struct{}{}
    50  
    51  func WithCacheContext(ctx context.Context) context.Context {
    52  	return context.WithValue(ctx, cacheContextKey, &cacheContext{
    53  		ctx:  ctx,
    54  		data: make(map[any]map[any]any),
    55  	})
    56  }
    57  
    58  func GetContextData(ctx context.Context, tp, key any) any {
    59  	if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
    60  		return c.Get(tp, key)
    61  	}
    62  	log.Warn("cannot get cache context when getting data: %v", ctx)
    63  	return nil
    64  }
    65  
    66  func SetContextData(ctx context.Context, tp, key, value any) {
    67  	if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
    68  		c.Put(tp, key, value)
    69  		return
    70  	}
    71  	log.Warn("cannot get cache context when setting data: %v", ctx)
    72  }
    73  
    74  func RemoveContextData(ctx context.Context, tp, key any) {
    75  	if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
    76  		c.Delete(tp, key)
    77  	}
    78  }
    79  
    80  // GetWithContextCache returns the cache value of the given key in the given context.
    81  func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) {
    82  	v := GetContextData(ctx, cacheGroupKey, cacheTargetID)
    83  	if vv, ok := v.(T); ok {
    84  		return vv, nil
    85  	}
    86  	t, err := f()
    87  	if err != nil {
    88  		return t, err
    89  	}
    90  	SetContextData(ctx, cacheGroupKey, cacheTargetID, t)
    91  	return t, nil
    92  }