-
Notifications
You must be signed in to change notification settings - Fork 5
Add metrics hooks to the listener #72
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
Changes from all commits
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 |
|---|---|---|
|
|
@@ -52,22 +52,57 @@ type Client interface { | |
| Session() scaleset.RunnerScaleSetSession | ||
| } | ||
|
|
||
| type Option func(*Listener) | ||
| // MetricsRecorder defines the hook methods that will be called by the listener at | ||
| // various points in the message handling process. This allows for custom | ||
| // metrics to be collected without coupling the listener to a specific metrics | ||
| // implementation. The methods in this interface will be called with relevant | ||
| // information about the message handling process, such as the number of jobs | ||
| // started/completed, the desired runner count, and any errors that occur. | ||
| // Implementers can use this information to track the performance and behavior | ||
| // of the listener and the scaleset service. | ||
| type MetricsRecorder interface { | ||
| RecordStatistics(statistics *scaleset.RunnerScaleSetStatistic) | ||
| RecordJobStarted(msg *scaleset.JobStarted) | ||
| RecordJobCompleted(msg *scaleset.JobCompleted) | ||
| RecordDesiredRunners(count int) | ||
| } | ||
|
|
||
| type discardMetricsRecorder struct{} | ||
|
|
||
| func (d *discardMetricsRecorder) RecordStatistics(statistics *scaleset.RunnerScaleSetStatistic) {} | ||
| func (d *discardMetricsRecorder) RecordJobStarted(msg *scaleset.JobStarted) {} | ||
| func (d *discardMetricsRecorder) RecordJobCompleted(msg *scaleset.JobCompleted) {} | ||
| func (d *discardMetricsRecorder) RecordDesiredRunners(count int) {} | ||
|
|
||
| // Listener listens for messages from the scaleset service and handles them. It automatically handles session | ||
| // creation/deletion/refreshing and message polling and acking. | ||
| type Listener struct { | ||
| // The main client responsible for communicating with the scaleset service | ||
| client Client | ||
| client Client | ||
| metricsRecorder MetricsRecorder | ||
|
|
||
| // Configuration for the listener | ||
| scaleSetID int | ||
| maxRunners atomic.Uint32 | ||
| scaleSetID int | ||
| maxRunners atomic.Uint32 | ||
| latestStatistics *scaleset.RunnerScaleSetStatistic | ||
|
|
||
| // configuration for the listener | ||
| logger *slog.Logger | ||
| } | ||
|
|
||
| type Option func(*Listener) | ||
|
|
||
| // WithMetricsRecorder sets the MetricsRecorder for the listener. If not set, a no-op recorder will be used. | ||
| // If the nil is passed, the MetricsRecorder will not be updated and the existing one will be used (which is a no-op by default). | ||
| func WithMetricsRecorder(recorder MetricsRecorder) Option { | ||
| return func(l *Listener) { | ||
| if recorder == nil { | ||
| return | ||
| } | ||
| l.metricsRecorder = recorder | ||
| } | ||
| } | ||
|
Comment on lines
+97
to
+104
|
||
|
|
||
| // SetMaxRunners sets the capacity of the scaleset. It is concurrently | ||
| // safe to update the max runners during listener.Run. | ||
| func (l *Listener) SetMaxRunners(count int) { | ||
|
|
@@ -85,12 +120,17 @@ func New(client Client, config Config, options ...Option) (*Listener, error) { | |
| } | ||
|
|
||
| listener := &Listener{ | ||
| client: client, | ||
| scaleSetID: config.ScaleSetID, | ||
| logger: config.Logger, | ||
| client: client, | ||
| metricsRecorder: &discardMetricsRecorder{}, | ||
| scaleSetID: config.ScaleSetID, | ||
| logger: config.Logger, | ||
| } | ||
| listener.SetMaxRunners(config.MaxRunners) | ||
|
|
||
| for _, option := range options { | ||
| option(listener) | ||
| } | ||
|
|
||
| return listener, nil | ||
| } | ||
|
|
||
|
|
@@ -114,13 +154,17 @@ func (l *Listener) Run(ctx context.Context, scaler Scaler) error { | |
| return fmt.Errorf("session statistics is nil") | ||
| } | ||
|
|
||
| l.handleStatistics(ctx, initialSession.Statistics) | ||
|
|
||
| l.logger.Info( | ||
| "Handling initial session statistics", | ||
| slog.Int("totalAssignedJobs", initialSession.Statistics.TotalAssignedJobs), | ||
| ) | ||
| if _, err := scaler.HandleDesiredRunnerCount(ctx, initialSession.Statistics.TotalAssignedJobs); err != nil { | ||
| desiredCount, err := scaler.HandleDesiredRunnerCount(ctx, initialSession.Statistics.TotalAssignedJobs) | ||
| if err != nil { | ||
| return fmt.Errorf("handling initial message failed: %w", err) | ||
| } | ||
| l.metricsRecorder.RecordDesiredRunners(desiredCount) | ||
| } | ||
|
|
||
| var lastMessageID int | ||
|
|
@@ -142,7 +186,7 @@ func (l *Listener) Run(ctx context.Context, scaler Scaler) error { | |
| } | ||
|
|
||
| if msg == nil { | ||
| _, err := scaler.HandleDesiredRunnerCount(ctx, 0) | ||
| _, err := scaler.HandleDesiredRunnerCount(ctx, l.latestStatistics.TotalAssignedJobs) | ||
| if err != nil { | ||
| return fmt.Errorf("handling nil message failed: %w", err) | ||
| } | ||
|
|
@@ -160,24 +204,35 @@ func (l *Listener) Run(ctx context.Context, scaler Scaler) error { | |
| } | ||
|
|
||
| func (l *Listener) handleMessage(ctx context.Context, handler Scaler, msg *scaleset.RunnerScaleSetMessage) error { | ||
| l.handleStatistics(ctx, msg.Statistics) | ||
|
|
||
| if err := l.client.DeleteMessage(ctx, msg.MessageID); err != nil { | ||
| return fmt.Errorf("failed to delete message: %w", err) | ||
| } | ||
|
|
||
| for _, jobStarted := range msg.JobStartedMessages { | ||
| l.metricsRecorder.RecordJobStarted(jobStarted) | ||
| if err := handler.HandleJobStarted(ctx, jobStarted); err != nil { | ||
| return fmt.Errorf("failed to handle job started: %w", err) | ||
| } | ||
| } | ||
| for _, jobCompleted := range msg.JobCompletedMessages { | ||
| l.metricsRecorder.RecordJobCompleted(jobCompleted) | ||
| if err := handler.HandleJobCompleted(ctx, jobCompleted); err != nil { | ||
| return fmt.Errorf("failed to handle job completed: %w", err) | ||
| } | ||
| } | ||
|
|
||
| if _, err := handler.HandleDesiredRunnerCount(ctx, msg.Statistics.TotalAssignedJobs); err != nil { | ||
| desiredCount, err := handler.HandleDesiredRunnerCount(ctx, msg.Statistics.TotalAssignedJobs) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to handle desired runner count: %w", err) | ||
|
Comment on lines
206
to
228
|
||
| } | ||
| l.metricsRecorder.RecordDesiredRunners(desiredCount) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (l *Listener) handleStatistics(ctx context.Context, msg *scaleset.RunnerScaleSetStatistic) { | ||
| l.latestStatistics = msg | ||
| l.metricsRecorder.RecordStatistics(msg) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.