pin.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. package source
  2. // PinnableMetricsQuerier is optionally implemented by a MetricsQuerier whose underlying data can change
  3. // between queries, for example one that serves from in-memory snapshots replaced on an interval.
  4. //
  5. // Computations that issue many queries for one window (allocation, assets, kube model) pin the querier
  6. // once and issue every query through the pinned view, so that all results come from one consistent
  7. // state of the data rather than a mix of states from before and after an update.
  8. //
  9. // No in-tree data source implements this today: the Prometheus and collector sources are unaffected.
  10. // It is intended for data sources that replace their state wholesale, such as an adapter serving
  11. // immutable snapshots.
  12. type PinnableMetricsQuerier interface {
  13. MetricsQuerier
  14. // Pin returns a MetricsQuerier bound to the current state of the data, and a release function.
  15. //
  16. // Contract:
  17. // - The pinned querier must serve every query from the state current at the time of Pin, and
  18. // keep that view valid until release is called, even if the underlying data is updated.
  19. // - Callers call release exactly once, after every query issued through the pinned querier has
  20. // completed. Implementations must make release safe to call exactly once; the pinned querier
  21. // must not be used after release.
  22. // - Pin may be called concurrently; each call returns an independent pinned view.
  23. Pin() (MetricsQuerier, func())
  24. }
  25. // noRelease is the release function for a querier that was not pinned.
  26. func noRelease() {
  27. // nothing was pinned, so there is nothing to release
  28. }
  29. // PinMetrics pins the querier if it implements PinnableMetricsQuerier. Otherwise it returns the querier
  30. // unchanged and a no-op release function.
  31. func PinMetrics(q MetricsQuerier) (MetricsQuerier, func()) {
  32. if p, ok := q.(PinnableMetricsQuerier); ok {
  33. pinned, release := p.Pin()
  34. if release == nil {
  35. release = noRelease
  36. }
  37. return pinned, release
  38. }
  39. return q, noRelease
  40. }
  41. // PinDataSource returns a data source whose Metrics() always returns the same pinned querier, for use
  42. // by a computation that reads metrics through several helpers taking an OpenCostDataSource. If the data
  43. // source's querier does not implement PinnableMetricsQuerier, the data source is returned unchanged.
  44. //
  45. // The returned data source only forwards the OpenCostDataSource methods; optional interfaces
  46. // implemented by the original are not visible through it.
  47. func PinDataSource(ds OpenCostDataSource) (OpenCostDataSource, func()) {
  48. q := ds.Metrics()
  49. if _, ok := q.(PinnableMetricsQuerier); !ok {
  50. return ds, noRelease
  51. }
  52. pinned, release := PinMetrics(q)
  53. return &pinnedDataSource{OpenCostDataSource: ds, metrics: pinned}, release
  54. }
  55. // pinnedDataSource is an OpenCostDataSource whose Metrics() returns a pinned querier.
  56. type pinnedDataSource struct {
  57. OpenCostDataSource
  58. metrics MetricsQuerier
  59. }
  60. func (p *pinnedDataSource) Metrics() MetricsQuerier {
  61. return p.metrics
  62. }