purgeTick performs a single purge iteration. It returns an error if the purge fails.
(ctx context.Context, db database.Store, start time.Time)
| 183 | // purgeTick performs a single purge iteration. It returns an error if the |
| 184 | // purge fails. |
| 185 | func (i *instance) purgeTick(ctx context.Context, db database.Store, start time.Time) error { |
| 186 | // Read chat configs outside the tx so a corrupt value can't |
| 187 | // poison subsequent queries. On config read errors, log and stash |
| 188 | // the error, then run unrelated purges best-effort. Retention and |
| 189 | // auto-archive errors skip only the conversation purge and |
| 190 | // auto-archive work. Debug retention errors skip only the debug |
| 191 | // purge. purgeTick returns chatConfigErr after the tx so the failed |
| 192 | // iteration is operator-visible via metric and logs. |
| 193 | chatRetentionDays, chatRetentionErr := db.GetChatRetentionDays(ctx) |
| 194 | if chatRetentionErr != nil { |
| 195 | i.logger.Error(ctx, "failed to read chat retention config: skipping chat purge and auto-archive this tick", slog.Error(chatRetentionErr)) |
| 196 | } |
| 197 | |
| 198 | chatAutoArchiveDays, chatAutoArchiveErr := db.GetChatAutoArchiveDays(ctx, codersdk.DefaultChatAutoArchiveDays) |
| 199 | if chatAutoArchiveErr != nil { |
| 200 | i.logger.Error(ctx, "failed to read chat auto-archive config: skipping chat purge and auto-archive this tick", slog.Error(chatAutoArchiveErr)) |
| 201 | } |
| 202 | |
| 203 | chatDebugRetentionDays, chatDebugRetentionErr := db.GetChatDebugRetentionDays(ctx, codersdk.DefaultChatDebugRetentionDays) |
| 204 | if chatDebugRetentionErr != nil { |
| 205 | i.logger.Error(ctx, "failed to read chat debug retention config: skipping chat debug purge this tick", slog.Error(chatDebugRetentionErr)) |
| 206 | } |
| 207 | |
| 208 | chatRetentionConfigErr := errors.Join(chatRetentionErr, chatAutoArchiveErr) |
| 209 | chatConfigErr := errors.Join(chatRetentionConfigErr, chatDebugRetentionErr) |
| 210 | |
| 211 | // Populated inside the tx; dispatched post-commit. |
| 212 | var archivedChats []database.AutoArchiveInactiveChatsRow |
| 213 | |
| 214 | // Start a transaction to grab advisory lock, we don't want to run |
| 215 | // multiple purges at the same time (multiple replicas). |
| 216 | err := db.InTx(func(tx database.Store) error { |
| 217 | // Acquire a lock to ensure that only one instance of the |
| 218 | // purge is running at a time. |
| 219 | ok, err := tx.TryAcquireLock(ctx, database.LockIDDBPurge) |
| 220 | if err != nil { |
| 221 | return err |
| 222 | } |
| 223 | if !ok { |
| 224 | i.logger.Debug(ctx, "unable to acquire lock for purging old database entries, skipping") |
| 225 | return nil |
| 226 | } |
| 227 | |
| 228 | var purgedWorkspaceAgentLogs int64 |
| 229 | workspaceAgentLogsRetention := i.vals.Retention.WorkspaceAgentLogs.Value() |
| 230 | if workspaceAgentLogsRetention > 0 { |
| 231 | deleteOldWorkspaceAgentLogsBefore := start.Add(-workspaceAgentLogsRetention) |
| 232 | purgedWorkspaceAgentLogs, err = tx.DeleteOldWorkspaceAgentLogs(ctx, deleteOldWorkspaceAgentLogsBefore) |
| 233 | if err != nil { |
| 234 | return xerrors.Errorf("failed to delete old workspace agent logs: %w", err) |
| 235 | } |
| 236 | } |
| 237 | if err := tx.DeleteOldWorkspaceAgentStats(ctx); err != nil { |
| 238 | return xerrors.Errorf("failed to delete old workspace agent stats: %w", err) |
| 239 | } |
| 240 | if err := tx.DeleteOldProvisionerDaemons(ctx); err != nil { |
| 241 | return xerrors.Errorf("failed to delete old provisioner daemons: %w", err) |
| 242 | } |