ModernUO/Projects/UOContent
Kamron Batman 8e5b7af1cf
perf: retire auto-denylist holds from an expiry ring instead of scanning
Reclaiming lapsed holds was O(entries held). Every cap-triggered reclaim
during a flood, and every periodic sweep, walked the whole dictionary to
find the few that had expired. That is what forced the cap to be sized for
the scan rather than for the flood.

A hold is now never refreshed: the first detection sets the expiry and
later ones leave it alone. That makes insertion order equal to expiry order,
so a ring of the same keys is sorted by construction and retiring lapsed
entries stops at the first live record -- the cost is the number expiring,
not the number held. Nothing is lost by dropping the refresh: the rate
limiter runs ahead of the connection filters (NetState.Network.cs) and
reports to the ban channel, so a flooder whose hold lapses is re-held on its
next attempt.

Because the ring carries the expiry, the dictionary only had to answer
membership, so it is a HashSet now: 36 bytes a slot against 52. The ring is
parallel UInt128[]/long[] rather than an array of structs -- UInt128 forces
16-byte alignment, so a packed pair would cost 32 bytes where these cost 24,
and the drain reads only the long[].

Measured, standalone copies of both designs head to head at the shipped
324,449 cap:

  accept path, nothing held      9.1ns -> 6.1ns per call
  sustained flood at cap        26.7ms -> 9.3ms over 60k rejected addresses
  flood end, realistic spread    9.49ms -> 0.05ms worst single call
  flood end, all at one instant  8.85ms -> 10.69ms

The third line is the point: the on-loop stall drops 190x because the work
is spread across the calls that were happening anyway. Two honest costs.
Total work over that spread rises 1.9x (sequential dictionary scan beats
random-access set removes on cache), and the synthetic case where every
entry shares one expiry millisecond is 21% worse. Peak latency is the
currency for a game loop, and reaching that synthetic case needs an entire
flood to land inside one millisecond.

The SweepThrottleMs added earlier goes away with the scan it was throttling.
The periodic timer stays, now O(expiring), purely to reclaim memory on a
shard that goes quiet after a flood.

Release now purges the ring record too. Nothing records that a key was
released, so a re-detection before the old record lapsed would otherwise be
retired early by it. O(n), on an operator retraction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 22:13:31 -07:00
..
Accounting perf(login): run password hashing on a parked worker thread (#2566) 2026-08-09 00:13:34 -07:00
Assistants chore: Use var everywhere (#2294) 2025-12-27 16:47:28 -08:00
Commands perf: Sleep the event loop when idle. Fixes networking micro-stalls. Adds event loop instrumentation. (#2559) 2026-08-09 13:24:59 -07:00
Compression feat: Implements new robust/pluggable backup/archive system. (#2388) 2026-03-22 19:51:20 -07:00
Configuration fix: Streamlines insurance. Insurance only executes when enabled. (#2550) 2026-07-26 09:47:49 -07:00
Console feat: Adds console commands (#1714) 2024-03-30 09:40:43 -07:00
Context Menus fix: Moves ContextMenu out of core, streamlines code, fixes bugs (#1873) 2024-07-20 21:33:23 -07:00
Engines fix: Consolidate PlayerConstructed onto Item, stamped by the craft system (#2574) 2026-08-13 18:34:59 -07:00
Gumps fix: harden idle-sleep scheduling against bad config and misattributed saves (#2567) 2026-08-09 22:05:18 -07:00
Holiday Stuff perf: Migrate Veteran Reward gumps to DynamicGump/StaticGump (#2415) 2026-04-25 20:09:27 -07:00
Items refactor: Move legacy deserialization into the .Migrations.cs partials (#2575) 2026-08-13 18:43:53 -07:00
Migrations fix: Consolidate PlayerConstructed onto Item, stamped by the craft system (#2574) 2026-08-13 18:34:59 -07:00
Misc fix: Removes unnecessary dictionary removal guards (#2565) 2026-08-08 11:50:01 -07:00
Mobiles fix: Removes unnecessary dictionary removal guards (#2565) 2026-08-08 11:50:01 -07:00
Multis fix(housing): register doors, and stop crashing on client component sheets (#2557) 2026-08-04 20:05:28 -07:00
Network perf: retire auto-denylist holds from an expiry ring instead of scanning 2026-08-13 22:13:31 -07:00
Regions fix: Fixes criminals having guards called on them (#2348) 2026-03-13 17:35:47 -07:00
Skills fix: Removes unnecessary dictionary removal guards (#2565) 2026-08-08 11:50:01 -07:00
Special Systems feat: Add zero-alloc interpolation handler to ValueStringBuilder, replace all StringBuilder usage (#2387) 2026-03-22 14:23:44 -07:00
Spells fix(necromancy): correct Blood Oath duration, reflection, and expiry timing (#1690) (#2480) 2026-06-08 23:42:28 -07:00
Systems/JailSystem fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
Targets chore: Use var everywhere (#2294) 2025-12-27 16:47:28 -08:00
Text fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
Utilities perf(login): run password hashing on a parked worker thread (#2566) 2026-08-09 00:13:34 -07:00
World Saves perf(messages): mechanical interpolation cleanups (#2436) 2026-05-03 18:26:49 -07:00
Module.cs fix: Fixes sending packets and sidesteps a major issue with stackalloc and PGO in .NET 8 (#1607) 2023-11-21 12:18:20 -08:00
UOContent.csproj fix: Fixes Argon2 verify correctness and the password upgrade lockout (#2562) 2026-08-08 00:24:59 -07:00