feat(coderd/database): track user status changes over time (#16019)

RE: https://github.com/coder/coder/issues/15740,
https://github.com/coder/coder/issues/15297

In order to add a graph to the coder frontend to show user status over
time as an indicator of license usage, this PR adds the following:

* a new `api.insightsUserStatusCountsOverTime` endpoint to the API
* which calls a new `GetUserStatusCountsOverTime` query from postgres
* which relies on two new tables `user_status_changes` and
`user_deleted`
* which are populated by a new trigger and function that tracks updates
to the users table

The chart itself will be added in a subsequent PR

---------

Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
This commit is contained in:
Sas Swart
2025-01-13 13:08:16 +02:00
committed by GitHub
co-authored by Mathias Fredriksson
parent 73d8dde6ed
commit 4543b21b7c
25 changed files with 1456 additions and 3 deletions
+13
View File
@@ -289,6 +289,19 @@ type sqlcQuerier interface {
GetUserLinkByUserIDLoginType(ctx context.Context, arg GetUserLinkByUserIDLoginTypeParams) (UserLink, error)
GetUserLinksByUserID(ctx context.Context, userID uuid.UUID) ([]UserLink, error)
GetUserNotificationPreferences(ctx context.Context, userID uuid.UUID) ([]NotificationPreference, error)
// GetUserStatusCounts returns the count of users in each status over time.
// The time range is inclusively defined by the start_time and end_time parameters.
//
// Bucketing:
// Between the start_time and end_time, we include each timestamp where a user's status changed or they were deleted.
// We do not bucket these results by day or some other time unit. This is because such bucketing would hide potentially
// important patterns. If a user was active for 23 hours and 59 minutes, and then suspended, a daily bucket would hide this.
// A daily bucket would also have required us to carefully manage the timezone of the bucket based on the timezone of the user.
//
// Accumulation:
// We do not start counting from 0 at the start_time. We check the last status change before the start_time for each user. As such,
// the result shows the total number of users in each status on any particular day.
GetUserStatusCounts(ctx context.Context, arg GetUserStatusCountsParams) ([]GetUserStatusCountsRow, error)
GetUserWorkspaceBuildParameters(ctx context.Context, arg GetUserWorkspaceBuildParametersParams) ([]GetUserWorkspaceBuildParametersRow, error)
// This will never return deleted users.
GetUsers(ctx context.Context, arg GetUsersParams) ([]GetUsersRow, error)