verifyWithTimeout runs the upstream verify in a background goroutine and races it against verifyStaleTimeout. If the verify completes within the timeout and the response is cacheable (NoError or NXDomain), the freshly cached entry is served to the client and served is true. Otherwise served is false
(ctx context.Context, state request.Request, w dns.ResponseWriter, cw *verifyStaleResponseWriter, r *dns.Msg, do, ad bool)
| 137 | // through to serve stale; the goroutine continues to run and any successful response |
| 138 | // will update the cache without writing to the (now-detached) client connection. |
| 139 | func (c *Cache) verifyWithTimeout(ctx context.Context, state request.Request, w dns.ResponseWriter, cw *verifyStaleResponseWriter, r *dns.Msg, do, ad bool) (served bool, code int, err error) { |
| 140 | type result struct { |
| 141 | code int |
| 142 | err error |
| 143 | } |
| 144 | done := make(chan result, 1) |
| 145 | go func() { |
| 146 | rc, re := c.doRefresh(ctx, state, cw) |
| 147 | done <- result{rc, re} |
| 148 | }() |
| 149 | timer := time.NewTimer(c.verifyStaleTimeout) |
| 150 | defer timer.Stop() |
| 151 | select { |
| 152 | case res := <-done: |
| 153 | if !cw.refreshed { |
| 154 | return false, 0, nil |
| 155 | } |
| 156 | fresh := c.exists(state.Name(), state.QType(), state.Do(), state.Req.CheckingDisabled) |
| 157 | if fresh == nil { |
| 158 | // Should not happen: refreshed=true means the upstream response was cacheable. |
| 159 | return true, res.code, res.err |
| 160 | } |
| 161 | now := c.now().UTC() |
| 162 | if c.keepttl { |
| 163 | now = fresh.stored |
| 164 | } |
| 165 | resp := fresh.toMsg(r, now, do, ad) |
| 166 | if err := w.WriteMsg(resp); err != nil { |
| 167 | return true, dns.RcodeServerFailure, err |
| 168 | } |
| 169 | return true, dns.RcodeSuccess, nil |
| 170 | case <-timer.C: |
| 171 | return false, 0, nil |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | func (c *Cache) shouldPrefetch(i *item, now time.Time) bool { |
| 176 | if c.prefetch <= 0 { |