これは何?
Rails で Feature Flags (Feature Toggles) を導入するため、Flipper (gem) を使ってみます。
確認環境
$ bundle exec ruby --version
ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-darwin19]
$ bundle exec rails --version
Rails 6.0.4.6
インストール
Gemfile
# For flipper
gem 'flipper'
gem 'flipper-active_record'
$ bundle install
$ bundle exec rails g flipper:active_record
create db/migrate/20220813075433_create_flipper_tables.rb
$ bundle exec rails db:migrate
== 20220813075433 CreateFlipperTables: migrating ==============================
-- create_table(:flipper_features)
-> 0.0029s
-- add_index(:flipper_features, :key, {:unique=>true})
-> 0.0013s
-- create_table(:flipper_gates)
-> 0.0012s
-- add_index(:flipper_gates, [:feature_key, :key, :value], {:unique=>true})
-> 0.0013s
== 20220813075433 CreateFlipperTables: migrated (0.0076s) =====================
使ってみる
全体
$ rails c
...
irb(main):009:0> Flipper.enabled? :search
Flipper::Adapters::ActiveRecord::Gate Load (0.2ms) SELECT "flipper_gates".* FROM "flipper_gates" WHERE "flipper_gates"."feature_key" = ? [["feature_key", "search"]]
Flipper feature(search) enabled? false (0.8ms) [ thing=nil ]
=> false
irb(main):010:0> Flipper.enable :search
(0.1ms) begin transaction
Flipper::Adapters::ActiveRecord::Feature Load (0.1ms) SELECT "flipper_features".* FROM "flipper_features" WHERE "flipper_features"."key" = ? ORDER BY "flipper_features"."id" ASC LIMIT ? [["key", "search"], ["LIMIT", 1]]
(0.0ms) commit transaction
(0.0ms) begin transaction
Flipper::Adapters::ActiveRecord::Gate Load (0.1ms) SELECT "flipper_gates".* FROM "flipper_gates" WHERE "flipper_gates"."feature_key" = ? [["feature_key", "search"]]
Flipper::Adapters::ActiveRecord::Gate Load (0.1ms) SELECT "flipper_gates".* FROM "flipper_gates" WHERE "flipper_gates"."feature_key" = ? AND "flipper_gates"."key" = ? [["feature_key", "search"], ["key", "boolean"]]
Flipper::Adapters::ActiveRecord::Gate Create (1.1ms) INSERT INTO "flipper_gates" ("feature_key", "key", "value", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["feature_key", "search"], ["key", "boolean"], ["value", "true"], ["created_at", "2022-08-28 15:47:53.857786"], ["updated_at", "2022-08-28 15:47:53.857786"]]
(0.8ms) commit transaction
Flipper feature(search) enable true (5.5ms) [ thing=#<Flipper::Types::Boolean:0x00007f829903c550 @value=true> gate_name=boolean ]
=> true
irb(main):011:0> Flipper.enabled? :search
Flipper::Adapters::ActiveRecord::Gate Load (0.1ms) SELECT "flipper_gates".* FROM "flipper_gates" WHERE "flipper_gates"."feature_key" = ? [["feature_key", "search"]]
Flipper feature(search) enabled? true (0.8ms) [ thing=nil gate_name=boolean ]
=> true
特定のユーザーだけ指定
$ rails c
...
irb(main):024:0> u = User.last
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, email: "xxxxx@example.com", created_at: "2022-08-28 04:31:57", updated_at: "2022-08-28 04:31:57">
irb(main):025:0> Flipper.enabled? :search, u
Flipper::Adapters::ActiveRecord::Gate Load (0.1ms) SELECT "flipper_gates".* FROM "flipper_gates" WHERE "flipper_gates"."feature_key" = ? [["feature_key", "search"]]
Flipper feature(search) enabled? false (0.9ms) [ thing=#<Flipper::Types::Actor:0x00007f8299209770 @thing=#<User id: 1, email: "xxxxx@example.com", created_at: "2022-08-28 04:31:57", updated_at: "2022-08-28 04:31:57">, @value="User;1"> ]
=> false
irb(main):026:0> Flipper.enable_actor :search, u
(0.1ms) begin transaction
Flipper::Adapters::ActiveRecord::Feature Load (0.1ms) SELECT "flipper_features".* FROM "flipper_features" WHERE "flipper_features"."key" = ? ORDER BY "flipper_features"."id" ASC LIMIT ? [["key", "search"], ["LIMIT", 1]]
(0.0ms) commit transaction
(0.0ms) begin transaction
Flipper::Adapters::ActiveRecord::Gate Create (0.4ms) INSERT INTO "flipper_gates" ("feature_key", "key", "value", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["feature_key", "search"], ["key", "actors"], ["value", "User;1"], ["created_at", "2022-08-28 15:51:58.809694"], ["updated_at", "2022-08-28 15:51:58.809694"]]
(0.7ms) commit transaction
Flipper feature(search) enable true (3.0ms) [ thing=#<Flipper::Types::Actor:0x00007f8296df6950 @thing=#<User id: 1, email: "xxxxx@example.com", created_at: "2022-08-28 04:31:57", updated_at: "2022-08-28 04:31:57">, @value="User;1"> gate_name=actor ]
=> true
irb(main):027:0> Flipper.enabled? :search, u
Flipper::Adapters::ActiveRecord::Gate Load (0.2ms) SELECT "flipper_gates".* FROM "flipper_gates" WHERE "flipper_gates"."feature_key" = ? [["feature_key", "search"]]
Flipper feature(search) enabled? true (0.9ms) [ thing=#<Flipper::Types::Actor:0x00007f8291b780c0 @thing=#<User id: 1, email: "xxxxx@example.com", created_at: "2022-08-28 04:31:57", updated_at: "2022-08-28 04:31:57">, @value="User;1"> gate_name=actor ]
=> true
パーセント指定
$ rails c
...
irb(main):024:0> u = User.last
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, email: "xxxxx@example.com", created_at: "2022-08-28 04:31:57", updated_at: "2022-08-28 04:31:57">
irb(main):030:0> Flipper.enabled? :search, u
Flipper::Adapters::ActiveRecord::Gate Load (0.2ms) SELECT "flipper_gates".* FROM "flipper_gates" WHERE "flipper_gates"."feature_key" = ? [["feature_key", "search"]]
Flipper feature(search) enabled? false (1.0ms) [ thing=#<Flipper::Types::Actor:0x00007f8296fd3b10 @thing=#<User id: 1, email: "xxxxx@example.com", created_at: "2022-08-28 04:31:57", updated_at: "2022-08-28 04:31:57">, @value="User;1"> ]
=> false
どうやら、パーセント指定の時は、Zlib.crc32
を使っているようでした。
- On Feature Flipping, Deli Coffee, and Hashing Algorithms
- flipper/percentage_of_actors.rb at f4a50467a6da84f4d886b8bec1df56127446002b · jnunemaker/flipper