Skip to content

Commit 00ddefe

Browse files
committed
backend: Add metrics endpoints
Pull request #656 adds the necessary instance_stats database table as well as functions for querying instance counts (by channel, version, and architecture) from the groups, instance, and instance_application tables and storing the results in the new table. To make this data accessible outside Nebraska, we create the following: - Prometheus metrics endpoint: A new HTTP endpoint (instance-metrics/prometheus) that serves only the latest snapshot from the instance_stats table. As Prometheus is a time-series database, this serves instance counts with the latest timestamp only. - JSON metrics data endpoint: A new HTTP endpoint that emits all instance_stats data in JSON format. The difference here is that we query for all data, and we emit one JSON document per row.
1 parent b641c2b commit 00ddefe

File tree

10 files changed

+490
-76
lines changed

10 files changed

+490
-76
lines changed

backend/api/spec.yaml

+59
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,41 @@ paths:
11771177
description: Activity not found response
11781178
"500":
11791179
description: List activity error response
1180+
/instance-metrics/prometheus:
1181+
get:
1182+
description: Get latest instance stats
1183+
operationId: getLatestInstanceStats
1184+
security:
1185+
- oidcBearerAuth: []
1186+
- oidcCookieAuth: []
1187+
- githubCookieAuth: []
1188+
responses:
1189+
"200":
1190+
description: Get latest instance stats success response
1191+
content:
1192+
text/plain:
1193+
schema:
1194+
type: string
1195+
"500":
1196+
description: Get latest instance stats error response
1197+
/instance-metrics/json:
1198+
get:
1199+
description: Get instance stats
1200+
operationId: getInstanceStats
1201+
security:
1202+
- oidcBearerAuth: []
1203+
- oidcCookieAuth: []
1204+
- githubCookieAuth: []
1205+
responses:
1206+
"200":
1207+
description: Get instance stats success response
1208+
content:
1209+
application/x-ndjson:
1210+
schema:
1211+
$ref: "#/components/schemas/instanceStats"
1212+
"500":
1213+
description: Get instance stats error response
1214+
11801215
components:
11811216
schemas:
11821217
## request Body
@@ -1339,6 +1374,30 @@ components:
13391374
package_id:
13401375
type: string
13411376

1377+
instanceStats:
1378+
type: object
1379+
required:
1380+
- type
1381+
- channel
1382+
- version
1383+
- arch
1384+
- timestamp
1385+
- instances
1386+
properties:
1387+
type:
1388+
type: string
1389+
enum: ["instance_count"]
1390+
channel:
1391+
type: string
1392+
version:
1393+
type: string
1394+
arch:
1395+
type: string
1396+
timestamp:
1397+
type: string
1398+
count:
1399+
type: integer
1400+
13421401

13431402
## response
13441403
config:

backend/pkg/api/instances.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const (
4949

5050
const (
5151
validityInterval postgresDuration = "1 days"
52-
defaultInterval time.Duration = time.Hour
52+
defaultInterval time.Duration = 2 * time.Hour
5353
)
5454

5555
// Instance represents an instance running one or more applications for which
@@ -651,7 +651,7 @@ func (api *API) GetDefaultInterval() time.Duration {
651651
// that have been checked in during a given duration from a given time.
652652
func (api *API) instanceStatsQuery(t *time.Time, duration *time.Duration) *goqu.SelectDataset {
653653
if t == nil {
654-
now := time.Now()
654+
now := time.Now().UTC()
655655
t = &now
656656
}
657657

backend/pkg/api/metrics.go

+35
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ WHERE a.id = e.application_id AND e.event_type_id = et.id AND et.result = 0 AND
2121
GROUP BY app_name
2222
ORDER BY app_name
2323
`, ignoreFakeInstanceCondition("e.instance_id"))
24+
25+
latestInstanceStatsSQL string = `
26+
SELECT channel_name, version, arch, timestamp, instances AS instances_count
27+
FROM instance_stats
28+
WHERE timestamp = (SELECT MAX(timestamp) FROM instance_stats)
29+
`
2430
)
2531

2632
type AppInstancesPerChannelMetric struct {
@@ -77,6 +83,35 @@ func (api *API) GetFailedUpdatesMetrics() ([]FailedUpdatesMetric, error) {
7783
return metrics, nil
7884
}
7985

86+
type LatestInstanceStatsMetric struct {
87+
ChannelName string `db:"channel_name" json:"channel_name"`
88+
Version string `db:"version" json:"version"`
89+
Arch string `db:"arch" json:"arch"`
90+
Timestamp string `db:"timestamp" json:"timestamp"`
91+
InstancesCount int `db:"instances_count" json:"instances_count"`
92+
}
93+
94+
func (api *API) GetLatestInstanceStatsMetrics() ([]LatestInstanceStatsMetric, error) {
95+
var metrics []LatestInstanceStatsMetric
96+
rows, err := api.db.Queryx(latestInstanceStatsSQL)
97+
if err != nil {
98+
return nil, err
99+
}
100+
defer rows.Close()
101+
for rows.Next() {
102+
var metric LatestInstanceStatsMetric
103+
err := rows.StructScan(&metric)
104+
if err != nil {
105+
return nil, err
106+
}
107+
metrics = append(metrics, metric)
108+
}
109+
if err := rows.Err(); err != nil {
110+
return nil, err
111+
}
112+
return metrics, nil
113+
}
114+
80115
func (api *API) DbStats() sql.DBStats {
81116
return api.db.Stats()
82117
}

backend/pkg/codegen/client.gen.go

+182
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)