Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 13, 21:45 UTC
core: Retry dynamic config load if config unchanged
(see discussion in #4603)
Matthew Holt committed
Mar 3, 2022 at 21:41 UTC
a72acd21b0ef17d6cd9064dd105042b6edb3b8dc
2 files changed
+33
-14
admin.go
+1
-1
@@ -938,7 +938,7 @@ func handleConfig(w http.ResponseWriter, r *http.Request) error {
938
forceReload := r.Header.Get("Cache-Control") == "must-revalidate"
939
940
err := changeConfig(r.Method, r.URL.Path, body, forceReload)
941
- if err != nil {
941
+ if err != nil && !errors.Is(err, errSameConfig) {
942
return err
943
}
944
caddy.go
+32
-13
@@ -18,6 +18,7 @@ import (
18
"bytes"
19
"context"
20
"encoding/json"
21
+ "errors"
22
"fmt"
23
"io"
24
"log"
@@ -110,14 +111,19 @@ func Load(cfgJSON []byte, forceReload bool) error {
111
}
112
}()
113
113
- return changeConfig(http.MethodPost, "/"+rawConfigKey, cfgJSON, forceReload)
114
+ err := changeConfig(http.MethodPost, "/"+rawConfigKey, cfgJSON, forceReload)
115
+ if errors.Is(err, errSameConfig) {
116
+ err = nil // not really an error
117
+ }
118
+ return err
119
}
120
121
// changeConfig changes the current config (rawCfg) according to the
122
// method, traversed via the given path, and uses the given input as
123
// the new value (if applicable; i.e. "DELETE" doesn't have an input).
124
// If the resulting config is the same as the previous, no reload will
120
-// occur unless forceReload is true. This function is safe for
125
+// occur unless forceReload is true. If the config is unchanged and not
126
+// forcefully reloaded, then errConfigUnchanged This function is safe for
127
// concurrent use.
128
func changeConfig(method, path string, input []byte, forceReload bool) error {
129
switch method {
@@ -149,7 +155,7 @@ func changeConfig(method, path string, input []byte, forceReload bool) error {
155
// if nothing changed, no need to do a whole reload unless the client forces it
156
if !forceReload && bytes.Equal(rawCfgJSON, newCfg) {
157
Log().Info("config is unchanged")
152
- return nil
158
+ return errSameConfig
159
}
160
161
// find any IDs in this config and index them
@@ -492,39 +498,47 @@ func finishSettingUp(ctx Context, cfg *Config) error {
498
zap.String("module", val.(Module).CaddyModule().ID.Name()),
499
zap.Int("load_delay", int(cfg.Admin.Config.LoadDelay)))
500
495
- runLoadedConfig := func(config []byte) {
501
+ runLoadedConfig := func(config []byte) error {
502
logger.Info("applying dynamically-loaded config")
503
err := changeConfig(http.MethodPost, "/"+rawConfigKey, config, false)
498
- if err == nil {
499
- logger.Info("successfully applied dynamically-loaded config")
500
- } else {
504
+ if errors.Is(err, errSameConfig) {
505
+ return err
506
+ }
507
+ if err != nil {
508
logger.Error("failed to run dynamically-loaded config", zap.Error(err))
509
+ return err
510
}
511
+ logger.Info("successfully applied dynamically-loaded config")
512
+ return nil
513
}
514
515
if cfg.Admin.Config.LoadDelay > 0 {
516
go func() {
507
- // the loop is here only to iterate if there is an error or a no-op config load,
508
- // in which case we simply wait the delay and try again
517
+ // the loop is here to iterate ONLY if there is an error, a no-op config load,
518
+ // or an unchanged config; in which case we simply wait the delay and try again
519
for {
520
timer := time.NewTimer(time.Duration(cfg.Admin.Config.LoadDelay))
521
select {
522
case <-timer.C:
523
loadedConfig, err := val.(ConfigLoader).LoadConfig(ctx)
524
if err != nil {
515
- Log().Error("failed loading dynamic config; will retry", zap.Error(err))
525
+ logger.Error("failed loading dynamic config; will retry", zap.Error(err))
526
continue
527
}
528
if loadedConfig == nil {
519
- Log().Info("dynamically-loaded config was nil; will retry")
529
+ logger.Info("dynamically-loaded config was nil; will retry")
530
+ continue
531
+ }
532
+ err = runLoadedConfig(loadedConfig)
533
+ if errors.Is(err, errSameConfig) {
534
+ logger.Info("dynamically-loaded config was unchanged; will retry")
535
continue
536
}
522
- runLoadedConfig(loadedConfig)
537
case <-ctx.Done():
538
if !timer.Stop() {
539
<-timer.C
540
}
527
- Log().Info("stopping dynamic config loading")
541
+ logger.Info("stopping dynamic config loading")
542
}
543
break
544
}
@@ -798,5 +812,10 @@ var (
812
rawCfgIndex map[string]string
813
)
814
815
+// errSameConfig is returned if the new config is the same
816
+// as the old one. This isn't usually an actual, actionable
817
+// error; it's mostly a sentinel value.
818
+var errSameConfig = errors.New("config is unchanged")
819
+
820
// ImportPath is the package import path for Caddy core.
821
const ImportPath = "github.com/caddyserver/caddy/v2"