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 }