fix: isolate deletion notifications into per-user transactions
On Postgres, a failed operation puts the transaction in an error state where subsequent operations fail. The previous loop with continue would keep trying to use a broken transaction. Each user now gets its own transaction so a single notification failure doesn't affect others.
This commit is contained in:
parent
312648d7d6
commit
eea59c33c7
|
|
@ -28,10 +28,10 @@ import (
|
|||
|
||||
type disabledMailNotifiable struct{}
|
||||
|
||||
func (d *disabledMailNotifiable) RouteForMail() (string, error) { return "test@example.com", nil }
|
||||
func (d *disabledMailNotifiable) RouteForDB() int64 { return 1 }
|
||||
func (d *disabledMailNotifiable) RouteForMail() (string, error) { return "test@example.com", nil }
|
||||
func (d *disabledMailNotifiable) RouteForDB() int64 { return 1 }
|
||||
func (d *disabledMailNotifiable) ShouldNotify(_ ...*xorm.Session) (bool, error) { return true, nil }
|
||||
func (d *disabledMailNotifiable) Lang() string { return "en" }
|
||||
func (d *disabledMailNotifiable) Lang() string { return "en" }
|
||||
|
||||
type disabledMailNotification struct{}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/cron"
|
||||
|
|
@ -50,6 +51,9 @@ func notifyUsersScheduledForDeletion() {
|
|||
return
|
||||
}
|
||||
|
||||
// Close the read-only session before processing each user in its own transaction.
|
||||
s.Close()
|
||||
|
||||
log.Debugf("Found %d users scheduled for deletion to notify", len(users))
|
||||
|
||||
for _, user := range users {
|
||||
|
|
@ -67,27 +71,33 @@ func notifyUsersScheduledForDeletion() {
|
|||
|
||||
log.Debugf("Notifying user %d of the deletion of their account...", user.ID)
|
||||
|
||||
err = notifications.Notify(user, &AccountDeletionNotification{
|
||||
User: user,
|
||||
NotificationNumber: number,
|
||||
}, s)
|
||||
if err != nil {
|
||||
log.Errorf("Could not notify user %d of their deletion: %s", user.ID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
user.DeletionLastReminderSent = time.Now()
|
||||
_, err = s.Where("id = ?", user.ID).
|
||||
Cols("deletion_last_reminder_sent").
|
||||
Update(user)
|
||||
if err != nil {
|
||||
log.Errorf("Could update user %d last deletion reminder sent date: %s", user.ID, err)
|
||||
if err := notifyUserOfDeletion(user, number); err != nil {
|
||||
log.Errorf("Could not process deletion notification for user %d: %s", user.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
log.Errorf("Could not commit user deletion notifications: %s", err)
|
||||
func notifyUserOfDeletion(user *User, number int) error {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
err := notifications.Notify(user, &AccountDeletionNotification{
|
||||
User: user,
|
||||
NotificationNumber: number,
|
||||
}, s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not notify user: %w", err)
|
||||
}
|
||||
|
||||
user.DeletionLastReminderSent = time.Now()
|
||||
_, err = s.Where("id = ?", user.ID).
|
||||
Cols("deletion_last_reminder_sent").
|
||||
Update(user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not update last deletion reminder sent date: %w", err)
|
||||
}
|
||||
|
||||
return s.Commit()
|
||||
}
|
||||
|
||||
// RequestDeletion creates a user deletion confirm token and sends a notification to the user
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ func ResetPassword(s *xorm.Session, reset *PasswordReset) (userID int64, err err
|
|||
return
|
||||
}
|
||||
|
||||
|
||||
// PasswordTokenRequest defines the request format for password reset resqest
|
||||
type PasswordTokenRequest struct {
|
||||
Email string `json:"email" valid:"email,length(0|250)" maxLength:"250"`
|
||||
|
|
|
|||
Loading…
Reference in New Issue