caching_handler.go 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package syspolicy
  4. import (
  5. "errors"
  6. "sync"
  7. )
  8. // CachingHandler is a handler that reads policies from an underlying handler the first time each key is requested
  9. // and permanently caches the result unless there is an error. If there is an ErrNoSuchKey error, that result is cached,
  10. // otherwise the actual error is returned and the next read for that key will retry using the handler.
  11. type CachingHandler struct {
  12. mu sync.Mutex
  13. strings map[string]string
  14. uint64s map[string]uint64
  15. bools map[string]bool
  16. notFound map[string]bool
  17. handler Handler
  18. }
  19. // NewCachingHandler creates a CachingHandler given a handler.
  20. func NewCachingHandler(handler Handler) *CachingHandler {
  21. return &CachingHandler{
  22. handler: handler,
  23. strings: make(map[string]string),
  24. uint64s: make(map[string]uint64),
  25. bools: make(map[string]bool),
  26. notFound: make(map[string]bool),
  27. }
  28. }
  29. // ReadString reads the policy settings value string given the key.
  30. // ReadString first reads from the handler's cache before resorting to using the handler.
  31. func (ch *CachingHandler) ReadString(key string) (string, error) {
  32. ch.mu.Lock()
  33. defer ch.mu.Unlock()
  34. if val, ok := ch.strings[key]; ok {
  35. return val, nil
  36. }
  37. if notFound := ch.notFound[key]; notFound {
  38. return "", ErrNoSuchKey
  39. }
  40. val, err := ch.handler.ReadString(key)
  41. if errors.Is(err, ErrNoSuchKey) {
  42. ch.notFound[key] = true
  43. return "", err
  44. } else if err != nil {
  45. return "", err
  46. }
  47. ch.strings[key] = val
  48. return val, nil
  49. }
  50. // ReadUInt64 reads the policy settings uint64 value given the key.
  51. // ReadUInt64 first reads from the handler's cache before resorting to using the handler.
  52. func (ch *CachingHandler) ReadUInt64(key string) (uint64, error) {
  53. ch.mu.Lock()
  54. defer ch.mu.Unlock()
  55. if val, ok := ch.uint64s[key]; ok {
  56. return val, nil
  57. }
  58. if notFound := ch.notFound[key]; notFound {
  59. return 0, ErrNoSuchKey
  60. }
  61. val, err := ch.handler.ReadUInt64(key)
  62. if errors.Is(err, ErrNoSuchKey) {
  63. ch.notFound[key] = true
  64. return 0, err
  65. } else if err != nil {
  66. return 0, err
  67. }
  68. ch.uint64s[key] = val
  69. return val, nil
  70. }
  71. // ReadBoolean reads the policy settings boolean value given the key.
  72. // ReadBoolean first reads from the handler's cache before resorting to using the handler.
  73. func (ch *CachingHandler) ReadBoolean(key string) (bool, error) {
  74. ch.mu.Lock()
  75. defer ch.mu.Unlock()
  76. if val, ok := ch.bools[key]; ok {
  77. return val, nil
  78. }
  79. if notFound := ch.notFound[key]; notFound {
  80. return false, ErrNoSuchKey
  81. }
  82. val, err := ch.handler.ReadBoolean(key)
  83. if errors.Is(err, ErrNoSuchKey) {
  84. ch.notFound[key] = true
  85. return false, err
  86. } else if err != nil {
  87. return false, err
  88. }
  89. ch.bools[key] = val
  90. return val, nil
  91. }