90 lines
2.4 KiB
Go
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
|
||
|
|
}
|