(ctx context.Context, chatID uuid.UUID)
| 2971 | } |
| 2972 | |
| 2973 | func (p *Server) acquireManualTitleLock(ctx context.Context, chatID uuid.UUID) error { |
| 2974 | now := time.Now() |
| 2975 | return p.db.InTx(func(tx database.Store) error { |
| 2976 | lockedChat, err := tx.GetChatByIDForUpdate(ctx, chatID) |
| 2977 | if err != nil { |
| 2978 | return xerrors.Errorf("lock chat for manual title regeneration: %w", err) |
| 2979 | } |
| 2980 | // Only a fresh manual lock or a chat without a real worker should |
| 2981 | // block title regeneration. Running chats with a real worker may |
| 2982 | // regenerate their title concurrently, and last write wins. |
| 2983 | hasRealWorker := lockedChat.Status == database.ChatStatusRunning && |
| 2984 | lockedChat.WorkerID.Valid && |
| 2985 | lockedChat.WorkerID.UUID != manualTitleLockWorkerID |
| 2986 | if lockedChat.Status == database.ChatStatusPending || |
| 2987 | (lockedChat.Status == database.ChatStatusRunning && !hasRealWorker) || |
| 2988 | isFreshManualTitleLock(lockedChat, now) { |
| 2989 | return ErrManualTitleRegenerationInProgress |
| 2990 | } |
| 2991 | if hasRealWorker { |
| 2992 | return nil |
| 2993 | } |
| 2994 | |
| 2995 | _, err = updateChatStatusPreserveUpdatedAt( |
| 2996 | ctx, |
| 2997 | tx, |
| 2998 | lockedChat, |
| 2999 | uuid.NullUUID{UUID: manualTitleLockWorkerID, Valid: true}, |
| 3000 | sql.NullTime{Time: now, Valid: true}, |
| 3001 | sql.NullTime{}, |
| 3002 | ) |
| 3003 | if err != nil { |
| 3004 | return xerrors.Errorf("mark chat for manual title regeneration: %w", err) |
| 3005 | } |
| 3006 | return nil |
| 3007 | }, database.DefaultTXOptions().WithID("chat_title_regenerate_lock")) |
| 3008 | } |
| 3009 | |
| 3010 | func (p *Server) releaseManualTitleLock(ctx context.Context, chatID uuid.UUID) { |
| 3011 | cleanupCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) |
no test coverage detected