snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
196
server/channels/jobs/schedulers.go
Normal file
196
server/channels/jobs/schedulers.go
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
)
|
||||
|
||||
type Scheduler interface {
|
||||
Enabled(cfg *model.Config) bool
|
||||
NextScheduleTime(cfg *model.Config, now time.Time, pendingJobs bool, lastSuccessfulJob *model.Job) *time.Time
|
||||
ScheduleJob(rctx request.CTX, cfg *model.Config, pendingJobs bool, lastSuccessfulJob *model.Job) (*model.Job, *model.AppError)
|
||||
}
|
||||
|
||||
type Schedulers struct {
|
||||
stop chan bool
|
||||
stopped chan bool
|
||||
configChanged chan *model.Config
|
||||
clusterLeaderChanged chan bool
|
||||
listenerId string
|
||||
jobs *JobServer
|
||||
isLeader bool
|
||||
running bool
|
||||
|
||||
schedulers map[string]Scheduler
|
||||
nextRunTimes map[string]*time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
ErrSchedulersNotRunning = errors.New("job schedulers are not running")
|
||||
ErrSchedulersRunning = errors.New("job schedulers are running")
|
||||
ErrSchedulersUninitialized = errors.New("job schedulers are not initialized")
|
||||
)
|
||||
|
||||
func (schedulers *Schedulers) AddScheduler(name string, scheduler Scheduler) {
|
||||
schedulers.schedulers[name] = scheduler
|
||||
}
|
||||
|
||||
// Start starts the schedulers. This call is not safe for concurrent use.
|
||||
// Synchronization should be implemented by the caller.
|
||||
func (schedulers *Schedulers) Start() {
|
||||
schedulers.stop = make(chan bool)
|
||||
schedulers.stopped = make(chan bool)
|
||||
schedulers.listenerId = schedulers.jobs.ConfigService.AddConfigListener(schedulers.handleConfigChange)
|
||||
|
||||
go func() {
|
||||
mlog.Info("Starting schedulers.")
|
||||
|
||||
defer func() {
|
||||
mlog.Info("Schedulers stopped.")
|
||||
close(schedulers.stopped)
|
||||
}()
|
||||
|
||||
now := time.Now()
|
||||
for name, scheduler := range schedulers.schedulers {
|
||||
if !scheduler.Enabled(schedulers.jobs.Config()) {
|
||||
schedulers.nextRunTimes[name] = nil
|
||||
} else {
|
||||
schedulers.setNextRunTime(schedulers.jobs.Config(), name, now, false)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
timer := time.NewTimer(1 * time.Minute)
|
||||
select {
|
||||
case <-schedulers.stop:
|
||||
mlog.Debug("Schedulers received stop signal.")
|
||||
timer.Stop()
|
||||
return
|
||||
case now = <-timer.C:
|
||||
cfg := schedulers.jobs.Config()
|
||||
|
||||
for name, nextTime := range schedulers.nextRunTimes {
|
||||
if nextTime == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if time.Now().After(*nextTime) {
|
||||
scheduler := schedulers.schedulers[name]
|
||||
if scheduler == nil || !schedulers.isLeader || !scheduler.Enabled(cfg) {
|
||||
continue
|
||||
}
|
||||
rctx := request.EmptyContext(schedulers.jobs.Logger())
|
||||
if _, err := schedulers.scheduleJob(rctx, cfg, name, scheduler); err != nil {
|
||||
mlog.Error("Failed to schedule job", mlog.String("scheduler", name), mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
schedulers.setNextRunTime(cfg, name, now, true)
|
||||
}
|
||||
}
|
||||
case newCfg := <-schedulers.configChanged:
|
||||
for name, scheduler := range schedulers.schedulers {
|
||||
if !schedulers.isLeader || !scheduler.Enabled(newCfg) {
|
||||
schedulers.nextRunTimes[name] = nil
|
||||
} else {
|
||||
schedulers.setNextRunTime(newCfg, name, now, false)
|
||||
}
|
||||
}
|
||||
case isLeader := <-schedulers.clusterLeaderChanged:
|
||||
for name := range schedulers.schedulers {
|
||||
schedulers.isLeader = isLeader
|
||||
if !isLeader {
|
||||
schedulers.nextRunTimes[name] = nil
|
||||
} else {
|
||||
schedulers.setNextRunTime(schedulers.jobs.Config(), name, now, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
timer.Stop()
|
||||
}
|
||||
}()
|
||||
|
||||
schedulers.running = true
|
||||
}
|
||||
|
||||
// Stop stops the schedulers. This call is not safe for concurrent use.
|
||||
// Synchronization should be implemented by the caller.
|
||||
func (schedulers *Schedulers) Stop() {
|
||||
mlog.Info("Stopping schedulers.")
|
||||
close(schedulers.stop)
|
||||
<-schedulers.stopped
|
||||
schedulers.jobs.ConfigService.RemoveConfigListener(schedulers.listenerId)
|
||||
schedulers.listenerId = ""
|
||||
schedulers.running = false
|
||||
}
|
||||
|
||||
func (schedulers *Schedulers) setNextRunTime(cfg *model.Config, name string, now time.Time, pendingJobs bool) {
|
||||
scheduler := schedulers.schedulers[name]
|
||||
|
||||
if !pendingJobs {
|
||||
pj, err := schedulers.jobs.CheckForPendingJobsByType(name)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to set next job run time", mlog.Err(err))
|
||||
schedulers.nextRunTimes[name] = nil
|
||||
return
|
||||
}
|
||||
pendingJobs = pj
|
||||
}
|
||||
|
||||
lastSuccessfulJob, err := schedulers.jobs.GetLastSuccessfulJobByType(name)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to set next job run time", mlog.Err(err))
|
||||
schedulers.nextRunTimes[name] = nil
|
||||
return
|
||||
}
|
||||
|
||||
schedulers.nextRunTimes[name] = scheduler.NextScheduleTime(cfg, now, pendingJobs, lastSuccessfulJob)
|
||||
mlog.Debug("Next run time for scheduler", mlog.String("scheduler_name", name), mlog.String("next_runtime", fmt.Sprintf("%v", schedulers.nextRunTimes[name])))
|
||||
}
|
||||
|
||||
func (schedulers *Schedulers) scheduleJob(rctx request.CTX, cfg *model.Config, name string, scheduler Scheduler) (*model.Job, *model.AppError) {
|
||||
pendingJobs, err := schedulers.jobs.CheckForPendingJobsByType(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lastSuccessfulJob, err := schedulers.jobs.GetLastSuccessfulJobByType(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return scheduler.ScheduleJob(rctx, cfg, pendingJobs, lastSuccessfulJob)
|
||||
}
|
||||
|
||||
func (schedulers *Schedulers) handleConfigChange(_, newConfig *model.Config) {
|
||||
mlog.Debug("Schedulers received config change.")
|
||||
select {
|
||||
case schedulers.configChanged <- newConfig:
|
||||
case <-schedulers.stop:
|
||||
}
|
||||
}
|
||||
|
||||
func (schedulers *Schedulers) handleClusterLeaderChange(isLeader bool) {
|
||||
select {
|
||||
case schedulers.clusterLeaderChanged <- isLeader:
|
||||
default:
|
||||
mlog.Debug("Sending cluster leader change message to schedulers failed.")
|
||||
|
||||
// Drain the buffered channel to make room for the latest change.
|
||||
select {
|
||||
case <-schedulers.clusterLeaderChanged:
|
||||
default:
|
||||
}
|
||||
|
||||
// Enqueue the latest change. This operation is safe due to this method
|
||||
// being called under lock.
|
||||
schedulers.clusterLeaderChanged <- isLeader
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user