github.com/lingyao2333/mo-zero@v1.4.1/core/syncx/immutableresource.go (about)

     1  package syncx
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/lingyao2333/mo-zero/core/timex"
     8  )
     9  
    10  const defaultRefreshInterval = time.Second
    11  
    12  type (
    13  	// ImmutableResourceOption defines the method to customize an ImmutableResource.
    14  	ImmutableResourceOption func(resource *ImmutableResource)
    15  
    16  	// An ImmutableResource is used to manage an immutable resource.
    17  	ImmutableResource struct {
    18  		fetch           func() (interface{}, error)
    19  		resource        interface{}
    20  		err             error
    21  		lock            sync.RWMutex
    22  		refreshInterval time.Duration
    23  		lastTime        *AtomicDuration
    24  	}
    25  )
    26  
    27  // NewImmutableResource returns an ImmutableResource.
    28  func NewImmutableResource(fn func() (interface{}, error), opts ...ImmutableResourceOption) *ImmutableResource {
    29  	// cannot use executors.LessExecutor because of cycle imports
    30  	ir := ImmutableResource{
    31  		fetch:           fn,
    32  		refreshInterval: defaultRefreshInterval,
    33  		lastTime:        NewAtomicDuration(),
    34  	}
    35  	for _, opt := range opts {
    36  		opt(&ir)
    37  	}
    38  	return &ir
    39  }
    40  
    41  // Get gets the immutable resource, fetches automatically if not loaded.
    42  func (ir *ImmutableResource) Get() (interface{}, error) {
    43  	ir.lock.RLock()
    44  	resource := ir.resource
    45  	ir.lock.RUnlock()
    46  	if resource != nil {
    47  		return resource, nil
    48  	}
    49  
    50  	ir.maybeRefresh(func() {
    51  		res, err := ir.fetch()
    52  		ir.lock.Lock()
    53  		if err != nil {
    54  			ir.err = err
    55  		} else {
    56  			ir.resource, ir.err = res, nil
    57  		}
    58  		ir.lock.Unlock()
    59  	})
    60  
    61  	ir.lock.RLock()
    62  	resource, err := ir.resource, ir.err
    63  	ir.lock.RUnlock()
    64  	return resource, err
    65  }
    66  
    67  func (ir *ImmutableResource) maybeRefresh(execute func()) {
    68  	now := timex.Now()
    69  	lastTime := ir.lastTime.Load()
    70  	if lastTime == 0 || lastTime+ir.refreshInterval < now {
    71  		ir.lastTime.Set(now)
    72  		execute()
    73  	}
    74  }
    75  
    76  // WithRefreshIntervalOnFailure sets refresh interval on failure.
    77  // Set interval to 0 to enforce refresh every time if not succeeded, default is time.Second.
    78  func WithRefreshIntervalOnFailure(interval time.Duration) ImmutableResourceOption {
    79  	return func(resource *ImmutableResource) {
    80  		resource.refreshInterval = interval
    81  	}
    82  }