-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Conditionally emit metrics based on enablement #19903
Changes from 14 commits
f378a1a
8aea593
ee6fac9
91f7b6b
6ac4f46
de6950e
75dafb6
a27839d
42435f4
0390f3e
88eb4bc
5e58525
e49123d
a76e4f8
af0ba45
9897209
e2642b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import ( | |
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"sync/atomic" | ||
"time" | ||
|
||
"github.com/hashicorp/go-metrics" | ||
|
@@ -14,6 +15,21 @@ import ( | |
"github.com/prometheus/common/expfmt" | ||
) | ||
|
||
// globalTelemetryEnabled is a private variable that stores the telemetry enabled state. | ||
// It is set on initialization and does not change for the lifetime of the program. | ||
var globalTelemetryEnabled atomic.Bool | ||
|
||
// initTelemetry sets the global variable based on the configuration. | ||
// It is called only once, at startup, to set the telemetry enabled state. | ||
func initTelemetry(enabled bool) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: this is called only once (beside tests). No need for a function IMHO |
||
globalTelemetryEnabled.Store(enabled) | ||
} | ||
|
||
// isTelemetryEnabled provides controlled access to check if telemetry is enabled. | ||
func isTelemetryEnabled() bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. personal preference: with a standard bool, this method can be inlined. If you want to provide the information to other packages or modules, it can make sense to make this public though. |
||
return globalTelemetryEnabled.Load() | ||
} | ||
|
||
// globalLabels defines the set of global labels that will be applied to all | ||
// metrics emitted using the telemetry package function wrappers. | ||
var globalLabels = []metrics.Label{} | ||
|
@@ -95,6 +111,7 @@ type GatherResponse struct { | |
|
||
// New creates a new instance of Metrics | ||
func New(cfg Config) (_ *Metrics, rerr error) { | ||
initTelemetry(cfg.Enabled) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: inline the function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please note: the constructor is called by the |
||
if !cfg.Enabled { | ||
return nil, nil | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package telemetry | ||
|
||
import ( | ||
"sync" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/suite" | ||
) | ||
|
||
// TelemetrySuite is a struct that holds the setup for the telemetry tests. | ||
// It includes a mutex to ensure that tests that depend on the global state | ||
// do not run in parallel, which can cause race conditions and unpredictable results. | ||
type TelemetrySuite struct { | ||
suite.Suite | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. personal preference: the suite type adds a lot of complexity and boiler plate code to the test cases compared to vanilla go + testify asserts. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I share this preference. |
||
mu sync.Mutex | ||
} | ||
|
||
// SetupTest is called before each test to reset the global state to a known disabled state. | ||
// This ensures each test starts with the telemetry disabled | ||
func (suite *TelemetrySuite) SetupTest() { | ||
initTelemetry(false) | ||
} | ||
|
||
// TestNow tests the Now function when telemetry is enabled and disabled. | ||
func (suite *TelemetrySuite) TestNow() { | ||
suite.mu.Lock() | ||
defer suite.mu.Unlock() | ||
|
||
initTelemetry(true) | ||
telemetryTime := Now() | ||
suite.NotEqual(time.Time{}, telemetryTime, "Now() should not return zero time when telemetry is enabled") | ||
|
||
initTelemetry(false) | ||
telemetryTime = Now() | ||
suite.Equal(time.Time{}, telemetryTime, "Now() should return zero time when telemetry is disabled") | ||
} | ||
|
||
// TestIsTelemetryEnabled tests the isTelemetryEnabled function. | ||
func (suite *TelemetrySuite) TestIsTelemetryEnabled() { | ||
suite.mu.Lock() | ||
defer suite.mu.Unlock() | ||
|
||
initTelemetry(true) | ||
suite.True(isTelemetryEnabled(), "isTelemetryEnabled() should return true when globalTelemetryEnabled is set to true") | ||
|
||
initTelemetry(false) | ||
suite.False(isTelemetryEnabled(), "isTelemetryEnabled() should return false when globalTelemetryEnabled is set to false") | ||
} | ||
|
||
// TestTelemetrySuite initiates the test suite. | ||
func TestTelemetrySuite(t *testing.T) { | ||
suite.Run(t, new(TelemetrySuite)) | ||
} |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -4,7 +4,6 @@ import ( | |||||||
"context" | ||||||||
"encoding/json" | ||||||||
"fmt" | ||||||||
"time" | ||||||||
|
||||||||
gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime" | ||||||||
"google.golang.org/grpc" | ||||||||
|
@@ -93,7 +92,7 @@ func (am AppModule) ValidateGenesis(bz json.RawMessage) error { | |||||||
|
||||||||
// InitGenesis performs genesis initialization for the circuit module. | ||||||||
func (am AppModule) InitGenesis(ctx context.Context, data json.RawMessage) error { | ||||||||
start := time.Now() | ||||||||
start := telemetry.Now() | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change from using + // Using telemetry.Now() to optimize performance when telemetry is disabled
start := telemetry.Now() Committable suggestion
Suggested change
|
||||||||
var genesisState types.GenesisState | ||||||||
if err := am.cdc.UnmarshalJSON(data, &genesisState); err != nil { | ||||||||
return err | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,6 @@ package crisis | |
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"github.com/cosmos/cosmos-sdk/telemetry" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
@@ -12,7 +11,8 @@ import ( | |
|
||
// check all registered invariants | ||
func EndBlocker(ctx context.Context, k keeper.Keeper) { | ||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) | ||
start := telemetry.Now() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this would take the time here and pass it into the defer later on, is that expected? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for the comment! if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The statement is not correct. The parameters are evaluated when the function is deferred already and not on execution. See demo or stackoverflow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💯 the parameters are evaluated and closed over when the function is deferred, not executed, I prefer the prior syntax (not using a local var) since it's fewer LoC. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks you both! So I'll use the prior syntax! |
||
defer telemetry.ModuleMeasureSince(types.ModuleName, start, telemetry.MetricKeyEndBlocker) | ||
|
||
sdkCtx := sdk.UnwrapSDKContext(ctx) | ||
if k.InvCheckPeriod() == 0 || sdkCtx.BlockHeight()%int64(k.InvCheckPeriod()) != 0 { | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -4,7 +4,6 @@ import ( | |||||||
"context" | ||||||||
"encoding/json" | ||||||||
"fmt" | ||||||||
"time" | ||||||||
|
||||||||
"github.com/spf13/cobra" | ||||||||
"google.golang.org/grpc" | ||||||||
|
@@ -118,7 +117,7 @@ func (am AppModule) ValidateGenesis(bz json.RawMessage) error { | |||||||
|
||||||||
// InitGenesis performs genesis initialization for the crisis module. | ||||||||
func (am AppModule) InitGenesis(ctx context.Context, data json.RawMessage) error { | ||||||||
start := time.Now() | ||||||||
start := telemetry.Now() | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replacing + // Using telemetry.Now() to optimize performance when telemetry is disabled
start := telemetry.Now() Committable suggestion
Suggested change
|
||||||||
var genesisState types.GenesisState | ||||||||
if err := am.cdc.UnmarshalJSON(data, &genesisState); err != nil { | ||||||||
return err | ||||||||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -21,7 +21,8 @@ import ( | |||||||
|
||||||||
// EndBlocker is called every block. | ||||||||
func (k Keeper) EndBlocker(ctx context.Context) error { | ||||||||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) | ||||||||
start := telemetry.Now() | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The adjustment to use + // Using telemetry.Now() to optimize performance when telemetry is disabled
start := telemetry.Now() Committable suggestion
Suggested change
|
||||||||
defer telemetry.ModuleMeasureSince(types.ModuleName, start, telemetry.MetricKeyEndBlocker) | ||||||||
|
||||||||
logger := k.Logger() | ||||||||
// delete dead proposals from store and returns theirs deposits. | ||||||||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -4,7 +4,6 @@ import ( | |||||||
"context" | ||||||||
"errors" | ||||||||
"fmt" | ||||||||
"time" | ||||||||
|
||||||||
storetypes "cosmossdk.io/store/types" | ||||||||
"cosmossdk.io/x/upgrade/types" | ||||||||
|
@@ -22,7 +21,8 @@ import ( | |||||||
// a migration to be executed if needed upon this switch (migration defined in the new binary) | ||||||||
// skipUpgradeHeightArray is a set of block heights for which the upgrade must be skipped | ||||||||
func (k Keeper) PreBlocker(ctx context.Context) error { | ||||||||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) | ||||||||
start := telemetry.Now() | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The update to use + // Using telemetry.Now() to optimize performance when telemetry is disabled
start := telemetry.Now() Committable suggestion
Suggested change
|
||||||||
defer telemetry.ModuleMeasureSince(types.ModuleName, start, telemetry.MetricKeyBeginBlocker) | ||||||||
|
||||||||
blockHeight := k.environment.HeaderService.GetHeaderInfo(ctx).Height | ||||||||
plan, err := k.GetUpgradePlan(ctx) | ||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good use of global var but why do you use an atomic bool when it is not modified after init? Could be a simple bool.