Commit 099f9e0e authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Enable real-time issue assignees by default

This also mounts the Action Cable engine all the time.
Mounting the engine has minimal impact on memory so we should just mount
it all the time to reduce configuration complexity.

Changelog: added
parent cb341440
...@@ -48,9 +48,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -48,9 +48,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
before_action only: :show do before_action only: :show do
real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:real_time_issue_sidebar, @project) push_frontend_feature_flag(:real_time_issue_sidebar, @project, default_enabled: :yaml)
push_to_gon_attributes(:features, :real_time_issue_sidebar, real_time_enabled)
push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml) push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml)
push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml) push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml)
push_frontend_feature_flag(:labels_widget, @project, default_enabled: :yaml) push_frontend_feature_flag(:labels_widget, @project, default_enabled: :yaml)
......
...@@ -80,7 +80,7 @@ module Issues ...@@ -80,7 +80,7 @@ module Issues
todo_service.reassigned_assignable(issue, current_user, old_assignees) todo_service.reassigned_assignable(issue, current_user, old_assignees)
track_incident_action(current_user, issue, :incident_assigned) track_incident_action(current_user, issue, :incident_assigned)
if Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:broadcast_issue_updates, issue.project) if Feature.enabled?(:broadcast_issue_updates, issue.project, default_enabled: :yaml)
GraphqlTriggers.issuable_assignees_updated(issue) GraphqlTriggers.issuable_assignees_updated(issue)
end end
end end
......
...@@ -59,8 +59,4 @@ Rails.application.configure do ...@@ -59,8 +59,4 @@ Rails.application.configure do
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil)) config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil))
config.log_level = :fatal config.log_level = :fatal
end end
# Mount the ActionCable Engine in-app so that we don't have to spawn another Puma
# process for feature specs
ENV['ACTION_CABLE_IN_APP'] = 'true'
end end
--- ---
name: broadcast_issue_updates name: broadcast_issue_updates
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30732 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30732
rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/1210 rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/3413
milestone: '13.0' milestone: '13.0'
type: development type: development
group: group::project management group: group::project management
default_enabled: false default_enabled: true
...@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/1210 ...@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/1210
milestone: '13.0' milestone: '13.0'
type: development type: development
group: group::project management group: group::project management
default_enabled: false default_enabled: true
...@@ -65,7 +65,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled? ...@@ -65,7 +65,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
Gitlab::Metrics::Samplers::DatabaseSampler.initialize_instance.start Gitlab::Metrics::Samplers::DatabaseSampler.initialize_instance.start
Gitlab::Metrics::Samplers::ThreadsSampler.initialize_instance.start Gitlab::Metrics::Samplers::ThreadsSampler.initialize_instance.start
if Gitlab::Runtime.action_cable? if Gitlab::Runtime.web_server?
Gitlab::Metrics::Samplers::ActionCableSampler.instance.start Gitlab::Metrics::Samplers::ActionCableSampler.instance.start
end end
......
...@@ -3,8 +3,7 @@ ...@@ -3,8 +3,7 @@
require 'action_cable/subscription_adapter/redis' require 'action_cable/subscription_adapter/redis'
Rails.application.configure do Rails.application.configure do
# Mount the ActionCable engine when in-app mode is enabled config.action_cable.mount_path = '/-/cable'
config.action_cable.mount_path = Gitlab::ActionCable::Config.in_app? ? '/-/cable' : nil
config.action_cable.url = Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, '/-/cable') config.action_cable.url = Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, '/-/cable')
config.action_cable.worker_pool_size = Gitlab::ActionCable::Config.worker_pool_size config.action_cable.worker_pool_size = Gitlab::ActionCable::Config.worker_pool_size
......
...@@ -380,12 +380,18 @@ You can also use the `/iteration` ...@@ -380,12 +380,18 @@ You can also use the `/iteration`
[quick action](../quick_actions.md#issues-merge-requests-and-epics) [quick action](../quick_actions.md#issues-merge-requests-and-epics)
in a comment or description field. in a comment or description field.
## Real-time sidebar **(FREE SELF)** ## Real-time sidebar
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 13.3. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 13.3. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/3413) in GitLab 13.9.
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 14.5.
Assignees in the sidebar are updated in real time. This feature is **disabled by default**. FLAG:
To enable it, you need to enable [Action Cable in-app mode](https://docs.gitlab.com/omnibus/settings/actioncable.html). On self-managed GitLab, by default this feature is available. To hide the feature per project or for your entire instance, ask an administrator to
[disable the feature flags](../../../administration/feature_flags.md) named `real_time_issue_sidebar` and `broadcast_issue_updates`.
On GitLab.com, this feature is available.
Assignees in the sidebar are updated in real time.
## Similar issues ## Similar issues
......
...@@ -4,10 +4,6 @@ module Gitlab ...@@ -4,10 +4,6 @@ module Gitlab
module ActionCable module ActionCable
class Config class Config
class << self class << self
def in_app?
Gitlab::Utils.to_boolean(ENV.fetch('ACTION_CABLE_IN_APP', false))
end
def worker_pool_size def worker_pool_size
ENV.fetch('ACTION_CABLE_WORKER_POOL_SIZE', 4).to_i ENV.fetch('ACTION_CABLE_WORKER_POOL_SIZE', 4).to_i
end end
......
...@@ -39,23 +39,14 @@ module Gitlab ...@@ -39,23 +39,14 @@ module Gitlab
def sample def sample
pool = @action_cable.worker_pool.executor pool = @action_cable.worker_pool.executor
labels = {
server_mode: server_mode
}
metrics[:active_connections].set(labels, @action_cable.connections.size)
metrics[:pool_min_size].set(labels, pool.min_length)
metrics[:pool_max_size].set(labels, pool.max_length)
metrics[:pool_current_size].set(labels, pool.length)
metrics[:pool_largest_size].set(labels, pool.largest_length)
metrics[:pool_completed_tasks].set(labels, pool.completed_task_count)
metrics[:pool_pending_tasks].set(labels, pool.queue_length)
end
private
def server_mode metrics[:active_connections].set({}, @action_cable.connections.size)
Gitlab::ActionCable::Config.in_app? ? 'in-app' : 'standalone' metrics[:pool_min_size].set({}, pool.min_length)
metrics[:pool_max_size].set({}, pool.max_length)
metrics[:pool_current_size].set({}, pool.length)
metrics[:pool_largest_size].set({}, pool.largest_length)
metrics[:pool_completed_tasks].set({}, pool.completed_task_count)
metrics[:pool_pending_tasks].set({}, pool.queue_length)
end end
end end
end end
......
...@@ -63,12 +63,8 @@ module Gitlab ...@@ -63,12 +63,8 @@ module Gitlab
puma? puma?
end end
def action_cable?
web_server? && (!!defined?(ACTION_CABLE_SERVER) || Gitlab::ActionCable::Config.in_app?)
end
def multi_threaded? def multi_threaded?
puma? || sidekiq? || action_cable? puma? || sidekiq?
end end
def puma_in_clustered_mode? def puma_in_clustered_mode?
...@@ -92,7 +88,7 @@ module Gitlab ...@@ -92,7 +88,7 @@ module Gitlab
threads += Sidekiq.options[:concurrency] + 2 threads += Sidekiq.options[:concurrency] + 2
end end
if action_cable? if web_server?
threads += Gitlab::ActionCable::Config.worker_pool_size threads += Gitlab::ActionCable::Config.worker_pool_size
end end
......
...@@ -1084,28 +1084,30 @@ RSpec.describe Projects::IssuesController do ...@@ -1084,28 +1084,30 @@ RSpec.describe Projects::IssuesController do
end end
context 'real-time sidebar feature flag' do context 'real-time sidebar feature flag' do
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project, :public) } let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
where(:action_cable_in_app_enabled, :feature_flag_enabled, :gon_feature_flag) do context 'when enabled' do
true | true | true before do
true | false | true stub_feature_flags(real_time_issue_sidebar: true)
false | true | true end
false | false | false
it 'pushes the correct value to the frontend' do
go(id: issue.to_param)
expect(Gon.features).to include('realTimeIssueSidebar' => true)
end
end end
with_them do context 'when disabled' do
before do before do
expect(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(action_cable_in_app_enabled) stub_feature_flags(real_time_issue_sidebar: false)
stub_feature_flags(real_time_issue_sidebar: feature_flag_enabled)
end end
it 'broadcasts to the issues channel based on ActionCable and feature flag values' do it 'pushes the correct value to the frontend' do
go(id: issue.to_param) go(id: issue.to_param)
expect(Gon.features).to include('realTimeIssueSidebar' => gon_feature_flag) expect(Gon.features).to include('realTimeIssueSidebar' => false)
end end
end end
end end
......
...@@ -23,64 +23,46 @@ RSpec.describe Gitlab::Metrics::Samplers::ActionCableSampler do ...@@ -23,64 +23,46 @@ RSpec.describe Gitlab::Metrics::Samplers::ActionCableSampler do
allow(pool).to receive(:queue_length).and_return(6) allow(pool).to receive(:queue_length).and_return(6)
end end
shared_examples 'collects metrics' do |expected_labels| it 'includes active connections' do
it 'includes active connections' do expect(subject.metrics[:active_connections]).to receive(:set).with({}, 0)
expect(subject.metrics[:active_connections]).to receive(:set).with(expected_labels, 0)
subject.sample subject.sample
end end
it 'includes minimum worker pool size' do
expect(subject.metrics[:pool_min_size]).to receive(:set).with(expected_labels, 1)
subject.sample
end
it 'includes maximum worker pool size' do
expect(subject.metrics[:pool_max_size]).to receive(:set).with(expected_labels, 2)
subject.sample
end
it 'includes current worker pool size' do it 'includes minimum worker pool size' do
expect(subject.metrics[:pool_current_size]).to receive(:set).with(expected_labels, 3) expect(subject.metrics[:pool_min_size]).to receive(:set).with({}, 1)
subject.sample subject.sample
end end
it 'includes largest worker pool size' do it 'includes maximum worker pool size' do
expect(subject.metrics[:pool_largest_size]).to receive(:set).with(expected_labels, 4) expect(subject.metrics[:pool_max_size]).to receive(:set).with({}, 2)
subject.sample subject.sample
end end
it 'includes worker pool completed task count' do it 'includes current worker pool size' do
expect(subject.metrics[:pool_completed_tasks]).to receive(:set).with(expected_labels, 5) expect(subject.metrics[:pool_current_size]).to receive(:set).with({}, 3)
subject.sample subject.sample
end end
it 'includes worker pool pending task count' do it 'includes largest worker pool size' do
expect(subject.metrics[:pool_pending_tasks]).to receive(:set).with(expected_labels, 6) expect(subject.metrics[:pool_largest_size]).to receive(:set).with({}, 4)
subject.sample subject.sample
end
end end
context 'for in-app mode' do it 'includes worker pool completed task count' do
before do expect(subject.metrics[:pool_completed_tasks]).to receive(:set).with({}, 5)
expect(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(true)
end
it_behaves_like 'collects metrics', server_mode: 'in-app' subject.sample
end end
context 'for standalone mode' do it 'includes worker pool pending task count' do
before do expect(subject.metrics[:pool_pending_tasks]).to receive(:set).with({}, 6)
expect(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(false)
end
it_behaves_like 'collects metrics', server_mode: 'standalone' subject.sample
end end
end end
end end
...@@ -48,10 +48,9 @@ RSpec.describe Gitlab::Runtime do ...@@ -48,10 +48,9 @@ RSpec.describe Gitlab::Runtime do
before do before do
stub_const('::Puma', puma_type) stub_const('::Puma', puma_type)
stub_env('ACTION_CABLE_IN_APP', 'false')
end end
it_behaves_like "valid runtime", :puma, 1 it_behaves_like "valid runtime", :puma, 1 + Gitlab::ActionCable::Config.worker_pool_size
end end
context "puma with cli_config" do context "puma with cli_config" do
...@@ -61,27 +60,16 @@ RSpec.describe Gitlab::Runtime do ...@@ -61,27 +60,16 @@ RSpec.describe Gitlab::Runtime do
before do before do
stub_const('::Puma', puma_type) stub_const('::Puma', puma_type)
allow(puma_type).to receive_message_chain(:cli_config, :options).and_return(max_threads: 2, workers: max_workers) allow(puma_type).to receive_message_chain(:cli_config, :options).and_return(max_threads: 2, workers: max_workers)
stub_env('ACTION_CABLE_IN_APP', 'false')
end end
it_behaves_like "valid runtime", :puma, 3 it_behaves_like "valid runtime", :puma, 3 + Gitlab::ActionCable::Config.worker_pool_size
context "when ActionCable in-app mode is enabled" do context "when ActionCable worker pool size is configured" do
before do before do
stub_env('ACTION_CABLE_IN_APP', 'true') stub_env('ACTION_CABLE_WORKER_POOL_SIZE', 10)
stub_env('ACTION_CABLE_WORKER_POOL_SIZE', '3')
end end
it_behaves_like "valid runtime", :puma, 6 it_behaves_like "valid runtime", :puma, 13
end
context "when ActionCable standalone is run" do
before do
stub_const('ACTION_CABLE_SERVER', true)
stub_env('ACTION_CABLE_WORKER_POOL_SIZE', '8')
end
it_behaves_like "valid runtime", :puma, 11
end end
describe ".puma_in_clustered_mode?" do describe ".puma_in_clustered_mode?" do
......
...@@ -1248,28 +1248,38 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -1248,28 +1248,38 @@ RSpec.describe Issues::UpdateService, :mailer do
let(:closed_issuable) { create(:closed_issue, project: project) } let(:closed_issuable) { create(:closed_issue, project: project) }
end end
context 'real-time updates' do context 'broadcasting issue assignee updates' do
using RSpec::Parameterized::TableSyntax
let(:update_params) { { assignee_ids: [user2.id] } } let(:update_params) { { assignee_ids: [user2.id] } }
where(:action_cable_in_app_enabled, :feature_flag_enabled, :should_broadcast) do context 'when feature flag is enabled' do
true | true | true before do
true | false | true stub_feature_flags(broadcast_issue_updates: true)
false | true | true end
false | false | false
end it 'triggers the GraphQL subscription' do
expect(GraphqlTriggers).to receive(:issuable_assignees_updated).with(issue)
update_issue(update_params)
end
with_them do context 'when assignee is not updated' do
it 'broadcasts to the issues channel based on ActionCable and feature flag values' do let(:update_params) { { title: 'Some other title' } }
allow(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(action_cable_in_app_enabled)
stub_feature_flags(broadcast_issue_updates: feature_flag_enabled)
if should_broadcast it 'does not trigger the GraphQL subscription' do
expect(GraphqlTriggers).to receive(:issuable_assignees_updated).with(issue)
else
expect(GraphqlTriggers).not_to receive(:issuable_assignees_updated).with(issue) expect(GraphqlTriggers).not_to receive(:issuable_assignees_updated).with(issue)
update_issue(update_params)
end end
end
end
context 'when feature flag is disabled' do
before do
stub_feature_flags(broadcast_issue_updates: false)
end
it 'does not trigger the GraphQL subscription' do
expect(GraphqlTriggers).not_to receive(:issuable_assignees_updated).with(issue)
update_issue(update_params) update_issue(update_params)
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment