- Only load cert from storage (or manager) if allowed to do so (fix#174)
- Sync cert loading so storage isn't stampeded (fix#185)
- Update dependencies
- Constructor blocks until loop() goroutine is actually ready to send
(this prevents callers from constructing a brand new rate limiter and
immediately calling Allow() and getting false, even when it should be
true, just because the goroutine hasn't gotten to its select{} yet)
- Don't treat 0 time as a special case; if a clock is <= unix 0, that's
potentially a problem, but that would also cause cert validations for
all TLS connections to fail, so we assume computer clock is correct.
By removing the special case, we gain some simplicity.
The previous rate limiter design did not allow reservation cancellation.
This became problematic with lots of config reloads in Caddy for large
numbers of domain names. While the rate limiter had a backlog, a new
config would come in and add even more to the rate limiter, and even
more over time as background maintenance (renewals) kicked in. This
leaked goroutines and memory as a side-effect, and blocked the issuance
of certificates nigh indefinitely.
The new rate limiter does not make future reservations like the previous
one did. However, this requires us to run a single scheduler goroutine
when a rate limiter is created, which requires being cleaned up when the
rate limiter is no longer needed. As rate limits are global and should
live up to the life of the process, there is currently no actual cleanup
that takes place, but if it did happen, one would simply call Stop() on
the rate limiter to stop that goroutine.
With this new design, reservations are made only as the event actually
happens; implementing cancellation with the old design would have been
almost impossible to do correctly in a practical, elegant way. Although
the trade-off is an extra goroutine that needs cleaning up, this is
seldom (if ever?) needed in practice, and the benefit is that waiting
goroutines can be unblocked when their context is canceled. This allows
Caddy, for example, to reload configs often and cancel any goroutines
that were merely waiting on the rate limiter.
Now, all Obtain, Renew, and Revoke calls accept a context that can be
cancelled.
We also eliminate the acmeMu, a mutex that permitted only a single ACME
operation at a time by the process, which was our early, naive form of
rate limiting, which should no longer be necessary.
On-demand obtain and renew do not yet use cancelable contexts, because
what defines the context of a TLS handshake is still unclear. We might
end up using a simple context with a timeout that is the maximum length
of a TLS handshake in practice, say, 1 minute.
This is a breaking change, but critical for larger deployments with very
dynamic configurations.