@marcel91200 / caddy Mirror · read-only / commits / 42b7134f
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

caddytls: Configurable storage clean interval

Can drastically reduce costs on storage backends where scans are expensive. Also reduced default interval to 24h. See https://github.com/silinternational/certmagic-storage-dynamodb/issues/18

Matthew Holt committed Apr 12, 2021 at 15:41 UTC 42b7134ffa3bf3e9e86514c82407979c2627a5ab
2 files changed +52 -9
modules/caddytls/automation.go
+13
@@ -56,6 +56,19 @@ type AutomationConfig struct {
56 // performed. Default: 10m
57 RenewCheckInterval caddy.Duration `json:"renew_interval,omitempty"`
58
59 + // How often to scan storage units for old or expired
60 + // assets and remove them. These scans exert lots of
61 + // reads (and list operations) on the storage module, so
62 + // choose a longer interval for large deployments.
63 + // Default: 24h
64 + //
65 + // Storage will always be cleaned when the process first
66 + // starts. Then, a new cleaning will be started this
67 + // duration after the previous cleaning started if the
68 + // previous cleaning finished in less than half the time
69 + // of this interval (otherwise next start will be skipped).
70 + StorageCleanInterval caddy.Duration `json:"storage_clean_interval,omitempty"`
71 +
72 defaultPublicAutomationPolicy *AutomationPolicy
73 defaultInternalAutomationPolicy *AutomationPolicy // only initialized if necessary
74 }
modules/caddytls/tls.go
+39 -9
@@ -414,7 +414,7 @@ func (t *TLS) AllMatchingCertificates(san string) []certmagic.Certificate {
414 // known storage units if it was not recently done, and then runs the
415 // operation at every tick from t.storageCleanTicker.
416 func (t *TLS) keepStorageClean() {
417 - t.storageCleanTicker = time.NewTicker(storageCleanInterval)
417 + t.storageCleanTicker = time.NewTicker(t.storageCleanInterval())
418 t.storageCleanStop = make(chan struct{})
419 go func() {
420 defer func() {
@@ -438,31 +438,61 @@ func (t *TLS) cleanStorageUnits() {
438 storageCleanMu.Lock()
439 defer storageCleanMu.Unlock()
440
441 - if !storageClean.IsZero() && time.Since(storageClean) < storageCleanInterval {
441 + // If storage was cleaned recently, don't do it again for now. Although the ticker
442 + // drops missed ticks for us, config reloads discard the old ticker and replace it
443 + // with a new one, possibly invoking a cleaning to happen again too soon.
444 + // (We divide the interval by 2 because the actual cleaning takes non-zero time,
445 + // and we don't want to skip cleanings if we don't have to; whereas if a cleaning
446 + // took the entire interval, we'd probably want to skip the next one so we aren't
447 + // constantly cleaning. This allows cleanings to take up to half the interval's
448 + // duration before we decide to skip the next one.)
449 + if !storageClean.IsZero() && time.Since(storageClean) < t.storageCleanInterval()/2 {
450 return
451 }
452
453 + // mark when storage cleaning was last initiated
454 + storageClean = time.Now()
455 +
456 options := certmagic.CleanStorageOptions{
457 OCSPStaples: true,
458 ExpiredCerts: true,
459 ExpiredCertGracePeriod: 24 * time.Hour * 14,
460 }
461
451 - // start with the default storage
452 - certmagic.CleanStorage(t.ctx, t.ctx.Storage(), options)
462 + // avoid cleaning same storage more than once per cleaning cycle
463 + storagesCleaned := make(map[string]struct{})
464 +
465 + // start with the default/global storage
466 + storage := t.ctx.Storage()
467 + storageStr := fmt.Sprintf("%v", storage)
468 + t.logger.Info("cleaning storage unit", zap.String("description", storageStr))
469 + certmagic.CleanStorage(t.ctx, storage, options)
470 + storagesCleaned[storageStr] = struct{}{}
471
472 // then clean each storage defined in ACME automation policies
473 if t.Automation != nil {
474 for _, ap := range t.Automation.Policies {
457 - if ap.storage != nil {
458 - certmagic.CleanStorage(t.ctx, ap.storage, options)
475 + if ap.storage == nil {
476 + continue
477 + }
478 + storageStr := fmt.Sprintf("%v", ap.storage)
479 + if _, ok := storagesCleaned[storageStr]; ok {
480 + continue
481 }
482 + t.logger.Info("cleaning storage unit", zap.String("description", storageStr))
483 + certmagic.CleanStorage(t.ctx, ap.storage, options)
484 + storagesCleaned[storageStr] = struct{}{}
485 }
486 }
487
463 - storageClean = time.Now()
488 + t.logger.Info("finished cleaning storage units")
489 +}
490
465 - t.logger.Info("cleaned up storage units")
491 +func (t *TLS) storageCleanInterval() time.Duration {
492 + if t.Automation != nil && t.Automation.StorageCleanInterval > 0 {
493 + return time.Duration(t.Automation.StorageCleanInterval)
494 + }
495 + return defaultStorageCleanInterval
496 }
497
498 // CertificateLoader is a type that can load certificates.
@@ -507,7 +537,7 @@ type CertCacheOptions struct {
537
538 // Variables related to storage cleaning.
539 var (
510 - storageCleanInterval = 12 * time.Hour
540 + defaultStorageCleanInterval = 24 * time.Hour
541
542 storageClean time.Time
543 storageCleanMu sync.Mutex