ops_metrics_collector_memory_test.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package service
  2. import (
  3. "testing"
  4. "github.com/shirou/gopsutil/v4/mem"
  5. "github.com/stretchr/testify/require"
  6. )
  7. const (
  8. testMiB = 1024 * 1024
  9. testGiB = 1024 * testMiB
  10. )
  11. // TestResolveMemoryStatsCgroupUsageButUnlimitedFallsBackToHost is the core
  12. // regression: in Docker + cgroup v2 with NO memory limit, memory.current is a
  13. // small container number while memory.max = "max" (so cgroupTotal == 0). The
  14. // old code reported the container "used" against the host "total", producing a
  15. // misleadingly tiny percentage (~0.3%). The fix must fall back ENTIRELY to host
  16. // metrics instead of mixing the two sources.
  17. func TestResolveMemoryStatsCgroupUsageButUnlimitedFallsBackToHost(t *testing.T) {
  18. const cgroupUsed = uint64(64573440) // memory.current, ~61 MiB
  19. host := &mem.VirtualMemoryStat{
  20. Used: 16 * testGiB,
  21. Total: 24 * testGiB,
  22. UsedPercent: 66.7,
  23. }
  24. usedMB, totalMB, pct := resolveMemoryStats(cgroupUsed, 0 /* memory.max = "max" */, true, host)
  25. require.NotNil(t, usedMB)
  26. require.NotNil(t, totalMB)
  27. require.NotNil(t, pct)
  28. // Everything must come from the host, not from the cgroup container value.
  29. require.Equal(t, int64(16*1024), *usedMB, "used must be host used, not container used")
  30. require.Equal(t, int64(24*1024), *totalMB, "total must be host total")
  31. require.InDelta(t, 66.7, *pct, 0.05, "percent must be host-derived, not container/host mix")
  32. // Guard against the specific bug: container used (~61 MiB) vs host total (~0.3%).
  33. require.NotEqual(t, int64(cgroupUsed/testMiB), *usedMB, "must not report the container used value")
  34. require.Greater(t, *pct, 1.0, "percent must not collapse to the ~0.3%% mixed value")
  35. }
  36. // TestResolveMemoryStatsExplicitContainerLimitUsesCgroup covers the case where
  37. // a real container memory limit is set: memory.current = 512 MiB and
  38. // memory.max = 2 GiB must yield ~25% entirely from cgroup, ignoring the host.
  39. func TestResolveMemoryStatsExplicitContainerLimitUsesCgroup(t *testing.T) {
  40. host := &mem.VirtualMemoryStat{
  41. Used: 16 * testGiB, // deliberately different; must be ignored
  42. Total: 24 * testGiB,
  43. UsedPercent: 66.7,
  44. }
  45. usedMB, totalMB, pct := resolveMemoryStats(512*testMiB, 2*testGiB, true, host)
  46. require.NotNil(t, usedMB)
  47. require.NotNil(t, totalMB)
  48. require.NotNil(t, pct)
  49. require.Equal(t, int64(512), *usedMB)
  50. require.Equal(t, int64(2048), *totalMB)
  51. require.InDelta(t, 25.0, *pct, 0.05)
  52. }
  53. // TestResolveMemoryStatsNoCgroupUsesHost covers bare-metal / no-cgroup hosts:
  54. // all three values come from the host reading.
  55. func TestResolveMemoryStatsNoCgroupUsesHost(t *testing.T) {
  56. host := &mem.VirtualMemoryStat{
  57. Used: 16 * testGiB,
  58. Total: 24 * testGiB,
  59. UsedPercent: 66.7,
  60. }
  61. usedMB, totalMB, pct := resolveMemoryStats(0, 0, false, host)
  62. require.NotNil(t, usedMB)
  63. require.NotNil(t, totalMB)
  64. require.NotNil(t, pct)
  65. require.Equal(t, int64(16*1024), *usedMB)
  66. require.Equal(t, int64(24*1024), *totalMB)
  67. require.InDelta(t, 66.7, *pct, 0.05)
  68. }
  69. // TestResolveMemoryStatsNoDataReturnsNil: when neither cgroup nor host data is
  70. // available, all outputs are nil (best-effort, nothing persisted).
  71. func TestResolveMemoryStatsNoDataReturnsNil(t *testing.T) {
  72. usedMB, totalMB, pct := resolveMemoryStats(0, 0, false, nil)
  73. require.Nil(t, usedMB)
  74. require.Nil(t, totalMB)
  75. require.Nil(t, pct)
  76. }
  77. // TestResolveMemoryStatsHostWithoutTotalKeepsGopsutilPercent: degenerate host
  78. // reading with no total still yields a used value and gopsutil's own percentage.
  79. func TestResolveMemoryStatsHostWithoutTotalKeepsGopsutilPercent(t *testing.T) {
  80. host := &mem.VirtualMemoryStat{
  81. Used: 8 * testGiB,
  82. Total: 0,
  83. UsedPercent: 42.5,
  84. }
  85. usedMB, totalMB, pct := resolveMemoryStats(0, 0, false, host)
  86. require.NotNil(t, usedMB)
  87. require.Nil(t, totalMB)
  88. require.NotNil(t, pct)
  89. require.Equal(t, int64(8*1024), *usedMB)
  90. require.InDelta(t, 42.5, *pct, 0.05)
  91. }