From f42b138fb18d080861f70e831c479738e3cdcd14 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 14 Feb 2020 11:14:52 -0700 Subject: tls: Avoid duplication AutomationPolicies for large quantities of names This should greatly reduce memory usage at scale. Part of an overall effort between Caddy 2 and CertMagic to optimize for large numbers of names. --- caddyconfig/httpcaddyfile/httptype.go | 6 +++--- modules/caddyhttp/autohttps.go | 2 +- modules/caddytls/tls.go | 29 +++++++++++++++++++++-------- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/caddyconfig/httpcaddyfile/httptype.go b/caddyconfig/httpcaddyfile/httptype.go index e54456e..4773ac8 100644 --- a/caddyconfig/httpcaddyfile/httptype.go +++ b/caddyconfig/httpcaddyfile/httptype.go @@ -200,7 +200,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock, if tlsApp.Automation == nil { tlsApp.Automation = new(caddytls.AutomationConfig) } - tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, caddytls.AutomationPolicy{ + tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, &caddytls.AutomationPolicy{ Hosts: sblockHosts, ManagementRaw: caddyconfig.JSONModuleObject(mm, "module", mm.(caddy.Module).CaddyModule().ID.Name(), &warnings), }) @@ -251,7 +251,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock, DNSRaw: caddyconfig.JSONModuleObject(dnsProvModule.New(), "provider", provName, &warnings), } } - tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, caddytls.AutomationPolicy{ + tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, &caddytls.AutomationPolicy{ ManagementRaw: caddyconfig.JSONModuleObject(mgr, "module", "acme", &warnings), }) } @@ -593,7 +593,7 @@ func consolidateRoutes(routes caddyhttp.RouteList) caddyhttp.RouteList { // consolidateAutomationPolicies combines automation policies that are the same, // for a cleaner overall output. -func consolidateAutomationPolicies(aps []caddytls.AutomationPolicy) []caddytls.AutomationPolicy { +func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls.AutomationPolicy { for i := 0; i < len(aps); i++ { for j := 0; j < len(aps); j++ { if j == i { diff --git a/modules/caddyhttp/autohttps.go b/modules/caddyhttp/autohttps.go index d60e955..8b6fa4d 100644 --- a/modules/caddyhttp/autohttps.go +++ b/modules/caddyhttp/autohttps.go @@ -338,7 +338,7 @@ func (app *App) automaticHTTPSPhase3() error { srv.tlsApp.Automation = new(caddytls.AutomationConfig) } srv.tlsApp.Automation.Policies = append(srv.tlsApp.Automation.Policies, - caddytls.AutomationPolicy{ + &caddytls.AutomationPolicy{ Hosts: domainsForCerts, Management: acmeManager, }) diff --git a/modules/caddytls/tls.go b/modules/caddytls/tls.go index f9beb6f..7d80bfb 100644 --- a/modules/caddytls/tls.go +++ b/modules/caddytls/tls.go @@ -202,19 +202,32 @@ func (t *TLS) Cleanup() error { // Manage immediately begins managing names according to the // matching automation policy. func (t *TLS) Manage(names []string) error { + // for a large number of names, we can be more memory-efficient + // by making only one certmagic.Config for all the names that + // use that config, rather than calling ManageSync/ManageAsync + // once for every name; so first, bin names by AutomationPolicy + policyToNames := make(map[*AutomationPolicy][]string) for _, name := range names { ap := t.getAutomationPolicyForName(name) + policyToNames[ap] = append(policyToNames[ap], name) + } + + // now that names are grouped by policy, we can simply make one + // certmagic.Config for each (potentially large) group of names + // and call ManageSync/ManageAsync just once for the whole batch + for ap, names := range policyToNames { magic := certmagic.New(t.certCache, ap.makeCertMagicConfig(t.ctx)) var err error if ap.ManageSync { - err = magic.ManageSync([]string{name}) + err = magic.ManageSync(names) } else { - err = magic.ManageAsync(t.ctx.Context, []string{name}) + err = magic.ManageAsync(t.ctx.Context, names) } if err != nil { - return fmt.Errorf("automate: manage %s: %v", name, err) + return fmt.Errorf("automate: manage %v: %v", names, err) } } + return nil } @@ -234,7 +247,7 @@ func (t *TLS) getConfigForName(name string) (certmagic.Config, error) { return ap.makeCertMagicConfig(t.ctx), nil } -func (t *TLS) getAutomationPolicyForName(name string) AutomationPolicy { +func (t *TLS) getAutomationPolicyForName(name string) *AutomationPolicy { if t.Automation != nil { for _, ap := range t.Automation.Policies { if len(ap.Hosts) == 0 { @@ -248,9 +261,7 @@ func (t *TLS) getAutomationPolicyForName(name string) AutomationPolicy { } } } - - // default automation policy - return AutomationPolicy{Management: new(ACMEManagerMaker)} + return defaultAutomationPolicy } // AllMatchingCertificates returns the list of all certificates in @@ -329,7 +340,7 @@ type Certificate struct { type AutomationConfig struct { // The list of automation policies. The first matching // policy will be applied for a given certificate/name. - Policies []AutomationPolicy `json:"policies,omitempty"` + Policies []*AutomationPolicy `json:"policies,omitempty"` // On-Demand TLS defers certificate operations to the // moment they are needed, e.g. during a TLS handshake. @@ -510,6 +521,8 @@ var ( storageCleanMu sync.Mutex ) +var defaultAutomationPolicy = &AutomationPolicy{Management: new(ACMEManagerMaker)} + // Interface guards var ( _ caddy.App = (*TLS)(nil) -- cgit v1.2.3