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
end
before_action only: :show do
real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:real_time_issue_sidebar, @project)
push_to_gon_attributes(:features, :real_time_issue_sidebar, real_time_enabled)
push_frontend_feature_flag(:real_time_issue_sidebar, @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(:labels_widget, @project, default_enabled: :yaml)
......
......@@ -80,7 +80,7 @@ module Issues
todo_service.reassigned_assignable(issue, current_user, old_assignees)
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)
end
end
......
......@@ -59,8 +59,4 @@ Rails.application.configure do
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil))
config.log_level = :fatal
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
---
name: broadcast_issue_updates
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'
type: development
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
milestone: '13.0'
type: development
group: group::project management
default_enabled: false
default_enabled: true
......@@ -65,7 +65,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
Gitlab::Metrics::Samplers::DatabaseSampler.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
end
......
......@@ -3,8 +3,7 @@
require 'action_cable/subscription_adapter/redis'
Rails.application.configure do
# Mount the ActionCable engine when in-app mode is enabled
config.action_cable.mount_path = Gitlab::ActionCable::Config.in_app? ? '/-/cable' : nil
config.action_cable.mount_path = '/-/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
......
......@@ -380,12 +380,18 @@ You can also use the `/iteration`
[quick action](../quick_actions.md#issues-merge-requests-and-epics)
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**.
To enable it, you need to enable [Action Cable in-app mode](https://docs.gitlab.com/omnibus/settings/actioncable.html).
FLAG:
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
......
......@@ -4,10 +4,6 @@ module Gitlab
module ActionCable
class Config
class << self
def in_app?
Gitlab::Utils.to_boolean(ENV.fetch('ACTION_CABLE_IN_APP', false))
end
def worker_pool_size
ENV.fetch('ACTION_CABLE_WORKER_POOL_SIZE', 4).to_i
end
......
......@@ -39,23 +39,14 @@ module Gitlab
def sample
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
Gitlab::ActionCable::Config.in_app? ? 'in-app' : 'standalone'
metrics[:active_connections].set({}, @action_cable.connections.size)
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
......
......@@ -63,12 +63,8 @@ module Gitlab
puma?
end
def action_cable?
web_server? && (!!defined?(ACTION_CABLE_SERVER) || Gitlab::ActionCable::Config.in_app?)
end
def multi_threaded?
puma? || sidekiq? || action_cable?
puma? || sidekiq?
end
def puma_in_clustered_mode?
......@@ -92,7 +88,7 @@ module Gitlab
threads += Sidekiq.options[:concurrency] + 2
end
if action_cable?
if web_server?
threads += Gitlab::ActionCable::Config.worker_pool_size
end
......
......@@ -1084,28 +1084,30 @@ RSpec.describe Projects::IssuesController do
end
context 'real-time sidebar feature flag' do
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
where(:action_cable_in_app_enabled, :feature_flag_enabled, :gon_feature_flag) do
true | true | true
true | false | true
false | true | true
false | false | false
context 'when enabled' do
before do
stub_feature_flags(real_time_issue_sidebar: true)
end
it 'pushes the correct value to the frontend' do
go(id: issue.to_param)
expect(Gon.features).to include('realTimeIssueSidebar' => true)
end
end
with_them do
context 'when disabled' 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: feature_flag_enabled)
stub_feature_flags(real_time_issue_sidebar: false)
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)
expect(Gon.features).to include('realTimeIssueSidebar' => gon_feature_flag)
expect(Gon.features).to include('realTimeIssueSidebar' => false)
end
end
end
......
......@@ -23,64 +23,46 @@ RSpec.describe Gitlab::Metrics::Samplers::ActionCableSampler do
allow(pool).to receive(:queue_length).and_return(6)
end
shared_examples 'collects metrics' do |expected_labels|
it 'includes active connections' do
expect(subject.metrics[:active_connections]).to receive(:set).with(expected_labels, 0)
it 'includes active connections' do
expect(subject.metrics[:active_connections]).to receive(:set).with({}, 0)
subject.sample
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
subject.sample
end
it 'includes current worker pool size' do
expect(subject.metrics[:pool_current_size]).to receive(:set).with(expected_labels, 3)
it 'includes minimum worker pool size' do
expect(subject.metrics[:pool_min_size]).to receive(:set).with({}, 1)
subject.sample
end
subject.sample
end
it 'includes largest worker pool size' do
expect(subject.metrics[:pool_largest_size]).to receive(:set).with(expected_labels, 4)
it 'includes maximum worker pool size' do
expect(subject.metrics[:pool_max_size]).to receive(:set).with({}, 2)
subject.sample
end
subject.sample
end
it 'includes worker pool completed task count' do
expect(subject.metrics[:pool_completed_tasks]).to receive(:set).with(expected_labels, 5)
it 'includes current worker pool size' do
expect(subject.metrics[:pool_current_size]).to receive(:set).with({}, 3)
subject.sample
end
subject.sample
end
it 'includes worker pool pending task count' do
expect(subject.metrics[:pool_pending_tasks]).to receive(:set).with(expected_labels, 6)
it 'includes largest worker pool size' do
expect(subject.metrics[:pool_largest_size]).to receive(:set).with({}, 4)
subject.sample
end
subject.sample
end
context 'for in-app mode' do
before do
expect(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(true)
end
it 'includes worker pool completed task count' do
expect(subject.metrics[:pool_completed_tasks]).to receive(:set).with({}, 5)
it_behaves_like 'collects metrics', server_mode: 'in-app'
subject.sample
end
context 'for standalone mode' do
before do
expect(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(false)
end
it 'includes worker pool pending task count' do
expect(subject.metrics[:pool_pending_tasks]).to receive(:set).with({}, 6)
it_behaves_like 'collects metrics', server_mode: 'standalone'
subject.sample
end
end
end
......@@ -48,10 +48,9 @@ RSpec.describe Gitlab::Runtime do
before do
stub_const('::Puma', puma_type)
stub_env('ACTION_CABLE_IN_APP', 'false')
end
it_behaves_like "valid runtime", :puma, 1
it_behaves_like "valid runtime", :puma, 1 + Gitlab::ActionCable::Config.worker_pool_size
end
context "puma with cli_config" do
......@@ -61,27 +60,16 @@ RSpec.describe Gitlab::Runtime do
before do
stub_const('::Puma', puma_type)
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
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
stub_env('ACTION_CABLE_IN_APP', 'true')
stub_env('ACTION_CABLE_WORKER_POOL_SIZE', '3')
stub_env('ACTION_CABLE_WORKER_POOL_SIZE', 10)
end
it_behaves_like "valid runtime", :puma, 6
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
it_behaves_like "valid runtime", :puma, 13
end
describe ".puma_in_clustered_mode?" do
......
......@@ -1248,28 +1248,38 @@ RSpec.describe Issues::UpdateService, :mailer do
let(:closed_issuable) { create(:closed_issue, project: project) }
end
context 'real-time updates' do
using RSpec::Parameterized::TableSyntax
context 'broadcasting issue assignee updates' do
let(:update_params) { { assignee_ids: [user2.id] } }
where(:action_cable_in_app_enabled, :feature_flag_enabled, :should_broadcast) do
true | true | true
true | false | true
false | true | true
false | false | false
end
context 'when feature flag is enabled' do
before do
stub_feature_flags(broadcast_issue_updates: true)
end
it 'triggers the GraphQL subscription' do
expect(GraphqlTriggers).to receive(:issuable_assignees_updated).with(issue)
update_issue(update_params)
end
with_them do
it 'broadcasts to the issues channel based on ActionCable and feature flag values' do
allow(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(action_cable_in_app_enabled)
stub_feature_flags(broadcast_issue_updates: feature_flag_enabled)
context 'when assignee is not updated' do
let(:update_params) { { title: 'Some other title' } }
if should_broadcast
expect(GraphqlTriggers).to receive(:issuable_assignees_updated).with(issue)
else
it 'does not trigger the GraphQL subscription' do
expect(GraphqlTriggers).not_to receive(:issuable_assignees_updated).with(issue)
update_issue(update_params)
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)
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