Commit c726a89

Browse files
jasnelladuh95
authored andcommitted
quic: cache timestamp for address lru cache
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 11778a7 commit c726a89

8 files changed

Lines changed: 192 additions & 107 deletions

File tree

β€Žsrc/node_sockaddr-inl.hβ€Ž

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ typename T::Type* SocketAddressLRU<T>::Peek(
186186
}
187187

188188
template <typename T>
189-
void SocketAddressLRU<T>::CheckExpired() {
189+
void SocketAddressLRU<T>::CheckExpired(uint64_t now) {
190190
auto it = list_.rbegin();
191191
while (it != list_.rend()) {
192-
if (T::CheckExpired(it->first, it->second)) {
192+
if (T::CheckExpired(it->first, it->second, now)) {
193193
map_.erase(it->first);
194194
list_.pop_back();
195195
it = list_.rbegin();
@@ -211,21 +211,20 @@ void SocketAddressLRU<T>::MemoryInfo(MemoryTracker* tracker) const {
211211
// cache and adjust if necessary. Whether the item exists or not,
212212
// purge expired items.
213213
template <typename T>
214-
typename T::Type* SocketAddressLRU<T>::Upsert(
215-
const SocketAddress& address) {
216-
217-
auto on_exit = OnScopeLeave([&]() { CheckExpired(); });
214+
typename T::Type* SocketAddressLRU<T>::Upsert(const SocketAddress& address,
215+
uint64_t now) {
216+
auto on_exit = OnScopeLeave([&]() { CheckExpired(now); });
218217

219218
auto it = map_.find(address);
220219
if (it != std::end(map_)) {
221220
list_.splice(list_.begin(), list_, it->second);
222-
T::Touch(it->first, &it->second->second);
221+
T::Touch(it->first, &it->second->second, now);
223222
return &it->second->second;
224223
}
225224

226225
list_.push_front(Pair(address, { }));
227226
map_[address] = list_.begin();
228-
T::Touch(list_.begin()->first, &list_.begin()->second);
227+
T::Touch(list_.begin()->first, &list_.begin()->second, now);
229228

230229
// Drop the last item in the list if we are
231230
// over the size limit...

β€Žsrc/node_sockaddr.hβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ class SocketAddressLRU : public MemoryRetainer {
213213
// If the item already exists, returns a reference to
214214
// the existing item, adjusting items position in the
215215
// LRU. If the item does not exist, emplaces the item
216-
// and returns the new item.
217-
Type* Upsert(const SocketAddress& address);
216+
// and returns the new item. The caller provides a
217+
// timestamp to avoid redundant uv_hrtime() calls.
218+
Type* Upsert(const SocketAddress& address, uint64_t now);
218219

219220
// Returns a reference to the item if it exists, or
220221
// nullptr. The position in the LRU is not modified.
@@ -231,7 +232,7 @@ class SocketAddressLRU : public MemoryRetainer {
231232
using Pair = std::pair<SocketAddress, Type>;
232233
using Iterator = typename std::list<Pair>::iterator;
233234

234-
voidCheckExpired();
235+
voidCheckExpired(uint64_t now);
235236

236237
std::list<Pair> list_;
237238
SocketAddress::Map<Iterator> map_;

β€Žsrc/quic/defs.hβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,13 @@ struct TokenBucket final {
378378
// hasn't been initialized yet (last_ts == 0). Used for per-host
379379
// buckets in the address LRU where the rate/burst aren't known
380380
// at construction time.
381-
voidInitOnce(double r, double b);
381+
voidInitOnce(double r, double b, uint64_t now);
382382

383383
// Try to consume one token. Refills based on elapsed time, then
384384
// attempts to consume. Returns true if the request is allowed.
385-
boolconsume();
385+
// The caller provides the current timestamp to avoid redundant
386+
// uv_hrtime() calls in hot paths.
387+
boolconsume(uint64_t now);
386388
};
387389

388390
classDebugIndentScopefinal {

β€Žsrc/quic/endpoint.ccβ€Ž

Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,18 @@ STAT_STRUCT(Endpoint, ENDPOINT)
9292
TokenBucket::TokenBucket(double rate, double burst)
9393
: rate(rate), burst(burst), tokens(burst), last_ts(uv_hrtime()) {}
9494

95-
voidTokenBucket::InitOnce(double r, double b) {
95+
voidTokenBucket::InitOnce(double r, double b, uint64_t now) {
9696
if (last_ts == 0) {
9797
rate = r;
9898
burst = b;
9999
tokens = b;
100-
last_ts = uv_hrtime();
100+
last_ts = now;
101101
}
102102
}
103103

104104
// Try to consume one token. Refills based on elapsed time, then
105105
// attempts to consume. Returns true if the request is allowed.
106-
boolTokenBucket::consume() {
107-
uint64_t now = uv_hrtime();
106+
boolTokenBucket::consume(uint64_t now) {
108107
double elapsed = static_cast<double>(now - last_ts) / 1e9; // seconds
109108
last_ts = now;
110109
tokens = std::min(burst, tokens + elapsed * rate);
@@ -1025,9 +1024,9 @@ void Endpoint::SendBatch(Packet::Ptr* packets, size_t count) {
10251024
}
10261025
}
10271026

1028-
voidEndpoint::SendRetry(const PathDescriptor& options) {
1027+
voidEndpoint::SendRetry(const PathDescriptor& options, uint64_t now) {
10291028
Debug(this, "Sending retry on path %s", options);
1030-
if (!retry_bucket_.consume()) {
1029+
if (!retry_bucket_.consume(now)) {
10311030
Debug(this, "Retry rate limit exceeded (global)");
10321031
STAT_INCREMENT(Stats, retry_rate_limited);
10331032
return;
@@ -1041,9 +1040,10 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
10411040
}
10421041
}
10431042

1044-
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options) {
1043+
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options,
1044+
uint64_t now) {
10451045
Debug(this, "Sending version negotiation on path %s", options);
1046-
if (!version_negotiation_bucket_.consume()) {
1046+
if (!version_negotiation_bucket_.consume(now)) {
10471047
Debug(this, "Version negotiation rate limit exceeded (global)");
10481048
STAT_INCREMENT(Stats, version_negotiation_rate_limited);
10491049
return;
@@ -1057,7 +1057,8 @@ void Endpoint::SendVersionNegotiation(const PathDescriptor& options) {
10571057
}
10581058

10591059
boolEndpoint::SendStatelessReset(const PathDescriptor& options,
1060-
size_t source_len) {
1060+
size_t source_len,
1061+
uint64_t now) {
10611062
if (options_.disable_stateless_reset) [[unlikely]] {
10621063
returnfalse;
10631064
}
@@ -1066,7 +1067,7 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10661067
options,
10671068
source_len);
10681069

1069-
if (!stateless_reset_bucket_.consume()) {
1070+
if (!stateless_reset_bucket_.consume(now)) {
10701071
Debug(this, "Stateless reset rate limit exceeded (global)");
10711072
STAT_INCREMENT(Stats, stateless_reset_rate_limited);
10721073
returnfalse;
@@ -1086,12 +1087,13 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10861087
}
10871088

10881089
voidEndpoint::SendImmediateConnectionClose(const PathDescriptor& options,
1089-
QuicError reason) {
1090+
QuicError reason,
1091+
uint64_t now) {
10901092
Debug(this,
10911093
"Sending immediate connection close on path %s with reason %s",
10921094
options,
10931095
reason);
1094-
if (!immediate_close_bucket_.consume()) {
1096+
if (!immediate_close_bucket_.consume(now)) {
10951097
Debug(this, "Immediate connection close rate limit exceeded (global)");
10961098
STAT_INCREMENT(Stats, immediate_close_rate_limited);
10971099
return;
@@ -1313,6 +1315,8 @@ void Endpoint::CloseGracefully() {
13131315
voidEndpoint::Receive(constuint8_t* data,
13141316
size_t len,
13151317
const SocketAddress& remote_address) {
1318+
constuint64_t now = uv_hrtime();
1319+
13161320
constauto receive = [&](Session* session,
13171321
constuint8_t* pkt_data,
13181322
size_t pkt_len,
@@ -1327,7 +1331,12 @@ void Endpoint::Receive(const uint8_t* data,
13271331
// are generated. The deferred flush via BindingData's uv_check
13281332
// callback calls SendPendingData once per dirty session after all
13291333
// packets in the burst have been read.
1330-
if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) {
1334+
if (session->ReadPacket(pkt_data,
1335+
pkt_len,
1336+
local_address,
1337+
remote_address,
1338+
PacketInfo(),
1339+
now)) {
13311340
STAT_INCREMENT_N(Stats, bytes_received, pkt_len);
13321341
STAT_INCREMENT(Stats, packets_received);
13331342
}
@@ -1349,10 +1358,10 @@ void Endpoint::Receive(const uint8_t* data,
13491358

13501359
// Per-host session creation rate limit. The bucket is initialized
13511360
// on first access with the configured rate/burst from options.
1352-
auto info = addr_validation_lru_.Upsert(config.remote_address);
1353-
info->session_creation_bucket.InitOnce(options_.session_creation_rate,
1354-
options_.session_creation_burst);
1355-
if (!info->session_creation_bucket.consume()) {
1361+
auto info = addr_validation_lru_.Upsert(config.remote_address, now);
1362+
info->session_creation_bucket.InitOnce(
1363+
options_.session_creation_rate, options_.session_creation_burst, now);
1364+
if (!info->session_creation_bucket.consume(now)) {
13561365
Debug(this,
13571366
"Session creation rate limit exceeded for %s",
13581367
config.remote_address);
@@ -1451,7 +1460,8 @@ void Endpoint::Receive(const uint8_t* data,
14511460
if (state_->busy) STAT_INCREMENT(Stats, server_busy_count);
14521461
SendImmediateConnectionClose(
14531462
PathDescriptor{version, dcid, scid, local_address, remote_address},
1454-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1463+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1464+
now);
14551465
// The packet was successfully processed, even if we did refuse the
14561466
// connection.
14571467
STAT_INCREMENT(Stats, packets_received);
@@ -1525,7 +1535,8 @@ void Endpoint::Receive(const uint8_t* data,
15251535
Debug(this, "Retry token from %s is invalid.", remote_address);
15261536
SendImmediateConnectionClose(
15271537
PathDescriptor{version, scid, dcid, local_address, remote_address},
1528-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1538+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1539+
now);
15291540
STAT_INCREMENT(Stats, packets_received);
15301541
return;
15311542
}
@@ -1541,7 +1552,7 @@ void Endpoint::Receive(const uint8_t* data,
15411552
// Mark the address as validated since the retry round-trip proves
15421553
// reachability.
15431554
Debug(this, "Remote address %s is validated", remote_address);
1544-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1555+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
15451556
}
15461557

15471558
// Step 2: Address validation β€” decide whether to send a Retry or
@@ -1557,13 +1568,15 @@ void Endpoint::Receive(const uint8_t* data,
15571568
"Initial packet has no token. Sending retry to %s to start "
15581569
"validation",
15591570
remote_address);
1560-
SendRetry(PathDescriptor{
1561-
version,
1562-
dcid,
1563-
scid,
1564-
local_address,
1565-
remote_address,
1566-
});
1571+
SendRetry(
1572+
PathDescriptor{
1573+
version,
1574+
dcid,
1575+
scid,
1576+
local_address,
1577+
remote_address,
1578+
},
1579+
now);
15671580
STAT_INCREMENT(Stats, packets_received);
15681581
return;
15691582
}
@@ -1584,13 +1597,15 @@ void Endpoint::Receive(const uint8_t* data,
15841597
Debug(this,
15851598
"Regular token from %s is invalid.",
15861599
remote_address);
1587-
SendRetry(PathDescriptor{
1588-
version,
1589-
dcid,
1590-
scid,
1591-
local_address,
1592-
remote_address,
1593-
});
1600+
SendRetry(
1601+
PathDescriptor{
1602+
version,
1603+
dcid,
1604+
scid,
1605+
local_address,
1606+
remote_address,
1607+
},
1608+
now);
15941609
STAT_INCREMENT(Stats, packets_received);
15951610
return;
15961611
}
@@ -1602,20 +1617,22 @@ void Endpoint::Receive(const uint8_t* data,
16021617
Debug(this,
16031618
"Initial packet from %s has unknown token type",
16041619
remote_address);
1605-
SendRetry(PathDescriptor{
1606-
version,
1607-
dcid,
1608-
scid,
1609-
local_address,
1610-
remote_address,
1611-
});
1620+
SendRetry(
1621+
PathDescriptor{
1622+
version,
1623+
dcid,
1624+
scid,
1625+
local_address,
1626+
remote_address,
1627+
},
1628+
now);
16121629
STAT_INCREMENT(Stats, packets_received);
16131630
return;
16141631
}
16151632
}
16161633

16171634
Debug(this, "Remote address %s is validated", remote_address);
1618-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1635+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
16191636
} elseif (hd.tokenlen > 0) {
16201637
Debug(this,
16211638
"Ignoring initial packet from %s with unexpected token",
@@ -1627,13 +1644,15 @@ void Endpoint::Receive(const uint8_t* data,
16271644
if (options_.validate_address) {
16281645
Debug(
16291646
this, "Sending retry to %s due to 0RTT packet", remote_address);
1630-
SendRetry(PathDescriptor{
1631-
version,
1632-
dcid,
1633-
scid,
1634-
local_address,
1635-
remote_address,
1636-
});
1647+
SendRetry(
1648+
PathDescriptor{
1649+
version,
1650+
dcid,
1651+
scid,
1652+
local_address,
1653+
remote_address,
1654+
},
1655+
now);
16371656
STAT_INCREMENT(Stats, packets_received);
16381657
return;
16391658
}
@@ -1742,8 +1761,12 @@ void Endpoint::Receive(const uint8_t* data,
17421761
pversion_cid.version);
17431762
CIDdcid(pversion_cid.dcid, pversion_cid.dcidlen);
17441763
CIDscid(pversion_cid.scid, pversion_cid.scidlen);
1745-
SendVersionNegotiation(PathDescriptor{
1746-
pversion_cid.version, dcid, scid, local_address(), remote_address});
1764+
SendVersionNegotiation(PathDescriptor{pversion_cid.version,
1765+
dcid,
1766+
scid,
1767+
local_address(),
1768+
remote_address},
1769+
now);
17471770
STAT_INCREMENT(Stats, packets_received);
17481771
return;
17491772
}
@@ -1822,7 +1845,8 @@ void Endpoint::Receive(const uint8_t* data,
18221845
SendStatelessReset(
18231846
PathDescriptor{
18241847
pversion_cid.version, dcid, scid, addr, remote_address},
1825-
len);
1848+
len,
1849+
now);
18261850
return;
18271851
}
18281852

@@ -1884,13 +1908,14 @@ void Endpoint::MemoryInfo(MemoryTracker* tracker) const {
18841908
// Endpoint::SocketAddressInfoTraits
18851909

18861910
boolEndpoint::SocketAddressInfoTraits::CheckExpired(
1887-
const SocketAddress& address, const Type& type) {
1888-
return (uv_hrtime() - type.timestamp) > kSocketAddressInfoTimeout;
1911+
const SocketAddress& address, const Type& type, uint64_t now) {
1912+
return (now - type.timestamp) > kSocketAddressInfoTimeout;
18891913
}
18901914

18911915
voidEndpoint::SocketAddressInfoTraits::Touch(const SocketAddress& address,
1892-
Type* type) {
1893-
type->timestamp = uv_hrtime();
1916+
Type* type,
1917+
uint64_t now) {
1918+
type->timestamp = now;
18941919
}
18951920

18961921
// ======================================================================================

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Commit c726a89

Browse files
jasnelladuh95
authored andcommitted
quic: cache timestamp for address lru cache
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 11778a7 commit c726a89

8 files changed

Lines changed: 192 additions & 107 deletions

File tree

β€Žsrc/node_sockaddr-inl.hβ€Ž

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ typename T::Type* SocketAddressLRU<T>::Peek(
186186
}
187187

188188
template <typename T>
189-
void SocketAddressLRU<T>::CheckExpired() {
189+
void SocketAddressLRU<T>::CheckExpired(uint64_t now) {
190190
auto it = list_.rbegin();
191191
while (it != list_.rend()) {
192-
if (T::CheckExpired(it->first, it->second)) {
192+
if (T::CheckExpired(it->first, it->second, now)) {
193193
map_.erase(it->first);
194194
list_.pop_back();
195195
it = list_.rbegin();
@@ -211,21 +211,20 @@ void SocketAddressLRU<T>::MemoryInfo(MemoryTracker* tracker) const {
211211
// cache and adjust if necessary. Whether the item exists or not,
212212
// purge expired items.
213213
template <typename T>
214-
typename T::Type* SocketAddressLRU<T>::Upsert(
215-
const SocketAddress& address) {
216-
217-
auto on_exit = OnScopeLeave([&]() { CheckExpired(); });
214+
typename T::Type* SocketAddressLRU<T>::Upsert(const SocketAddress& address,
215+
uint64_t now) {
216+
auto on_exit = OnScopeLeave([&]() { CheckExpired(now); });
218217

219218
auto it = map_.find(address);
220219
if (it != std::end(map_)) {
221220
list_.splice(list_.begin(), list_, it->second);
222-
T::Touch(it->first, &it->second->second);
221+
T::Touch(it->first, &it->second->second, now);
223222
return &it->second->second;
224223
}
225224

226225
list_.push_front(Pair(address, { }));
227226
map_[address] = list_.begin();
228-
T::Touch(list_.begin()->first, &list_.begin()->second);
227+
T::Touch(list_.begin()->first, &list_.begin()->second, now);
229228

230229
// Drop the last item in the list if we are
231230
// over the size limit...

β€Žsrc/node_sockaddr.hβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ class SocketAddressLRU : public MemoryRetainer {
213213
// If the item already exists, returns a reference to
214214
// the existing item, adjusting items position in the
215215
// LRU. If the item does not exist, emplaces the item
216-
// and returns the new item.
217-
Type* Upsert(const SocketAddress& address);
216+
// and returns the new item. The caller provides a
217+
// timestamp to avoid redundant uv_hrtime() calls.
218+
Type* Upsert(const SocketAddress& address, uint64_t now);
218219

219220
// Returns a reference to the item if it exists, or
220221
// nullptr. The position in the LRU is not modified.
@@ -231,7 +232,7 @@ class SocketAddressLRU : public MemoryRetainer {
231232
using Pair = std::pair<SocketAddress, Type>;
232233
using Iterator = typename std::list<Pair>::iterator;
233234

234-
voidCheckExpired();
235+
voidCheckExpired(uint64_t now);
235236

236237
std::list<Pair> list_;
237238
SocketAddress::Map<Iterator> map_;

β€Žsrc/quic/defs.hβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,13 @@ struct TokenBucket final {
378378
// hasn't been initialized yet (last_ts == 0). Used for per-host
379379
// buckets in the address LRU where the rate/burst aren't known
380380
// at construction time.
381-
voidInitOnce(double r, double b);
381+
voidInitOnce(double r, double b, uint64_t now);
382382

383383
// Try to consume one token. Refills based on elapsed time, then
384384
// attempts to consume. Returns true if the request is allowed.
385-
boolconsume();
385+
// The caller provides the current timestamp to avoid redundant
386+
// uv_hrtime() calls in hot paths.
387+
boolconsume(uint64_t now);
386388
};
387389

388390
classDebugIndentScopefinal {

β€Žsrc/quic/endpoint.ccβ€Ž

Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,18 @@ STAT_STRUCT(Endpoint, ENDPOINT)
9292
TokenBucket::TokenBucket(double rate, double burst)
9393
: rate(rate), burst(burst), tokens(burst), last_ts(uv_hrtime()) {}
9494

95-
voidTokenBucket::InitOnce(double r, double b) {
95+
voidTokenBucket::InitOnce(double r, double b, uint64_t now) {
9696
if (last_ts == 0) {
9797
rate = r;
9898
burst = b;
9999
tokens = b;
100-
last_ts = uv_hrtime();
100+
last_ts = now;
101101
}
102102
}
103103

104104
// Try to consume one token. Refills based on elapsed time, then
105105
// attempts to consume. Returns true if the request is allowed.
106-
boolTokenBucket::consume() {
107-
uint64_t now = uv_hrtime();
106+
boolTokenBucket::consume(uint64_t now) {
108107
double elapsed = static_cast<double>(now - last_ts) / 1e9; // seconds
109108
last_ts = now;
110109
tokens = std::min(burst, tokens + elapsed * rate);
@@ -1025,9 +1024,9 @@ void Endpoint::SendBatch(Packet::Ptr* packets, size_t count) {
10251024
}
10261025
}
10271026

1028-
voidEndpoint::SendRetry(const PathDescriptor& options) {
1027+
voidEndpoint::SendRetry(const PathDescriptor& options, uint64_t now) {
10291028
Debug(this, "Sending retry on path %s", options);
1030-
if (!retry_bucket_.consume()) {
1029+
if (!retry_bucket_.consume(now)) {
10311030
Debug(this, "Retry rate limit exceeded (global)");
10321031
STAT_INCREMENT(Stats, retry_rate_limited);
10331032
return;
@@ -1041,9 +1040,10 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
10411040
}
10421041
}
10431042

1044-
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options) {
1043+
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options,
1044+
uint64_t now) {
10451045
Debug(this, "Sending version negotiation on path %s", options);
1046-
if (!version_negotiation_bucket_.consume()) {
1046+
if (!version_negotiation_bucket_.consume(now)) {
10471047
Debug(this, "Version negotiation rate limit exceeded (global)");
10481048
STAT_INCREMENT(Stats, version_negotiation_rate_limited);
10491049
return;
@@ -1057,7 +1057,8 @@ void Endpoint::SendVersionNegotiation(const PathDescriptor& options) {
10571057
}
10581058

10591059
boolEndpoint::SendStatelessReset(const PathDescriptor& options,
1060-
size_t source_len) {
1060+
size_t source_len,
1061+
uint64_t now) {
10611062
if (options_.disable_stateless_reset) [[unlikely]] {
10621063
returnfalse;
10631064
}
@@ -1066,7 +1067,7 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10661067
options,
10671068
source_len);
10681069

1069-
if (!stateless_reset_bucket_.consume()) {
1070+
if (!stateless_reset_bucket_.consume(now)) {
10701071
Debug(this, "Stateless reset rate limit exceeded (global)");
10711072
STAT_INCREMENT(Stats, stateless_reset_rate_limited);
10721073
returnfalse;
@@ -1086,12 +1087,13 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10861087
}
10871088

10881089
voidEndpoint::SendImmediateConnectionClose(const PathDescriptor& options,
1089-
QuicError reason) {
1090+
QuicError reason,
1091+
uint64_t now) {
10901092
Debug(this,
10911093
"Sending immediate connection close on path %s with reason %s",
10921094
options,
10931095
reason);
1094-
if (!immediate_close_bucket_.consume()) {
1096+
if (!immediate_close_bucket_.consume(now)) {
10951097
Debug(this, "Immediate connection close rate limit exceeded (global)");
10961098
STAT_INCREMENT(Stats, immediate_close_rate_limited);
10971099
return;
@@ -1313,6 +1315,8 @@ void Endpoint::CloseGracefully() {
13131315
voidEndpoint::Receive(constuint8_t* data,
13141316
size_t len,
13151317
const SocketAddress& remote_address) {
1318+
constuint64_t now = uv_hrtime();
1319+
13161320
constauto receive = [&](Session* session,
13171321
constuint8_t* pkt_data,
13181322
size_t pkt_len,
@@ -1327,7 +1331,12 @@ void Endpoint::Receive(const uint8_t* data,
13271331
// are generated. The deferred flush via BindingData's uv_check
13281332
// callback calls SendPendingData once per dirty session after all
13291333
// packets in the burst have been read.
1330-
if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) {
1334+
if (session->ReadPacket(pkt_data,
1335+
pkt_len,
1336+
local_address,
1337+
remote_address,
1338+
PacketInfo(),
1339+
now)) {
13311340
STAT_INCREMENT_N(Stats, bytes_received, pkt_len);
13321341
STAT_INCREMENT(Stats, packets_received);
13331342
}
@@ -1349,10 +1358,10 @@ void Endpoint::Receive(const uint8_t* data,
13491358

13501359
// Per-host session creation rate limit. The bucket is initialized
13511360
// on first access with the configured rate/burst from options.
1352-
auto info = addr_validation_lru_.Upsert(config.remote_address);
1353-
info->session_creation_bucket.InitOnce(options_.session_creation_rate,
1354-
options_.session_creation_burst);
1355-
if (!info->session_creation_bucket.consume()) {
1361+
auto info = addr_validation_lru_.Upsert(config.remote_address, now);
1362+
info->session_creation_bucket.InitOnce(
1363+
options_.session_creation_rate, options_.session_creation_burst, now);
1364+
if (!info->session_creation_bucket.consume(now)) {
13561365
Debug(this,
13571366
"Session creation rate limit exceeded for %s",
13581367
config.remote_address);
@@ -1451,7 +1460,8 @@ void Endpoint::Receive(const uint8_t* data,
14511460
if (state_->busy) STAT_INCREMENT(Stats, server_busy_count);
14521461
SendImmediateConnectionClose(
14531462
PathDescriptor{version, dcid, scid, local_address, remote_address},
1454-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1463+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1464+
now);
14551465
// The packet was successfully processed, even if we did refuse the
14561466
// connection.
14571467
STAT_INCREMENT(Stats, packets_received);
@@ -1525,7 +1535,8 @@ void Endpoint::Receive(const uint8_t* data,
15251535
Debug(this, "Retry token from %s is invalid.", remote_address);
15261536
SendImmediateConnectionClose(
15271537
PathDescriptor{version, scid, dcid, local_address, remote_address},
1528-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1538+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1539+
now);
15291540
STAT_INCREMENT(Stats, packets_received);
15301541
return;
15311542
}
@@ -1541,7 +1552,7 @@ void Endpoint::Receive(const uint8_t* data,
15411552
// Mark the address as validated since the retry round-trip proves
15421553
// reachability.
15431554
Debug(this, "Remote address %s is validated", remote_address);
1544-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1555+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
15451556
}
15461557

15471558
// Step 2: Address validation β€” decide whether to send a Retry or
@@ -1557,13 +1568,15 @@ void Endpoint::Receive(const uint8_t* data,
15571568
"Initial packet has no token. Sending retry to %s to start "
15581569
"validation",
15591570
remote_address);
1560-
SendRetry(PathDescriptor{
1561-
version,
1562-
dcid,
1563-
scid,
1564-
local_address,
1565-
remote_address,
1566-
});
1571+
SendRetry(
1572+
PathDescriptor{
1573+
version,
1574+
dcid,
1575+
scid,
1576+
local_address,
1577+
remote_address,
1578+
},
1579+
now);
15671580
STAT_INCREMENT(Stats, packets_received);
15681581
return;
15691582
}
@@ -1584,13 +1597,15 @@ void Endpoint::Receive(const uint8_t* data,
15841597
Debug(this,
15851598
"Regular token from %s is invalid.",
15861599
remote_address);
1587-
SendRetry(PathDescriptor{
1588-
version,
1589-
dcid,
1590-
scid,
1591-
local_address,
1592-
remote_address,
1593-
});
1600+
SendRetry(
1601+
PathDescriptor{
1602+
version,
1603+
dcid,
1604+
scid,
1605+
local_address,
1606+
remote_address,
1607+
},
1608+
now);
15941609
STAT_INCREMENT(Stats, packets_received);
15951610
return;
15961611
}
@@ -1602,20 +1617,22 @@ void Endpoint::Receive(const uint8_t* data,
16021617
Debug(this,
16031618
"Initial packet from %s has unknown token type",
16041619
remote_address);
1605-
SendRetry(PathDescriptor{
1606-
version,
1607-
dcid,
1608-
scid,
1609-
local_address,
1610-
remote_address,
1611-
});
1620+
SendRetry(
1621+
PathDescriptor{
1622+
version,
1623+
dcid,
1624+
scid,
1625+
local_address,
1626+
remote_address,
1627+
},
1628+
now);
16121629
STAT_INCREMENT(Stats, packets_received);
16131630
return;
16141631
}
16151632
}
16161633

16171634
Debug(this, "Remote address %s is validated", remote_address);
1618-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1635+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
16191636
} elseif (hd.tokenlen > 0) {
16201637
Debug(this,
16211638
"Ignoring initial packet from %s with unexpected token",
@@ -1627,13 +1644,15 @@ void Endpoint::Receive(const uint8_t* data,
16271644
if (options_.validate_address) {
16281645
Debug(
16291646
this, "Sending retry to %s due to 0RTT packet", remote_address);
1630-
SendRetry(PathDescriptor{
1631-
version,
1632-
dcid,
1633-
scid,
1634-
local_address,
1635-
remote_address,
1636-
});
1647+
SendRetry(
1648+
PathDescriptor{
1649+
version,
1650+
dcid,
1651+
scid,
1652+
local_address,
1653+
remote_address,
1654+
},
1655+
now);
16371656
STAT_INCREMENT(Stats, packets_received);
16381657
return;
16391658
}
@@ -1742,8 +1761,12 @@ void Endpoint::Receive(const uint8_t* data,
17421761
pversion_cid.version);
17431762
CIDdcid(pversion_cid.dcid, pversion_cid.dcidlen);
17441763
CIDscid(pversion_cid.scid, pversion_cid.scidlen);
1745-
SendVersionNegotiation(PathDescriptor{
1746-
pversion_cid.version, dcid, scid, local_address(), remote_address});
1764+
SendVersionNegotiation(PathDescriptor{pversion_cid.version,
1765+
dcid,
1766+
scid,
1767+
local_address(),
1768+
remote_address},
1769+
now);
17471770
STAT_INCREMENT(Stats, packets_received);
17481771
return;
17491772
}
@@ -1822,7 +1845,8 @@ void Endpoint::Receive(const uint8_t* data,
18221845
SendStatelessReset(
18231846
PathDescriptor{
18241847
pversion_cid.version, dcid, scid, addr, remote_address},
1825-
len);
1848+
len,
1849+
now);
18261850
return;
18271851
}
18281852

@@ -1884,13 +1908,14 @@ void Endpoint::MemoryInfo(MemoryTracker* tracker) const {
18841908
// Endpoint::SocketAddressInfoTraits
18851909

18861910
boolEndpoint::SocketAddressInfoTraits::CheckExpired(
1887-
const SocketAddress& address, const Type& type) {
1888-
return (uv_hrtime() - type.timestamp) > kSocketAddressInfoTimeout;
1911+
const SocketAddress& address, const Type& type, uint64_t now) {
1912+
return (now - type.timestamp) > kSocketAddressInfoTimeout;
18891913
}
18901914

18911915
voidEndpoint::SocketAddressInfoTraits::Touch(const SocketAddress& address,
1892-
Type* type) {
1893-
type->timestamp = uv_hrtime();
1916+
Type* type,
1917+
uint64_t now) {
1918+
type->timestamp = now;
18941919
}
18951920

18961921
// ======================================================================================

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit c726a89

Browse files
jasnelladuh95
authored andcommitted
quic: cache timestamp for address lru cache
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 11778a7 commit c726a89

8 files changed

Lines changed: 192 additions & 107 deletions

File tree

β€Žsrc/node_sockaddr-inl.hβ€Ž

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ typename T::Type* SocketAddressLRU<T>::Peek(
186186
}
187187

188188
template <typename T>
189-
void SocketAddressLRU<T>::CheckExpired() {
189+
void SocketAddressLRU<T>::CheckExpired(uint64_t now) {
190190
auto it = list_.rbegin();
191191
while (it != list_.rend()) {
192-
if (T::CheckExpired(it->first, it->second)) {
192+
if (T::CheckExpired(it->first, it->second, now)) {
193193
map_.erase(it->first);
194194
list_.pop_back();
195195
it = list_.rbegin();
@@ -211,21 +211,20 @@ void SocketAddressLRU<T>::MemoryInfo(MemoryTracker* tracker) const {
211211
// cache and adjust if necessary. Whether the item exists or not,
212212
// purge expired items.
213213
template <typename T>
214-
typename T::Type* SocketAddressLRU<T>::Upsert(
215-
const SocketAddress& address) {
216-
217-
auto on_exit = OnScopeLeave([&]() { CheckExpired(); });
214+
typename T::Type* SocketAddressLRU<T>::Upsert(const SocketAddress& address,
215+
uint64_t now) {
216+
auto on_exit = OnScopeLeave([&]() { CheckExpired(now); });
218217

219218
auto it = map_.find(address);
220219
if (it != std::end(map_)) {
221220
list_.splice(list_.begin(), list_, it->second);
222-
T::Touch(it->first, &it->second->second);
221+
T::Touch(it->first, &it->second->second, now);
223222
return &it->second->second;
224223
}
225224

226225
list_.push_front(Pair(address, { }));
227226
map_[address] = list_.begin();
228-
T::Touch(list_.begin()->first, &list_.begin()->second);
227+
T::Touch(list_.begin()->first, &list_.begin()->second, now);
229228

230229
// Drop the last item in the list if we are
231230
// over the size limit...

β€Žsrc/node_sockaddr.hβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ class SocketAddressLRU : public MemoryRetainer {
213213
// If the item already exists, returns a reference to
214214
// the existing item, adjusting items position in the
215215
// LRU. If the item does not exist, emplaces the item
216-
// and returns the new item.
217-
Type* Upsert(const SocketAddress& address);
216+
// and returns the new item. The caller provides a
217+
// timestamp to avoid redundant uv_hrtime() calls.
218+
Type* Upsert(const SocketAddress& address, uint64_t now);
218219

219220
// Returns a reference to the item if it exists, or
220221
// nullptr. The position in the LRU is not modified.
@@ -231,7 +232,7 @@ class SocketAddressLRU : public MemoryRetainer {
231232
using Pair = std::pair<SocketAddress, Type>;
232233
using Iterator = typename std::list<Pair>::iterator;
233234

234-
voidCheckExpired();
235+
voidCheckExpired(uint64_t now);
235236

236237
std::list<Pair> list_;
237238
SocketAddress::Map<Iterator> map_;

β€Žsrc/quic/defs.hβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,13 @@ struct TokenBucket final {
378378
// hasn't been initialized yet (last_ts == 0). Used for per-host
379379
// buckets in the address LRU where the rate/burst aren't known
380380
// at construction time.
381-
voidInitOnce(double r, double b);
381+
voidInitOnce(double r, double b, uint64_t now);
382382

383383
// Try to consume one token. Refills based on elapsed time, then
384384
// attempts to consume. Returns true if the request is allowed.
385-
boolconsume();
385+
// The caller provides the current timestamp to avoid redundant
386+
// uv_hrtime() calls in hot paths.
387+
boolconsume(uint64_t now);
386388
};
387389

388390
classDebugIndentScopefinal {

β€Žsrc/quic/endpoint.ccβ€Ž

Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,18 @@ STAT_STRUCT(Endpoint, ENDPOINT)
9292
TokenBucket::TokenBucket(double rate, double burst)
9393
: rate(rate), burst(burst), tokens(burst), last_ts(uv_hrtime()) {}
9494

95-
voidTokenBucket::InitOnce(double r, double b) {
95+
voidTokenBucket::InitOnce(double r, double b, uint64_t now) {
9696
if (last_ts == 0) {
9797
rate = r;
9898
burst = b;
9999
tokens = b;
100-
last_ts = uv_hrtime();
100+
last_ts = now;
101101
}
102102
}
103103

104104
// Try to consume one token. Refills based on elapsed time, then
105105
// attempts to consume. Returns true if the request is allowed.
106-
boolTokenBucket::consume() {
107-
uint64_t now = uv_hrtime();
106+
boolTokenBucket::consume(uint64_t now) {
108107
double elapsed = static_cast<double>(now - last_ts) / 1e9; // seconds
109108
last_ts = now;
110109
tokens = std::min(burst, tokens + elapsed * rate);
@@ -1025,9 +1024,9 @@ void Endpoint::SendBatch(Packet::Ptr* packets, size_t count) {
10251024
}
10261025
}
10271026

1028-
voidEndpoint::SendRetry(const PathDescriptor& options) {
1027+
voidEndpoint::SendRetry(const PathDescriptor& options, uint64_t now) {
10291028
Debug(this, "Sending retry on path %s", options);
1030-
if (!retry_bucket_.consume()) {
1029+
if (!retry_bucket_.consume(now)) {
10311030
Debug(this, "Retry rate limit exceeded (global)");
10321031
STAT_INCREMENT(Stats, retry_rate_limited);
10331032
return;
@@ -1041,9 +1040,10 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
10411040
}
10421041
}
10431042

1044-
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options) {
1043+
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options,
1044+
uint64_t now) {
10451045
Debug(this, "Sending version negotiation on path %s", options);
1046-
if (!version_negotiation_bucket_.consume()) {
1046+
if (!version_negotiation_bucket_.consume(now)) {
10471047
Debug(this, "Version negotiation rate limit exceeded (global)");
10481048
STAT_INCREMENT(Stats, version_negotiation_rate_limited);
10491049
return;
@@ -1057,7 +1057,8 @@ void Endpoint::SendVersionNegotiation(const PathDescriptor& options) {
10571057
}
10581058

10591059
boolEndpoint::SendStatelessReset(const PathDescriptor& options,
1060-
size_t source_len) {
1060+
size_t source_len,
1061+
uint64_t now) {
10611062
if (options_.disable_stateless_reset) [[unlikely]] {
10621063
returnfalse;
10631064
}
@@ -1066,7 +1067,7 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10661067
options,
10671068
source_len);
10681069

1069-
if (!stateless_reset_bucket_.consume()) {
1070+
if (!stateless_reset_bucket_.consume(now)) {
10701071
Debug(this, "Stateless reset rate limit exceeded (global)");
10711072
STAT_INCREMENT(Stats, stateless_reset_rate_limited);
10721073
returnfalse;
@@ -1086,12 +1087,13 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10861087
}
10871088

10881089
voidEndpoint::SendImmediateConnectionClose(const PathDescriptor& options,
1089-
QuicError reason) {
1090+
QuicError reason,
1091+
uint64_t now) {
10901092
Debug(this,
10911093
"Sending immediate connection close on path %s with reason %s",
10921094
options,
10931095
reason);
1094-
if (!immediate_close_bucket_.consume()) {
1096+
if (!immediate_close_bucket_.consume(now)) {
10951097
Debug(this, "Immediate connection close rate limit exceeded (global)");
10961098
STAT_INCREMENT(Stats, immediate_close_rate_limited);
10971099
return;
@@ -1313,6 +1315,8 @@ void Endpoint::CloseGracefully() {
13131315
voidEndpoint::Receive(constuint8_t* data,
13141316
size_t len,
13151317
const SocketAddress& remote_address) {
1318+
constuint64_t now = uv_hrtime();
1319+
13161320
constauto receive = [&](Session* session,
13171321
constuint8_t* pkt_data,
13181322
size_t pkt_len,
@@ -1327,7 +1331,12 @@ void Endpoint::Receive(const uint8_t* data,
13271331
// are generated. The deferred flush via BindingData's uv_check
13281332
// callback calls SendPendingData once per dirty session after all
13291333
// packets in the burst have been read.
1330-
if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) {
1334+
if (session->ReadPacket(pkt_data,
1335+
pkt_len,
1336+
local_address,
1337+
remote_address,
1338+
PacketInfo(),
1339+
now)) {
13311340
STAT_INCREMENT_N(Stats, bytes_received, pkt_len);
13321341
STAT_INCREMENT(Stats, packets_received);
13331342
}
@@ -1349,10 +1358,10 @@ void Endpoint::Receive(const uint8_t* data,
13491358

13501359
// Per-host session creation rate limit. The bucket is initialized
13511360
// on first access with the configured rate/burst from options.
1352-
auto info = addr_validation_lru_.Upsert(config.remote_address);
1353-
info->session_creation_bucket.InitOnce(options_.session_creation_rate,
1354-
options_.session_creation_burst);
1355-
if (!info->session_creation_bucket.consume()) {
1361+
auto info = addr_validation_lru_.Upsert(config.remote_address, now);
1362+
info->session_creation_bucket.InitOnce(
1363+
options_.session_creation_rate, options_.session_creation_burst, now);
1364+
if (!info->session_creation_bucket.consume(now)) {
13561365
Debug(this,
13571366
"Session creation rate limit exceeded for %s",
13581367
config.remote_address);
@@ -1451,7 +1460,8 @@ void Endpoint::Receive(const uint8_t* data,
14511460
if (state_->busy) STAT_INCREMENT(Stats, server_busy_count);
14521461
SendImmediateConnectionClose(
14531462
PathDescriptor{version, dcid, scid, local_address, remote_address},
1454-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1463+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1464+
now);
14551465
// The packet was successfully processed, even if we did refuse the
14561466
// connection.
14571467
STAT_INCREMENT(Stats, packets_received);
@@ -1525,7 +1535,8 @@ void Endpoint::Receive(const uint8_t* data,
15251535
Debug(this, "Retry token from %s is invalid.", remote_address);
15261536
SendImmediateConnectionClose(
15271537
PathDescriptor{version, scid, dcid, local_address, remote_address},
1528-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1538+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1539+
now);
15291540
STAT_INCREMENT(Stats, packets_received);
15301541
return;
15311542
}
@@ -1541,7 +1552,7 @@ void Endpoint::Receive(const uint8_t* data,
15411552
// Mark the address as validated since the retry round-trip proves
15421553
// reachability.
15431554
Debug(this, "Remote address %s is validated", remote_address);
1544-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1555+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
15451556
}
15461557

15471558
// Step 2: Address validation β€” decide whether to send a Retry or
@@ -1557,13 +1568,15 @@ void Endpoint::Receive(const uint8_t* data,
15571568
"Initial packet has no token. Sending retry to %s to start "
15581569
"validation",
15591570
remote_address);
1560-
SendRetry(PathDescriptor{
1561-
version,
1562-
dcid,
1563-
scid,
1564-
local_address,
1565-
remote_address,
1566-
});
1571+
SendRetry(
1572+
PathDescriptor{
1573+
version,
1574+
dcid,
1575+
scid,
1576+
local_address,
1577+
remote_address,
1578+
},
1579+
now);
15671580
STAT_INCREMENT(Stats, packets_received);
15681581
return;
15691582
}
@@ -1584,13 +1597,15 @@ void Endpoint::Receive(const uint8_t* data,
15841597
Debug(this,
15851598
"Regular token from %s is invalid.",
15861599
remote_address);
1587-
SendRetry(PathDescriptor{
1588-
version,
1589-
dcid,
1590-
scid,
1591-
local_address,
1592-
remote_address,
1593-
});
1600+
SendRetry(
1601+
PathDescriptor{
1602+
version,
1603+
dcid,
1604+
scid,
1605+
local_address,
1606+
remote_address,
1607+
},
1608+
now);
15941609
STAT_INCREMENT(Stats, packets_received);
15951610
return;
15961611
}
@@ -1602,20 +1617,22 @@ void Endpoint::Receive(const uint8_t* data,
16021617
Debug(this,
16031618
"Initial packet from %s has unknown token type",
16041619
remote_address);
1605-
SendRetry(PathDescriptor{
1606-
version,
1607-
dcid,
1608-
scid,
1609-
local_address,
1610-
remote_address,
1611-
});
1620+
SendRetry(
1621+
PathDescriptor{
1622+
version,
1623+
dcid,
1624+
scid,
1625+
local_address,
1626+
remote_address,
1627+
},
1628+
now);
16121629
STAT_INCREMENT(Stats, packets_received);
16131630
return;
16141631
}
16151632
}
16161633

16171634
Debug(this, "Remote address %s is validated", remote_address);
1618-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1635+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
16191636
} elseif (hd.tokenlen > 0) {
16201637
Debug(this,
16211638
"Ignoring initial packet from %s with unexpected token",
@@ -1627,13 +1644,15 @@ void Endpoint::Receive(const uint8_t* data,
16271644
if (options_.validate_address) {
16281645
Debug(
16291646
this, "Sending retry to %s due to 0RTT packet", remote_address);
1630-
SendRetry(PathDescriptor{
1631-
version,
1632-
dcid,
1633-
scid,
1634-
local_address,
1635-
remote_address,
1636-
});
1647+
SendRetry(
1648+
PathDescriptor{
1649+
version,
1650+
dcid,
1651+
scid,
1652+
local_address,
1653+
remote_address,
1654+
},
1655+
now);
16371656
STAT_INCREMENT(Stats, packets_received);
16381657
return;
16391658
}
@@ -1742,8 +1761,12 @@ void Endpoint::Receive(const uint8_t* data,
17421761
pversion_cid.version);
17431762
CIDdcid(pversion_cid.dcid, pversion_cid.dcidlen);
17441763
CIDscid(pversion_cid.scid, pversion_cid.scidlen);
1745-
SendVersionNegotiation(PathDescriptor{
1746-
pversion_cid.version, dcid, scid, local_address(), remote_address});
1764+
SendVersionNegotiation(PathDescriptor{pversion_cid.version,
1765+
dcid,
1766+
scid,
1767+
local_address(),
1768+
remote_address},
1769+
now);
17471770
STAT_INCREMENT(Stats, packets_received);
17481771
return;
17491772
}
@@ -1822,7 +1845,8 @@ void Endpoint::Receive(const uint8_t* data,
18221845
SendStatelessReset(
18231846
PathDescriptor{
18241847
pversion_cid.version, dcid, scid, addr, remote_address},
1825-
len);
1848+
len,
1849+
now);
18261850
return;
18271851
}
18281852

@@ -1884,13 +1908,14 @@ void Endpoint::MemoryInfo(MemoryTracker* tracker) const {
18841908
// Endpoint::SocketAddressInfoTraits
18851909

18861910
boolEndpoint::SocketAddressInfoTraits::CheckExpired(
1887-
const SocketAddress& address, const Type& type) {
1888-
return (uv_hrtime() - type.timestamp) > kSocketAddressInfoTimeout;
1911+
const SocketAddress& address, const Type& type, uint64_t now) {
1912+
return (now - type.timestamp) > kSocketAddressInfoTimeout;
18891913
}
18901914

18911915
voidEndpoint::SocketAddressInfoTraits::Touch(const SocketAddress& address,
1892-
Type* type) {
1893-
type->timestamp = uv_hrtime();
1916+
Type* type,
1917+
uint64_t now) {
1918+
type->timestamp = now;
18941919
}
18951920

18961921
// ======================================================================================

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit c726a89

Browse files
jasnelladuh95
authored andcommitted
quic: cache timestamp for address lru cache
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 11778a7 commit c726a89

8 files changed

Lines changed: 192 additions & 107 deletions

File tree

β€Žsrc/node_sockaddr-inl.hβ€Ž

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ typename T::Type* SocketAddressLRU<T>::Peek(
186186
}
187187

188188
template <typename T>
189-
void SocketAddressLRU<T>::CheckExpired() {
189+
void SocketAddressLRU<T>::CheckExpired(uint64_t now) {
190190
auto it = list_.rbegin();
191191
while (it != list_.rend()) {
192-
if (T::CheckExpired(it->first, it->second)) {
192+
if (T::CheckExpired(it->first, it->second, now)) {
193193
map_.erase(it->first);
194194
list_.pop_back();
195195
it = list_.rbegin();
@@ -211,21 +211,20 @@ void SocketAddressLRU<T>::MemoryInfo(MemoryTracker* tracker) const {
211211
// cache and adjust if necessary. Whether the item exists or not,
212212
// purge expired items.
213213
template <typename T>
214-
typename T::Type* SocketAddressLRU<T>::Upsert(
215-
const SocketAddress& address) {
216-
217-
auto on_exit = OnScopeLeave([&]() { CheckExpired(); });
214+
typename T::Type* SocketAddressLRU<T>::Upsert(const SocketAddress& address,
215+
uint64_t now) {
216+
auto on_exit = OnScopeLeave([&]() { CheckExpired(now); });
218217

219218
auto it = map_.find(address);
220219
if (it != std::end(map_)) {
221220
list_.splice(list_.begin(), list_, it->second);
222-
T::Touch(it->first, &it->second->second);
221+
T::Touch(it->first, &it->second->second, now);
223222
return &it->second->second;
224223
}
225224

226225
list_.push_front(Pair(address, { }));
227226
map_[address] = list_.begin();
228-
T::Touch(list_.begin()->first, &list_.begin()->second);
227+
T::Touch(list_.begin()->first, &list_.begin()->second, now);
229228

230229
// Drop the last item in the list if we are
231230
// over the size limit...

β€Žsrc/node_sockaddr.hβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ class SocketAddressLRU : public MemoryRetainer {
213213
// If the item already exists, returns a reference to
214214
// the existing item, adjusting items position in the
215215
// LRU. If the item does not exist, emplaces the item
216-
// and returns the new item.
217-
Type* Upsert(const SocketAddress& address);
216+
// and returns the new item. The caller provides a
217+
// timestamp to avoid redundant uv_hrtime() calls.
218+
Type* Upsert(const SocketAddress& address, uint64_t now);
218219

219220
// Returns a reference to the item if it exists, or
220221
// nullptr. The position in the LRU is not modified.
@@ -231,7 +232,7 @@ class SocketAddressLRU : public MemoryRetainer {
231232
using Pair = std::pair<SocketAddress, Type>;
232233
using Iterator = typename std::list<Pair>::iterator;
233234

234-
voidCheckExpired();
235+
voidCheckExpired(uint64_t now);
235236

236237
std::list<Pair> list_;
237238
SocketAddress::Map<Iterator> map_;

β€Žsrc/quic/defs.hβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,13 @@ struct TokenBucket final {
378378
// hasn't been initialized yet (last_ts == 0). Used for per-host
379379
// buckets in the address LRU where the rate/burst aren't known
380380
// at construction time.
381-
voidInitOnce(double r, double b);
381+
voidInitOnce(double r, double b, uint64_t now);
382382

383383
// Try to consume one token. Refills based on elapsed time, then
384384
// attempts to consume. Returns true if the request is allowed.
385-
boolconsume();
385+
// The caller provides the current timestamp to avoid redundant
386+
// uv_hrtime() calls in hot paths.
387+
boolconsume(uint64_t now);
386388
};
387389

388390
classDebugIndentScopefinal {

β€Žsrc/quic/endpoint.ccβ€Ž

Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,18 @@ STAT_STRUCT(Endpoint, ENDPOINT)
9292
TokenBucket::TokenBucket(double rate, double burst)
9393
: rate(rate), burst(burst), tokens(burst), last_ts(uv_hrtime()) {}
9494

95-
voidTokenBucket::InitOnce(double r, double b) {
95+
voidTokenBucket::InitOnce(double r, double b, uint64_t now) {
9696
if (last_ts == 0) {
9797
rate = r;
9898
burst = b;
9999
tokens = b;
100-
last_ts = uv_hrtime();
100+
last_ts = now;
101101
}
102102
}
103103

104104
// Try to consume one token. Refills based on elapsed time, then
105105
// attempts to consume. Returns true if the request is allowed.
106-
boolTokenBucket::consume() {
107-
uint64_t now = uv_hrtime();
106+
boolTokenBucket::consume(uint64_t now) {
108107
double elapsed = static_cast<double>(now - last_ts) / 1e9; // seconds
109108
last_ts = now;
110109
tokens = std::min(burst, tokens + elapsed * rate);
@@ -1025,9 +1024,9 @@ void Endpoint::SendBatch(Packet::Ptr* packets, size_t count) {
10251024
}
10261025
}
10271026

1028-
voidEndpoint::SendRetry(const PathDescriptor& options) {
1027+
voidEndpoint::SendRetry(const PathDescriptor& options, uint64_t now) {
10291028
Debug(this, "Sending retry on path %s", options);
1030-
if (!retry_bucket_.consume()) {
1029+
if (!retry_bucket_.consume(now)) {
10311030
Debug(this, "Retry rate limit exceeded (global)");
10321031
STAT_INCREMENT(Stats, retry_rate_limited);
10331032
return;
@@ -1041,9 +1040,10 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
10411040
}
10421041
}
10431042

1044-
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options) {
1043+
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options,
1044+
uint64_t now) {
10451045
Debug(this, "Sending version negotiation on path %s", options);
1046-
if (!version_negotiation_bucket_.consume()) {
1046+
if (!version_negotiation_bucket_.consume(now)) {
10471047
Debug(this, "Version negotiation rate limit exceeded (global)");
10481048
STAT_INCREMENT(Stats, version_negotiation_rate_limited);
10491049
return;
@@ -1057,7 +1057,8 @@ void Endpoint::SendVersionNegotiation(const PathDescriptor& options) {
10571057
}
10581058

10591059
boolEndpoint::SendStatelessReset(const PathDescriptor& options,
1060-
size_t source_len) {
1060+
size_t source_len,
1061+
uint64_t now) {
10611062
if (options_.disable_stateless_reset) [[unlikely]] {
10621063
returnfalse;
10631064
}
@@ -1066,7 +1067,7 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10661067
options,
10671068
source_len);
10681069

1069-
if (!stateless_reset_bucket_.consume()) {
1070+
if (!stateless_reset_bucket_.consume(now)) {
10701071
Debug(this, "Stateless reset rate limit exceeded (global)");
10711072
STAT_INCREMENT(Stats, stateless_reset_rate_limited);
10721073
returnfalse;
@@ -1086,12 +1087,13 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10861087
}
10871088

10881089
voidEndpoint::SendImmediateConnectionClose(const PathDescriptor& options,
1089-
QuicError reason) {
1090+
QuicError reason,
1091+
uint64_t now) {
10901092
Debug(this,
10911093
"Sending immediate connection close on path %s with reason %s",
10921094
options,
10931095
reason);
1094-
if (!immediate_close_bucket_.consume()) {
1096+
if (!immediate_close_bucket_.consume(now)) {
10951097
Debug(this, "Immediate connection close rate limit exceeded (global)");
10961098
STAT_INCREMENT(Stats, immediate_close_rate_limited);
10971099
return;
@@ -1313,6 +1315,8 @@ void Endpoint::CloseGracefully() {
13131315
voidEndpoint::Receive(constuint8_t* data,
13141316
size_t len,
13151317
const SocketAddress& remote_address) {
1318+
constuint64_t now = uv_hrtime();
1319+
13161320
constauto receive = [&](Session* session,
13171321
constuint8_t* pkt_data,
13181322
size_t pkt_len,
@@ -1327,7 +1331,12 @@ void Endpoint::Receive(const uint8_t* data,
13271331
// are generated. The deferred flush via BindingData's uv_check
13281332
// callback calls SendPendingData once per dirty session after all
13291333
// packets in the burst have been read.
1330-
if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) {
1334+
if (session->ReadPacket(pkt_data,
1335+
pkt_len,
1336+
local_address,
1337+
remote_address,
1338+
PacketInfo(),
1339+
now)) {
13311340
STAT_INCREMENT_N(Stats, bytes_received, pkt_len);
13321341
STAT_INCREMENT(Stats, packets_received);
13331342
}
@@ -1349,10 +1358,10 @@ void Endpoint::Receive(const uint8_t* data,
13491358

13501359
// Per-host session creation rate limit. The bucket is initialized
13511360
// on first access with the configured rate/burst from options.
1352-
auto info = addr_validation_lru_.Upsert(config.remote_address);
1353-
info->session_creation_bucket.InitOnce(options_.session_creation_rate,
1354-
options_.session_creation_burst);
1355-
if (!info->session_creation_bucket.consume()) {
1361+
auto info = addr_validation_lru_.Upsert(config.remote_address, now);
1362+
info->session_creation_bucket.InitOnce(
1363+
options_.session_creation_rate, options_.session_creation_burst, now);
1364+
if (!info->session_creation_bucket.consume(now)) {
13561365
Debug(this,
13571366
"Session creation rate limit exceeded for %s",
13581367
config.remote_address);
@@ -1451,7 +1460,8 @@ void Endpoint::Receive(const uint8_t* data,
14511460
if (state_->busy) STAT_INCREMENT(Stats, server_busy_count);
14521461
SendImmediateConnectionClose(
14531462
PathDescriptor{version, dcid, scid, local_address, remote_address},
1454-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1463+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1464+
now);
14551465
// The packet was successfully processed, even if we did refuse the
14561466
// connection.
14571467
STAT_INCREMENT(Stats, packets_received);
@@ -1525,7 +1535,8 @@ void Endpoint::Receive(const uint8_t* data,
15251535
Debug(this, "Retry token from %s is invalid.", remote_address);
15261536
SendImmediateConnectionClose(
15271537
PathDescriptor{version, scid, dcid, local_address, remote_address},
1528-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1538+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1539+
now);
15291540
STAT_INCREMENT(Stats, packets_received);
15301541
return;
15311542
}
@@ -1541,7 +1552,7 @@ void Endpoint::Receive(const uint8_t* data,
15411552
// Mark the address as validated since the retry round-trip proves
15421553
// reachability.
15431554
Debug(this, "Remote address %s is validated", remote_address);
1544-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1555+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
15451556
}
15461557

15471558
// Step 2: Address validation β€” decide whether to send a Retry or
@@ -1557,13 +1568,15 @@ void Endpoint::Receive(const uint8_t* data,
15571568
"Initial packet has no token. Sending retry to %s to start "
15581569
"validation",
15591570
remote_address);
1560-
SendRetry(PathDescriptor{
1561-
version,
1562-
dcid,
1563-
scid,
1564-
local_address,
1565-
remote_address,
1566-
});
1571+
SendRetry(
1572+
PathDescriptor{
1573+
version,
1574+
dcid,
1575+
scid,
1576+
local_address,
1577+
remote_address,
1578+
},
1579+
now);
15671580
STAT_INCREMENT(Stats, packets_received);
15681581
return;
15691582
}
@@ -1584,13 +1597,15 @@ void Endpoint::Receive(const uint8_t* data,
15841597
Debug(this,
15851598
"Regular token from %s is invalid.",
15861599
remote_address);
1587-
SendRetry(PathDescriptor{
1588-
version,
1589-
dcid,
1590-
scid,
1591-
local_address,
1592-
remote_address,
1593-
});
1600+
SendRetry(
1601+
PathDescriptor{
1602+
version,
1603+
dcid,
1604+
scid,
1605+
local_address,
1606+
remote_address,
1607+
},
1608+
now);
15941609
STAT_INCREMENT(Stats, packets_received);
15951610
return;
15961611
}
@@ -1602,20 +1617,22 @@ void Endpoint::Receive(const uint8_t* data,
16021617
Debug(this,
16031618
"Initial packet from %s has unknown token type",
16041619
remote_address);
1605-
SendRetry(PathDescriptor{
1606-
version,
1607-
dcid,
1608-
scid,
1609-
local_address,
1610-
remote_address,
1611-
});
1620+
SendRetry(
1621+
PathDescriptor{
1622+
version,
1623+
dcid,
1624+
scid,
1625+
local_address,
1626+
remote_address,
1627+
},
1628+
now);
16121629
STAT_INCREMENT(Stats, packets_received);
16131630
return;
16141631
}
16151632
}
16161633

16171634
Debug(this, "Remote address %s is validated", remote_address);
1618-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1635+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
16191636
} elseif (hd.tokenlen > 0) {
16201637
Debug(this,
16211638
"Ignoring initial packet from %s with unexpected token",
@@ -1627,13 +1644,15 @@ void Endpoint::Receive(const uint8_t* data,
16271644
if (options_.validate_address) {
16281645
Debug(
16291646
this, "Sending retry to %s due to 0RTT packet", remote_address);
1630-
SendRetry(PathDescriptor{
1631-
version,
1632-
dcid,
1633-
scid,
1634-
local_address,
1635-
remote_address,
1636-
});
1647+
SendRetry(
1648+
PathDescriptor{
1649+
version,
1650+
dcid,
1651+
scid,
1652+
local_address,
1653+
remote_address,
1654+
},
1655+
now);
16371656
STAT_INCREMENT(Stats, packets_received);
16381657
return;
16391658
}
@@ -1742,8 +1761,12 @@ void Endpoint::Receive(const uint8_t* data,
17421761
pversion_cid.version);
17431762
CIDdcid(pversion_cid.dcid, pversion_cid.dcidlen);
17441763
CIDscid(pversion_cid.scid, pversion_cid.scidlen);
1745-
SendVersionNegotiation(PathDescriptor{
1746-
pversion_cid.version, dcid, scid, local_address(), remote_address});
1764+
SendVersionNegotiation(PathDescriptor{pversion_cid.version,
1765+
dcid,
1766+
scid,
1767+
local_address(),
1768+
remote_address},
1769+
now);
17471770
STAT_INCREMENT(Stats, packets_received);
17481771
return;
17491772
}
@@ -1822,7 +1845,8 @@ void Endpoint::Receive(const uint8_t* data,
18221845
SendStatelessReset(
18231846
PathDescriptor{
18241847
pversion_cid.version, dcid, scid, addr, remote_address},
1825-
len);
1848+
len,
1849+
now);
18261850
return;
18271851
}
18281852

@@ -1884,13 +1908,14 @@ void Endpoint::MemoryInfo(MemoryTracker* tracker) const {
18841908
// Endpoint::SocketAddressInfoTraits
18851909

18861910
boolEndpoint::SocketAddressInfoTraits::CheckExpired(
1887-
const SocketAddress& address, const Type& type) {
1888-
return (uv_hrtime() - type.timestamp) > kSocketAddressInfoTimeout;
1911+
const SocketAddress& address, const Type& type, uint64_t now) {
1912+
return (now - type.timestamp) > kSocketAddressInfoTimeout;
18891913
}
18901914

18911915
voidEndpoint::SocketAddressInfoTraits::Touch(const SocketAddress& address,
1892-
Type* type) {
1893-
type->timestamp = uv_hrtime();
1916+
Type* type,
1917+
uint64_t now) {
1918+
type->timestamp = now;
18941919
}
18951920

18961921
// ======================================================================================

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Commit c726a89

Browse files
jasnelladuh95
authored andcommitted
quic: cache timestamp for address lru cache
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 11778a7 commit c726a89

8 files changed

Lines changed: 192 additions & 107 deletions

File tree

β€Žsrc/node_sockaddr-inl.hβ€Ž

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ typename T::Type* SocketAddressLRU<T>::Peek(
186186
}
187187

188188
template <typename T>
189-
void SocketAddressLRU<T>::CheckExpired() {
189+
void SocketAddressLRU<T>::CheckExpired(uint64_t now) {
190190
auto it = list_.rbegin();
191191
while (it != list_.rend()) {
192-
if (T::CheckExpired(it->first, it->second)) {
192+
if (T::CheckExpired(it->first, it->second, now)) {
193193
map_.erase(it->first);
194194
list_.pop_back();
195195
it = list_.rbegin();
@@ -211,21 +211,20 @@ void SocketAddressLRU<T>::MemoryInfo(MemoryTracker* tracker) const {
211211
// cache and adjust if necessary. Whether the item exists or not,
212212
// purge expired items.
213213
template <typename T>
214-
typename T::Type* SocketAddressLRU<T>::Upsert(
215-
const SocketAddress& address) {
216-
217-
auto on_exit = OnScopeLeave([&]() { CheckExpired(); });
214+
typename T::Type* SocketAddressLRU<T>::Upsert(const SocketAddress& address,
215+
uint64_t now) {
216+
auto on_exit = OnScopeLeave([&]() { CheckExpired(now); });
218217

219218
auto it = map_.find(address);
220219
if (it != std::end(map_)) {
221220
list_.splice(list_.begin(), list_, it->second);
222-
T::Touch(it->first, &it->second->second);
221+
T::Touch(it->first, &it->second->second, now);
223222
return &it->second->second;
224223
}
225224

226225
list_.push_front(Pair(address, { }));
227226
map_[address] = list_.begin();
228-
T::Touch(list_.begin()->first, &list_.begin()->second);
227+
T::Touch(list_.begin()->first, &list_.begin()->second, now);
229228

230229
// Drop the last item in the list if we are
231230
// over the size limit...

β€Žsrc/node_sockaddr.hβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ class SocketAddressLRU : public MemoryRetainer {
213213
// If the item already exists, returns a reference to
214214
// the existing item, adjusting items position in the
215215
// LRU. If the item does not exist, emplaces the item
216-
// and returns the new item.
217-
Type* Upsert(const SocketAddress& address);
216+
// and returns the new item. The caller provides a
217+
// timestamp to avoid redundant uv_hrtime() calls.
218+
Type* Upsert(const SocketAddress& address, uint64_t now);
218219

219220
// Returns a reference to the item if it exists, or
220221
// nullptr. The position in the LRU is not modified.
@@ -231,7 +232,7 @@ class SocketAddressLRU : public MemoryRetainer {
231232
using Pair = std::pair<SocketAddress, Type>;
232233
using Iterator = typename std::list<Pair>::iterator;
233234

234-
voidCheckExpired();
235+
voidCheckExpired(uint64_t now);
235236

236237
std::list<Pair> list_;
237238
SocketAddress::Map<Iterator> map_;

β€Žsrc/quic/defs.hβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,13 @@ struct TokenBucket final {
378378
// hasn't been initialized yet (last_ts == 0). Used for per-host
379379
// buckets in the address LRU where the rate/burst aren't known
380380
// at construction time.
381-
voidInitOnce(double r, double b);
381+
voidInitOnce(double r, double b, uint64_t now);
382382

383383
// Try to consume one token. Refills based on elapsed time, then
384384
// attempts to consume. Returns true if the request is allowed.
385-
boolconsume();
385+
// The caller provides the current timestamp to avoid redundant
386+
// uv_hrtime() calls in hot paths.
387+
boolconsume(uint64_t now);
386388
};
387389

388390
classDebugIndentScopefinal {

β€Žsrc/quic/endpoint.ccβ€Ž

Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,18 @@ STAT_STRUCT(Endpoint, ENDPOINT)
9292
TokenBucket::TokenBucket(double rate, double burst)
9393
: rate(rate), burst(burst), tokens(burst), last_ts(uv_hrtime()) {}
9494

95-
voidTokenBucket::InitOnce(double r, double b) {
95+
voidTokenBucket::InitOnce(double r, double b, uint64_t now) {
9696
if (last_ts == 0) {
9797
rate = r;
9898
burst = b;
9999
tokens = b;
100-
last_ts = uv_hrtime();
100+
last_ts = now;
101101
}
102102
}
103103

104104
// Try to consume one token. Refills based on elapsed time, then
105105
// attempts to consume. Returns true if the request is allowed.
106-
boolTokenBucket::consume() {
107-
uint64_t now = uv_hrtime();
106+
boolTokenBucket::consume(uint64_t now) {
108107
double elapsed = static_cast<double>(now - last_ts) / 1e9; // seconds
109108
last_ts = now;
110109
tokens = std::min(burst, tokens + elapsed * rate);
@@ -1025,9 +1024,9 @@ void Endpoint::SendBatch(Packet::Ptr* packets, size_t count) {
10251024
}
10261025
}
10271026

1028-
voidEndpoint::SendRetry(const PathDescriptor& options) {
1027+
voidEndpoint::SendRetry(const PathDescriptor& options, uint64_t now) {
10291028
Debug(this, "Sending retry on path %s", options);
1030-
if (!retry_bucket_.consume()) {
1029+
if (!retry_bucket_.consume(now)) {
10311030
Debug(this, "Retry rate limit exceeded (global)");
10321031
STAT_INCREMENT(Stats, retry_rate_limited);
10331032
return;
@@ -1041,9 +1040,10 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
10411040
}
10421041
}
10431042

1044-
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options) {
1043+
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options,
1044+
uint64_t now) {
10451045
Debug(this, "Sending version negotiation on path %s", options);
1046-
if (!version_negotiation_bucket_.consume()) {
1046+
if (!version_negotiation_bucket_.consume(now)) {
10471047
Debug(this, "Version negotiation rate limit exceeded (global)");
10481048
STAT_INCREMENT(Stats, version_negotiation_rate_limited);
10491049
return;
@@ -1057,7 +1057,8 @@ void Endpoint::SendVersionNegotiation(const PathDescriptor& options) {
10571057
}
10581058

10591059
boolEndpoint::SendStatelessReset(const PathDescriptor& options,
1060-
size_t source_len) {
1060+
size_t source_len,
1061+
uint64_t now) {
10611062
if (options_.disable_stateless_reset) [[unlikely]] {
10621063
returnfalse;
10631064
}
@@ -1066,7 +1067,7 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10661067
options,
10671068
source_len);
10681069

1069-
if (!stateless_reset_bucket_.consume()) {
1070+
if (!stateless_reset_bucket_.consume(now)) {
10701071
Debug(this, "Stateless reset rate limit exceeded (global)");
10711072
STAT_INCREMENT(Stats, stateless_reset_rate_limited);
10721073
returnfalse;
@@ -1086,12 +1087,13 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10861087
}
10871088

10881089
voidEndpoint::SendImmediateConnectionClose(const PathDescriptor& options,
1089-
QuicError reason) {
1090+
QuicError reason,
1091+
uint64_t now) {
10901092
Debug(this,
10911093
"Sending immediate connection close on path %s with reason %s",
10921094
options,
10931095
reason);
1094-
if (!immediate_close_bucket_.consume()) {
1096+
if (!immediate_close_bucket_.consume(now)) {
10951097
Debug(this, "Immediate connection close rate limit exceeded (global)");
10961098
STAT_INCREMENT(Stats, immediate_close_rate_limited);
10971099
return;
@@ -1313,6 +1315,8 @@ void Endpoint::CloseGracefully() {
13131315
voidEndpoint::Receive(constuint8_t* data,
13141316
size_t len,
13151317
const SocketAddress& remote_address) {
1318+
constuint64_t now = uv_hrtime();
1319+
13161320
constauto receive = [&](Session* session,
13171321
constuint8_t* pkt_data,
13181322
size_t pkt_len,
@@ -1327,7 +1331,12 @@ void Endpoint::Receive(const uint8_t* data,
13271331
// are generated. The deferred flush via BindingData's uv_check
13281332
// callback calls SendPendingData once per dirty session after all
13291333
// packets in the burst have been read.
1330-
if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) {
1334+
if (session->ReadPacket(pkt_data,
1335+
pkt_len,
1336+
local_address,
1337+
remote_address,
1338+
PacketInfo(),
1339+
now)) {
13311340
STAT_INCREMENT_N(Stats, bytes_received, pkt_len);
13321341
STAT_INCREMENT(Stats, packets_received);
13331342
}
@@ -1349,10 +1358,10 @@ void Endpoint::Receive(const uint8_t* data,
13491358

13501359
// Per-host session creation rate limit. The bucket is initialized
13511360
// on first access with the configured rate/burst from options.
1352-
auto info = addr_validation_lru_.Upsert(config.remote_address);
1353-
info->session_creation_bucket.InitOnce(options_.session_creation_rate,
1354-
options_.session_creation_burst);
1355-
if (!info->session_creation_bucket.consume()) {
1361+
auto info = addr_validation_lru_.Upsert(config.remote_address, now);
1362+
info->session_creation_bucket.InitOnce(
1363+
options_.session_creation_rate, options_.session_creation_burst, now);
1364+
if (!info->session_creation_bucket.consume(now)) {
13561365
Debug(this,
13571366
"Session creation rate limit exceeded for %s",
13581367
config.remote_address);
@@ -1451,7 +1460,8 @@ void Endpoint::Receive(const uint8_t* data,
14511460
if (state_->busy) STAT_INCREMENT(Stats, server_busy_count);
14521461
SendImmediateConnectionClose(
14531462
PathDescriptor{version, dcid, scid, local_address, remote_address},
1454-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1463+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1464+
now);
14551465
// The packet was successfully processed, even if we did refuse the
14561466
// connection.
14571467
STAT_INCREMENT(Stats, packets_received);
@@ -1525,7 +1535,8 @@ void Endpoint::Receive(const uint8_t* data,
15251535
Debug(this, "Retry token from %s is invalid.", remote_address);
15261536
SendImmediateConnectionClose(
15271537
PathDescriptor{version, scid, dcid, local_address, remote_address},
1528-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1538+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1539+
now);
15291540
STAT_INCREMENT(Stats, packets_received);
15301541
return;
15311542
}
@@ -1541,7 +1552,7 @@ void Endpoint::Receive(const uint8_t* data,
15411552
// Mark the address as validated since the retry round-trip proves
15421553
// reachability.
15431554
Debug(this, "Remote address %s is validated", remote_address);
1544-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1555+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
15451556
}
15461557

15471558
// Step 2: Address validation β€” decide whether to send a Retry or
@@ -1557,13 +1568,15 @@ void Endpoint::Receive(const uint8_t* data,
15571568
"Initial packet has no token. Sending retry to %s to start "
15581569
"validation",
15591570
remote_address);
1560-
SendRetry(PathDescriptor{
1561-
version,
1562-
dcid,
1563-
scid,
1564-
local_address,
1565-
remote_address,
1566-
});
1571+
SendRetry(
1572+
PathDescriptor{
1573+
version,
1574+
dcid,
1575+
scid,
1576+
local_address,
1577+
remote_address,
1578+
},
1579+
now);
15671580
STAT_INCREMENT(Stats, packets_received);
15681581
return;
15691582
}
@@ -1584,13 +1597,15 @@ void Endpoint::Receive(const uint8_t* data,
15841597
Debug(this,
15851598
"Regular token from %s is invalid.",
15861599
remote_address);
1587-
SendRetry(PathDescriptor{
1588-
version,
1589-
dcid,
1590-
scid,
1591-
local_address,
1592-
remote_address,
1593-
});
1600+
SendRetry(
1601+
PathDescriptor{
1602+
version,
1603+
dcid,
1604+
scid,
1605+
local_address,
1606+
remote_address,
1607+
},
1608+
now);
15941609
STAT_INCREMENT(Stats, packets_received);
15951610
return;
15961611
}
@@ -1602,20 +1617,22 @@ void Endpoint::Receive(const uint8_t* data,
16021617
Debug(this,
16031618
"Initial packet from %s has unknown token type",
16041619
remote_address);
1605-
SendRetry(PathDescriptor{
1606-
version,
1607-
dcid,
1608-
scid,
1609-
local_address,
1610-
remote_address,
1611-
});
1620+
SendRetry(
1621+
PathDescriptor{
1622+
version,
1623+
dcid,
1624+
scid,
1625+
local_address,
1626+
remote_address,
1627+
},
1628+
now);
16121629
STAT_INCREMENT(Stats, packets_received);
16131630
return;
16141631
}
16151632
}
16161633

16171634
Debug(this, "Remote address %s is validated", remote_address);
1618-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1635+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
16191636
} elseif (hd.tokenlen > 0) {
16201637
Debug(this,
16211638
"Ignoring initial packet from %s with unexpected token",
@@ -1627,13 +1644,15 @@ void Endpoint::Receive(const uint8_t* data,
16271644
if (options_.validate_address) {
16281645
Debug(
16291646
this, "Sending retry to %s due to 0RTT packet", remote_address);
1630-
SendRetry(PathDescriptor{
1631-
version,
1632-
dcid,
1633-
scid,
1634-
local_address,
1635-
remote_address,
1636-
});
1647+
SendRetry(
1648+
PathDescriptor{
1649+
version,
1650+
dcid,
1651+
scid,
1652+
local_address,
1653+
remote_address,
1654+
},
1655+
now);
16371656
STAT_INCREMENT(Stats, packets_received);
16381657
return;
16391658
}
@@ -1742,8 +1761,12 @@ void Endpoint::Receive(const uint8_t* data,
17421761
pversion_cid.version);
17431762
CIDdcid(pversion_cid.dcid, pversion_cid.dcidlen);
17441763
CIDscid(pversion_cid.scid, pversion_cid.scidlen);
1745-
SendVersionNegotiation(PathDescriptor{
1746-
pversion_cid.version, dcid, scid, local_address(), remote_address});
1764+
SendVersionNegotiation(PathDescriptor{pversion_cid.version,
1765+
dcid,
1766+
scid,
1767+
local_address(),
1768+
remote_address},
1769+
now);
17471770
STAT_INCREMENT(Stats, packets_received);
17481771
return;
17491772
}
@@ -1822,7 +1845,8 @@ void Endpoint::Receive(const uint8_t* data,
18221845
SendStatelessReset(
18231846
PathDescriptor{
18241847
pversion_cid.version, dcid, scid, addr, remote_address},
1825-
len);
1848+
len,
1849+
now);
18261850
return;
18271851
}
18281852

@@ -1884,13 +1908,14 @@ void Endpoint::MemoryInfo(MemoryTracker* tracker) const {
18841908
// Endpoint::SocketAddressInfoTraits
18851909

18861910
boolEndpoint::SocketAddressInfoTraits::CheckExpired(
1887-
const SocketAddress& address, const Type& type) {
1888-
return (uv_hrtime() - type.timestamp) > kSocketAddressInfoTimeout;
1911+
const SocketAddress& address, const Type& type, uint64_t now) {
1912+
return (now - type.timestamp) > kSocketAddressInfoTimeout;
18891913
}
18901914

18911915
voidEndpoint::SocketAddressInfoTraits::Touch(const SocketAddress& address,
1892-
Type* type) {
1893-
type->timestamp = uv_hrtime();
1916+
Type* type,
1917+
uint64_t now) {
1918+
type->timestamp = now;
18941919
}
18951920

18961921
// ======================================================================================

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit c726a89

Browse files
jasnelladuh95
authored andcommitted
quic: cache timestamp for address lru cache
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 11778a7 commit c726a89

8 files changed

Lines changed: 192 additions & 107 deletions

File tree

β€Žsrc/node_sockaddr-inl.hβ€Ž

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ typename T::Type* SocketAddressLRU<T>::Peek(
186186
}
187187

188188
template <typename T>
189-
void SocketAddressLRU<T>::CheckExpired() {
189+
void SocketAddressLRU<T>::CheckExpired(uint64_t now) {
190190
auto it = list_.rbegin();
191191
while (it != list_.rend()) {
192-
if (T::CheckExpired(it->first, it->second)) {
192+
if (T::CheckExpired(it->first, it->second, now)) {
193193
map_.erase(it->first);
194194
list_.pop_back();
195195
it = list_.rbegin();
@@ -211,21 +211,20 @@ void SocketAddressLRU<T>::MemoryInfo(MemoryTracker* tracker) const {
211211
// cache and adjust if necessary. Whether the item exists or not,
212212
// purge expired items.
213213
template <typename T>
214-
typename T::Type* SocketAddressLRU<T>::Upsert(
215-
const SocketAddress& address) {
216-
217-
auto on_exit = OnScopeLeave([&]() { CheckExpired(); });
214+
typename T::Type* SocketAddressLRU<T>::Upsert(const SocketAddress& address,
215+
uint64_t now) {
216+
auto on_exit = OnScopeLeave([&]() { CheckExpired(now); });
218217

219218
auto it = map_.find(address);
220219
if (it != std::end(map_)) {
221220
list_.splice(list_.begin(), list_, it->second);
222-
T::Touch(it->first, &it->second->second);
221+
T::Touch(it->first, &it->second->second, now);
223222
return &it->second->second;
224223
}
225224

226225
list_.push_front(Pair(address, { }));
227226
map_[address] = list_.begin();
228-
T::Touch(list_.begin()->first, &list_.begin()->second);
227+
T::Touch(list_.begin()->first, &list_.begin()->second, now);
229228

230229
// Drop the last item in the list if we are
231230
// over the size limit...

β€Žsrc/node_sockaddr.hβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ class SocketAddressLRU : public MemoryRetainer {
213213
// If the item already exists, returns a reference to
214214
// the existing item, adjusting items position in the
215215
// LRU. If the item does not exist, emplaces the item
216-
// and returns the new item.
217-
Type* Upsert(const SocketAddress& address);
216+
// and returns the new item. The caller provides a
217+
// timestamp to avoid redundant uv_hrtime() calls.
218+
Type* Upsert(const SocketAddress& address, uint64_t now);
218219

219220
// Returns a reference to the item if it exists, or
220221
// nullptr. The position in the LRU is not modified.
@@ -231,7 +232,7 @@ class SocketAddressLRU : public MemoryRetainer {
231232
using Pair = std::pair<SocketAddress, Type>;
232233
using Iterator = typename std::list<Pair>::iterator;
233234

234-
voidCheckExpired();
235+
voidCheckExpired(uint64_t now);
235236

236237
std::list<Pair> list_;
237238
SocketAddress::Map<Iterator> map_;

β€Žsrc/quic/defs.hβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,13 @@ struct TokenBucket final {
378378
// hasn't been initialized yet (last_ts == 0). Used for per-host
379379
// buckets in the address LRU where the rate/burst aren't known
380380
// at construction time.
381-
voidInitOnce(double r, double b);
381+
voidInitOnce(double r, double b, uint64_t now);
382382

383383
// Try to consume one token. Refills based on elapsed time, then
384384
// attempts to consume. Returns true if the request is allowed.
385-
boolconsume();
385+
// The caller provides the current timestamp to avoid redundant
386+
// uv_hrtime() calls in hot paths.
387+
boolconsume(uint64_t now);
386388
};
387389

388390
classDebugIndentScopefinal {

β€Žsrc/quic/endpoint.ccβ€Ž

Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,18 @@ STAT_STRUCT(Endpoint, ENDPOINT)
9292
TokenBucket::TokenBucket(double rate, double burst)
9393
: rate(rate), burst(burst), tokens(burst), last_ts(uv_hrtime()) {}
9494

95-
voidTokenBucket::InitOnce(double r, double b) {
95+
voidTokenBucket::InitOnce(double r, double b, uint64_t now) {
9696
if (last_ts == 0) {
9797
rate = r;
9898
burst = b;
9999
tokens = b;
100-
last_ts = uv_hrtime();
100+
last_ts = now;
101101
}
102102
}
103103

104104
// Try to consume one token. Refills based on elapsed time, then
105105
// attempts to consume. Returns true if the request is allowed.
106-
boolTokenBucket::consume() {
107-
uint64_t now = uv_hrtime();
106+
boolTokenBucket::consume(uint64_t now) {
108107
double elapsed = static_cast<double>(now - last_ts) / 1e9; // seconds
109108
last_ts = now;
110109
tokens = std::min(burst, tokens + elapsed * rate);
@@ -1025,9 +1024,9 @@ void Endpoint::SendBatch(Packet::Ptr* packets, size_t count) {
10251024
}
10261025
}
10271026

1028-
voidEndpoint::SendRetry(const PathDescriptor& options) {
1027+
voidEndpoint::SendRetry(const PathDescriptor& options, uint64_t now) {
10291028
Debug(this, "Sending retry on path %s", options);
1030-
if (!retry_bucket_.consume()) {
1029+
if (!retry_bucket_.consume(now)) {
10311030
Debug(this, "Retry rate limit exceeded (global)");
10321031
STAT_INCREMENT(Stats, retry_rate_limited);
10331032
return;
@@ -1041,9 +1040,10 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
10411040
}
10421041
}
10431042

1044-
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options) {
1043+
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options,
1044+
uint64_t now) {
10451045
Debug(this, "Sending version negotiation on path %s", options);
1046-
if (!version_negotiation_bucket_.consume()) {
1046+
if (!version_negotiation_bucket_.consume(now)) {
10471047
Debug(this, "Version negotiation rate limit exceeded (global)");
10481048
STAT_INCREMENT(Stats, version_negotiation_rate_limited);
10491049
return;
@@ -1057,7 +1057,8 @@ void Endpoint::SendVersionNegotiation(const PathDescriptor& options) {
10571057
}
10581058

10591059
boolEndpoint::SendStatelessReset(const PathDescriptor& options,
1060-
size_t source_len) {
1060+
size_t source_len,
1061+
uint64_t now) {
10611062
if (options_.disable_stateless_reset) [[unlikely]] {
10621063
returnfalse;
10631064
}
@@ -1066,7 +1067,7 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10661067
options,
10671068
source_len);
10681069

1069-
if (!stateless_reset_bucket_.consume()) {
1070+
if (!stateless_reset_bucket_.consume(now)) {
10701071
Debug(this, "Stateless reset rate limit exceeded (global)");
10711072
STAT_INCREMENT(Stats, stateless_reset_rate_limited);
10721073
returnfalse;
@@ -1086,12 +1087,13 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10861087
}
10871088

10881089
voidEndpoint::SendImmediateConnectionClose(const PathDescriptor& options,
1089-
QuicError reason) {
1090+
QuicError reason,
1091+
uint64_t now) {
10901092
Debug(this,
10911093
"Sending immediate connection close on path %s with reason %s",
10921094
options,
10931095
reason);
1094-
if (!immediate_close_bucket_.consume()) {
1096+
if (!immediate_close_bucket_.consume(now)) {
10951097
Debug(this, "Immediate connection close rate limit exceeded (global)");
10961098
STAT_INCREMENT(Stats, immediate_close_rate_limited);
10971099
return;
@@ -1313,6 +1315,8 @@ void Endpoint::CloseGracefully() {
13131315
voidEndpoint::Receive(constuint8_t* data,
13141316
size_t len,
13151317
const SocketAddress& remote_address) {
1318+
constuint64_t now = uv_hrtime();
1319+
13161320
constauto receive = [&](Session* session,
13171321
constuint8_t* pkt_data,
13181322
size_t pkt_len,
@@ -1327,7 +1331,12 @@ void Endpoint::Receive(const uint8_t* data,
13271331
// are generated. The deferred flush via BindingData's uv_check
13281332
// callback calls SendPendingData once per dirty session after all
13291333
// packets in the burst have been read.
1330-
if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) {
1334+
if (session->ReadPacket(pkt_data,
1335+
pkt_len,
1336+
local_address,
1337+
remote_address,
1338+
PacketInfo(),
1339+
now)) {
13311340
STAT_INCREMENT_N(Stats, bytes_received, pkt_len);
13321341
STAT_INCREMENT(Stats, packets_received);
13331342
}
@@ -1349,10 +1358,10 @@ void Endpoint::Receive(const uint8_t* data,
13491358

13501359
// Per-host session creation rate limit. The bucket is initialized
13511360
// on first access with the configured rate/burst from options.
1352-
auto info = addr_validation_lru_.Upsert(config.remote_address);
1353-
info->session_creation_bucket.InitOnce(options_.session_creation_rate,
1354-
options_.session_creation_burst);
1355-
if (!info->session_creation_bucket.consume()) {
1361+
auto info = addr_validation_lru_.Upsert(config.remote_address, now);
1362+
info->session_creation_bucket.InitOnce(
1363+
options_.session_creation_rate, options_.session_creation_burst, now);
1364+
if (!info->session_creation_bucket.consume(now)) {
13561365
Debug(this,
13571366
"Session creation rate limit exceeded for %s",
13581367
config.remote_address);
@@ -1451,7 +1460,8 @@ void Endpoint::Receive(const uint8_t* data,
14511460
if (state_->busy) STAT_INCREMENT(Stats, server_busy_count);
14521461
SendImmediateConnectionClose(
14531462
PathDescriptor{version, dcid, scid, local_address, remote_address},
1454-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1463+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1464+
now);
14551465
// The packet was successfully processed, even if we did refuse the
14561466
// connection.
14571467
STAT_INCREMENT(Stats, packets_received);
@@ -1525,7 +1535,8 @@ void Endpoint::Receive(const uint8_t* data,
15251535
Debug(this, "Retry token from %s is invalid.", remote_address);
15261536
SendImmediateConnectionClose(
15271537
PathDescriptor{version, scid, dcid, local_address, remote_address},
1528-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1538+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1539+
now);
15291540
STAT_INCREMENT(Stats, packets_received);
15301541
return;
15311542
}
@@ -1541,7 +1552,7 @@ void Endpoint::Receive(const uint8_t* data,
15411552
// Mark the address as validated since the retry round-trip proves
15421553
// reachability.
15431554
Debug(this, "Remote address %s is validated", remote_address);
1544-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1555+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
15451556
}
15461557

15471558
// Step 2: Address validation β€” decide whether to send a Retry or
@@ -1557,13 +1568,15 @@ void Endpoint::Receive(const uint8_t* data,
15571568
"Initial packet has no token. Sending retry to %s to start "
15581569
"validation",
15591570
remote_address);
1560-
SendRetry(PathDescriptor{
1561-
version,
1562-
dcid,
1563-
scid,
1564-
local_address,
1565-
remote_address,
1566-
});
1571+
SendRetry(
1572+
PathDescriptor{
1573+
version,
1574+
dcid,
1575+
scid,
1576+
local_address,
1577+
remote_address,
1578+
},
1579+
now);
15671580
STAT_INCREMENT(Stats, packets_received);
15681581
return;
15691582
}
@@ -1584,13 +1597,15 @@ void Endpoint::Receive(const uint8_t* data,
15841597
Debug(this,
15851598
"Regular token from %s is invalid.",
15861599
remote_address);
1587-
SendRetry(PathDescriptor{
1588-
version,
1589-
dcid,
1590-
scid,
1591-
local_address,
1592-
remote_address,
1593-
});
1600+
SendRetry(
1601+
PathDescriptor{
1602+
version,
1603+
dcid,
1604+
scid,
1605+
local_address,
1606+
remote_address,
1607+
},
1608+
now);
15941609
STAT_INCREMENT(Stats, packets_received);
15951610
return;
15961611
}
@@ -1602,20 +1617,22 @@ void Endpoint::Receive(const uint8_t* data,
16021617
Debug(this,
16031618
"Initial packet from %s has unknown token type",
16041619
remote_address);
1605-
SendRetry(PathDescriptor{
1606-
version,
1607-
dcid,
1608-
scid,
1609-
local_address,
1610-
remote_address,
1611-
});
1620+
SendRetry(
1621+
PathDescriptor{
1622+
version,
1623+
dcid,
1624+
scid,
1625+
local_address,
1626+
remote_address,
1627+
},
1628+
now);
16121629
STAT_INCREMENT(Stats, packets_received);
16131630
return;
16141631
}
16151632
}
16161633

16171634
Debug(this, "Remote address %s is validated", remote_address);
1618-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1635+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
16191636
} elseif (hd.tokenlen > 0) {
16201637
Debug(this,
16211638
"Ignoring initial packet from %s with unexpected token",
@@ -1627,13 +1644,15 @@ void Endpoint::Receive(const uint8_t* data,
16271644
if (options_.validate_address) {
16281645
Debug(
16291646
this, "Sending retry to %s due to 0RTT packet", remote_address);
1630-
SendRetry(PathDescriptor{
1631-
version,
1632-
dcid,
1633-
scid,
1634-
local_address,
1635-
remote_address,
1636-
});
1647+
SendRetry(
1648+
PathDescriptor{
1649+
version,
1650+
dcid,
1651+
scid,
1652+
local_address,
1653+
remote_address,
1654+
},
1655+
now);
16371656
STAT_INCREMENT(Stats, packets_received);
16381657
return;
16391658
}
@@ -1742,8 +1761,12 @@ void Endpoint::Receive(const uint8_t* data,
17421761
pversion_cid.version);
17431762
CIDdcid(pversion_cid.dcid, pversion_cid.dcidlen);
17441763
CIDscid(pversion_cid.scid, pversion_cid.scidlen);
1745-
SendVersionNegotiation(PathDescriptor{
1746-
pversion_cid.version, dcid, scid, local_address(), remote_address});
1764+
SendVersionNegotiation(PathDescriptor{pversion_cid.version,
1765+
dcid,
1766+
scid,
1767+
local_address(),
1768+
remote_address},
1769+
now);
17471770
STAT_INCREMENT(Stats, packets_received);
17481771
return;
17491772
}
@@ -1822,7 +1845,8 @@ void Endpoint::Receive(const uint8_t* data,
18221845
SendStatelessReset(
18231846
PathDescriptor{
18241847
pversion_cid.version, dcid, scid, addr, remote_address},
1825-
len);
1848+
len,
1849+
now);
18261850
return;
18271851
}
18281852

@@ -1884,13 +1908,14 @@ void Endpoint::MemoryInfo(MemoryTracker* tracker) const {
18841908
// Endpoint::SocketAddressInfoTraits
18851909

18861910
boolEndpoint::SocketAddressInfoTraits::CheckExpired(
1887-
const SocketAddress& address, const Type& type) {
1888-
return (uv_hrtime() - type.timestamp) > kSocketAddressInfoTimeout;
1911+
const SocketAddress& address, const Type& type, uint64_t now) {
1912+
return (now - type.timestamp) > kSocketAddressInfoTimeout;
18891913
}
18901914

18911915
voidEndpoint::SocketAddressInfoTraits::Touch(const SocketAddress& address,
1892-
Type* type) {
1893-
type->timestamp = uv_hrtime();
1916+
Type* type,
1917+
uint64_t now) {
1918+
type->timestamp = now;
18941919
}
18951920

18961921
// ======================================================================================

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit c726a89

Browse files
jasnelladuh95
authored andcommitted
quic: cache timestamp for address lru cache
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 11778a7 commit c726a89

8 files changed

Lines changed: 192 additions & 107 deletions

File tree

β€Žsrc/node_sockaddr-inl.hβ€Ž

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ typename T::Type* SocketAddressLRU<T>::Peek(
186186
}
187187

188188
template <typename T>
189-
void SocketAddressLRU<T>::CheckExpired() {
189+
void SocketAddressLRU<T>::CheckExpired(uint64_t now) {
190190
auto it = list_.rbegin();
191191
while (it != list_.rend()) {
192-
if (T::CheckExpired(it->first, it->second)) {
192+
if (T::CheckExpired(it->first, it->second, now)) {
193193
map_.erase(it->first);
194194
list_.pop_back();
195195
it = list_.rbegin();
@@ -211,21 +211,20 @@ void SocketAddressLRU<T>::MemoryInfo(MemoryTracker* tracker) const {
211211
// cache and adjust if necessary. Whether the item exists or not,
212212
// purge expired items.
213213
template <typename T>
214-
typename T::Type* SocketAddressLRU<T>::Upsert(
215-
const SocketAddress& address) {
216-
217-
auto on_exit = OnScopeLeave([&]() { CheckExpired(); });
214+
typename T::Type* SocketAddressLRU<T>::Upsert(const SocketAddress& address,
215+
uint64_t now) {
216+
auto on_exit = OnScopeLeave([&]() { CheckExpired(now); });
218217

219218
auto it = map_.find(address);
220219
if (it != std::end(map_)) {
221220
list_.splice(list_.begin(), list_, it->second);
222-
T::Touch(it->first, &it->second->second);
221+
T::Touch(it->first, &it->second->second, now);
223222
return &it->second->second;
224223
}
225224

226225
list_.push_front(Pair(address, { }));
227226
map_[address] = list_.begin();
228-
T::Touch(list_.begin()->first, &list_.begin()->second);
227+
T::Touch(list_.begin()->first, &list_.begin()->second, now);
229228

230229
// Drop the last item in the list if we are
231230
// over the size limit...

β€Žsrc/node_sockaddr.hβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ class SocketAddressLRU : public MemoryRetainer {
213213
// If the item already exists, returns a reference to
214214
// the existing item, adjusting items position in the
215215
// LRU. If the item does not exist, emplaces the item
216-
// and returns the new item.
217-
Type* Upsert(const SocketAddress& address);
216+
// and returns the new item. The caller provides a
217+
// timestamp to avoid redundant uv_hrtime() calls.
218+
Type* Upsert(const SocketAddress& address, uint64_t now);
218219

219220
// Returns a reference to the item if it exists, or
220221
// nullptr. The position in the LRU is not modified.
@@ -231,7 +232,7 @@ class SocketAddressLRU : public MemoryRetainer {
231232
using Pair = std::pair<SocketAddress, Type>;
232233
using Iterator = typename std::list<Pair>::iterator;
233234

234-
voidCheckExpired();
235+
voidCheckExpired(uint64_t now);
235236

236237
std::list<Pair> list_;
237238
SocketAddress::Map<Iterator> map_;

β€Žsrc/quic/defs.hβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,13 @@ struct TokenBucket final {
378378
// hasn't been initialized yet (last_ts == 0). Used for per-host
379379
// buckets in the address LRU where the rate/burst aren't known
380380
// at construction time.
381-
voidInitOnce(double r, double b);
381+
voidInitOnce(double r, double b, uint64_t now);
382382

383383
// Try to consume one token. Refills based on elapsed time, then
384384
// attempts to consume. Returns true if the request is allowed.
385-
boolconsume();
385+
// The caller provides the current timestamp to avoid redundant
386+
// uv_hrtime() calls in hot paths.
387+
boolconsume(uint64_t now);
386388
};
387389

388390
classDebugIndentScopefinal {

β€Žsrc/quic/endpoint.ccβ€Ž

Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,18 @@ STAT_STRUCT(Endpoint, ENDPOINT)
9292
TokenBucket::TokenBucket(double rate, double burst)
9393
: rate(rate), burst(burst), tokens(burst), last_ts(uv_hrtime()) {}
9494

95-
voidTokenBucket::InitOnce(double r, double b) {
95+
voidTokenBucket::InitOnce(double r, double b, uint64_t now) {
9696
if (last_ts == 0) {
9797
rate = r;
9898
burst = b;
9999
tokens = b;
100-
last_ts = uv_hrtime();
100+
last_ts = now;
101101
}
102102
}
103103

104104
// Try to consume one token. Refills based on elapsed time, then
105105
// attempts to consume. Returns true if the request is allowed.
106-
boolTokenBucket::consume() {
107-
uint64_t now = uv_hrtime();
106+
boolTokenBucket::consume(uint64_t now) {
108107
double elapsed = static_cast<double>(now - last_ts) / 1e9; // seconds
109108
last_ts = now;
110109
tokens = std::min(burst, tokens + elapsed * rate);
@@ -1025,9 +1024,9 @@ void Endpoint::SendBatch(Packet::Ptr* packets, size_t count) {
10251024
}
10261025
}
10271026

1028-
voidEndpoint::SendRetry(const PathDescriptor& options) {
1027+
voidEndpoint::SendRetry(const PathDescriptor& options, uint64_t now) {
10291028
Debug(this, "Sending retry on path %s", options);
1030-
if (!retry_bucket_.consume()) {
1029+
if (!retry_bucket_.consume(now)) {
10311030
Debug(this, "Retry rate limit exceeded (global)");
10321031
STAT_INCREMENT(Stats, retry_rate_limited);
10331032
return;
@@ -1041,9 +1040,10 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
10411040
}
10421041
}
10431042

1044-
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options) {
1043+
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options,
1044+
uint64_t now) {
10451045
Debug(this, "Sending version negotiation on path %s", options);
1046-
if (!version_negotiation_bucket_.consume()) {
1046+
if (!version_negotiation_bucket_.consume(now)) {
10471047
Debug(this, "Version negotiation rate limit exceeded (global)");
10481048
STAT_INCREMENT(Stats, version_negotiation_rate_limited);
10491049
return;
@@ -1057,7 +1057,8 @@ void Endpoint::SendVersionNegotiation(const PathDescriptor& options) {
10571057
}
10581058

10591059
boolEndpoint::SendStatelessReset(const PathDescriptor& options,
1060-
size_t source_len) {
1060+
size_t source_len,
1061+
uint64_t now) {
10611062
if (options_.disable_stateless_reset) [[unlikely]] {
10621063
returnfalse;
10631064
}
@@ -1066,7 +1067,7 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10661067
options,
10671068
source_len);
10681069

1069-
if (!stateless_reset_bucket_.consume()) {
1070+
if (!stateless_reset_bucket_.consume(now)) {
10701071
Debug(this, "Stateless reset rate limit exceeded (global)");
10711072
STAT_INCREMENT(Stats, stateless_reset_rate_limited);
10721073
returnfalse;
@@ -1086,12 +1087,13 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10861087
}
10871088

10881089
voidEndpoint::SendImmediateConnectionClose(const PathDescriptor& options,
1089-
QuicError reason) {
1090+
QuicError reason,
1091+
uint64_t now) {
10901092
Debug(this,
10911093
"Sending immediate connection close on path %s with reason %s",
10921094
options,
10931095
reason);
1094-
if (!immediate_close_bucket_.consume()) {
1096+
if (!immediate_close_bucket_.consume(now)) {
10951097
Debug(this, "Immediate connection close rate limit exceeded (global)");
10961098
STAT_INCREMENT(Stats, immediate_close_rate_limited);
10971099
return;
@@ -1313,6 +1315,8 @@ void Endpoint::CloseGracefully() {
13131315
voidEndpoint::Receive(constuint8_t* data,
13141316
size_t len,
13151317
const SocketAddress& remote_address) {
1318+
constuint64_t now = uv_hrtime();
1319+
13161320
constauto receive = [&](Session* session,
13171321
constuint8_t* pkt_data,
13181322
size_t pkt_len,
@@ -1327,7 +1331,12 @@ void Endpoint::Receive(const uint8_t* data,
13271331
// are generated. The deferred flush via BindingData's uv_check
13281332
// callback calls SendPendingData once per dirty session after all
13291333
// packets in the burst have been read.
1330-
if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) {
1334+
if (session->ReadPacket(pkt_data,
1335+
pkt_len,
1336+
local_address,
1337+
remote_address,
1338+
PacketInfo(),
1339+
now)) {
13311340
STAT_INCREMENT_N(Stats, bytes_received, pkt_len);
13321341
STAT_INCREMENT(Stats, packets_received);
13331342
}
@@ -1349,10 +1358,10 @@ void Endpoint::Receive(const uint8_t* data,
13491358

13501359
// Per-host session creation rate limit. The bucket is initialized
13511360
// on first access with the configured rate/burst from options.
1352-
auto info = addr_validation_lru_.Upsert(config.remote_address);
1353-
info->session_creation_bucket.InitOnce(options_.session_creation_rate,
1354-
options_.session_creation_burst);
1355-
if (!info->session_creation_bucket.consume()) {
1361+
auto info = addr_validation_lru_.Upsert(config.remote_address, now);
1362+
info->session_creation_bucket.InitOnce(
1363+
options_.session_creation_rate, options_.session_creation_burst, now);
1364+
if (!info->session_creation_bucket.consume(now)) {
13561365
Debug(this,
13571366
"Session creation rate limit exceeded for %s",
13581367
config.remote_address);
@@ -1451,7 +1460,8 @@ void Endpoint::Receive(const uint8_t* data,
14511460
if (state_->busy) STAT_INCREMENT(Stats, server_busy_count);
14521461
SendImmediateConnectionClose(
14531462
PathDescriptor{version, dcid, scid, local_address, remote_address},
1454-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1463+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1464+
now);
14551465
// The packet was successfully processed, even if we did refuse the
14561466
// connection.
14571467
STAT_INCREMENT(Stats, packets_received);
@@ -1525,7 +1535,8 @@ void Endpoint::Receive(const uint8_t* data,
15251535
Debug(this, "Retry token from %s is invalid.", remote_address);
15261536
SendImmediateConnectionClose(
15271537
PathDescriptor{version, scid, dcid, local_address, remote_address},
1528-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1538+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1539+
now);
15291540
STAT_INCREMENT(Stats, packets_received);
15301541
return;
15311542
}
@@ -1541,7 +1552,7 @@ void Endpoint::Receive(const uint8_t* data,
15411552
// Mark the address as validated since the retry round-trip proves
15421553
// reachability.
15431554
Debug(this, "Remote address %s is validated", remote_address);
1544-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1555+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
15451556
}
15461557

15471558
// Step 2: Address validation β€” decide whether to send a Retry or
@@ -1557,13 +1568,15 @@ void Endpoint::Receive(const uint8_t* data,
15571568
"Initial packet has no token. Sending retry to %s to start "
15581569
"validation",
15591570
remote_address);
1560-
SendRetry(PathDescriptor{
1561-
version,
1562-
dcid,
1563-
scid,
1564-
local_address,
1565-
remote_address,
1566-
});
1571+
SendRetry(
1572+
PathDescriptor{
1573+
version,
1574+
dcid,
1575+
scid,
1576+
local_address,
1577+
remote_address,
1578+
},
1579+
now);
15671580
STAT_INCREMENT(Stats, packets_received);
15681581
return;
15691582
}
@@ -1584,13 +1597,15 @@ void Endpoint::Receive(const uint8_t* data,
15841597
Debug(this,
15851598
"Regular token from %s is invalid.",
15861599
remote_address);
1587-
SendRetry(PathDescriptor{
1588-
version,
1589-
dcid,
1590-
scid,
1591-
local_address,
1592-
remote_address,
1593-
});
1600+
SendRetry(
1601+
PathDescriptor{
1602+
version,
1603+
dcid,
1604+
scid,
1605+
local_address,
1606+
remote_address,
1607+
},
1608+
now);
15941609
STAT_INCREMENT(Stats, packets_received);
15951610
return;
15961611
}
@@ -1602,20 +1617,22 @@ void Endpoint::Receive(const uint8_t* data,
16021617
Debug(this,
16031618
"Initial packet from %s has unknown token type",
16041619
remote_address);
1605-
SendRetry(PathDescriptor{
1606-
version,
1607-
dcid,
1608-
scid,
1609-
local_address,
1610-
remote_address,
1611-
});
1620+
SendRetry(
1621+
PathDescriptor{
1622+
version,
1623+
dcid,
1624+
scid,
1625+
local_address,
1626+
remote_address,
1627+
},
1628+
now);
16121629
STAT_INCREMENT(Stats, packets_received);
16131630
return;
16141631
}
16151632
}
16161633

16171634
Debug(this, "Remote address %s is validated", remote_address);
1618-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1635+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
16191636
} elseif (hd.tokenlen > 0) {
16201637
Debug(this,
16211638
"Ignoring initial packet from %s with unexpected token",
@@ -1627,13 +1644,15 @@ void Endpoint::Receive(const uint8_t* data,
16271644
if (options_.validate_address) {
16281645
Debug(
16291646
this, "Sending retry to %s due to 0RTT packet", remote_address);
1630-
SendRetry(PathDescriptor{
1631-
version,
1632-
dcid,
1633-
scid,
1634-
local_address,
1635-
remote_address,
1636-
});
1647+
SendRetry(
1648+
PathDescriptor{
1649+
version,
1650+
dcid,
1651+
scid,
1652+
local_address,
1653+
remote_address,
1654+
},
1655+
now);
16371656
STAT_INCREMENT(Stats, packets_received);
16381657
return;
16391658
}
@@ -1742,8 +1761,12 @@ void Endpoint::Receive(const uint8_t* data,
17421761
pversion_cid.version);
17431762
CIDdcid(pversion_cid.dcid, pversion_cid.dcidlen);
17441763
CIDscid(pversion_cid.scid, pversion_cid.scidlen);
1745-
SendVersionNegotiation(PathDescriptor{
1746-
pversion_cid.version, dcid, scid, local_address(), remote_address});
1764+
SendVersionNegotiation(PathDescriptor{pversion_cid.version,
1765+
dcid,
1766+
scid,
1767+
local_address(),
1768+
remote_address},
1769+
now);
17471770
STAT_INCREMENT(Stats, packets_received);
17481771
return;
17491772
}
@@ -1822,7 +1845,8 @@ void Endpoint::Receive(const uint8_t* data,
18221845
SendStatelessReset(
18231846
PathDescriptor{
18241847
pversion_cid.version, dcid, scid, addr, remote_address},
1825-
len);
1848+
len,
1849+
now);
18261850
return;
18271851
}
18281852

@@ -1884,13 +1908,14 @@ void Endpoint::MemoryInfo(MemoryTracker* tracker) const {
18841908
// Endpoint::SocketAddressInfoTraits
18851909

18861910
boolEndpoint::SocketAddressInfoTraits::CheckExpired(
1887-
const SocketAddress& address, const Type& type) {
1888-
return (uv_hrtime() - type.timestamp) > kSocketAddressInfoTimeout;
1911+
const SocketAddress& address, const Type& type, uint64_t now) {
1912+
return (now - type.timestamp) > kSocketAddressInfoTimeout;
18891913
}
18901914

18911915
voidEndpoint::SocketAddressInfoTraits::Touch(const SocketAddress& address,
1892-
Type* type) {
1893-
type->timestamp = uv_hrtime();
1916+
Type* type,
1917+
uint64_t now) {
1918+
type->timestamp = now;
18941919
}
18951920

18961921
// ======================================================================================

0 commit comments

Comments
Β (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Commit c726a89

Browse files
jasnelladuh95
authored andcommitted
quic: cache timestamp for address lru cache
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Backport-PR-URL: #64675 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 11778a7 commit c726a89

8 files changed

Lines changed: 192 additions & 107 deletions

File tree

β€Žsrc/node_sockaddr-inl.hβ€Ž

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ typename T::Type* SocketAddressLRU<T>::Peek(
186186
}
187187

188188
template <typename T>
189-
void SocketAddressLRU<T>::CheckExpired() {
189+
void SocketAddressLRU<T>::CheckExpired(uint64_t now) {
190190
auto it = list_.rbegin();
191191
while (it != list_.rend()) {
192-
if (T::CheckExpired(it->first, it->second)) {
192+
if (T::CheckExpired(it->first, it->second, now)) {
193193
map_.erase(it->first);
194194
list_.pop_back();
195195
it = list_.rbegin();
@@ -211,21 +211,20 @@ void SocketAddressLRU<T>::MemoryInfo(MemoryTracker* tracker) const {
211211
// cache and adjust if necessary. Whether the item exists or not,
212212
// purge expired items.
213213
template <typename T>
214-
typename T::Type* SocketAddressLRU<T>::Upsert(
215-
const SocketAddress& address) {
216-
217-
auto on_exit = OnScopeLeave([&]() { CheckExpired(); });
214+
typename T::Type* SocketAddressLRU<T>::Upsert(const SocketAddress& address,
215+
uint64_t now) {
216+
auto on_exit = OnScopeLeave([&]() { CheckExpired(now); });
218217

219218
auto it = map_.find(address);
220219
if (it != std::end(map_)) {
221220
list_.splice(list_.begin(), list_, it->second);
222-
T::Touch(it->first, &it->second->second);
221+
T::Touch(it->first, &it->second->second, now);
223222
return &it->second->second;
224223
}
225224

226225
list_.push_front(Pair(address, { }));
227226
map_[address] = list_.begin();
228-
T::Touch(list_.begin()->first, &list_.begin()->second);
227+
T::Touch(list_.begin()->first, &list_.begin()->second, now);
229228

230229
// Drop the last item in the list if we are
231230
// over the size limit...

β€Žsrc/node_sockaddr.hβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ class SocketAddressLRU : public MemoryRetainer {
213213
// If the item already exists, returns a reference to
214214
// the existing item, adjusting items position in the
215215
// LRU. If the item does not exist, emplaces the item
216-
// and returns the new item.
217-
Type* Upsert(const SocketAddress& address);
216+
// and returns the new item. The caller provides a
217+
// timestamp to avoid redundant uv_hrtime() calls.
218+
Type* Upsert(const SocketAddress& address, uint64_t now);
218219

219220
// Returns a reference to the item if it exists, or
220221
// nullptr. The position in the LRU is not modified.
@@ -231,7 +232,7 @@ class SocketAddressLRU : public MemoryRetainer {
231232
using Pair = std::pair<SocketAddress, Type>;
232233
using Iterator = typename std::list<Pair>::iterator;
233234

234-
voidCheckExpired();
235+
voidCheckExpired(uint64_t now);
235236

236237
std::list<Pair> list_;
237238
SocketAddress::Map<Iterator> map_;

β€Žsrc/quic/defs.hβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,13 @@ struct TokenBucket final {
378378
// hasn't been initialized yet (last_ts == 0). Used for per-host
379379
// buckets in the address LRU where the rate/burst aren't known
380380
// at construction time.
381-
voidInitOnce(double r, double b);
381+
voidInitOnce(double r, double b, uint64_t now);
382382

383383
// Try to consume one token. Refills based on elapsed time, then
384384
// attempts to consume. Returns true if the request is allowed.
385-
boolconsume();
385+
// The caller provides the current timestamp to avoid redundant
386+
// uv_hrtime() calls in hot paths.
387+
boolconsume(uint64_t now);
386388
};
387389

388390
classDebugIndentScopefinal {

β€Žsrc/quic/endpoint.ccβ€Ž

Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,18 @@ STAT_STRUCT(Endpoint, ENDPOINT)
9292
TokenBucket::TokenBucket(double rate, double burst)
9393
: rate(rate), burst(burst), tokens(burst), last_ts(uv_hrtime()) {}
9494

95-
voidTokenBucket::InitOnce(double r, double b) {
95+
voidTokenBucket::InitOnce(double r, double b, uint64_t now) {
9696
if (last_ts == 0) {
9797
rate = r;
9898
burst = b;
9999
tokens = b;
100-
last_ts = uv_hrtime();
100+
last_ts = now;
101101
}
102102
}
103103

104104
// Try to consume one token. Refills based on elapsed time, then
105105
// attempts to consume. Returns true if the request is allowed.
106-
boolTokenBucket::consume() {
107-
uint64_t now = uv_hrtime();
106+
boolTokenBucket::consume(uint64_t now) {
108107
double elapsed = static_cast<double>(now - last_ts) / 1e9; // seconds
109108
last_ts = now;
110109
tokens = std::min(burst, tokens + elapsed * rate);
@@ -1025,9 +1024,9 @@ void Endpoint::SendBatch(Packet::Ptr* packets, size_t count) {
10251024
}
10261025
}
10271026

1028-
voidEndpoint::SendRetry(const PathDescriptor& options) {
1027+
voidEndpoint::SendRetry(const PathDescriptor& options, uint64_t now) {
10291028
Debug(this, "Sending retry on path %s", options);
1030-
if (!retry_bucket_.consume()) {
1029+
if (!retry_bucket_.consume(now)) {
10311030
Debug(this, "Retry rate limit exceeded (global)");
10321031
STAT_INCREMENT(Stats, retry_rate_limited);
10331032
return;
@@ -1041,9 +1040,10 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
10411040
}
10421041
}
10431042

1044-
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options) {
1043+
voidEndpoint::SendVersionNegotiation(const PathDescriptor& options,
1044+
uint64_t now) {
10451045
Debug(this, "Sending version negotiation on path %s", options);
1046-
if (!version_negotiation_bucket_.consume()) {
1046+
if (!version_negotiation_bucket_.consume(now)) {
10471047
Debug(this, "Version negotiation rate limit exceeded (global)");
10481048
STAT_INCREMENT(Stats, version_negotiation_rate_limited);
10491049
return;
@@ -1057,7 +1057,8 @@ void Endpoint::SendVersionNegotiation(const PathDescriptor& options) {
10571057
}
10581058

10591059
boolEndpoint::SendStatelessReset(const PathDescriptor& options,
1060-
size_t source_len) {
1060+
size_t source_len,
1061+
uint64_t now) {
10611062
if (options_.disable_stateless_reset) [[unlikely]] {
10621063
returnfalse;
10631064
}
@@ -1066,7 +1067,7 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10661067
options,
10671068
source_len);
10681069

1069-
if (!stateless_reset_bucket_.consume()) {
1070+
if (!stateless_reset_bucket_.consume(now)) {
10701071
Debug(this, "Stateless reset rate limit exceeded (global)");
10711072
STAT_INCREMENT(Stats, stateless_reset_rate_limited);
10721073
returnfalse;
@@ -1086,12 +1087,13 @@ bool Endpoint::SendStatelessReset(const PathDescriptor& options,
10861087
}
10871088

10881089
voidEndpoint::SendImmediateConnectionClose(const PathDescriptor& options,
1089-
QuicError reason) {
1090+
QuicError reason,
1091+
uint64_t now) {
10901092
Debug(this,
10911093
"Sending immediate connection close on path %s with reason %s",
10921094
options,
10931095
reason);
1094-
if (!immediate_close_bucket_.consume()) {
1096+
if (!immediate_close_bucket_.consume(now)) {
10951097
Debug(this, "Immediate connection close rate limit exceeded (global)");
10961098
STAT_INCREMENT(Stats, immediate_close_rate_limited);
10971099
return;
@@ -1313,6 +1315,8 @@ void Endpoint::CloseGracefully() {
13131315
voidEndpoint::Receive(constuint8_t* data,
13141316
size_t len,
13151317
const SocketAddress& remote_address) {
1318+
constuint64_t now = uv_hrtime();
1319+
13161320
constauto receive = [&](Session* session,
13171321
constuint8_t* pkt_data,
13181322
size_t pkt_len,
@@ -1327,7 +1331,12 @@ void Endpoint::Receive(const uint8_t* data,
13271331
// are generated. The deferred flush via BindingData's uv_check
13281332
// callback calls SendPendingData once per dirty session after all
13291333
// packets in the burst have been read.
1330-
if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) {
1334+
if (session->ReadPacket(pkt_data,
1335+
pkt_len,
1336+
local_address,
1337+
remote_address,
1338+
PacketInfo(),
1339+
now)) {
13311340
STAT_INCREMENT_N(Stats, bytes_received, pkt_len);
13321341
STAT_INCREMENT(Stats, packets_received);
13331342
}
@@ -1349,10 +1358,10 @@ void Endpoint::Receive(const uint8_t* data,
13491358

13501359
// Per-host session creation rate limit. The bucket is initialized
13511360
// on first access with the configured rate/burst from options.
1352-
auto info = addr_validation_lru_.Upsert(config.remote_address);
1353-
info->session_creation_bucket.InitOnce(options_.session_creation_rate,
1354-
options_.session_creation_burst);
1355-
if (!info->session_creation_bucket.consume()) {
1361+
auto info = addr_validation_lru_.Upsert(config.remote_address, now);
1362+
info->session_creation_bucket.InitOnce(
1363+
options_.session_creation_rate, options_.session_creation_burst, now);
1364+
if (!info->session_creation_bucket.consume(now)) {
13561365
Debug(this,
13571366
"Session creation rate limit exceeded for %s",
13581367
config.remote_address);
@@ -1451,7 +1460,8 @@ void Endpoint::Receive(const uint8_t* data,
14511460
if (state_->busy) STAT_INCREMENT(Stats, server_busy_count);
14521461
SendImmediateConnectionClose(
14531462
PathDescriptor{version, dcid, scid, local_address, remote_address},
1454-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1463+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1464+
now);
14551465
// The packet was successfully processed, even if we did refuse the
14561466
// connection.
14571467
STAT_INCREMENT(Stats, packets_received);
@@ -1525,7 +1535,8 @@ void Endpoint::Receive(const uint8_t* data,
15251535
Debug(this, "Retry token from %s is invalid.", remote_address);
15261536
SendImmediateConnectionClose(
15271537
PathDescriptor{version, scid, dcid, local_address, remote_address},
1528-
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED));
1538+
QuicError::ForTransport(NGTCP2_CONNECTION_REFUSED),
1539+
now);
15291540
STAT_INCREMENT(Stats, packets_received);
15301541
return;
15311542
}
@@ -1541,7 +1552,7 @@ void Endpoint::Receive(const uint8_t* data,
15411552
// Mark the address as validated since the retry round-trip proves
15421553
// reachability.
15431554
Debug(this, "Remote address %s is validated", remote_address);
1544-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1555+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
15451556
}
15461557

15471558
// Step 2: Address validation β€” decide whether to send a Retry or
@@ -1557,13 +1568,15 @@ void Endpoint::Receive(const uint8_t* data,
15571568
"Initial packet has no token. Sending retry to %s to start "
15581569
"validation",
15591570
remote_address);
1560-
SendRetry(PathDescriptor{
1561-
version,
1562-
dcid,
1563-
scid,
1564-
local_address,
1565-
remote_address,
1566-
});
1571+
SendRetry(
1572+
PathDescriptor{
1573+
version,
1574+
dcid,
1575+
scid,
1576+
local_address,
1577+
remote_address,
1578+
},
1579+
now);
15671580
STAT_INCREMENT(Stats, packets_received);
15681581
return;
15691582
}
@@ -1584,13 +1597,15 @@ void Endpoint::Receive(const uint8_t* data,
15841597
Debug(this,
15851598
"Regular token from %s is invalid.",
15861599
remote_address);
1587-
SendRetry(PathDescriptor{
1588-
version,
1589-
dcid,
1590-
scid,
1591-
local_address,
1592-
remote_address,
1593-
});
1600+
SendRetry(
1601+
PathDescriptor{
1602+
version,
1603+
dcid,
1604+
scid,
1605+
local_address,
1606+
remote_address,
1607+
},
1608+
now);
15941609
STAT_INCREMENT(Stats, packets_received);
15951610
return;
15961611
}
@@ -1602,20 +1617,22 @@ void Endpoint::Receive(const uint8_t* data,
16021617
Debug(this,
16031618
"Initial packet from %s has unknown token type",
16041619
remote_address);
1605-
SendRetry(PathDescriptor{
1606-
version,
1607-
dcid,
1608-
scid,
1609-
local_address,
1610-
remote_address,
1611-
});
1620+
SendRetry(
1621+
PathDescriptor{
1622+
version,
1623+
dcid,
1624+
scid,
1625+
local_address,
1626+
remote_address,
1627+
},
1628+
now);
16121629
STAT_INCREMENT(Stats, packets_received);
16131630
return;
16141631
}
16151632
}
16161633

16171634
Debug(this, "Remote address %s is validated", remote_address);
1618-
addr_validation_lru_.Upsert(remote_address)->validated = true;
1635+
addr_validation_lru_.Upsert(remote_address, now)->validated = true;
16191636
} elseif (hd.tokenlen > 0) {
16201637
Debug(this,
16211638
"Ignoring initial packet from %s with unexpected token",
@@ -1627,13 +1644,15 @@ void Endpoint::Receive(const uint8_t* data,
16271644
if (options_.validate_address) {
16281645
Debug(
16291646
this, "Sending retry to %s due to 0RTT packet", remote_address);
1630-
SendRetry(PathDescriptor{
1631-
version,
1632-
dcid,
1633-
scid,
1634-
local_address,
1635-
remote_address,
1636-
});
1647+
SendRetry(
1648+
PathDescriptor{
1649+
version,
1650+
dcid,
1651+
scid,
1652+
local_address,
1653+
remote_address,
1654+
},
1655+
now);
16371656
STAT_INCREMENT(Stats, packets_received);
16381657
return;
16391658
}
@@ -1742,8 +1761,12 @@ void Endpoint::Receive(const uint8_t* data,
17421761
pversion_cid.version);
17431762
CIDdcid(pversion_cid.dcid, pversion_cid.dcidlen);
17441763
CIDscid(pversion_cid.scid, pversion_cid.scidlen);
1745-
SendVersionNegotiation(PathDescriptor{
1746-
pversion_cid.version, dcid, scid, local_address(), remote_address});
1764+
SendVersionNegotiation(PathDescriptor{pversion_cid.version,
1765+
dcid,
1766+
scid,
1767+
local_address(),
1768+
remote_address},
1769+
now);
17471770
STAT_INCREMENT(Stats, packets_received);
17481771
return;
17491772
}
@@ -1822,7 +1845,8 @@ void Endpoint::Receive(const uint8_t* data,
18221845
SendStatelessReset(
18231846
PathDescriptor{
18241847
pversion_cid.version, dcid, scid, addr, remote_address},
1825-
len);
1848+
len,
1849+
now);
18261850
return;
18271851
}
18281852

@@ -1884,13 +1908,14 @@ void Endpoint::MemoryInfo(MemoryTracker* tracker) const {
18841908
// Endpoint::SocketAddressInfoTraits
18851909

18861910
boolEndpoint::SocketAddressInfoTraits::CheckExpired(
1887-
const SocketAddress& address, const Type& type) {
1888-
return (uv_hrtime() - type.timestamp) > kSocketAddressInfoTimeout;
1911+
const SocketAddress& address, const Type& type, uint64_t now) {
1912+
return (now - type.timestamp) > kSocketAddressInfoTimeout;
18891913
}
18901914

18911915
voidEndpoint::SocketAddressInfoTraits::Touch(const SocketAddress& address,
1892-
Type* type) {
1893-
type->timestamp = uv_hrtime();
1916+
Type* type,
1917+
uint64_t now) {
1918+
type->timestamp = now;
18941919
}
18951920

18961921
// ======================================================================================

0 commit comments

Comments
Β (0)