mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: rename 'last_used_at' column (#26749)
Renames the `last_used_at` column to `last_heartbeat_at` in `ai_gateway_keys` table. `ai_gateway_keys` table has not been released yet. All references updated.
This commit is contained in:
@@ -7033,14 +7033,14 @@ func (q *querier) UpdateAIBridgeInterceptionEnded(ctx context.Context, params da
|
||||
return q.db.UpdateAIBridgeInterceptionEnded(ctx, params)
|
||||
}
|
||||
|
||||
// Records liveness for a key used in active DRPC session between coderd and standalone AI Gateway.
|
||||
func (q *querier) UpdateAIGatewayKeyLastUsedAt(ctx context.Context, id uuid.UUID) (int64, error) {
|
||||
// Records heartbeat liveness for a key used in active DRPC session between coderd and standalone AI Gateway.
|
||||
func (q *querier) UpdateAIGatewayKeyLastHeartbeatAt(ctx context.Context, id uuid.UUID) (int64, error) {
|
||||
// Standalone AI Gateway has no Coder identity, so this runs under the
|
||||
// system actor recording connection liveness on the AI Gateway key.
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceAIGatewayKey); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return q.db.UpdateAIGatewayKeyLastUsedAt(ctx, id)
|
||||
return q.db.UpdateAIGatewayKeyLastHeartbeatAt(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateAIProvider(ctx context.Context, arg database.UpdateAIProviderParams) (database.AIProvider, error) {
|
||||
|
||||
@@ -6974,9 +6974,9 @@ func (s *MethodTestSuite) TestAIBridge() {
|
||||
dbm.EXPECT().GetAIGatewayKeyByHashedSecret(gomock.Any(), hashedSecret).Return(key, nil).AnyTimes()
|
||||
check.Args(hashedSecret).Asserts(rbac.ResourceAIGatewayKey, policy.ActionRead).Returns(key)
|
||||
}))
|
||||
s.Run("UpdateAIGatewayKeyLastUsedAt", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
s.Run("UpdateAIGatewayKeyLastHeartbeatAt", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
id := uuid.New()
|
||||
dbm.EXPECT().UpdateAIGatewayKeyLastUsedAt(gomock.Any(), id).Return(int64(1), nil).AnyTimes()
|
||||
dbm.EXPECT().UpdateAIGatewayKeyLastHeartbeatAt(gomock.Any(), id).Return(int64(1), nil).AnyTimes()
|
||||
check.Args(id).Asserts(rbac.ResourceAIGatewayKey, policy.ActionUpdate).Returns(int64(1))
|
||||
}))
|
||||
}
|
||||
|
||||
+4
-4
@@ -5050,11 +5050,11 @@ func (m queryMetricsStore) UpdateAIBridgeInterceptionEnded(ctx context.Context,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) UpdateAIGatewayKeyLastUsedAt(ctx context.Context, arg uuid.UUID) (int64, error) {
|
||||
func (m queryMetricsStore) UpdateAIGatewayKeyLastHeartbeatAt(ctx context.Context, id uuid.UUID) (int64, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.UpdateAIGatewayKeyLastUsedAt(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("UpdateAIGatewayKeyLastUsedAt").Observe(time.Since(start).Seconds())
|
||||
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "UpdateAIGatewayKeyLastUsedAt").Inc()
|
||||
r0, r1 := m.s.UpdateAIGatewayKeyLastHeartbeatAt(ctx, id)
|
||||
m.queryLatencies.WithLabelValues("UpdateAIGatewayKeyLastHeartbeatAt").Observe(time.Since(start).Seconds())
|
||||
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "UpdateAIGatewayKeyLastHeartbeatAt").Inc()
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
|
||||
Generated
+6
-6
@@ -9518,19 +9518,19 @@ func (mr *MockStoreMockRecorder) UpdateAIBridgeInterceptionEnded(ctx, arg any) *
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAIBridgeInterceptionEnded", reflect.TypeOf((*MockStore)(nil).UpdateAIBridgeInterceptionEnded), ctx, arg)
|
||||
}
|
||||
|
||||
// UpdateAIGatewayKeyLastUsedAt mocks base method.
|
||||
func (m *MockStore) UpdateAIGatewayKeyLastUsedAt(ctx context.Context, id uuid.UUID) (int64, error) {
|
||||
// UpdateAIGatewayKeyLastHeartbeatAt mocks base method.
|
||||
func (m *MockStore) UpdateAIGatewayKeyLastHeartbeatAt(ctx context.Context, id uuid.UUID) (int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateAIGatewayKeyLastUsedAt", ctx, id)
|
||||
ret := m.ctrl.Call(m, "UpdateAIGatewayKeyLastHeartbeatAt", ctx, id)
|
||||
ret0, _ := ret[0].(int64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// UpdateAIGatewayKeyLastUsedAt indicates an expected call of UpdateAIGatewayKeyLastUsedAt.
|
||||
func (mr *MockStoreMockRecorder) UpdateAIGatewayKeyLastUsedAt(ctx, id any) *gomock.Call {
|
||||
// UpdateAIGatewayKeyLastHeartbeatAt indicates an expected call of UpdateAIGatewayKeyLastHeartbeatAt.
|
||||
func (mr *MockStoreMockRecorder) UpdateAIGatewayKeyLastHeartbeatAt(ctx, id any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAIGatewayKeyLastUsedAt", reflect.TypeOf((*MockStore)(nil).UpdateAIGatewayKeyLastUsedAt), ctx, id)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAIGatewayKeyLastHeartbeatAt", reflect.TypeOf((*MockStore)(nil).UpdateAIGatewayKeyLastHeartbeatAt), ctx, id)
|
||||
}
|
||||
|
||||
// UpdateAIProvider mocks base method.
|
||||
|
||||
Generated
+1
-1
@@ -1440,7 +1440,7 @@ CREATE TABLE ai_gateway_keys (
|
||||
name text NOT NULL,
|
||||
secret_prefix character varying(11) NOT NULL,
|
||||
hashed_secret bytea NOT NULL,
|
||||
last_used_at timestamp with time zone,
|
||||
last_heartbeat_at timestamp with time zone,
|
||||
CONSTRAINT ai_gateway_keys_hashed_secret_check CHECK ((length(hashed_secret) > 0)),
|
||||
CONSTRAINT ai_gateway_keys_name_check CHECK (((length(name) <= 64) AND (name ~ '^[a-z0-9]+(-[a-z0-9]+)*$'::text))),
|
||||
CONSTRAINT ai_gateway_keys_secret_prefix_check CHECK ((length((secret_prefix)::text) = 11))
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE ai_gateway_keys
|
||||
RENAME COLUMN last_heartbeat_at TO last_used_at;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE ai_gateway_keys
|
||||
RENAME COLUMN last_used_at TO last_heartbeat_at;
|
||||
Generated
+3
-3
@@ -4618,9 +4618,9 @@ type AIGatewayKey struct {
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Name string `db:"name" json:"name"`
|
||||
// Public token prefix for display and audit correlation. Auth uses hashed_secret.
|
||||
SecretPrefix string `db:"secret_prefix" json:"secret_prefix"`
|
||||
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
|
||||
LastUsedAt sql.NullTime `db:"last_used_at" json:"last_used_at"`
|
||||
SecretPrefix string `db:"secret_prefix" json:"secret_prefix"`
|
||||
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
|
||||
LastHeartbeatAt sql.NullTime `db:"last_heartbeat_at" json:"last_heartbeat_at"`
|
||||
}
|
||||
|
||||
// Per-model token prices used by AI Bridge to compute interception cost.
|
||||
|
||||
Generated
+2
-2
@@ -1311,10 +1311,10 @@ type sqlcQuerier interface {
|
||||
UnpinChatByID(ctx context.Context, id uuid.UUID) error
|
||||
UnsetDefaultChatModelConfigs(ctx context.Context) error
|
||||
UpdateAIBridgeInterceptionEnded(ctx context.Context, arg UpdateAIBridgeInterceptionEndedParams) (AIBridgeInterception, error)
|
||||
// Records liveness for an active Gateway DRPC session. The database sets the
|
||||
// Records heartbeat liveness for an active Gateway DRPC session. The database sets the
|
||||
// timestamp so it stays consistent regardless of clock drift between API
|
||||
// replicas.
|
||||
UpdateAIGatewayKeyLastUsedAt(ctx context.Context, id uuid.UUID) (int64, error)
|
||||
UpdateAIGatewayKeyLastHeartbeatAt(ctx context.Context, id uuid.UUID) (int64, error)
|
||||
UpdateAIProvider(ctx context.Context, arg UpdateAIProviderParams) (AIProvider, error)
|
||||
UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error
|
||||
UpdateChatACLByID(ctx context.Context, arg UpdateChatACLByIDParams) error
|
||||
|
||||
@@ -14938,9 +14938,9 @@ func TestAIGatewayKeysQueries(t *testing.T) {
|
||||
require.Len(t, keys, 2)
|
||||
|
||||
requireAIGatewayKeysRow(t, keys[0], first, firstRow.CreatedAt)
|
||||
require.False(t, keys[0].LastUsedAt.Valid)
|
||||
require.False(t, keys[0].LastHeartbeatAt.Valid)
|
||||
requireAIGatewayKeysRow(t, keys[1], second, secondRow.CreatedAt)
|
||||
require.False(t, keys[1].LastUsedAt.Valid)
|
||||
require.False(t, keys[1].LastHeartbeatAt.Valid)
|
||||
|
||||
deleted, err := db.DeleteAIGatewayKey(ctx, first.ID)
|
||||
require.NoError(t, err)
|
||||
@@ -14989,7 +14989,7 @@ func TestGetAIGatewayKeyByHashedSecret(t *testing.T) {
|
||||
require.Empty(t, key.ID)
|
||||
}
|
||||
|
||||
func TestUpdateAIGatewayKeyLastUsedAt(t *testing.T) {
|
||||
func TestUpdateAIGatewayKeyLastHeartbeatAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _, sqlDB := dbtestutil.NewDBWithSQLDB(t)
|
||||
@@ -14999,42 +14999,42 @@ func TestUpdateAIGatewayKeyLastUsedAt(t *testing.T) {
|
||||
row, err := db.InsertAIGatewayKey(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
// last_used_at starts NULL until a session records liveness.
|
||||
// last_heartbeat_at starts NULL until a session records liveness.
|
||||
keys, err := db.ListAIGatewayKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, keys, 1)
|
||||
require.False(t, keys[0].LastUsedAt.Valid)
|
||||
require.False(t, keys[0].LastHeartbeatAt.Valid)
|
||||
|
||||
rows, err := db.UpdateAIGatewayKeyLastUsedAt(ctx, params.ID)
|
||||
rows, err := db.UpdateAIGatewayKeyLastHeartbeatAt(ctx, params.ID)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 1, rows)
|
||||
|
||||
keys, err = db.ListAIGatewayKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, keys, 1)
|
||||
require.True(t, keys[0].LastUsedAt.Valid)
|
||||
require.True(t, keys[0].LastHeartbeatAt.Valid)
|
||||
// The database stamps the timestamp, so compare against the row's
|
||||
// DB-generated CreatedAt to avoid client clock skew.
|
||||
require.False(t, keys[0].LastUsedAt.Time.Before(row.CreatedAt))
|
||||
require.False(t, keys[0].LastHeartbeatAt.Time.Before(row.CreatedAt))
|
||||
|
||||
// Updating a key that does not exist is a no-op, not an error.
|
||||
rows, err = db.UpdateAIGatewayKeyLastUsedAt(ctx, uuid.New())
|
||||
rows, err = db.UpdateAIGatewayKeyLastHeartbeatAt(ctx, uuid.New())
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 0, rows)
|
||||
|
||||
// Set last_used_at to old time to confirm the update overwrites it with a fresh timestamp.
|
||||
// Set last_heartbeat_at to old time to confirm the update overwrites it with a fresh timestamp.
|
||||
staleTime := row.CreatedAt.Add(-time.Hour)
|
||||
_, err = sqlDB.ExecContext(ctx, "UPDATE ai_gateway_keys SET last_used_at = $1 WHERE id = $2", staleTime, params.ID)
|
||||
_, err = sqlDB.ExecContext(ctx, "UPDATE ai_gateway_keys SET last_heartbeat_at = $1 WHERE id = $2", staleTime, params.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
rows, err = db.UpdateAIGatewayKeyLastUsedAt(ctx, params.ID)
|
||||
rows, err = db.UpdateAIGatewayKeyLastHeartbeatAt(ctx, params.ID)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 1, rows)
|
||||
|
||||
keys, err = db.ListAIGatewayKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, keys, 1)
|
||||
require.True(t, keys[0].LastUsedAt.Time.After(staleTime))
|
||||
require.True(t, keys[0].LastHeartbeatAt.Time.After(staleTime))
|
||||
}
|
||||
|
||||
func aiGatewayKeyParams(name string, secretPrefix string) database.InsertAIGatewayKeyParams {
|
||||
|
||||
Generated
+21
-21
@@ -113,15 +113,15 @@ func (q *sqlQuerier) ActivityBumpWorkspace(ctx context.Context, arg ActivityBump
|
||||
|
||||
const deleteAIGatewayKey = `-- name: DeleteAIGatewayKey :one
|
||||
DELETE FROM ai_gateway_keys WHERE id = $1
|
||||
RETURNING id, name, secret_prefix, created_at, last_used_at
|
||||
RETURNING id, name, secret_prefix, created_at, last_heartbeat_at
|
||||
`
|
||||
|
||||
type DeleteAIGatewayKeyRow struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
SecretPrefix string `db:"secret_prefix" json:"secret_prefix"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
LastUsedAt sql.NullTime `db:"last_used_at" json:"last_used_at"`
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
SecretPrefix string `db:"secret_prefix" json:"secret_prefix"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
LastHeartbeatAt sql.NullTime `db:"last_heartbeat_at" json:"last_heartbeat_at"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) DeleteAIGatewayKey(ctx context.Context, id uuid.UUID) (DeleteAIGatewayKeyRow, error) {
|
||||
@@ -132,13 +132,13 @@ func (q *sqlQuerier) DeleteAIGatewayKey(ctx context.Context, id uuid.UUID) (Dele
|
||||
&i.Name,
|
||||
&i.SecretPrefix,
|
||||
&i.CreatedAt,
|
||||
&i.LastUsedAt,
|
||||
&i.LastHeartbeatAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getAIGatewayKeyByHashedSecret = `-- name: GetAIGatewayKeyByHashedSecret :one
|
||||
SELECT id, created_at, name, secret_prefix, hashed_secret, last_used_at
|
||||
SELECT id, created_at, name, secret_prefix, hashed_secret, last_heartbeat_at
|
||||
FROM ai_gateway_keys
|
||||
WHERE hashed_secret = $1
|
||||
`
|
||||
@@ -155,7 +155,7 @@ func (q *sqlQuerier) GetAIGatewayKeyByHashedSecret(ctx context.Context, hashedSe
|
||||
&i.Name,
|
||||
&i.SecretPrefix,
|
||||
&i.HashedSecret,
|
||||
&i.LastUsedAt,
|
||||
&i.LastHeartbeatAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -198,17 +198,17 @@ func (q *sqlQuerier) InsertAIGatewayKey(ctx context.Context, arg InsertAIGateway
|
||||
}
|
||||
|
||||
const listAIGatewayKeys = `-- name: ListAIGatewayKeys :many
|
||||
SELECT id, name, secret_prefix, created_at, last_used_at
|
||||
SELECT id, name, secret_prefix, created_at, last_heartbeat_at
|
||||
FROM ai_gateway_keys
|
||||
ORDER BY created_at ASC
|
||||
`
|
||||
|
||||
type ListAIGatewayKeysRow struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
SecretPrefix string `db:"secret_prefix" json:"secret_prefix"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
LastUsedAt sql.NullTime `db:"last_used_at" json:"last_used_at"`
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
SecretPrefix string `db:"secret_prefix" json:"secret_prefix"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
LastHeartbeatAt sql.NullTime `db:"last_heartbeat_at" json:"last_heartbeat_at"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) ListAIGatewayKeys(ctx context.Context) ([]ListAIGatewayKeysRow, error) {
|
||||
@@ -225,7 +225,7 @@ func (q *sqlQuerier) ListAIGatewayKeys(ctx context.Context) ([]ListAIGatewayKeys
|
||||
&i.Name,
|
||||
&i.SecretPrefix,
|
||||
&i.CreatedAt,
|
||||
&i.LastUsedAt,
|
||||
&i.LastHeartbeatAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -240,17 +240,17 @@ func (q *sqlQuerier) ListAIGatewayKeys(ctx context.Context) ([]ListAIGatewayKeys
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateAIGatewayKeyLastUsedAt = `-- name: UpdateAIGatewayKeyLastUsedAt :execrows
|
||||
const updateAIGatewayKeyLastHeartbeatAt = `-- name: UpdateAIGatewayKeyLastHeartbeatAt :execrows
|
||||
UPDATE ai_gateway_keys
|
||||
SET last_used_at = NOW()
|
||||
SET last_heartbeat_at = NOW()
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
// Records liveness for an active Gateway DRPC session. The database sets the
|
||||
// Records heartbeat liveness for an active Gateway DRPC session. The database sets the
|
||||
// timestamp so it stays consistent regardless of clock drift between API
|
||||
// replicas.
|
||||
func (q *sqlQuerier) UpdateAIGatewayKeyLastUsedAt(ctx context.Context, id uuid.UUID) (int64, error) {
|
||||
result, err := q.db.ExecContext(ctx, updateAIGatewayKeyLastUsedAt, id)
|
||||
func (q *sqlQuerier) UpdateAIGatewayKeyLastHeartbeatAt(ctx context.Context, id uuid.UUID) (int64, error) {
|
||||
result, err := q.db.ExecContext(ctx, updateAIGatewayKeyLastHeartbeatAt, id)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ VALUES ($1, @name, $2, $3, NOW())
|
||||
RETURNING id, name, secret_prefix, created_at;
|
||||
|
||||
-- name: ListAIGatewayKeys :many
|
||||
SELECT id, name, secret_prefix, created_at, last_used_at
|
||||
SELECT id, name, secret_prefix, created_at, last_heartbeat_at
|
||||
FROM ai_gateway_keys
|
||||
ORDER BY created_at ASC;
|
||||
|
||||
-- name: DeleteAIGatewayKey :one
|
||||
DELETE FROM ai_gateway_keys WHERE id = $1
|
||||
RETURNING id, name, secret_prefix, created_at, last_used_at;
|
||||
RETURNING id, name, secret_prefix, created_at, last_heartbeat_at;
|
||||
|
||||
-- name: GetAIGatewayKeyByHashedSecret :one
|
||||
-- Authenticates a standalone AI Gateway replica by its hashed key secret,
|
||||
@@ -20,10 +20,10 @@ SELECT *
|
||||
FROM ai_gateway_keys
|
||||
WHERE hashed_secret = $1;
|
||||
|
||||
-- name: UpdateAIGatewayKeyLastUsedAt :execrows
|
||||
-- Records liveness for an active Gateway DRPC session. The database sets the
|
||||
-- name: UpdateAIGatewayKeyLastHeartbeatAt :execrows
|
||||
-- Records heartbeat liveness for an active Gateway DRPC session. The database sets the
|
||||
-- timestamp so it stays consistent regardless of clock drift between API
|
||||
-- replicas.
|
||||
UPDATE ai_gateway_keys
|
||||
SET last_used_at = NOW()
|
||||
SET last_heartbeat_at = NOW()
|
||||
WHERE id = $1;
|
||||
|
||||
Reference in New Issue
Block a user