Skip to content
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

Suggestion flag_query_mode: bit_operator by default #87

Open
giovannibonetti opened this issue Jul 14, 2020 · 0 comments
Open

Suggestion flag_query_mode: bit_operator by default #87

giovannibonetti opened this issue Jul 14, 2020 · 0 comments

Comments

@giovannibonetti
Copy link

Thanks for the awesome gem!

Anyway, after using it in production for a few months I would like to suggest (with a PR afterwards) to change the default query mode from :in_list to :bit_operator. Even though performance-wise it seems like :in_list is best for MySQL (haven't measured it with Postgres), I would like to argue that :bit_operator is safer.

The reason is that the :in_list query only works if the number of flags is fixed.

# reference state
class User < ApplicationRecord
  has_flags 1 => :warpdrive,
            2 => :shields
end

User.warprive.to_sql
=> "SELECT users.* FROM users WHERE users.flags in (1,3)

However, consider the case where a new flag is added:

# new state
class User < ApplicationRecord
  has_flags 1 => :warpdrive,
            2 => :shields,
            3 => :premium
            :flag_query_mode => :bit_operator
end

And in the same deploy a migration sets the flag for some records:

# db/migrate/...
class AddNewFlagToUsersTable < ApplicationRecord
  def up
    execute <<~SQL
      UPDATE users
        SET flags = flags | 4
        WHERE created_at < '...'
    SQL
  end

  def down
    # noop
  end
end

During the deploy the following query done in the old version of the app becomes invalid:

User.warprive.to_sql
=> "SELECT users.* FROM users WHERE users.flags in (1,3)

However, if the default query mode would be :bit_operator instead of :in_list, this problem wouldn't happen:

User.warprive.to_sql
=> "SELECT users.* FROM users WHERE users.flags & 1 = 1

The :bit_operator works regardless of the number of flags, so there would be no issue during a deploy setting a new flag.

Anyway, I decided to open this issue today because in my company we have been bitten by this a couple of times, and now we try to remember to always add :flag_query_mode => :bit_operator when using has_flags. I wonder if other users have been bitten by that, too. I'm a fan of the principle of least surprise, and think that safety should come before performance. If someone needs more performance he/she can always add the option later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant