immutableresource.go 1.7 KB

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