flush updates last_used_at of all current workspace IDs. If this is held while a previous flush is in progress, it will deadlock until the previous flush has completed.
(now time.Time)
| 109 | // If this is held while a previous flush is in progress, it will |
| 110 | // deadlock until the previous flush has completed. |
| 111 | func (tr *UsageTracker) flush(now time.Time) { |
| 112 | // Copy our current set of IDs |
| 113 | ids := tr.m.UniqueAndClear() |
| 114 | count := len(ids) |
| 115 | if tr.flushCh != nil { // only used for testing |
| 116 | defer func() { |
| 117 | tr.flushCh <- count |
| 118 | }() |
| 119 | } |
| 120 | if count == 0 { |
| 121 | tr.log.Debug(context.Background(), "nothing to flush") |
| 122 | return |
| 123 | } |
| 124 | |
| 125 | // Set a short-ish timeout for this. We don't want to hang forever. |
| 126 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) |
| 127 | defer cancel() |
| 128 | // nolint: gocritic // system function |
| 129 | authCtx := dbauthz.AsSystemRestricted(ctx) |
| 130 | tr.flushLock.Lock() |
| 131 | defer tr.flushLock.Unlock() |
| 132 | if err := tr.s.BatchUpdateWorkspaceLastUsedAt(authCtx, database.BatchUpdateWorkspaceLastUsedAtParams{ |
| 133 | LastUsedAt: now, |
| 134 | IDs: ids, |
| 135 | }); err != nil { |
| 136 | // A single failure to flush is likely not a huge problem. If the workspace is still connected at |
| 137 | // the next iteration, either another coderd instance will likely have this data or the CLI |
| 138 | // will tell us again that the workspace is in use. |
| 139 | tr.flushErrors++ |
| 140 | if tr.flushErrors > 1 { |
| 141 | tr.log.Error(ctx, "multiple failures updating workspaces last_used_at", slog.F("count", count), slog.F("consecutive_errors", tr.flushErrors), slog.Error(err)) |
| 142 | // TODO: if this keeps failing, it indicates a fundamental problem with the database connection. |
| 143 | // How to surface it correctly to admins besides just screaming into the logs? |
| 144 | } else { |
| 145 | tr.log.Warn(ctx, "failed updating workspaces last_used_at", slog.F("count", count), slog.Error(err)) |
| 146 | } |
| 147 | return |
| 148 | } |
| 149 | tr.flushErrors = 0 |
| 150 | tr.log.Info(ctx, "updated workspaces last_used_at", slog.F("count", count), slog.F("now", now)) |
| 151 | } |
| 152 | |
| 153 | // loop periodically flushes every tick. |
| 154 | // If loop is called after Close, it will exit immediately and log an error. |
no test coverage detected