feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
Этот коммит содержится в:
44
app/models/maintenance_notifications.go
Обычный файл
44
app/models/maintenance_notifications.go
Обычный файл
@@ -0,0 +1,44 @@
|
||||
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
|
||||
}
|
||||
Ссылка в новой задаче
Block a user