4
4
"context"
5
5
"database/sql"
6
6
"fmt"
7
+ "math"
7
8
"sync"
8
9
"sync/atomic"
9
10
"time"
@@ -30,8 +31,8 @@ type Cache struct {
30
31
log slog.Logger
31
32
intervals Intervals
32
33
33
- deploymentDAUResponses atomic.Pointer [codersdk.DAUsResponse ]
34
- templateDAUResponses atomic.Pointer [map [uuid.UUID ]codersdk.DAUsResponse ]
34
+ deploymentDAUResponses atomic.Pointer [map [ int ] codersdk.DAUsResponse ]
35
+ templateDAUResponses atomic.Pointer [map [int ] map [ uuid.UUID ]codersdk.DAUsResponse ]
35
36
templateUniqueUsers atomic.Pointer [map [uuid.UUID ]int ]
36
37
templateAverageBuildTime atomic.Pointer [map [uuid.UUID ]database.GetTemplateAverageBuildTimeRow ]
37
38
deploymentStatsResponse atomic.Pointer [codersdk.DeploymentStats ]
@@ -161,8 +162,8 @@ func (c *Cache) refreshTemplateDAUs(ctx context.Context) error {
161
162
}
162
163
163
164
var (
164
- deploymentDAUs = codersdk.DAUsResponse {}
165
- templateDAUs = make (map [uuid.UUID ]codersdk.DAUsResponse , len (templates ))
165
+ deploymentDAUs = map [ int ] codersdk.DAUsResponse {}
166
+ templateDAUs = make (map [int ] map [ uuid.UUID ]codersdk.DAUsResponse , len (templates ))
166
167
templateUniqueUsers = make (map [uuid.UUID ]int )
167
168
templateAverageBuildTimes = make (map [uuid.UUID ]database.GetTemplateAverageBuildTimeRow )
168
169
)
@@ -171,7 +172,7 @@ func (c *Cache) refreshTemplateDAUs(ctx context.Context) error {
171
172
if err != nil {
172
173
return err
173
174
}
174
- deploymentDAUs = convertDAUResponse (rows )
175
+ deploymentDAUs [ 0 ] = convertDAUResponse (rows )
175
176
c .deploymentDAUResponses .Store (& deploymentDAUs )
176
177
177
178
for _ , template := range templates {
@@ -182,7 +183,7 @@ func (c *Cache) refreshTemplateDAUs(ctx context.Context) error {
182
183
if err != nil {
183
184
return err
184
185
}
185
- templateDAUs [template .ID ] = convertDAUResponse (rows )
186
+ templateDAUs [0 ][ template .ID ] = convertDAUResponse (rows )
186
187
templateUniqueUsers [template .ID ] = countUniqueUsers (rows )
187
188
188
189
templateAvgBuildTime , err := c .database .GetTemplateAverageBuildTime (ctx , database.GetTemplateAverageBuildTimeParams {
@@ -284,26 +285,76 @@ func (c *Cache) Close() error {
284
285
return nil
285
286
}
286
287
287
- func (c * Cache ) DeploymentDAUs () (* codersdk.DAUsResponse , bool ) {
288
+ func (c * Cache ) DeploymentDAUs (offset int ) (int , * codersdk.DAUsResponse , bool ) {
288
289
m := c .deploymentDAUResponses .Load ()
289
- return m , m != nil
290
+ if m == nil {
291
+ return 0 , nil , false
292
+ }
293
+ closestOffset , resp , ok := closest (* m , offset )
294
+ if ! ok {
295
+ return 0 , nil , false
296
+ }
297
+ return closestOffset , & resp , ok
290
298
}
291
299
292
300
// TemplateDAUs returns an empty response if the template doesn't have users
293
301
// or is loading for the first time.
294
- func (c * Cache ) TemplateDAUs (id uuid.UUID ) (* codersdk.DAUsResponse , bool ) {
302
+ // The cache will select the closest DAUs response to given timezone offset.
303
+ func (c * Cache ) TemplateDAUs (id uuid.UUID , offset int ) (int , * codersdk.DAUsResponse , bool ) {
295
304
m := c .templateDAUResponses .Load ()
296
305
if m == nil {
297
306
// Data loading.
298
- return nil , false
307
+ return 0 , nil , false
299
308
}
300
309
301
- resp , ok := (* m )[ id ]
310
+ closestOffset , resp , ok := closest (* m , offset )
302
311
if ! ok {
303
312
// Probably no data.
304
- return nil , false
313
+ return 0 , nil , false
314
+ }
315
+
316
+ tpl , ok := resp [id ]
317
+ if ! ok {
318
+ // Probably no data.
319
+ return 0 , nil , false
320
+ }
321
+
322
+ return closestOffset , & tpl , true
323
+ }
324
+
325
+ // closest returns the value in the values map that has a key with the value most
326
+ // close to the requested key. This is so if a user requests a timezone offset that
327
+ // we do not have, we return the closest one we do have to the user.
328
+ func closest [V any ](values map [int ]V , offset int ) (int , V , bool ) {
329
+ if len (values ) == 0 {
330
+ var v V
331
+ return - 1 , v , false
332
+ }
333
+
334
+ v , ok := values [offset ]
335
+ if ok {
336
+ // We have the exact offset, that was easy!
337
+ return offset , v , true
338
+ }
339
+
340
+ var closest int
341
+ var closestV V
342
+ diff := math .MaxInt
343
+ for k , v := range values {
344
+ if abs (k - offset ) < diff {
345
+ // new closest
346
+ closest = k
347
+ closestV = v
348
+ }
349
+ }
350
+ return closest , closestV , true
351
+ }
352
+
353
+ func abs (a int ) int {
354
+ if a < 0 {
355
+ return - 1 * a
305
356
}
306
- return & resp , true
357
+ return a
307
358
}
308
359
309
360
// TemplateUniqueUsers returns the number of unique Template users
0 commit comments