@@ -14,9 +14,11 @@ import (
14
14
"github.com/ThreeDotsLabs/watermill/message"
15
15
"github.com/google/uuid"
16
16
"github.com/rs/zerolog"
17
+ "go.opentelemetry.io/otel"
17
18
18
19
"github.com/mindersec/minder/internal/db"
19
20
remindermessages "github.com/mindersec/minder/internal/reminder/messages"
21
+ "github.com/mindersec/minder/internal/reminder/metrics"
20
22
reminderconfig "github.com/mindersec/minder/pkg/config/reminder"
21
23
"github.com/mindersec/minder/pkg/eventer/constants"
22
24
)
@@ -42,6 +44,8 @@ type reminder struct {
42
44
ticker * time.Ticker
43
45
44
46
eventPublisher message.Publisher
47
+
48
+ metrics * metrics.Metrics
45
49
}
46
50
47
51
// NewReminder creates a new reminder instance
@@ -80,6 +84,20 @@ func (r *reminder) Start(ctx context.Context) error {
80
84
return fmt .Errorf ("invalid interval: %s" , r .cfg .RecurrenceConfig .Interval )
81
85
}
82
86
87
+ if r .cfg .MetricsConfig .Enabled {
88
+ go func () {
89
+ if err := r .startMetricServer (ctx ); err != nil {
90
+ logger .Error ().Err (err ).Msg ("error starting metrics server" )
91
+ }
92
+ }()
93
+
94
+ var err error
95
+ r .metrics , err = metrics .NewMetrics (otel .Meter ("reminder" ))
96
+ if err != nil {
97
+ return err
98
+ }
99
+ }
100
+
83
101
r .ticker = time .NewTicker (interval )
84
102
defer r .Stop ()
85
103
@@ -126,7 +144,7 @@ func (r *reminder) sendReminders(ctx context.Context) error {
126
144
logger := zerolog .Ctx (ctx )
127
145
128
146
// Fetch a batch of repositories
129
- repos , err := r .getRepositoryBatch (ctx )
147
+ repos , repoToLastUpdated , err := r .getRepositoryBatch (ctx )
130
148
if err != nil {
131
149
return fmt .Errorf ("error fetching repository batch: %w" , err )
132
150
}
@@ -143,6 +161,10 @@ func (r *reminder) sendReminders(ctx context.Context) error {
143
161
return fmt .Errorf ("error creating reminder messages: %w" , err )
144
162
}
145
163
164
+ if r .metrics != nil {
165
+ r .metrics .RecordBatch (ctx , int64 (len (repos )))
166
+ }
167
+
146
168
err = r .eventPublisher .Publish (constants .TopicQueueRepoReminder , messages ... )
147
169
if err != nil {
148
170
return fmt .Errorf ("error publishing messages: %w" , err )
@@ -151,14 +173,19 @@ func (r *reminder) sendReminders(ctx context.Context) error {
151
173
repoIds := make ([]uuid.UUID , len (repos ))
152
174
for _ , repo := range repos {
153
175
repoIds = append (repoIds , repo .ID )
176
+ if r .metrics != nil {
177
+ // sendDelay = Now() - ReminderLastSent - MinElapsed
178
+ reminderLastSent := repo .ReminderLastSent
179
+ if reminderLastSent .Valid {
180
+ r .metrics .SendDelay .Record (ctx , (time .Since (reminderLastSent .Time ) - r .cfg .RecurrenceConfig .MinElapsed ).Seconds ())
181
+ } else {
182
+ // Recording metric for first time reminders (after how long was the initial reminder sent)
183
+ newSendDelay := time .Since (repoToLastUpdated [repo .ID ]) - r .cfg .RecurrenceConfig .MinElapsed
184
+ r .metrics .NewSendDelay .Record (ctx , newSendDelay .Seconds ())
185
+ }
186
+ }
154
187
}
155
188
156
- // TODO: Collect Metrics
157
- // Potential metrics:
158
- // - Gauge: Number of reminders in the current batch
159
- // - UpDownCounter: Average reminders sent per batch
160
- // - Histogram: reminder_last_sent time distribution
161
-
162
189
err = r .store .UpdateReminderLastSentForRepositories (ctx , repoIds )
163
190
if err != nil {
164
191
return fmt .Errorf ("reminders published but error updating last sent time: %w" , err )
@@ -167,7 +194,7 @@ func (r *reminder) sendReminders(ctx context.Context) error {
167
194
return nil
168
195
}
169
196
170
- func (r * reminder ) getRepositoryBatch (ctx context.Context ) ([]db.Repository , error ) {
197
+ func (r * reminder ) getRepositoryBatch (ctx context.Context ) ([]db.Repository , map [uuid. UUID ]time. Time , error ) {
171
198
logger := zerolog .Ctx (ctx )
172
199
173
200
logger .Debug ().Msgf ("fetching repositories after cursor: %s" , r .repositoryCursor )
@@ -176,22 +203,25 @@ func (r *reminder) getRepositoryBatch(ctx context.Context) ([]db.Repository, err
176
203
Limit : int64 (r .cfg .RecurrenceConfig .BatchSize ),
177
204
})
178
205
if err != nil {
179
- return nil , err
206
+ return nil , nil , err
180
207
}
181
208
182
- eligibleRepos , err := r .getEligibleRepositories (ctx , repos )
209
+ eligibleRepos , eligibleReposLastUpdated , err := r .getEligibleRepositories (ctx , repos )
183
210
if err != nil {
184
- return nil , err
211
+ return nil , nil , err
185
212
}
186
213
logger .Debug ().Msgf ("%d/%d repositories are eligible for reminders" , len (eligibleRepos ), len (repos ))
187
214
188
215
r .updateRepositoryCursor (ctx , repos )
189
216
190
- return eligibleRepos , nil
217
+ return eligibleRepos , eligibleReposLastUpdated , nil
191
218
}
192
219
193
- func (r * reminder ) getEligibleRepositories (ctx context.Context , repos []db.Repository ) ([]db.Repository , error ) {
220
+ func (r * reminder ) getEligibleRepositories (ctx context.Context , repos []db.Repository ) (
221
+ []db.Repository , map [uuid.UUID ]time.Time , error ,
222
+ ) {
194
223
eligibleRepos := make ([]db.Repository , 0 , len (repos ))
224
+ eligibleReposLastUpdated := make (map [uuid.UUID ]time.Time , len (repos ))
195
225
196
226
// We have a slice of repositories, but the sqlc-generated code wants a slice of UUIDs,
197
227
// and similarly returns slices of ID -> date (in possibly different order), so we need
@@ -202,7 +232,7 @@ func (r *reminder) getEligibleRepositories(ctx context.Context, repos []db.Repos
202
232
}
203
233
oldestRuleEvals , err := r .store .ListOldestRuleEvaluationsByRepositoryId (ctx , repoIds )
204
234
if err != nil {
205
- return nil , err
235
+ return nil , nil , err
206
236
}
207
237
idToLastUpdate := make (map [uuid.UUID ]time.Time , len (oldestRuleEvals ))
208
238
for _ , times := range oldestRuleEvals {
@@ -213,10 +243,11 @@ func (r *reminder) getEligibleRepositories(ctx context.Context, repos []db.Repos
213
243
for _ , repo := range repos {
214
244
if t , ok := idToLastUpdate [repo .ID ]; ok && t .Before (cutoff ) {
215
245
eligibleRepos = append (eligibleRepos , repo )
246
+ eligibleReposLastUpdated [repo .ID ] = t
216
247
}
217
248
}
218
249
219
- return eligibleRepos , nil
250
+ return eligibleRepos , eligibleReposLastUpdated , nil
220
251
}
221
252
222
253
func (r * reminder ) updateRepositoryCursor (ctx context.Context , repos []db.Repository ) {
0 commit comments