cache.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package obfuscate
  2. import (
  3. "fmt"
  4. "time"
  5. "github.com/dgraph-io/ristretto"
  6. )
  7. // measuredCache is a wrapper on top of *ristretto.Cache which additionally
  8. // sends metrics (hits and misses) every 10 seconds.
  9. type measuredCache struct {
  10. *ristretto.Cache
  11. // close allows sending shutdown notification.
  12. close chan struct{}
  13. statsd StatsClient
  14. }
  15. // Close gracefully closes the cache when active.
  16. func (c *measuredCache) Close() {
  17. if c.Cache == nil {
  18. return
  19. }
  20. c.close <- struct{}{}
  21. <-c.close
  22. }
  23. func (c *measuredCache) statsLoop() {
  24. defer func() {
  25. c.close <- struct{}{}
  26. }()
  27. tick := time.NewTicker(10 * time.Second)
  28. defer tick.Stop()
  29. mx := c.Cache.Metrics
  30. for {
  31. select {
  32. case <-tick.C:
  33. c.statsd.Gauge("datadog.trace_agent.ofuscation.sql_cache.hits", float64(mx.Hits()), nil, 1) //nolint:errcheck
  34. c.statsd.Gauge("datadog.trace_agent.ofuscation.sql_cache.misses", float64(mx.Misses()), nil, 1) //nolint:errcheck
  35. case <-c.close:
  36. c.Cache.Close()
  37. return
  38. }
  39. }
  40. }
  41. type cacheOptions struct {
  42. On bool
  43. Statsd StatsClient
  44. }
  45. // newMeasuredCache returns a new measuredCache.
  46. func newMeasuredCache(opts cacheOptions) *measuredCache {
  47. if !opts.On {
  48. // a nil *ristretto.Cache is a no-op cache
  49. return &measuredCache{}
  50. }
  51. cfg := &ristretto.Config{
  52. // We know that the maximum allowed resource length is 5K. This means that
  53. // in 5MB we can store a minimum of 1000 queries.
  54. MaxCost: 5000000,
  55. // An appromixated worst-case scenario when the cache is filled with small
  56. // queries averaged as being of length 11 ("LOCK TABLES"), we would be able
  57. // to fit 476K of them into 5MB of cost.
  58. //
  59. // We average it to 500K and multiply 10x as the documentation recommends.
  60. NumCounters: 500000 * 10,
  61. BufferItems: 64, // default recommended value
  62. Metrics: true, // enable hit/miss counters
  63. }
  64. cache, err := ristretto.NewCache(cfg)
  65. if err != nil {
  66. panic(fmt.Errorf("Error starting obfuscator query cache: %v", err))
  67. }
  68. c := measuredCache{
  69. close: make(chan struct{}),
  70. statsd: opts.Statsd,
  71. Cache: cache,
  72. }
  73. go c.statsLoop()
  74. return &c
  75. }