github.com/grafana/pyroscope@v1.18.0/.cursor/rules/go-backend.mdc (about) 1 --- 2 description: Go backend coding standards and patterns for Pyroscope 3 globs: 4 - "**/*.go" 5 - "!**/*_test.go" 6 --- 7 8 # Go Backend Development 9 10 ## Import Organization 11 12 Always organize imports into three groups separated by blank lines: 13 14 ```go 15 import ( 16 // Standard library 17 "context" 18 "fmt" 19 20 // Third-party packages 21 "github.com/prometheus/client_golang/prometheus" 22 "go.uber.org/atomic" 23 24 // Internal packages 25 "github.com/grafana/pyroscope/pkg/model" 26 "github.com/grafana/pyroscope/pkg/objstore" 27 ) 28 ``` 29 30 **Don't** add imports within the three import groups (keep them separate). 31 32 ## Formatting & Linting 33 34 - Use `golangci-lint` (run via `make lint`) 35 - gofmt for formatting 36 - goimports with `-local github.com/grafana/pyroscope` 37 - Enabled linters: depguard, goconst, misspell, revive, unconvert, unparam 38 39 ## Logging 40 41 - **Use**: `github.com/go-kit/log` 42 - **Don't**: `github.com/go-kit/kit/log` (deprecated import path) 43 - **Don't**: `fmt.Println` for logging 44 45 Use structured logging: 46 47 ```go 48 import "github.com/go-kit/log/level" 49 50 level.Error(logger).Log("msg", "failed to process", "err", err) 51 level.Info(logger).Log("msg", "processing complete", "count", count) 52 ``` 53 54 ## Error Handling 55 56 - Always check errors explicitly 57 - Wrap errors with context: 58 59 ```go 60 if err != nil { 61 return fmt.Errorf("failed to query: %w", err) 62 } 63 ``` 64 65 ## Context Usage 66 67 - Always pass `context.Context` as the first parameter 68 - Respect context cancellation in loops and long operations: 69 70 ```go 71 func process(ctx context.Context, items []Item) error { 72 for _, item := range items { 73 select { 74 case <-ctx.Done(): 75 return ctx.Err() 76 default: 77 } 78 // process item 79 } 80 return nil 81 } 82 ``` 83 84 ## Multi-tenancy Pattern 85 86 All requests must include a tenant ID. Extract from context: 87 88 ```go 89 import "github.com/grafana/pyroscope/pkg/tenant" 90 91 tenantID, err := tenant.ExtractTenantIDFromContext(ctx) 92 if err != nil { 93 return err 94 } 95 ``` 96 97 **Never** hardcode tenant IDs. 98 99 ## Object Storage Pattern 100 101 Use the abstract Bucket interface: 102 103 ```go 104 import "github.com/grafana/pyroscope/pkg/objstore" 105 106 bucket := objstore.NewBucket(cfg) 107 reader, err := bucket.Get(ctx, "path/to/object") 108 ``` 109 110 ## Configuration Pattern 111 112 Use `github.com/grafana/dskit` for configuration: 113 114 ```go 115 type Config struct { 116 ListenPort int `yaml:"listen_port"` 117 } 118 119 func (cfg *Config) RegisterFlags(f *flag.FlagSet) { 120 f.IntVar(&cfg.ListenPort, "server.http-listen-port", 4040, "HTTP listen port") 121 } 122 ``` 123 124 ## Performance Considerations 125 126 1. **Minimize Allocations in Hot Paths**: 127 - Reuse buffers with `sync.Pool` 128 - Avoid string concatenation in loops 129 - Use `strings.Builder` for string building 130 131 2. **Concurrency**: 132 - Use worker pools for bounded concurrency 133 - Prefer channels for coordination over mutexes when possible 134 - Don't create unbounded goroutines - use worker pools or semaphores 135 136 3. **Profile Your Changes**: 137 ```bash 138 go test -cpuprofile=cpu.prof -memprofile=mem.prof -bench=. 139 go tool pprof cpu.prof 140 ``` 141 142 ## Code Generation 143 144 **IMPORTANT**: After changing protobuf, configs, or flags: 145 ```bash 146 make generate 147 ``` 148 Commit the generated files with your changes. 149 150 ## Security 151 152 1. **Input Validation**: Always validate and sanitize user input 153 2. **Path Traversal**: Validate object keys before storage operations 154 3. **Rate Limiting**: Distributor implements per-tenant rate limiting