fillDynamicHost is like fillHost, but stores the host in the separate dynamicHosts map rather than the reference-counted UsagePool. Dynamic hosts are not reference-counted; instead, they are retained as long as they are actively seen and are evicted by a background cleanup goroutine after dynamicHos
()
| 141 | // after dynamicHostIdleExpiry of inactivity. This preserves health state |
| 142 | // (e.g. passive fail counts) across sequential requests. |
| 143 | func (u *Upstream) fillDynamicHost() { |
| 144 | dynamicHostsMu.Lock() |
| 145 | entry, ok := dynamicHosts[u.String()] |
| 146 | if ok { |
| 147 | entry.lastSeen = time.Now() |
| 148 | dynamicHosts[u.String()] = entry |
| 149 | u.Host = entry.host |
| 150 | } else { |
| 151 | h := new(Host) |
| 152 | dynamicHosts[u.String()] = dynamicHostEntry{host: h, lastSeen: time.Now()} |
| 153 | u.Host = h |
| 154 | } |
| 155 | dynamicHostsMu.Unlock() |
| 156 | |
| 157 | // ensure the cleanup goroutine is running |
| 158 | dynamicHostsCleanerOnce.Do(func() { |
| 159 | go func() { |
| 160 | for { |
| 161 | time.Sleep(dynamicHostCleanupInterval) |
| 162 | dynamicHostsMu.Lock() |
| 163 | for addr, entry := range dynamicHosts { |
| 164 | if time.Since(entry.lastSeen) > dynamicHostIdleExpiry { |
| 165 | delete(dynamicHosts, addr) |
| 166 | } |
| 167 | } |
| 168 | dynamicHostsMu.Unlock() |
| 169 | } |
| 170 | }() |
| 171 | }) |
| 172 | } |
| 173 | |
| 174 | // Host is the basic, in-memory representation of the state of a remote host. |
| 175 | // Its fields are accessed atomically and Host values must not be copied. |