Compare commits

..

2 Commits
1.4.1 ... 1.4.2

Author SHA1 Message Date
Owen Schwartz
4f457db7b5 Merge pull request #97 from fosrl/dev
Add cache timeout of 2.5 seconds to record hp
2026-06-12 20:35:02 -07:00
Owen
6d548ea166 Add cache timeout of 2.5 seconds to record hp
Fixes #96
Closes #95
2026-06-12 20:32:43 -07:00

View File

@@ -149,6 +149,13 @@ type cachedEndpointState struct {
PublicKey string PublicKey string
} }
// cachedEndpointEntry wraps cachedEndpointState with a timestamp so the cache
// can be expired after a short TTL even when the endpoint fields are unchanged.
type cachedEndpointEntry struct {
state cachedEndpointState
cachedAt time.Time
}
// --- End Types --- // --- End Types ---
// bufferPool allows reusing buffers to reduce allocations. // bufferPool allows reusing buffers to reduce allocations.
@@ -406,6 +413,9 @@ func (s *UDPProxyServer) packetWorker() {
logger.Debug("Created endpoint from packet remoteAddr %s: IP=%s, Port=%d", packet.remoteAddr.String(), endpoint.IP, endpoint.Port) logger.Debug("Created endpoint from packet remoteAddr %s: IP=%s, Port=%d", packet.remoteAddr.String(), endpoint.IP, endpoint.Port)
// Check if anything meaningful changed before queuing an HTTP notification. // Check if anything meaningful changed before queuing an HTTP notification.
// The cache expires after 2.5 s so the server always receives a fresh
// timestamp within its 5-second staleness window.
const endpointCacheTTL = 2500 * time.Millisecond
cacheKey := endpoint.OlmID + ":" + endpoint.NewtID cacheKey := endpoint.OlmID + ":" + endpoint.NewtID
newState := cachedEndpointState{ newState := cachedEndpointState{
OlmID: endpoint.OlmID, OlmID: endpoint.OlmID,
@@ -415,16 +425,19 @@ func (s *UDPProxyServer) packetWorker() {
Port: endpoint.Port, Port: endpoint.Port,
PublicKey: endpoint.ClientPublicKey, PublicKey: endpoint.ClientPublicKey,
} }
if cached, ok := s.lastEndpointCache.Load(cacheKey); ok && cached.(cachedEndpointState) == newState { if cached, ok := s.lastEndpointCache.Load(cacheKey); ok {
// Endpoint unchanged - skip the HTTP call but still clear stale sessions. entry := cached.(cachedEndpointEntry)
logger.Debug("Endpoint unchanged for %s, skipping notification", cacheKey) if entry.state == newState && time.Since(entry.cachedAt) < endpointCacheTTL {
metrics.RecordHolePunchEvent(relayIfname, "deduplicated") // Endpoint unchanged and cache still fresh - skip the HTTP call.
s.clearSessionsForIP(endpoint.IP) logger.Debug("Endpoint unchanged for %s, skipping notification", cacheKey)
metrics.RecordHolePunchEvent(relayIfname, "success") metrics.RecordHolePunchEvent(relayIfname, "deduplicated")
bufferPool.Put(packet.data[:1500]) s.clearSessionsForIP(endpoint.IP)
continue metrics.RecordHolePunchEvent(relayIfname, "success")
bufferPool.Put(packet.data[:1500])
continue
}
} }
s.lastEndpointCache.Store(cacheKey, newState) s.lastEndpointCache.Store(cacheKey, cachedEndpointEntry{state: newState, cachedAt: time.Now()})
// Queue the notification asynchronously so the hot path is not blocked by HTTP. // Queue the notification asynchronously so the hot path is not blocked by HTTP.
select { select {