MCPcopy Index your code
hub / github.com/coder/coder / dispatchChatAutoArchive

Method dispatchChatAutoArchive

coderd/database/dbpurge/dbpurge.go:536–611  ·  view source on GitHub ↗

dispatchChatAutoArchive audits every archived root chat and enqueues one notification per owner covering the roots archived in this tick. Children inherit their root's archival decision and are skipped for audit, matching the manual archive path (patchChat audits the root only). Enqueue is per-tick:

(auditCtx, enqueueCtx context.Context, tickStart time.Time, autoArchiveDays, retentionDays int32, archived []database.AutoArchiveInactiveChatsRow)

Source from the content-addressed store, hash-verified

534// is the cancellable parent: on shutdown we abandon any remaining digests
535// rather than blocking Close.
536func (i *instance) dispatchChatAutoArchive(auditCtx, enqueueCtx context.Context, tickStart time.Time, autoArchiveDays, retentionDays int32, archived []database.AutoArchiveInactiveChatsRow) {
537 // Children inherit their root's archival decision and are skipped
538 // for both audit and digest. Partition once so the two loops
539 // cannot drift apart if the cascade shape ever changes.
540 roots := slice.Filter(archived, func(r database.AutoArchiveInactiveChatsRow) bool {
541 return !r.ParentChatID.Valid
542 })
543
544 auditor := *i.auditor.Load()
545 for _, row := range roots {
546 after := chatFromAutoArchiveRow(i.logger, row)
547 before := after
548 before.Archived = false
549 audit.BackgroundAudit(auditCtx, &audit.BackgroundAuditParams[database.Chat]{
550 Audit: auditor,
551 Log: i.logger,
552 UserID: row.OwnerID,
553 OrganizationID: row.OrganizationID,
554 Action: database.AuditActionWrite,
555 Old: before,
556 New: after,
557 Status: http.StatusOK,
558 AdditionalFields: audit.BackgroundTaskFieldsBytes(auditCtx, i.logger, audit.BackgroundSubsystemChatAutoArchive),
559 })
560 }
561
562 // Group archived roots by owner. Inline because this is the
563 // only call site and the loop body is self-explanatory.
564 rootsByOwner := make(map[uuid.UUID][]database.AutoArchiveInactiveChatsRow, len(roots))
565 for _, row := range roots {
566 rootsByOwner[row.OwnerID] = append(rootsByOwner[row.OwnerID], row)
567 }
568
569 // Sort owner IDs so shutdown abandons a deterministic tail of the dispatch list.
570 ownerIDs := make([]uuid.UUID, 0, len(rootsByOwner))
571 for id := range rootsByOwner {
572 ownerIDs = append(ownerIDs, id)
573 }
574 slices.SortFunc(ownerIDs, func(a, b uuid.UUID) int {
575 return cmp.Compare(a.String(), b.String())
576 })
577
578 dispatched := 0
579 for _, ownerID := range ownerIDs {
580 // Check between iterations so shutdown unblocks promptly. A
581 // hung in-flight enqueue is unblocked by enqueueCtx propagating
582 // cancellation into the DB call. Skipped owners are not
583 // re-notified on the next tick because AutoArchiveInactiveChats
584 // only returns rows with archived = false; we accept that
585 // tradeoff over hanging shutdown.
586 if err := enqueueCtx.Err(); err != nil {
587 i.logger.Warn(enqueueCtx, "chat auto-archive digest dispatch canceled",
588 slog.F("remaining_owners", len(ownerIDs)-dispatched),
589 slog.Error(err))
590 return
591 }
592 dispatched++
593

Callers 1

purgeTickMethod · 0.95

Calls 12

FilterFunction · 0.92
BackgroundAuditFunction · 0.92
AsNotifierFunction · 0.92
chatFromAutoArchiveRowFunction · 0.85
buildDigestDataFunction · 0.85
CompareMethod · 0.80
ErrMethod · 0.80
EnqueueWithDataMethod · 0.65
LoadMethod · 0.45
StringMethod · 0.45
ErrorMethod · 0.45

Tested by

no test coverage detected