Fix SessionManager world retention after player quit and world unload#2281
Fix SessionManager world retention after player quit and world unload#2281ikoliHU wants to merge 2 commits into
Conversation
The bypass cache is keyed by WorldPlayerTuple, which holds strong references to both the world and the player (and thus the platform player object and its world). With expireAfterWrite the entries are only evicted lazily, so when the cache goes idle an entry can keep a world reachable long after its 2 second lifetime. The session cache value likewise retains the last world through Session#lastValid for up to the 10 minute access timeout. Add SessionManager#forget(LocalPlayer) and #forgetWorld(World) to explicitly drop these entries, call forget on player quit, forgetWorld on world unload, and force cache cleanup on each session tick so idle entries cannot pin a player or world indefinitely.
This comment was marked as low quality.
This comment was marked as low quality.
|
Thanks for the PR, I haven't had a chance to look through it properly, but from a quick look it seems to break some functionality. Sessions are meant to stay around for a while after the player leaves, so clearing them from the cache breaks some functionality there. Clearing the world itself is likely fine, but not removing the session itself. That should likely instead be handled by setting lastValid to null in the uninitialize function, rather than clearing the player from the cache. |
Instead of forgetting the player, Session#uninitialize now clears lastValid and lastRegionSet, so the lingering session no longer keeps its last world reachable. The session itself stays cached. Also removed the now-unused SessionManager#forget(LocalPlayer). World-level clearing with forgetWorld and WorldUnloadEvent, plus the periodic cache cleanup, are retained. I tested the changes and my tests still passed. SlimeInMemoryWorld instances no longer got stuck.
|
Thanks for the review, you were right. I pushed a follow-up with the suggested approach. Updated from the previous version:
I kept the world-level cleanup:
I tested these changes as well, and my tests still passed. I hope this is the change you had in mind. |
Problem
SessionManagercan keep unloaded worlds reachable through cached player/session state.This is visible with island/Slime worlds. After a player leaves and the world is unloaded, a heap dump can still show the world retained through:
There are two relevant cache paths:
The bypass cache uses
LoadingCache<WorldPlayerTuple, Boolean>.WorldPlayerTuplestrongly references both theWorldand theLocalPlayer, which also keeps the platform player object reachable. The cache usesexpireAfterWrite(2s), but Guava removes expired entries lazily during cache operations. If the cache is idle, an expired entry can remain reachable longer than intended.The session cache uses
Cache<CacheKey, Session>. The key weakly references the player, but theSessionvalue can still retain the last world throughSession#lastValid. On quit the session is uninitialized, but the cache entry is not removed.Changes
SessionManager#forget(LocalPlayer).SessionManager#forgetWorld(World).AbstractSessionManager.forget(player)after session uninitialization onPlayerQuitEvent.forgetWorld(world)onWorldUnloadEvent.Result
forgetorforgetWorlddirectly instead of accessing WorldGuard internals.Testing
:worldguard-core:compileJavapassed.:worldguard-bukkit:compileJavapassed.:worldguard-core:checkstyleMainpassed.:worldguard-bukkit:checkstyleMainpassed.SlimeInMemoryWorldorSlimeLevelInstanceretained throughSessionManagerin heap dumps.