Skip to content

Commit 95c4fdd

Browse files
feat(worker): make maximum consecutive failures configurable
1 parent abfd893 commit 95c4fdd

14 files changed

Lines changed: 89 additions & 37 deletions

backoff.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func (e *exponentialBackoff) recordFailure() bool {
3333
e.backoff = min(e.backoff*2, e.maxBackoff)
3434

3535
e.mu.Unlock()
36-
return e.failureCount >= e.maxConsecutiveFailures
36+
return e.maxConsecutiveFailures != -1 && e.failureCount >= e.maxConsecutiveFailures
3737
}
3838

3939
// wait sleeps for the backoff duration if failureCount is non-zero.

caddy/app.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ func (f *FrankenPHPApp) Start() error {
114114
frankenphp.WithMaxWaitTime(f.MaxWaitTime),
115115
}
116116
for _, w := range append(f.Workers) {
117-
opts = append(opts, frankenphp.WithWorkers(w.Name, repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch))
117+
maxFailures := w.MaxConsecutiveFailures
118+
if maxFailures <= 0 {
119+
maxFailures = frankenphp.DefaultMaxConsecutiveFailures
120+
}
121+
opts = append(opts, frankenphp.WithWorkers(w.Name, repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch, maxFailures))
118122
}
119123

120124
frankenphp.Shutdown()

caddy/workerconfig.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ type workerConfig struct {
2929
Env map[string]string `json:"env,omitempty"`
3030
// Directories to watch for file changes
3131
Watch []string `json:"watch,omitempty"`
32+
// MaxConsecutiveFailures sets the maximum number of consecutive failures before panicking (defaults to 6)
33+
MaxConsecutiveFailures int `json:"max_consecutive_failures,omitempty"`
3234
}
3335

3436
func parseWorkerConfig(d *caddyfile.Dispenser) (workerConfig, error) {
@@ -94,8 +96,19 @@ func parseWorkerConfig(d *caddyfile.Dispenser) (workerConfig, error) {
9496
} else {
9597
wc.Watch = append(wc.Watch, d.Val())
9698
}
99+
case "max_consecutive_failures":
100+
if !d.NextArg() {
101+
return wc, d.ArgErr()
102+
}
103+
104+
v, err := strconv.ParseUint(d.Val(), 10, 32)
105+
if err != nil {
106+
return wc, err
107+
}
108+
109+
wc.MaxConsecutiveFailures = int(v)
97110
default:
98-
allowedDirectives := "name, file, num, env, watch"
111+
allowedDirectives := "name, file, num, env, watch, max_consecutive_failures"
99112
return wc, wrongSubDirectiveError("worker", allowedDirectives, v)
100113
}
101114
}

docs/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ The `frankenphp` [global option](https://caddyserver.com/docs/caddyfile/concepts
7171
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
7272
watch <path> # Sets the path to watch for file changes. Can be specified more than once for multiple paths.
7373
name <name> # Sets the name of the worker, used in logs and metrics. Default: absolute path of worker file
74+
max_consecutive_failures <num> # Sets the maximum number of consecutive failures before the worker is considered unhealthy, -1 means the worker will always restart. Default: 6.
7475
}
7576
}
7677
}

docs/fr/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ L'[option globale](https://caddyserver.com/docs/caddyfile/concepts#global-option
7070
env <key> <value> # Définit une variable d'environnement supplémentaire avec la valeur donnée. Peut être spécifié plusieurs fois pour régler plusieurs variables d'environnement.
7171
watch <path> # Définit le chemin d'accès à surveiller pour les modifications de fichiers. Peut être spécifié plusieurs fois pour plusieurs chemins.
7272
name <name> # Définit le nom du worker, utilisé dans les journaux et les métriques. Défaut : chemin absolu du fichier du worker
73+
max_consecutive_failures <num> # Définit le nombre maximum d'échecs consécutifs avant que le worker ne soit considéré comme défaillant, -1 signifie que le worker redémarre toujours. Par défaut : 6.
7374
}
7475
}
7576
}

docs/fr/worker.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@ Si le script worker reste en place plus longtemps que le dernier backoff \* 2, F
150150
Toutefois, si le script de worker continue d'échouer avec un code de sortie non nul dans un court laps de temps
151151
(par exemple, une faute de frappe dans un script), FrankenPHP plantera avec l'erreur : `too many consecutive failures` (trop d'échecs consécutifs).
152152

153+
Le nombre d'échecs consécutifs peut être configuré dans votre [Caddyfile](config.md#configuration-du-caddyfile) avec l'option `max_consecutive_failures` :
154+
155+
```caddyfile
156+
frankenphp {
157+
worker {
158+
# ...
159+
max_consecutive_failures 10
160+
}
161+
}
162+
```
163+
153164
## Comportement des superglobales
154165

