immutableresource.go 2.0 KB

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