Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.Sign up
Regionized Entity (and Tile) Ticking #1001
This issue is to serve as a note holder and idea registry for an idea I had today on being able to tick entities in a parallel fashion, safely (at least, safe enough) - Please read entire issue before you go jumping "THIS ISN'T SAFE!!!"
This design is intended to benefit servers that have players more spread out where they are nowhere near each other.
The goal of this idea is to not be 100% thread safe, but to do things that under most conditions, would be practically safe, that in realistic scenarios are unlikely to cause issues.
Break world ticking into a 3 stage process
Stage 1: doTick method - All World Operations that would happen for each world pre entity/tile entity ticking, sequentially, no parallelism, as it happens today.
Stage 3: All post tick entity operations per world such as Entity Tracker, done sequentially, with no parallel (we can investigate applying parallel behavior to Entity Tracker in a later change)
During Parallel Region Execution, the main thread will be suspended, blocked waiting for the thread pools to completely empty.
This reduces risk of concurrency issues down to if a plugin itself acts on an event based on an Entity or Tile Entity, and then finds a relationship or scans the entire world for another entity, that happens to be in a different region.
This scenario is of course possible to occur, but not going to be super common.
If a plugin does end up responding to 1 entity event by acting on a completely different entity event, we then have to consider what is the plugin doing to the other entity.
I see it as very unlikely that even if a plugin does act on another regions entity, that it would even do something that would have a negative outcome.
The closest risky scenario I could see is:
This is the worst case scenario I can see, as you have 2 threads operating in the same location of the world, both removing blocks, and an entity may be receiving damage while mid its own tick, causing weird results.
However, the likelyhood that the server would crash here should be pretty minimal. What ultimately would come out of this bad scenario is behavioral differences.
Ultimately, you would of never even knew the outcome would of been different without regionized ticking.
In the event that state does get in a weird way, any error thrown would simply remove the entity, it would not crash the server.
Concurrency of Plugin Events
For any event fired on these threads, we should synchronize ensuring that no 2 events are firing at the exact same time.
For plugins that are checking Bukkit.isPrimaryThread, we would update the isPrimaryThread to check the ThreadLocal isSafeThread mentioned in stage 3. If this boolean is set to true, we return true.
This will handle all of the Async Catcher operations too.
Entity Teleportation Mid Event - A resolution to teleporting Concerns (Creeper Scenario)
If an event causes an Entity to Teleport mid event, causing it to change regions, such as the Creeper scenario, we can even improve risk here by:
Closing / Feedback
This idea isn't "final". I am opening this now to collect feedback and more risk scenarios from the community so we can design ideas to solve them.
If we can pull this off, this will pretty much be the largest performance improvement we will ever do for the server.
There is no ETA on this. This is an idea I had, and I would love to implement myself, but I am also extremely busy with work for the next few months... so I don't know when.
I don't want to hear any "This won't work" or "This isn't safe". I perfectly know this isn't 100% safe. That's not my goal. My goal is practically safe.
Give me scenarios that could result in noticeable negative behavior that could be considered "Deal breaking" that would force people to keep this improvement off.
This honestly sounds look a cool concept, the practice of regions should keep us reasonably safe, especially as beyond teleporting, it's kinda rare that a plugin does something outside of its own "region" based upon some event.
The concept of regions should do a lot in terms of preventing issues around concurrency in "vanilla", only real concerns for me would be teleports (which you've covered).
my only real concern around this (beyond the obvious) is the plugin manager and operations such as teleporting where you intend to block.
Good catch. We would need to detect that scenario and then avoid the extra block, and "hope for best"
Redstone would be part of block ticking and thats not in scope of this attempt.
TE's are a "phase 2" goal, I first want us to get just Entity Ticking done (batch TE's into Stage 3)
As Command Blocks in particular cause a lot of concerns for this ,as they can execute commands that move entities and world data.
But now I just realized we have Command Block Minecarts too... so orz thats an issue.
Looking for ideas on how to handle command blocks? I'm thinking we can skip them in region phase and do them in stage 3.
What about plugins that use custom entities or custom AI for entities? I know that's not API, but there are a lot of widely used plugins that do things like that.. (Citizens, pets, a lot of minigames, ...) And those plugins often have a global state that would not be accessed synchronized
@Brokkonaut Plugin events would be synchronized, which would be the primary way to react to those entities.
Timers for a entity would not be parallel.
any custom aI rules would be accessing local entity state for the most part.
The only way you could run into issues here is if the plugin extends PathfinderGoal NMS level, and inserts the custom object into the goal selectors, and then that causes that goal to be ticked in parallel.
And we can resolve that too, by making any custom goal selector (ie, selector is not located in the NMS package), synchronize on execution, preventing any 2 custom goal selectors from ticking at same time.
I've attempted to implement (roughly) this in the past few days and fixed some small concurrency issues along the way, but a large issue remains: Chunk Loading.
Plugins have the ability to load chunks during events, which can be called during entity ticking. The chunk provider class and possibly the chunk would have to be made thread safe when creating chunks async and populating them to a map while another thread in parallel reads it. Accessing the chunk map is already a very hot method. Not sure if there is another alternative method without implementing some locking mechanism.
That's one notable issue I encountered on a ~100 player server for a few hours.
@M-AJ good catch. We would need to synchronize on chunk loading (but not getting if its already in map),
might have to handle delaying any unload called during this phase if a plugin did something really weird?
I removed some synchronization in 1.13, but maybe if we reduce how much sync is done by not using an object other stuff uses, we can reduce contention on main, especially compared to the gains received from PRT.
I would say it can be pretty common (unless I misunderstood you here), maybe not all plugins do it, but I see many places where this can be used, especially plugins that adds own entities, like pets. And event about pet might affect other pets or owning player, even if they are not in same tick region.
But events will be still executed on different threads? isn't that risky due to how JVM/jit works? Especially if someone will register event with simple non-volatile variables that are modified (set) by event. If I'm not mistaken there is a chance that change in this variable will not be visible from other thread as it will be cached.
Tho we can always add a bit of black magic and cover most basic cases like that by transforming plugins code on load. But it does not sounds right.