I call this Heisenbug error because the error only exists if it's not observed. Here is the call :
@selected_members = Member.where(id: params[:member_ids])
@existing_members = list.members
@new_members = @selected_members.where('id not in (?)', @existing_members.map(&:id))
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.map(&:id))
Rails.logger.info @members_to_lose.length # Remove this line and it no longer works
list.members = @selected_members
render json: {members_to_add: @new_members, members_to_lose: @members_to_lose}
Nothing out of the ordinary whatsoever.
If I remove the Rails.logger.info
call, then @members_to_lose
returns incorrectly ( as in it returns blank ).
If I place the Logger or the Debugger after the list.members = @selected_members
line, then the @members_to_lose
is emptied, and returns incorrectly ( as in it returns blank ).
If instead of a Rails.logger.info
, I just place a debugger
before the list.members
line, then it will return correctly.
What is happening here? Is this a ruby race condition? I have nothing in my code that would affect this code whatsoever. List.members
is a simple has_many
relationship.
Rails queries are lazily evaluated - they are executed the moment you actually need them.
Rails.logger.info @members_to_lose.length
This triggers the query because you call the method length. When you skip this line the query gets executed at:
render json: {members_to_add: @new_members, members_to_lose: @members_to_lose}
At this point you have saved the new members.
To execute the query add load:
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.map(&:id)).load
I'd also recommend using select instead of map for better performance:
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.select('enterprise_members.id')).load