gitee.com/go-spring2/spring-base@v1.1.3/cache/engine.go (about)

     1  /*
     2   * Copyright 2012-2019 the original author or authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *      https://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cache
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  	"time"
    23  )
    24  
    25  type cacheItem struct {
    26  	value            Result
    27  	writeTime        time.Time
    28  	locker           sync.Mutex
    29  	loader           ResultLoader
    30  	expireAfterWrite time.Duration
    31  	loading          *cacheItemLoading
    32  }
    33  
    34  func (e *cacheItem) expired() bool {
    35  	if e.expireAfterWrite <= 0 {
    36  		return false
    37  	}
    38  	return time.Since(e.writeTime)-e.expireAfterWrite > 0
    39  }
    40  
    41  type cacheItemLoading struct {
    42  	v   Result
    43  	err error
    44  	wg  sync.WaitGroup
    45  }
    46  
    47  func (e *cacheItem) load(ctx context.Context, key string) (LoadType, Result, error) {
    48  
    49  	e.locker.Lock()
    50  	if p := e.loading; p != nil {
    51  		e.locker.Unlock()
    52  		p.wg.Wait()
    53  		if p.err != nil {
    54  			return LoadNone, nil, p.err
    55  		}
    56  		return LoadCache, p.v, nil
    57  	}
    58  	if e.value != nil && !e.expired() {
    59  		e.locker.Unlock()
    60  		return LoadCache, e.value, nil
    61  	}
    62  	c := &cacheItemLoading{}
    63  	c.wg.Add(1)
    64  	e.loading = c
    65  	e.locker.Unlock()
    66  
    67  	c.v, c.err = e.loader(ctx, key)
    68  	c.wg.Done()
    69  
    70  	e.locker.Lock()
    71  	e.value = c.v
    72  	e.writeTime = time.Now()
    73  	e.loading = nil
    74  	e.locker.Unlock()
    75  
    76  	if c.err != nil {
    77  		return LoadNone, nil, c.err
    78  	}
    79  	return LoadSource, c.v, nil
    80  }
    81  
    82  type engine struct{}
    83  
    84  // Load loads value from m, if there is no cached value, call the loader
    85  // to get value, and then stores it into m.
    86  func (d *engine) Load(ctx context.Context, m *sync.Map, key string, loader ResultLoader, arg OptionArg) (loadType LoadType, result Result, err error) {
    87  	actual, _ := m.LoadOrStore(key, &cacheItem{
    88  		expireAfterWrite: arg.ExpireAfterWrite,
    89  		loader:           loader,
    90  	})
    91  	return actual.(*cacheItem).load(ctx, key)
    92  }