github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/framework/cache/singleflight.go (about)

     1  // The package is migrated from beego, you can get from following link:
     2  // import(
     3  //   "github.com/beego/beego/v2/client/cache"
     4  // )
     5  // Copyright 2023. All Rights Reserved.
     6  //
     7  // Licensed under the Apache License, Version 2.0 (the "License");
     8  // you may not use this file except in compliance with the License.
     9  // You may obtain a copy of the License at
    10  //
    11  //      http://www.apache.org/licenses/LICENSE-2.0
    12  //
    13  // Unless required by applicable law or agreed to in writing, software
    14  // distributed under the License is distributed on an "AS IS" BASIS,
    15  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  // See the License for the specific language governing permissions and
    17  // limitations under the License.
    18  
    19  package cache
    20  
    21  import (
    22  	"context"
    23  	"time"
    24  
    25  	"golang.org/x/sync/singleflight"
    26  
    27  	"github.com/mdaxf/iac/framework/berror"
    28  )
    29  
    30  // SingleflightCache
    31  // This is a very simple decorator mode
    32  type SingleflightCache struct {
    33  	Cache
    34  	group      *singleflight.Group
    35  	expiration time.Duration
    36  	loadFunc   func(ctx context.Context, key string) (any, error)
    37  }
    38  
    39  // NewSingleflightCache create SingleflightCache
    40  func NewSingleflightCache(c Cache, expiration time.Duration,
    41  	loadFunc func(ctx context.Context, key string) (any, error),
    42  ) (Cache, error) {
    43  	if loadFunc == nil {
    44  		return nil, berror.Error(InvalidLoadFunc, "loadFunc cannot be nil")
    45  	}
    46  	return &SingleflightCache{
    47  		Cache:      c,
    48  		group:      &singleflight.Group{},
    49  		expiration: expiration,
    50  		loadFunc:   loadFunc,
    51  	}, nil
    52  }
    53  
    54  // Get In the Get method, single flight is used to load data and write back the cache.
    55  func (s *SingleflightCache) Get(ctx context.Context, key string) (any, error) {
    56  	val, err := s.Cache.Get(ctx, key)
    57  	if val == nil || err != nil {
    58  		val, err, _ = s.group.Do(key, func() (interface{}, error) {
    59  			v, er := s.loadFunc(ctx, key)
    60  			if er != nil {
    61  				return nil, berror.Wrap(er, LoadFuncFailed, "cache unable to load data")
    62  			}
    63  			er = s.Cache.Put(ctx, key, v, s.expiration)
    64  			return v, er
    65  		})
    66  	}
    67  	return val, err
    68  }