155166
[Les superglobales PHP](https://www.php.net/manual/fr/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...)

docs/worker.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,19 @@ it will not penalize the worker script and restart it again.
146146
However, if the worker script continues to fail with a non-zero exit code in a short period of time
147147
(for example, having a typo in a script), FrankenPHP will crash with the error: `too many consecutive failures`.
148148

149+
The number of consecutive failures can be configured in your [Caddyfile](config.md#caddyfile-config) with the `max_consecutive_failures` option:
150+
151+
```caddyfile
152+
frankenphp {
153+
worker {
154+
# ...
155+
max_consecutive_failures 10
156+
}
157+
}
158+
```
159+
160+
```caddyfile
161+
149162
## Superglobals Behavior
150163
151164
[PHP superglobals](https://www.php.net/manual/en/language.variables.superglobals.php) (`$_SERVER`, `$_ENV`, `$_GET`...)

frankenphp_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func runTest(t *testing.T, test func(func(http.ResponseWriter, *http.Request), *
6666

6767
initOpts := []frankenphp.Option{frankenphp.WithLogger(opts.logger)}
6868
if opts.workerScript != "" {
69-
initOpts = append(initOpts, frankenphp.WithWorkers("workerName", testDataDir+opts.workerScript, opts.nbWorkers, opts.env, opts.watch))
69+
initOpts = append(initOpts, frankenphp.WithWorkers("workerName", testDataDir+opts.workerScript, opts.nbWorkers, opts.env, opts.watch, 6))
7070
}
7171
initOpts = append(initOpts, opts.initOpts...)
7272
if opts.phpIni != nil {

options.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import (
55
"time"
66
)
77

8+
// DefaultMaxConsecutiveFailures is the default maximum number of consecutive failures before panicking
9+
const DefaultMaxConsecutiveFailures = 6
10+
811
// Option instances allow to configure FrankenPHP.
912
type Option func(h *opt) error
1013

@@ -22,11 +25,12 @@ type opt struct {
2225
}
2326

2427
type workerOpt struct {
25-
name string
26-
fileName string
27-
num int
28-
env PreparedEnv
29-
watch []string
28+
name string
29+
fileName string
30+
num int
31+
env PreparedEnv
32+
watch []string
33+
maxConsecutiveFailures int
3034
}
3135

3236
// WithNumThreads configures the number of PHP threads to start.
@@ -55,9 +59,12 @@ func WithMetrics(m Metrics) Option {
5559
}
5660

5761
// WithWorkers configures the PHP workers to start
58-
func WithWorkers(name string, fileName string, num int, env map[string]string, watch []string) Option {
62+
func WithWorkers(name string, fileName string, num int, env map[string]string, watch []string, maxConsecutiveFailures int) Option {
5963
return func(o *opt) error {
60-
o.workers = append(o.workers, workerOpt{name, fileName, num, PrepareEnv(env), watch})
64+
if maxConsecutiveFailures < -1 {
65+
maxConsecutiveFailures = DefaultMaxConsecutiveFailures
66+
}
67+
o.workers = append(o.workers, workerOpt{name, fileName, num, PrepareEnv(env), watch, maxConsecutiveFailures})
6168

6269
return nil
6370
}

phpmainthread_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ func TestTransitionThreadsWhileDoingRequests(t *testing.T) {
9696

9797
assert.NoError(t, Init(
9898
WithNumThreads(numThreads),
99-
WithWorkers(worker1Name, worker1Path, 1, map[string]string{"ENV1": "foo"}, []string{}),
100-
WithWorkers(worker2Name, worker2Path, 1, map[string]string{"ENV1": "foo"}, []string{}),
99+
WithWorkers(worker1Name, worker1Path, 1, map[string]string{"ENV1": "foo"}, []string{}, 0),
100+
WithWorkers(worker2Name, worker2Path, 1, map[string]string{"ENV1": "foo"}, []string{}, 0),
101101
WithLogger(slog.New(slog.NewTextHandler(io.Discard, nil))),
102102
))
103103

@@ -182,17 +182,17 @@ func TestFinishBootingAWorkerScript(t *testing.T) {
182182

183183
func TestReturnAnErrorIf2WorkersHaveTheSameFileName(t *testing.T) {
184184
workers = make(map[string]*worker)
185-
_, err1 := newWorker(workerOpt{fileName: "filename.php"})
186-
_, err2 := newWorker(workerOpt{fileName: "filename.php"})
185+
_, err1 := newWorker(workerOpt{fileName: "filename.php", maxConsecutiveFailures: 6})
186+
_, err2 := newWorker(workerOpt{fileName: "filename.php", maxConsecutiveFailures: 6})
187187

188188
assert.NoError(t, err1)
189189
assert.Error(t, err2, "two workers cannot have the same filename")
190190
}
191191

192192
func TestReturnAnErrorIf2ModuleWorkersHaveTheSameName(t *testing.T) {
193193
workers = make(map[string]*worker)
194-
_, err1 := newWorker(workerOpt{fileName: "filename.php", name: "workername"})
195-
_, err2 := newWorker(workerOpt{fileName: "filename2.php", name: "workername"})
194+
_, err1 := newWorker(workerOpt{fileName: "filename.php", name: "workername", maxConsecutiveFailures: 6})
195+
_, err2 := newWorker(workerOpt{fileName: "filename2.php", name: "workername", maxConsecutiveFailures: 6})
196196

197197
assert.NoError(t, err1)
198198
assert.Error(t, err2, "two workers cannot have the same name")
@@ -203,8 +203,9 @@ func getDummyWorker(fileName string) *worker {
203203
workers = make(map[string]*worker)
204204
}
205205
worker, _ := newWorker(workerOpt{
206-
fileName: testDataPath + "/" + fileName,
207-
num: 1,
206+
fileName: testDataPath + "/" + fileName,
207+
num: 1,
208+
maxConsecutiveFailures: 6,
208209
})
209210
return worker
210211
}

0 commit comments

Comments
 (0)