Files
files/internal/lifecycle/deletion.go
2026-08-03 23:51:21 +08:00

90 lines
2.4 KiB
Go

package lifecycle
import (
"time"
"git.apinb.com/bsm-sdk/core/utils"
"git.apinb.com/ops/files/internal/models"
"gorm.io/gorm"
)
const (
deletionLeaseDuration = 5 * time.Minute
DeleteOperationTimeout = 30 * time.Second
)
type DeletionLease struct {
Token string
LeaseUntil time.Time
}
func ClaimDeletion(db *gorm.DB, id uint, allowedStatuses []models.FileStatus, now time.Time) (DeletionLease, bool, error) {
query := db.Model(&models.FileObject{}).
Where("id = ? AND status IN ?", id, allowedStatuses)
return claimDeletion(query, now)
}
func ClaimExpiredPendingDeletion(db *gorm.DB, id uint, now time.Time) (DeletionLease, bool, error) {
query := db.Model(&models.FileObject{}).
Where("id = ? AND status = ? AND expires_at <= ?", id, models.FileStatusPending, now)
return claimDeletion(query, now)
}
func ClaimDeletionRetry(db *gorm.DB, id uint, now time.Time) (DeletionLease, bool, error) {
query := db.Model(&models.FileObject{}).
Where("id = ? AND status = ?", id, models.FileStatusDeleting).
Where("delete_lease_until IS NULL OR delete_lease_until <= ?", now)
return claimDeletion(query, now)
}
func FinalizeDeletion(db *gorm.DB, id uint, token string) error {
if token == "" {
return gorm.ErrRecordNotFound
}
return db.Transaction(func(tx *gorm.DB) error {
result := tx.Model(&models.FileObject{}).
Where("id = ? AND status = ? AND delete_token = ?", id, models.FileStatusDeleting, token).
Updates(map[string]any{
"status": models.FileStatusExpired,
"delete_token": "",
"delete_lease_until": nil,
})
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return gorm.ErrRecordNotFound
}
result = tx.Where("id = ? AND status = ?", id, models.FileStatusExpired).
Delete(&models.FileObject{})
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return gorm.ErrRecordNotFound
}
return nil
})
}
func claimDeletion(query *gorm.DB, now time.Time) (DeletionLease, bool, error) {
lease := DeletionLease{
Token: utils.ULID(),
LeaseUntil: now.Add(deletionLeaseDuration),
}
result := query.Updates(map[string]any{
"status": models.FileStatusDeleting,
"delete_token": lease.Token,
"delete_lease_until": lease.LeaseUntil,
})
if result.Error != nil {
return DeletionLease{}, false, result.Error
}
if result.RowsAffected != 1 {
return DeletionLease{}, false, nil
}
return lease, true, nil
}