-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Optimize spawn_batch + relations #19932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Optimize spawn_batch + relations #19932
Conversation
DebugName::type_name::<Self>() | ||
); | ||
entity_world.remove::<Self>(); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure we want this branch but in the case where the original entity doesn't exist either, I thought that information could be useful to our user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My first thought was that warning here was consistent with the old code, but now I'm leaning towards not logging anything.
If we hit this branch, then the entity
was despawned in between the observer and the command. And the despawn hook would have tried to remove entity
from the Self::RelationshipTarget
when that happened. So it's okay that we can't add entity
to Self::RelationshipTarget
, because we want it removed anyway.
Looking at it another way: The warnings here are when you try to add a relation and the hook prevents it from being added. In this case, the hook didn't prevent it; some other hook or observer did by despawning the entity.
But mostly I don't expect that branch to ever be hit, so I don't think it matters much either way!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that branch is rarely ever hit and it hurts readability a bit. On the other hand, When it is hit, it means our user got some debugging to do! And even though the culprit is elsewhere, it helps to know that the hook failed instead of leaving our user in the dark.
@gwafotapa, is there anything you need to do before pulling this out of draft? |
@alice-i-cecile Nope. Just forgot! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
@@ -126,27 +126,40 @@ pub trait Relationship: Component + Sized { | |||
world.commands().entity(entity).remove::<Self>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I was expecting to pull in the code starting from let target_entity = ...
into the closure, as well, since it also ends with a command. But I can't convince myself it matters one way or the other, so keeping the change small seems better!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I preferred having the target_entity
locked in before deferring. If we get the target_entity
from the command, our Relationship
component could have been modified and we could end up applying the command to the wrong target. With that, it made sense to run the next block right away as well.
DebugName::type_name::<Self>() | ||
); | ||
entity_world.remove::<Self>(); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My first thought was that warning here was consistent with the old code, but now I'm leaning towards not logging anything.
If we hit this branch, then the entity
was despawned in between the observer and the command. And the despawn hook would have tried to remove entity
from the Self::RelationshipTarget
when that happened. So it's okay that we can't add entity
to Self::RelationshipTarget
, because we want it removed anyway.
Looking at it another way: The warnings here are when you try to add a relation and the hook prevents it from being added. In this case, the hook didn't prevent it; some other hook or observer did by despawning the entity.
But mostly I don't expect that branch to ever be hit, so I don't think it matters much either way!
world.commands().entity(entity).remove::<Self>(); | ||
} | ||
|
||
// Deferring is necessary for batch mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is a little unclear without the context of the original PR. If I understand the problem, maybe something like
// Deferring is necessary for batch mode | |
// Defer in case we need to insert a `Self::RelationshipTarget` component. | |
// It may be added by a hook for a different entity during batch insert, | |
// so also defer checking whether it exists. |
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. This is unclear for posterity. I'll complete it.
Objective
Fixes #19891
Follow-up of #19519
The
on_insert
hook ofRelationship
is deferring anEntityCommand
that could fail if the entity has been despawned and leave us in an invalid state. That command also usesEntityEntryCommands::or_insert_with
which may perform an unnecessary allocation because it always runs its closure at the time of this writing.See #19891 for details.
Solution
Defer the whole operation of the hook. This postpones the lookup, making sure the entity has not been despawned. It also uses Entry::or_insert_with instead of
EntityEntryCommands::or_insert_with
.Testing
No additional testing.