Skip to content

Commit 9914840

Browse files
committed
Add database latency to high availability
1 parent d7cc0ff commit 9914840

File tree

16 files changed

+180
-107
lines changed

16 files changed

+180
-107
lines changed

coderd/database/databasefake/databasefake.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ type data struct {
113113
lastLicenseID int32
114114
}
115115

116+
func (q *fakeQuerier) Ping(_ context.Context) (time.Duration, error) {
117+
return 0, nil
118+
}
119+
116120
// InTx doesn't rollback data properly for in-memory yet.
117121
func (q *fakeQuerier) InTx(fn func(database.Store) error) error {
118122
q.mutex.Lock()
@@ -3170,14 +3174,15 @@ func (q *fakeQuerier) InsertReplica(_ context.Context, arg database.InsertReplic
31703174
defer q.mutex.Unlock()
31713175

31723176
replica := database.Replica{
3173-
ID: arg.ID,
3174-
CreatedAt: arg.CreatedAt,
3175-
StartedAt: arg.StartedAt,
3176-
UpdatedAt: arg.UpdatedAt,
3177-
Hostname: arg.Hostname,
3178-
RegionID: arg.RegionID,
3179-
RelayAddress: arg.RelayAddress,
3180-
Version: arg.Version,
3177+
ID: arg.ID,
3178+
CreatedAt: arg.CreatedAt,
3179+
StartedAt: arg.StartedAt,
3180+
UpdatedAt: arg.UpdatedAt,
3181+
Hostname: arg.Hostname,
3182+
RegionID: arg.RegionID,
3183+
RelayAddress: arg.RelayAddress,
3184+
Version: arg.Version,
3185+
DatabaseLatency: arg.DatabaseLatency,
31813186
}
31823187
q.replicas = append(q.replicas, replica)
31833188
return replica, nil
@@ -3199,6 +3204,7 @@ func (q *fakeQuerier) UpdateReplica(_ context.Context, arg database.UpdateReplic
31993204
replica.RegionID = arg.RegionID
32003205
replica.Version = arg.Version
32013206
replica.Error = arg.Error
3207+
replica.DatabaseLatency = arg.DatabaseLatency
32023208
q.replicas[index] = replica
32033209
return replica, nil
32043210
}

coderd/database/db.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"context"
1313
"database/sql"
1414
"errors"
15+
"time"
1516

1617
"github.com/jmoiron/sqlx"
1718
"golang.org/x/xerrors"
@@ -24,6 +25,7 @@ type Store interface {
2425
// customQuerier contains custom queries that are not generated.
2526
customQuerier
2627

28+
Ping(ctx context.Context) (time.Duration, error)
2729
InTx(func(Store) error) error
2830
}
2931

@@ -58,6 +60,13 @@ type sqlQuerier struct {
5860
db DBTX
5961
}
6062

63+
// Ping returns the time it takes to ping the database.
64+
func (q *sqlQuerier) Ping(ctx context.Context) (time.Duration, error) {
65+
start := time.Now()
66+
err := q.sdb.PingContext(ctx)
67+
return time.Since(start), err
68+
}
69+
6170
// InTx performs database operations inside a transaction.
6271
func (q *sqlQuerier) InTx(function func(Store) error) error {
6372
if _, ok := q.db.(*sqlx.Tx); ok {

coderd/database/dump.sql

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/migrations/000061_replicas.up.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ CREATE TABLE IF NOT EXISTS replicas (
1717
region_id integer NOT NULL,
1818
-- An address that should be accessible to other replicas.
1919
relay_address text NOT NULL,
20+
-- The latency of the replica to the database in microseconds.
21+
database_latency int NOT NULL,
2022
-- Version is the Coder version of the replica.
2123
version text NOT NULL,
2224
error text

coderd/database/models.go

Lines changed: 11 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 33 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/replicas.sql

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ INSERT INTO replicas (
1313
hostname,
1414
region_id,
1515
relay_address,
16-
version
17-
18-
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *;
16+
version,
17+
database_latency
18+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *;
1919

2020
-- name: UpdateReplica :one
2121
UPDATE replicas SET
@@ -26,7 +26,8 @@ UPDATE replicas SET
2626
region_id = $6,
2727
hostname = $7,
2828
version = $8,
29-
error = $9
29+
error = $9,
30+
database_latency = $10
3031
WHERE id = $1 RETURNING *;
3132

3233
-- name: DeleteReplicasUpdatedBefore :exec

codersdk/replicas.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type Replica struct {
2323
RegionID int32 `json:"region_id"`
2424
// Error is the error.
2525
Error string `json:"error"`
26+
// DatabaseLatency is the latency in microseconds to the database.
27+
DatabaseLatency int32 `json:"database_latency"`
2628
}
2729

2830
// Replicas fetches the list of replicas.

enterprise/coderd/replicas.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ func (api *API) replicas(rw http.ResponseWriter, r *http.Request) {
2626

2727
func convertReplica(replica database.Replica) codersdk.Replica {
2828
return codersdk.Replica{
29-
ID: replica.ID,
30-
Hostname: replica.Hostname,
31-
CreatedAt: replica.CreatedAt,
32-
RelayAddress: replica.RelayAddress,
33-
RegionID: replica.RegionID,
34-
Error: replica.Error.String,
29+
ID: replica.ID,
30+
Hostname: replica.Hostname,
31+
CreatedAt: replica.CreatedAt,
32+
RelayAddress: replica.RelayAddress,
33+
RegionID: replica.RegionID,
34+
Error: replica.Error.String,
35+
DatabaseLatency: replica.DatabaseLatency,
3536
}
3637
}

enterprise/replicasync/replicasync.go

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,36 +48,42 @@ func New(ctx context.Context, logger slog.Logger, db database.Store, pubsub data
4848
if err != nil {
4949
return nil, xerrors.Errorf("get hostname: %w", err)
5050
}
51+
databaseLatency, err := db.Ping(ctx)
52+
if err != nil {
53+
return nil, xerrors.Errorf("ping database: %w", err)
54+
}
5155
var replica database.Replica
5256
_, err = db.GetReplicaByID(ctx, options.ID)
5357
if err != nil {
5458
if !errors.Is(err, sql.ErrNoRows) {
5559
return nil, xerrors.Errorf("get replica: %w", err)
5660
}
5761
replica, err = db.InsertReplica(ctx, database.InsertReplicaParams{
58-
ID: options.ID,
59-
CreatedAt: database.Now(),
60-
StartedAt: database.Now(),
61-
UpdatedAt: database.Now(),
62-
Hostname: hostname,
63-
RegionID: options.RegionID,
64-
RelayAddress: options.RelayAddress,
65-
Version: buildinfo.Version(),
62+
ID: options.ID,
63+
CreatedAt: database.Now(),
64+
StartedAt: database.Now(),
65+
UpdatedAt: database.Now(),
66+
Hostname: hostname,
67+
RegionID: options.RegionID,
68+
RelayAddress: options.RelayAddress,
69+
Version: buildinfo.Version(),
70+
DatabaseLatency: int32(databaseLatency.Microseconds()),
6671
})
6772
if err != nil {
6873
return nil, xerrors.Errorf("insert replica: %w", err)
6974
}
7075
} else {
7176
replica, err = db.UpdateReplica(ctx, database.UpdateReplicaParams{
72-
ID: options.ID,
73-
UpdatedAt: database.Now(),
74-
StartedAt: database.Now(),
75-
StoppedAt: sql.NullTime{},
76-
RelayAddress: options.RelayAddress,
77-
RegionID: options.RegionID,
78-
Hostname: hostname,
79-
Version: buildinfo.Version(),
80-
Error: sql.NullString{},
77+
ID: options.ID,
78+
UpdatedAt: database.Now(),
79+
StartedAt: database.Now(),
80+
StoppedAt: sql.NullTime{},
81+
RelayAddress: options.RelayAddress,
82+
RegionID: options.RegionID,
83+
Hostname: hostname,
84+
Version: buildinfo.Version(),
85+
Error: sql.NullString{},
86+
DatabaseLatency: int32(databaseLatency.Microseconds()),
8187
})
8288
if err != nil {
8389
return nil, xerrors.Errorf("update replica: %w", err)
@@ -268,16 +274,22 @@ func (m *Manager) run(ctx context.Context) error {
268274
}
269275
}
270276

277+
databaseLatency, err := m.db.Ping(ctx)
278+
if err != nil {
279+
return xerrors.Errorf("ping database: %w", err)
280+
}
281+
271282
replica, err := m.db.UpdateReplica(ctx, database.UpdateReplicaParams{
272-
ID: m.self.ID,
273-
UpdatedAt: database.Now(),
274-
StartedAt: m.self.StartedAt,
275-
StoppedAt: m.self.StoppedAt,
276-
RelayAddress: m.self.RelayAddress,
277-
RegionID: m.self.RegionID,
278-
Hostname: m.self.Hostname,
279-
Version: m.self.Version,
280-
Error: replicaError,
283+
ID: m.self.ID,
284+
UpdatedAt: database.Now(),
285+
StartedAt: m.self.StartedAt,
286+
StoppedAt: m.self.StoppedAt,
287+
RelayAddress: m.self.RelayAddress,
288+
RegionID: m.self.RegionID,
289+
Hostname: m.self.Hostname,
290+
Version: m.self.Version,
291+
Error: replicaError,
292+
DatabaseLatency: int32(databaseLatency.Microseconds()),
281293
})
282294
if err != nil {
283295
return xerrors.Errorf("update replica: %w", err)

0 commit comments

Comments
 (0)