Skip to content

Commit f708302

Browse files
committed
test memory metrics
1 parent b08cab0 commit f708302

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

.github/workflows/framework-golden-tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
test:
21+
- name: TestMemoryMetrics
22+
config: smoke_memory.toml
23+
count: 1
24+
timeout: 10m
2125
- name: TestSmoke
2226
config: smoke.toml
2327
count: 1
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
[blockchain_a]
3+
docker_cmd_params = ["-b", "1"]
4+
type = "anvil"
5+
6+
[data_provider]
7+
port = 9111
8+
9+
[nodeset]
10+
name = "don"
11+
nodes = 5
12+
override_mode = "all"
13+
14+
[nodeset.db]
15+
image = "postgres:12.0"
16+
17+
[[nodeset.node_specs]]
18+
19+
[nodeset.node_specs.node]
20+
image = "public.ecr.aws/chainlink/chainlink:v2.17.0"
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package examples
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"net/http"
7+
"strconv"
8+
"strings"
9+
"testing"
10+
"time"
11+
12+
"github.com/go-resty/resty/v2"
13+
"github.com/stretchr/testify/require"
14+
15+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
16+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
17+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/fake"
18+
ns "github.com/smartcontractkit/chainlink-testing-framework/framework/components/simple_node_set"
19+
)
20+
21+
type MemoryStats struct {
22+
HeapInUseMB float64
23+
HeapAllocMB float64
24+
SysMB float64
25+
Goroutines float64
26+
LastGCTime float64
27+
GCDurationSum float64
28+
GCCount float64
29+
AvgGCTimeMS float64
30+
}
31+
32+
type CfgMemory struct {
33+
BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"`
34+
MockerDataProvider *fake.Input `toml:"data_provider" validate:"required"`
35+
NodeSet *ns.Input `toml:"nodeset" validate:"required"`
36+
}
37+
38+
func TestMemoryMetrics(t *testing.T) {
39+
in, err := framework.Load[CfgMemory](t)
40+
require.NoError(t, err)
41+
42+
bcOut, err := blockchain.NewBlockchainNetwork(in.BlockchainA)
43+
require.NoError(t, err)
44+
_, err = fake.NewFakeDataProvider(in.MockerDataProvider)
45+
require.NoError(t, err)
46+
nsOut, err := ns.NewSharedDBNodeSet(in.NodeSet, bcOut)
47+
require.NoError(t, err)
48+
49+
t.Run("memory_metrics_monitoring", func(t *testing.T) {
50+
// add some OCR test here
51+
_ = bcOut
52+
_ = nsOut
53+
time.Sleep(5 * time.Second)
54+
55+
metrics, err := fetchMetrics(fmt.Sprintf("%s/metrics", nsOut.CLNodes[1].Node.HostURL))
56+
require.NoError(t, err)
57+
stats, err := parseMetrics(metrics)
58+
require.NoError(t, err)
59+
60+
framework.L.Info().
61+
Float64("heap_in_use_mb", stats.HeapInUseMB).
62+
Float64("heap_alloc_mb", stats.HeapAllocMB).
63+
Float64("system_mb", stats.SysMB).
64+
Float64("goroutines", stats.Goroutines).
65+
Float64("avg_gc_time_ms", stats.AvgGCTimeMS).
66+
Msg("Checking memory metrics")
67+
68+
// memory limits
69+
require.LessOrEqual(t, stats.SysMB, 1000.0)
70+
require.LessOrEqual(t, stats.HeapInUseMB, 300.0)
71+
require.LessOrEqual(t, stats.HeapAllocMB, 300.0)
72+
// goroutines
73+
require.LessOrEqual(t, stats.Goroutines, 1000.0)
74+
// gc pauses
75+
require.Less(t, stats.AvgGCTimeMS, 10.0)
76+
})
77+
}
78+
79+
func fetchMetrics(url string) (string, error) {
80+
client := resty.New()
81+
resp, err := client.R().
82+
SetHeader("Accept", "text/plain").
83+
Get(url)
84+
85+
if err != nil {
86+
return "", fmt.Errorf("failed to fetch metrics: %w", err)
87+
}
88+
if resp.StatusCode() != http.StatusOK {
89+
return "", fmt.Errorf("unexpected status code: %d", resp.StatusCode())
90+
}
91+
return resp.String(), nil
92+
}
93+
94+
func parseMetrics(metrics string) (MemoryStats, error) {
95+
var stats MemoryStats
96+
scanner := bufio.NewScanner(strings.NewReader(metrics))
97+
98+
for scanner.Scan() {
99+
line := scanner.Text()
100+
if strings.HasPrefix(line, "#") || len(strings.Split(line, " ")) < 2 {
101+
continue
102+
}
103+
104+
parts := strings.Split(line, " ")
105+
metricName := strings.Split(parts[0], "{")[0]
106+
value, _ := strconv.ParseFloat(parts[1], 64)
107+
108+
switch metricName {
109+
case "go_memstats_heap_inuse_bytes":
110+
stats.HeapInUseMB = value / 1024 / 1024
111+
case "go_memstats_alloc_bytes":
112+
stats.HeapAllocMB = value / 1024 / 1024
113+
case "go_memstats_sys_bytes":
114+
stats.SysMB = value / 1024 / 1024
115+
case "go_goroutines":
116+
stats.Goroutines = value
117+
case "go_gc_duration_seconds_sum":
118+
stats.GCDurationSum = value
119+
case "go_gc_duration_seconds_count":
120+
stats.GCCount = value
121+
}
122+
}
123+
124+
if stats.GCCount > 0 {
125+
stats.AvgGCTimeMS = (stats.GCDurationSum / stats.GCCount) * 1000
126+
}
127+
128+
return stats, scanner.Err()
129+
}

0 commit comments

Comments
 (0)