UnarchiveChat unarchives a chat family and broadcasts created events. Root chats cascade through UnarchiveChatByID. Child chats run under a row-level lock on the child (GetChatByIDForUpdate) with an in-transaction re-read of the parent, returning ErrChildUnarchiveParentArchived when the parent is ar
(ctx context.Context, chat database.Chat)
| 2294 | // with a concurrent ArchiveChatByID cascade, which visits child rows |
| 2295 | // before the parent. |
| 2296 | func (p *Server) UnarchiveChat(ctx context.Context, chat database.Chat) error { |
| 2297 | if chat.ID == uuid.Nil { |
| 2298 | return xerrors.New("chat_id is required") |
| 2299 | } |
| 2300 | |
| 2301 | if !chat.ParentChatID.Valid { |
| 2302 | return p.applyChatLifecycleTransition( |
| 2303 | ctx, |
| 2304 | chat.ID, |
| 2305 | "unarchive", |
| 2306 | codersdk.ChatWatchEventKindCreated, |
| 2307 | p.db.UnarchiveChatByID, |
| 2308 | ) |
| 2309 | } |
| 2310 | |
| 2311 | var updated []database.Chat |
| 2312 | if err := p.db.InTx(func(tx database.Store) error { |
| 2313 | locked, err := tx.GetChatByIDForUpdate(ctx, chat.ID) |
| 2314 | if err != nil { |
| 2315 | return xerrors.Errorf("lock child for unarchive: %w", err) |
| 2316 | } |
| 2317 | if !locked.Archived { |
| 2318 | // Already unarchived by a concurrent caller; idempotent no-op. |
| 2319 | return nil |
| 2320 | } |
| 2321 | parent, err := tx.GetChatByID(ctx, chat.ParentChatID.UUID) |
| 2322 | if err != nil { |
| 2323 | return xerrors.Errorf("load parent chat: %w", err) |
| 2324 | } |
| 2325 | if parent.Archived { |
| 2326 | return ErrChildUnarchiveParentArchived |
| 2327 | } |
| 2328 | updated, err = tx.UnarchiveChatByID(ctx, chat.ID) |
| 2329 | if err != nil { |
| 2330 | return xerrors.Errorf("unarchive child chat: %w", err) |
| 2331 | } |
| 2332 | return nil |
| 2333 | }, nil); err != nil { |
| 2334 | if errors.Is(err, ErrChildUnarchiveParentArchived) { |
| 2335 | return ErrChildUnarchiveParentArchived |
| 2336 | } |
| 2337 | return err |
| 2338 | } |
| 2339 | |
| 2340 | p.publishChatPubsubEvents(updated, codersdk.ChatWatchEventKindCreated) |
| 2341 | return nil |
| 2342 | } |
| 2343 | |
| 2344 | func (p *Server) applyChatLifecycleTransition( |
| 2345 | ctx context.Context, |