Bug in `Feature` prevents us from using flag with same name as a license feature
Steps to reproduce
feature = SecureRandom.hex
project = Project.last
# Works as expected before the feature is persisted
Feature.enabled?(feature, project, default_enabled: true) # => true
Feature.enabled?(feature, default_enabled: true) # => true
# This persists the feature
Feature.enable(feature, project)
# This still works as expected
Feature.enabled?(feature, project, default_enabled: true) # => true
# This doesn't :(
Feature.enabled?(feature, default_enabled: true) # => false (will be `true` at first, but wait for up to 1 minute!)
Note that the final line will generally be true
for a bit, then become false
. This is when the caching of Feature.persisted_names
expires.
Interaction with license features
This bug prevents us from using feature flags that take actors and have the same name as a license feature.
An example of this is within /ee/app/models/project.rb/
we have this method to check if design management is enabled for a project:
# ee/models/project.rb
def design_management_enabled?
lfs_enabled? &&
feature_available?(:design_management) &&
::Feature.enabled?(:design_management, self)
end
Initially, this will be false
for everyone:
-
feature_available?(:design_management)
will betrue
. This is because that method callsProject#licensed_feature_available?
which performs aFeature.enabled?(feature, project, default_enabled: true)
check of the feature. Because of thedefault_enabled
this istrue
-
::Feature.enabled?(:design_management, self)
will befalse
, because it is notdefault_enabled:
After enabling the feature on the project, it will still be false
for everyone:
-
feature_available?(:design_management)
will befalse
, because of the bug -
::Feature.enabled?(:design_management, self)
will betrue
, but it doesn't matter
We can't enable :design_management
globally, because this will switch it on for everyone:
-
feature_available?(:design_management)
will betrue
-
::Feature.enabled?(:design_management, self)
will betrue
for all projects, even though it's notdefault_enabled
as it's on globally now
What is the expected correct behavior?
In the above scenario:
Feature.enabled?(feature, default_enabled: true) # => true