Files
worker/app/models/maintenance_notifications.go
Gleb Tv 2c7a0236da feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
2026-07-13 17:55:14 +03:00

45 строки
2.1 KiB
Go

package models
import (
"fmt"
"time"
"gorm.io/gorm"
)
const maintenanceStartNotificationPrefix = "maintenance:%d:start:"
// MaintenanceStartNotificationKey uniquely identifies one contact's warning
// for one schedule revision and occurrence. Updated windows get a new revision
// while concurrent scheduler replicas share the same key.
func MaintenanceStartNotificationKey(maintenanceID int64, revision, startsAt time.Time, timezone string, notificationID, contactID int64) string {
loc, err := time.LoadLocation(timezone)
if err != nil || timezone == "SAME_AS_SERVER" || timezone == "" {
loc = time.UTC
}
// A fall-back hour can have two UTC instants for one wall-clock occurrence.
// Warnings are once per civil occurrence, matching the recurrence editor.
civilOccurrence := startsAt.In(loc).Format("200601021504")
return fmt.Sprintf("maintenance:%d:start:%d:%s:notification:%d:contact:%d", maintenanceID, revision.UnixNano(), civilOccurrence, notificationID, contactID)
}
// CancelMaintenanceStartNotificationsTx prevents queued warnings from being
// delivered after an operator pauses, changes, or deletes the maintenance.
// Leased work may already be executing and cannot be recalled from a worker.
func CancelMaintenanceStartNotificationsTx(tx *gorm.DB, maintenanceID int64, reason string) error {
prefix := fmt.Sprintf(maintenanceStartNotificationPrefix, maintenanceID) + "%"
var tasks []Task
if err := tx.Clauses(SkipLockedClause).Where("idempotency_key LIKE ? AND state IN ?", prefix, []string{TaskStateQueued, TaskStateFailedRetry}).Find(&tasks).Error; err != nil {
return err
}
for i := range tasks {
if err := tx.Model(&Task{}).Where("id = ? AND state IN ?", tasks[i].ID, []string{TaskStateQueued, TaskStateFailedRetry}).Updates(map[string]any{"state": TaskStateDead, "last_error": "canceled: " + reason, "payload": []byte(`{}`), "lease_owner": "", "lease_token": "", "lease_expires_at": nil}).Error; err != nil {
return err
}
if err := FinalizeNotificationTaskTx(tx, &tasks[i], "canceled", "canceled: "+reason); err != nil {
return err
}
}
return nil
}