feat(stats): add pyroscope profiling support

This commit is contained in:
Tommaso Gragnato 2025-01-18 18:57:31 +01:00
parent 78f6538902
commit 5d261e12df
No known key found for this signature in database
GPG Key ID: E4345A2BE53026FC
6 changed files with 114 additions and 1 deletions

2
go.mod
View File

@ -5,6 +5,7 @@ go 1.22.6
require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/bits-and-blooms/bloom/v3 v3.7.0
github.com/grafana/pyroscope-go v1.2.0
github.com/jackc/pgx/v5 v5.7.2
github.com/jessevdk/go-flags v1.6.1
github.com/mattn/go-sqlite3 v1.14.24
@ -22,6 +23,7 @@ require (
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect

6
go.sum
View File

@ -15,6 +15,10 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/grafana/pyroscope-go v1.2.0 h1:aILLKjTj8CS8f/24OPMGPewQSYlhmdQMBmol1d3KGj8=
github.com/grafana/pyroscope-go v1.2.0/go.mod h1:2GHr28Nr05bg2pElS+dDsc98f3JTUh2f6Fz1hWXrqwk=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@ -66,6 +70,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=

View File

@ -44,6 +44,14 @@ func main() {
}
}()
if opFlags.PyroscopeURL != "" {
profiler, err := stats.InitPyroscope(opFlags.PyroscopeURL)
if err != nil {
log.Fatalf("could not start pyroscope %s\n", err.Error())
}
defer profiler.Stop()
}
database, err := persistence.MakeDatabase(opFlags.DatabaseURL)
if err != nil {
log.Fatalf("Could not open the database %s. %s\n", opFlags.DatabaseURL, err.Error())

View File

@ -8,7 +8,8 @@ import (
)
type OpFlags struct {
DatabaseURL string `long:"database" description:"URL of the database." default:"postgres://magnetico:magnetico@localhost:5432/magnetico?sslmode=disable" yaml:"databaseURL"`
DatabaseURL string `long:"database" description:"URL of the database." default:"postgres://magnetico:magnetico@localhost:5432/magnetico?sslmode=disable" yaml:"databaseURL"`
PyroscopeURL string `long:"pyroscope" description:"URL of the Pyroscope server." default:"" yaml:"pyroscopeURL"`
IndexerAddrs []string `long:"indexer-addr" description:"Address(es) to be used by indexing DHT nodes." default:"0.0.0.0:0" yaml:"indexerAddrs"`
IndexerMaxNeighbors uint `long:"indexer-max-neighbors" description:"Maximum number of neighbors of an indexer." default:"5000" yaml:"indexerMaxNeighbors"`

37
stats/pyroscope.go Normal file
View File

@ -0,0 +1,37 @@
package stats
import (
"errors"
"os"
"runtime"
"github.com/grafana/pyroscope-go"
)
func InitPyroscope(serverAddress string) (*pyroscope.Profiler, error) {
if serverAddress == "" {
return nil, errors.New("pyroscope server address is required")
}
runtime.SetMutexProfileFraction(5)
runtime.SetBlockProfileRate(5)
return pyroscope.Start(pyroscope.Config{
ApplicationName: "magnetico.tgragnato.it",
ServerAddress: serverAddress,
Logger: nil,
Tags: map[string]string{"hostname": os.Getenv("HOSTNAME")},
ProfileTypes: []pyroscope.ProfileType{
pyroscope.ProfileCPU,
pyroscope.ProfileAllocObjects,
pyroscope.ProfileAllocSpace,
pyroscope.ProfileInuseObjects,
pyroscope.ProfileInuseSpace,
pyroscope.ProfileGoroutines,
pyroscope.ProfileMutexCount,
pyroscope.ProfileMutexDuration,
pyroscope.ProfileBlockCount,
pyroscope.ProfileBlockDuration,
},
})
}

59
stats/pyroscope_test.go Normal file
View File

@ -0,0 +1,59 @@
package stats
import (
"os"
"testing"
)
func TestInitPyroscope(t *testing.T) {
t.Parallel()
tests := []struct {
name string
serverAddr string
wantErr bool
expectedError string
}{
{
name: "Valid server address",
serverAddr: "http://localhost:4040",
wantErr: false,
},
{
name: "Empty server address",
serverAddr: "",
wantErr: true,
expectedError: "pyroscope server address is required",
},
}
origHostname := os.Getenv("HOSTNAME")
os.Setenv("HOSTNAME", "test-host")
defer os.Setenv("HOSTNAME", origHostname)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
profiler, err := InitPyroscope(tt.serverAddr)
if tt.wantErr {
if err == nil {
t.Error("expected error but got none")
}
if err != nil && err.Error() != tt.expectedError {
t.Errorf("expected error %v, got %v", tt.expectedError, err.Error())
}
} else {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if profiler == nil {
t.Error("expected non-nil profiler")
}
}
if profiler != nil {
profiler.Stop()
}
})
}
}