feat: batch connection logs to avoid DB lock contention (#23727)

- Running 30k connections was generating a ton of lock contention in the
DB
This commit is contained in:
Jon Ayers
2026-04-03 15:47:26 -05:00
committed by GitHub
parent 333503f74e
commit a1d51f0dab
21 changed files with 2168 additions and 426 deletions
+482 -197
View File
@@ -3566,9 +3566,11 @@ func connectionOnlyIDs[T database.ConnectionLog | database.GetConnectionLogsOffs
return ids
}
func TestUpsertConnectionLog(t *testing.T) {
func TestBatchUpsertConnectionLogs(t *testing.T) {
t.Parallel()
createWorkspace := func(t *testing.T, db database.Store) database.WorkspaceTable {
t.Helper()
u := dbgen.User(t, db, database.User{})
o := dbgen.Organization(t, db, database.Organization{})
tpl := dbgen.Template(t, db, database.Template{
@@ -3584,253 +3586,536 @@ func TestUpsertConnectionLog(t *testing.T) {
})
}
// zeroTime is the sentinel value that the SQL treats as "no
// connect/disconnect time provided".
zeroTime := time.Time{}
defaultIP := pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
Valid: true,
}
t.Run("SingleConnect", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
connID := uuid.New()
connectTime := dbtime.Now()
err := db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{connectTime},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{0},
CodeValid: []bool{false},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{""},
DisconnectTime: []time.Time{zeroTime},
})
require.NoError(t, err)
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows, 1)
require.True(t, connectTime.Equal(rows[0].ConnectionLog.ConnectTime))
require.False(t, rows[0].ConnectionLog.DisconnectTime.Valid,
"disconnect_time should be NULL for a connect-only event")
})
t.Run("ConnectThenDisconnect", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
connectionID := uuid.New()
agentName := "test-agent"
// 1. Insert a 'connect' event.
connID := uuid.New()
connectTime := dbtime.Now()
connectParams := database.UpsertConnectionLogParams{
ID: uuid.New(),
Time: connectTime,
OrganizationID: ws.OrganizationID,
WorkspaceOwnerID: ws.OwnerID,
WorkspaceID: ws.ID,
WorkspaceName: ws.Name,
AgentName: agentName,
Type: database.ConnectionTypeSsh,
ConnectionID: uuid.NullUUID{UUID: connectionID, Valid: true},
ConnectionStatus: database.ConnectionStatusConnected,
Ip: pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
Valid: true,
},
}
log1, err := db.UpsertConnectionLog(ctx, connectParams)
// Insert connect.
err := db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{connectTime},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{0},
CodeValid: []bool{false},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{""},
DisconnectTime: []time.Time{zeroTime},
})
require.NoError(t, err)
// Insert disconnect for same connection.
disconnectTime := connectTime.Add(time.Second)
err = db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{zeroTime},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{1},
CodeValid: []bool{true},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{"test disconnect"},
DisconnectTime: []time.Time{disconnectTime},
})
require.NoError(t, err)
require.Equal(t, connectParams.ID, log1.ID)
require.False(t, log1.DisconnectTime.Valid, "DisconnectTime should not be set on connect")
// Check that one row exists.
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows, 1)
// 2. Insert a 'disconnected' event for the same connection.
disconnectTime := connectTime.Add(time.Second)
disconnectParams := database.UpsertConnectionLogParams{
ConnectionID: uuid.NullUUID{UUID: connectionID, Valid: true},
WorkspaceID: ws.ID,
AgentName: agentName,
ConnectionStatus: database.ConnectionStatusDisconnected,
// Updated to:
Time: disconnectTime,
DisconnectReason: sql.NullString{String: "test disconnect", Valid: true},
Code: sql.NullInt32{Int32: 1, Valid: true},
// Ignored
ID: uuid.New(),
OrganizationID: ws.OrganizationID,
WorkspaceOwnerID: ws.OwnerID,
WorkspaceName: ws.Name,
Type: database.ConnectionTypeSsh,
Ip: pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 254),
},
Valid: true,
},
}
log2, err := db.UpsertConnectionLog(ctx, disconnectParams)
require.NoError(t, err)
// Updated
require.Equal(t, log1.ID, log2.ID)
require.True(t, log2.DisconnectTime.Valid)
require.True(t, disconnectTime.Equal(log2.DisconnectTime.Time))
require.Equal(t, disconnectParams.DisconnectReason.String, log2.DisconnectReason.String)
rows, err = db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{})
require.NoError(t, err)
require.Len(t, rows, 1)
row := rows[0].ConnectionLog
require.True(t, connectTime.Equal(row.ConnectTime))
require.True(t, row.DisconnectTime.Valid)
require.True(t, disconnectTime.Equal(row.DisconnectTime.Time))
require.Equal(t, "test disconnect", row.DisconnectReason.String)
require.Equal(t, int32(1), row.Code.Int32)
})
t.Run("ConnectDoesNotUpdate", func(t *testing.T) {
t.Run("DuplicateConnectIsNoOp", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
connectionID := uuid.New()
agentName := "test-agent"
// 1. Insert a 'connect' event.
connID := uuid.New()
connectTime := dbtime.Now()
connectParams := database.UpsertConnectionLogParams{
ID: uuid.New(),
Time: connectTime,
OrganizationID: ws.OrganizationID,
WorkspaceOwnerID: ws.OwnerID,
WorkspaceID: ws.ID,
WorkspaceName: ws.Name,
AgentName: agentName,
Type: database.ConnectionTypeSsh,
ConnectionID: uuid.NullUUID{UUID: connectionID, Valid: true},
ConnectionStatus: database.ConnectionStatusConnected,
Ip: pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
Valid: true,
},
mkParams := func(ct time.Time, ip pqtype.Inet) database.BatchUpsertConnectionLogsParams {
return database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{ct},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{0},
CodeValid: []bool{false},
Ip: []pqtype.Inet{ip},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{""},
DisconnectTime: []time.Time{zeroTime},
}
}
log, err := db.UpsertConnectionLog(ctx, connectParams)
err := db.BatchUpsertConnectionLogs(ctx, mkParams(connectTime, defaultIP))
require.NoError(t, err)
// 2. Insert another 'connect' event for the same connection.
connectTime2 := connectTime.Add(time.Second)
connectParams2 := database.UpsertConnectionLogParams{
ConnectionID: uuid.NullUUID{UUID: connectionID, Valid: true},
WorkspaceID: ws.ID,
AgentName: agentName,
ConnectionStatus: database.ConnectionStatusConnected,
rows1, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows1, 1)
// Ignored
ID: uuid.New(),
Time: connectTime2,
OrganizationID: ws.OrganizationID,
WorkspaceOwnerID: ws.OwnerID,
WorkspaceName: ws.Name,
Type: database.ConnectionTypeSsh,
Code: sql.NullInt32{Int32: 0, Valid: false},
Ip: pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 254),
},
Valid: true,
// Second connect with later time and different IP.
otherIP := pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
Valid: true,
}
origLog, err := db.UpsertConnectionLog(ctx, connectParams2)
err = db.BatchUpsertConnectionLogs(ctx, mkParams(connectTime.Add(time.Second), otherIP))
require.NoError(t, err)
require.Equal(t, log, origLog, "connect update should be a no-op")
// Check that still only one row exists.
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{})
rows2, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows, 1)
require.Equal(t, log, rows[0].ConnectionLog)
require.Len(t, rows2, 1)
// The LEAST logic should pick the earlier connect_time; IP and
// other fields are not updated on conflict.
require.True(t, connectTime.Equal(rows2[0].ConnectionLog.ConnectTime),
"connect_time should remain the original (earlier) value")
})
t.Run("DisconnectThenConnect", func(t *testing.T) {
t.Run("OrderIndependentConnectTime", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
connectionID := uuid.New()
agentName := "test-agent"
// Insert just a 'disconect' event
connID := uuid.New()
disconnectTime := dbtime.Now()
disconnectParams := database.UpsertConnectionLogParams{
ID: uuid.New(),
Time: disconnectTime,
OrganizationID: ws.OrganizationID,
WorkspaceOwnerID: ws.OwnerID,
WorkspaceID: ws.ID,
WorkspaceName: ws.Name,
AgentName: agentName,
Type: database.ConnectionTypeSsh,
ConnectionID: uuid.NullUUID{UUID: connectionID, Valid: true},
ConnectionStatus: database.ConnectionStatusDisconnected,
DisconnectReason: sql.NullString{String: "server shutting down", Valid: true},
Ip: pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
Valid: true,
},
connectTime := disconnectTime.Add(-5 * time.Second)
// Disconnect arrives first.
err := db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{disconnectTime},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{0},
CodeValid: []bool{true},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{"bye"},
DisconnectTime: []time.Time{disconnectTime},
})
require.NoError(t, err)
// Connect arrives second with the real (earlier) connect_time.
err = db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{connectTime},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{0},
CodeValid: []bool{false},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{""},
DisconnectTime: []time.Time{zeroTime},
})
require.NoError(t, err)
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows, 1)
require.True(t, connectTime.Equal(rows[0].ConnectionLog.ConnectTime),
"LEAST should pick the earlier connect_time")
})
t.Run("DisconnectFieldsAreWriteOnce", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
connID := uuid.New()
disconnectTime := dbtime.Now()
mkDisconnect := func(reason string, code int32) database.BatchUpsertConnectionLogsParams {
return database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{disconnectTime},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{code},
CodeValid: []bool{true},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{reason},
DisconnectTime: []time.Time{disconnectTime},
}
}
_, err := db.UpsertConnectionLog(ctx, disconnectParams)
err := db.BatchUpsertConnectionLogs(ctx, mkDisconnect("first reason", 1))
require.NoError(t, err)
firstRows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{})
// Second disconnect with different reason and code.
err = db.BatchUpsertConnectionLogs(ctx, mkDisconnect("second reason", 2))
require.NoError(t, err)
require.Len(t, firstRows, 1)
// We expect the connection event to be marked as closed with the start
// and close time being the same.
require.True(t, firstRows[0].ConnectionLog.DisconnectTime.Valid)
require.Equal(t, disconnectTime, firstRows[0].ConnectionLog.DisconnectTime.Time.UTC())
require.Equal(t, firstRows[0].ConnectionLog.ConnectTime.UTC(), firstRows[0].ConnectionLog.DisconnectTime.Time.UTC())
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows, 1)
row := rows[0].ConnectionLog
require.Equal(t, "first reason", row.DisconnectReason.String,
"disconnect_reason should not be overwritten")
require.Equal(t, int32(1), row.Code.Int32,
"code should not be overwritten")
})
// Now insert a 'connect' event for the same connection.
// This should be a no op
connectTime := disconnectTime.Add(time.Second)
connectParams := database.UpsertConnectionLogParams{
ID: uuid.New(),
Time: connectTime,
OrganizationID: ws.OrganizationID,
WorkspaceOwnerID: ws.OwnerID,
WorkspaceID: ws.ID,
WorkspaceName: ws.Name,
AgentName: agentName,
Type: database.ConnectionTypeSsh,
ConnectionID: uuid.NullUUID{UUID: connectionID, Valid: true},
ConnectionStatus: database.ConnectionStatusConnected,
DisconnectReason: sql.NullString{String: "reconnected", Valid: true},
Code: sql.NullInt32{Int32: 0, Valid: false},
Ip: pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
Valid: true,
},
t.Run("ConnectAfterDisconnectIsNoOp", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
connID := uuid.New()
disconnectTime := dbtime.Now()
// Insert disconnect first.
err := db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{disconnectTime},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{42},
CodeValid: []bool{true},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{"server shutdown"},
DisconnectTime: []time.Time{disconnectTime},
})
require.NoError(t, err)
rows1, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows1, 1)
require.True(t, rows1[0].ConnectionLog.DisconnectTime.Valid)
require.Equal(t, "server shutdown", rows1[0].ConnectionLog.DisconnectReason.String)
require.Equal(t, int32(42), rows1[0].ConnectionLog.Code.Int32)
// Insert connect for same connection_id.
err = db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{disconnectTime.Add(time.Second)},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{0},
CodeValid: []bool{false},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{""},
DisconnectTime: []time.Time{zeroTime},
})
require.NoError(t, err)
rows2, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows2, 1)
row := rows2[0].ConnectionLog
require.True(t, row.DisconnectTime.Valid,
"disconnect_time should not be cleared by a later connect")
require.Equal(t, "server shutdown", row.DisconnectReason.String,
"disconnect_reason should not be cleared")
require.Equal(t, int32(42), row.Code.Int32,
"code should not be cleared")
})
t.Run("CodeZeroPreserved", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
connID := uuid.New()
now := dbtime.Now()
err := db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{now},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{0},
CodeValid: []bool{true},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{"normal"},
DisconnectTime: []time.Time{now},
})
require.NoError(t, err)
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows, 1)
require.True(t, rows[0].ConnectionLog.Code.Valid, "code should be non-NULL")
require.Equal(t, int32(0), rows[0].ConnectionLog.Code.Int32,
"code=0 should be preserved, not treated as NULL")
})
t.Run("CodeNullWhenInvalid", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
connID := uuid.New()
now := dbtime.Now()
err := db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{now},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{99},
CodeValid: []bool{false},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{""},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{""},
ConnectionID: []uuid.UUID{connID},
DisconnectReason: []string{""},
DisconnectTime: []time.Time{zeroTime},
})
require.NoError(t, err)
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows, 1)
require.False(t, rows[0].ConnectionLog.Code.Valid,
"code should be NULL when code_valid is false")
})
t.Run("NullConnectionIDEvents", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
now := dbtime.Now()
// Insert two web events with NULL connection_id (uuid.Nil →
// NULL via NULLIF) for the same workspace/agent.
for i := range 2 {
err := db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: []uuid.UUID{uuid.New()},
ConnectTime: []time.Time{now.Add(time.Duration(i) * time.Second)},
OrganizationID: []uuid.UUID{ws.OrganizationID},
WorkspaceOwnerID: []uuid.UUID{ws.OwnerID},
WorkspaceID: []uuid.UUID{ws.ID},
WorkspaceName: []string{ws.Name},
AgentName: []string{"agent"},
Type: []database.ConnectionType{database.ConnectionTypeSsh},
Code: []int32{200},
CodeValid: []bool{true},
Ip: []pqtype.Inet{defaultIP},
UserAgent: []string{"Mozilla/5.0"},
UserID: []uuid.UUID{uuid.Nil},
SlugOrPort: []string{"web-terminal"},
ConnectionID: []uuid.UUID{uuid.Nil},
DisconnectReason: []string{""},
DisconnectTime: []time.Time{zeroTime},
})
require.NoError(t, err)
}
_, err = db.UpsertConnectionLog(ctx, connectParams)
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, rows, 2,
"NULL connection_id rows should not conflict with each other")
})
secondRows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{})
require.NoError(t, err)
require.Len(t, secondRows, 1)
require.Equal(t, firstRows, secondRows)
t.Run("MultipleIndependentConnections", func(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
ctx := context.Background()
ws := createWorkspace(t, db)
now := dbtime.Now()
// Upsert a disconnection, which should also be a no op
disconnectParams.DisconnectReason = sql.NullString{
String: "updated close reason",
Valid: true,
n := 5
ids := make([]uuid.UUID, n)
connectTimes := make([]time.Time, n)
orgIDs := make([]uuid.UUID, n)
ownerIDs := make([]uuid.UUID, n)
wsIDs := make([]uuid.UUID, n)
wsNames := make([]string, n)
agentNames := make([]string, n)
types := make([]database.ConnectionType, n)
codes := make([]int32, n)
codeValids := make([]bool, n)
ips := make([]pqtype.Inet, n)
userAgents := make([]string, n)
userIDs := make([]uuid.UUID, n)
slugOrPorts := make([]string, n)
connIDs := make([]uuid.UUID, n)
disconnectReasons := make([]string, n)
disconnectTimes := make([]time.Time, n)
for i := range n {
ids[i] = uuid.New()
connectTimes[i] = now.Add(time.Duration(i) * time.Second)
orgIDs[i] = ws.OrganizationID
ownerIDs[i] = ws.OwnerID
wsIDs[i] = ws.ID
wsNames[i] = ws.Name
agentNames[i] = "agent"
types[i] = database.ConnectionTypeSsh
codes[i] = 0
codeValids[i] = false
ips[i] = defaultIP
userAgents[i] = ""
userIDs[i] = uuid.Nil
slugOrPorts[i] = ""
connIDs[i] = uuid.New()
disconnectReasons[i] = ""
disconnectTimes[i] = zeroTime
}
_, err = db.UpsertConnectionLog(ctx, disconnectParams)
err := db.BatchUpsertConnectionLogs(ctx, database.BatchUpsertConnectionLogsParams{
ID: ids,
ConnectTime: connectTimes,
OrganizationID: orgIDs,
WorkspaceOwnerID: ownerIDs,
WorkspaceID: wsIDs,
WorkspaceName: wsNames,
AgentName: agentNames,
Type: types,
Code: codes,
CodeValid: codeValids,
Ip: ips,
UserAgent: userAgents,
UserID: userIDs,
SlugOrPort: slugOrPorts,
ConnectionID: connIDs,
DisconnectReason: disconnectReasons,
DisconnectTime: disconnectTimes,
})
require.NoError(t, err)
thirdRows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{})
rows, err := db.GetConnectionLogsOffset(ctx, database.GetConnectionLogsOffsetParams{LimitOpt: 10})
require.NoError(t, err)
require.Len(t, secondRows, 1)
// The close reason shouldn't be updated
require.Equal(t, secondRows, thirdRows)
require.Len(t, rows, n, "each unique connection_id should produce its own row")
})
}