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.
This commit is contained in:
Matthew Holt 2020-02-14 11:14:52 -07:00
parent 2cc5d2227d
commit f42b138fb1
No known key found for this signature in database
GPG Key ID: 2A349DD577D586A5
3 changed files with 25 additions and 12 deletions

View File

@ -200,7 +200,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
if tlsApp.Automation == nil { if tlsApp.Automation == nil {
tlsApp.Automation = new(caddytls.AutomationConfig) 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, Hosts: sblockHosts,
ManagementRaw: caddyconfig.JSONModuleObject(mm, "module", mm.(caddy.Module).CaddyModule().ID.Name(), &warnings), 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), 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), 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, // consolidateAutomationPolicies combines automation policies that are the same,
// for a cleaner overall output. // 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 i := 0; i < len(aps); i++ {
for j := 0; j < len(aps); j++ { for j := 0; j < len(aps); j++ {
if j == i { if j == i {

View File

@ -338,7 +338,7 @@ func (app *App) automaticHTTPSPhase3() error {
srv.tlsApp.Automation = new(caddytls.AutomationConfig) srv.tlsApp.Automation = new(caddytls.AutomationConfig)
} }
srv.tlsApp.Automation.Policies = append(srv.tlsApp.Automation.Policies, srv.tlsApp.Automation.Policies = append(srv.tlsApp.Automation.Policies,
caddytls.AutomationPolicy{ &caddytls.AutomationPolicy{
Hosts: domainsForCerts, Hosts: domainsForCerts,
Management: acmeManager, Management: acmeManager,
}) })

View File

@ -202,19 +202,32 @@ func (t *TLS) Cleanup() error {
// Manage immediately begins managing names according to the // Manage immediately begins managing names according to the
// matching automation policy. // matching automation policy.
func (t *TLS) Manage(names []string) error { 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 { for _, name := range names {
ap := t.getAutomationPolicyForName(name) 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)) magic := certmagic.New(t.certCache, ap.makeCertMagicConfig(t.ctx))
var err error var err error
if ap.ManageSync { if ap.ManageSync {
err = magic.ManageSync([]string{name}) err = magic.ManageSync(names)
} else { } else {
err = magic.ManageAsync(t.ctx.Context, []string{name}) err = magic.ManageAsync(t.ctx.Context, names)
} }
if err != nil { if err != nil {
return fmt.Errorf("automate: manage %s: %v", name, err) return fmt.Errorf("automate: manage %v: %v", names, err)
} }
} }
return nil return nil
} }
@ -234,7 +247,7 @@ func (t *TLS) getConfigForName(name string) (certmagic.Config, error) {
return ap.makeCertMagicConfig(t.ctx), nil return ap.makeCertMagicConfig(t.ctx), nil
} }
func (t *TLS) getAutomationPolicyForName(name string) AutomationPolicy { func (t *TLS) getAutomationPolicyForName(name string) *AutomationPolicy {
if t.Automation != nil { if t.Automation != nil {
for _, ap := range t.Automation.Policies { for _, ap := range t.Automation.Policies {
if len(ap.Hosts) == 0 { if len(ap.Hosts) == 0 {
@ -248,9 +261,7 @@ func (t *TLS) getAutomationPolicyForName(name string) AutomationPolicy {
} }
} }
} }
return defaultAutomationPolicy
// default automation policy
return AutomationPolicy{Management: new(ACMEManagerMaker)}
} }
// AllMatchingCertificates returns the list of all certificates in // AllMatchingCertificates returns the list of all certificates in
@ -329,7 +340,7 @@ type Certificate struct {
type AutomationConfig struct { type AutomationConfig struct {
// The list of automation policies. The first matching // The list of automation policies. The first matching
// policy will be applied for a given certificate/name. // 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 // On-Demand TLS defers certificate operations to the
// moment they are needed, e.g. during a TLS handshake. // moment they are needed, e.g. during a TLS handshake.
@ -510,6 +521,8 @@ var (
storageCleanMu sync.Mutex storageCleanMu sync.Mutex
) )
var defaultAutomationPolicy = &AutomationPolicy{Management: new(ACMEManagerMaker)}
// Interface guards // Interface guards
var ( var (
_ caddy.App = (*TLS)(nil) _ caddy.App = (*TLS)(nil)