Merge branch 'introduce-base-polling-interval' into 'master'
Introduce "polling_interval_multiplier" as application setting Closes #29759 See merge request !10280
This commit is contained in:
commit
872b3215d2
|
@ -134,6 +134,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||||
:unique_ips_limit_enabled,
|
:unique_ips_limit_enabled,
|
||||||
:version_check_enabled,
|
:version_check_enabled,
|
||||||
:terminal_max_session_time,
|
:terminal_max_session_time,
|
||||||
|
:polling_interval_multiplier,
|
||||||
|
|
||||||
disabled_oauth_sign_in_sources: [],
|
disabled_oauth_sign_in_sources: [],
|
||||||
import_sources: [],
|
import_sources: [],
|
||||||
|
|
|
@ -131,6 +131,10 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
presence: true,
|
presence: true,
|
||||||
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
||||||
|
|
||||||
|
validates :polling_interval_multiplier,
|
||||||
|
presence: true,
|
||||||
|
numericality: { greater_than_or_equal_to: 0 }
|
||||||
|
|
||||||
validates_each :restricted_visibility_levels do |record, attr, value|
|
validates_each :restricted_visibility_levels do |record, attr, value|
|
||||||
value&.each do |level|
|
value&.each do |level|
|
||||||
unless Gitlab::VisibilityLevel.options.has_value?(level)
|
unless Gitlab::VisibilityLevel.options.has_value?(level)
|
||||||
|
@ -233,7 +237,8 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
signup_enabled: Settings.gitlab['signup_enabled'],
|
signup_enabled: Settings.gitlab['signup_enabled'],
|
||||||
terminal_max_session_time: 0,
|
terminal_max_session_time: 0,
|
||||||
two_factor_grace_period: 48,
|
two_factor_grace_period: 48,
|
||||||
user_default_external: false
|
user_default_external: false,
|
||||||
|
polling_interval_multiplier: 1
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -558,5 +558,19 @@
|
||||||
Maximum time for web terminal websocket connection (in seconds).
|
Maximum time for web terminal websocket connection (in seconds).
|
||||||
0 for unlimited.
|
0 for unlimited.
|
||||||
|
|
||||||
|
%fieldset
|
||||||
|
%legend Real-time features
|
||||||
|
.form-group
|
||||||
|
= f.label :polling_interval_multiplier, 'Polling interval multiplier', class: 'control-label col-sm-2'
|
||||||
|
.col-sm-10
|
||||||
|
= f.text_field :polling_interval_multiplier, class: 'form-control'
|
||||||
|
.help-block
|
||||||
|
Change this value to influence how frequently the GitLab UI polls for updates.
|
||||||
|
If you set the value to 2 all polling intervals are multiplied
|
||||||
|
by 2, which means that polling happens half as frequently.
|
||||||
|
The multiplier can also have a decimal value.
|
||||||
|
The default value (1) is a reasonable choice for the majority of GitLab
|
||||||
|
installations. Set to 0 to completely disable polling.
|
||||||
|
|
||||||
.form-actions
|
.form-actions
|
||||||
= f.submit 'Save', class: 'btn btn-save'
|
= f.submit 'Save', class: 'btn btn-save'
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: Introduce "polling_interval_multiplier" as application setting
|
||||||
|
merge_request: 10280
|
||||||
|
author:
|
|
@ -0,0 +1,33 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class AddPollingIntervalMultiplierToApplicationSettings < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
# Set this constant to true if this migration requires downtime.
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
# When a migration requires downtime you **must** uncomment the following
|
||||||
|
# constant and define a short and easy to understand explanation as to why the
|
||||||
|
# migration requires downtime.
|
||||||
|
# DOWNTIME_REASON = ''
|
||||||
|
|
||||||
|
# When using the methods "add_concurrent_index" or "add_column_with_default"
|
||||||
|
# you must disable the use of transactions as these methods can not run in an
|
||||||
|
# existing transaction. When using "add_concurrent_index" make sure that this
|
||||||
|
# method is the _only_ method called in the migration, any other changes
|
||||||
|
# should go in a separate migration. This ensures that upon failure _only_ the
|
||||||
|
# index creation fails and can be retried or reverted easily.
|
||||||
|
#
|
||||||
|
# To disable transactions uncomment the following line and remove these
|
||||||
|
# comments:
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_column_with_default :application_settings, :polling_interval_multiplier, :decimal, default: 1, allow_null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_column :application_settings, :polling_interval_multiplier
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20170317203554) do
|
ActiveRecord::Schema.define(version: 20170329124448) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -115,6 +115,7 @@ ActiveRecord::Schema.define(version: 20170317203554) do
|
||||||
t.integer "unique_ips_limit_per_user"
|
t.integer "unique_ips_limit_per_user"
|
||||||
t.integer "unique_ips_limit_time_window"
|
t.integer "unique_ips_limit_time_window"
|
||||||
t.boolean "unique_ips_limit_enabled", default: false, null: false
|
t.boolean "unique_ips_limit_enabled", default: false, null: false
|
||||||
|
t.decimal "polling_interval_multiplier", default: 1.0, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "audit_events", force: :cascade do |t|
|
create_table "audit_events", force: :cascade do |t|
|
||||||
|
|
|
@ -48,7 +48,8 @@ Example response:
|
||||||
"koding_url": null,
|
"koding_url": null,
|
||||||
"plantuml_enabled": false,
|
"plantuml_enabled": false,
|
||||||
"plantuml_url": null,
|
"plantuml_url": null,
|
||||||
"terminal_max_session_time": 0
|
"terminal_max_session_time": 0,
|
||||||
|
"polling_interval_multiplier": 1.0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -88,6 +89,7 @@ PUT /application/settings
|
||||||
| `plantuml_enabled` | boolean | no | Enable PlantUML integration. Default is `false`. |
|
| `plantuml_enabled` | boolean | no | Enable PlantUML integration. Default is `false`. |
|
||||||
| `plantuml_url` | string | yes (if `plantuml_enabled` is `true`) | The PlantUML instance URL for integration. |
|
| `plantuml_url` | string | yes (if `plantuml_enabled` is `true`) | The PlantUML instance URL for integration. |
|
||||||
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time. |
|
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time. |
|
||||||
|
| `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling. |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings?signup_enabled=false&default_project_visibility=internal
|
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings?signup_enabled=false&default_project_visibility=internal
|
||||||
|
@ -124,6 +126,7 @@ Example response:
|
||||||
"koding_url": null,
|
"koding_url": null,
|
||||||
"plantuml_enabled": false,
|
"plantuml_enabled": false,
|
||||||
"plantuml_url": null,
|
"plantuml_url": null,
|
||||||
"terminal_max_session_time": 0
|
"terminal_max_session_time": 0,
|
||||||
|
"polling_interval_multiplier": 1.0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -581,6 +581,7 @@ module API
|
||||||
expose :plantuml_enabled
|
expose :plantuml_enabled
|
||||||
expose :plantuml_url
|
expose :plantuml_url
|
||||||
expose :terminal_max_session_time
|
expose :terminal_max_session_time
|
||||||
|
expose :polling_interval_multiplier
|
||||||
end
|
end
|
||||||
|
|
||||||
class Release < Grape::Entity
|
class Release < Grape::Entity
|
||||||
|
|
|
@ -110,6 +110,7 @@ module API
|
||||||
requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
|
requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
|
||||||
end
|
end
|
||||||
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
|
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
|
||||||
|
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
|
||||||
at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility,
|
at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility,
|
||||||
:default_group_visibility, :restricted_visibility_levels, :import_sources,
|
:default_group_visibility, :restricted_visibility_levels, :import_sources,
|
||||||
:enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit,
|
:enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit,
|
||||||
|
@ -125,7 +126,7 @@ module API
|
||||||
:akismet_enabled, :admin_notification_email, :sentry_enabled,
|
:akismet_enabled, :admin_notification_email, :sentry_enabled,
|
||||||
:repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled,
|
:repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled,
|
||||||
:version_check_enabled, :email_author_in_body, :html_emails_enabled,
|
:version_check_enabled, :email_author_in_body, :html_emails_enabled,
|
||||||
:housekeeping_enabled, :terminal_max_session_time
|
:housekeeping_enabled, :terminal_max_session_time, :polling_interval_multiplier
|
||||||
end
|
end
|
||||||
put "application/settings" do
|
put "application/settings" do
|
||||||
attrs = declared_params(include_missing: false)
|
attrs = declared_params(include_missing: false)
|
||||||
|
|
|
@ -18,8 +18,7 @@ module Gitlab
|
||||||
if_none_match = env['HTTP_IF_NONE_MATCH']
|
if_none_match = env['HTTP_IF_NONE_MATCH']
|
||||||
|
|
||||||
if if_none_match == etag
|
if if_none_match == etag
|
||||||
Gitlab::Metrics.add_event(:etag_caching_cache_hit)
|
handle_cache_hit(etag)
|
||||||
[304, { 'ETag' => etag }, ['']]
|
|
||||||
else
|
else
|
||||||
track_cache_miss(if_none_match, cached_value_present)
|
track_cache_miss(if_none_match, cached_value_present)
|
||||||
|
|
||||||
|
@ -52,6 +51,14 @@ module Gitlab
|
||||||
%Q{W/"#{value}"}
|
%Q{W/"#{value}"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_cache_hit(etag)
|
||||||
|
Gitlab::Metrics.add_event(:etag_caching_cache_hit)
|
||||||
|
|
||||||
|
status_code = Gitlab::PollingInterval.polling_enabled? ? 304 : 429
|
||||||
|
|
||||||
|
[status_code, { 'ETag' => etag }, ['']]
|
||||||
|
end
|
||||||
|
|
||||||
def track_cache_miss(if_none_match, cached_value_present)
|
def track_cache_miss(if_none_match, cached_value_present)
|
||||||
if if_none_match.blank?
|
if if_none_match.blank?
|
||||||
Gitlab::Metrics.add_event(:etag_caching_header_missing)
|
Gitlab::Metrics.add_event(:etag_caching_header_missing)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
module Gitlab
|
||||||
|
class PollingInterval
|
||||||
|
include Gitlab::CurrentSettings
|
||||||
|
|
||||||
|
HEADER_NAME = 'Poll-Interval'.freeze
|
||||||
|
|
||||||
|
def self.set_header(response, interval:)
|
||||||
|
if polling_enabled?
|
||||||
|
multiplier = current_application_settings.polling_interval_multiplier
|
||||||
|
value = (interval * multiplier).to_i
|
||||||
|
else
|
||||||
|
value = -1
|
||||||
|
end
|
||||||
|
|
||||||
|
response.headers[HEADER_NAME] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.polling_enabled?
|
||||||
|
!current_application_settings.polling_interval_multiplier.zero?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -99,6 +99,19 @@ describe Gitlab::EtagCaching::Middleware do
|
||||||
|
|
||||||
middleware.call(build_env(path, if_none_match))
|
middleware.call(build_env(path, if_none_match))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when polling is disabled' do
|
||||||
|
before do
|
||||||
|
allow(Gitlab::PollingInterval).to receive(:polling_enabled?).
|
||||||
|
and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns status code 429' do
|
||||||
|
status, _, _ = middleware.call(build_env(path, if_none_match))
|
||||||
|
|
||||||
|
expect(status).to eq 429
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when If-None-Match header does not match ETag in store' do
|
context 'when If-None-Match header does not match ETag in store' do
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Gitlab::PollingInterval, lib: true do
|
||||||
|
let(:polling_interval) { described_class }
|
||||||
|
|
||||||
|
describe '.set_header' do
|
||||||
|
let(:headers) { {} }
|
||||||
|
let(:response) { double(headers: headers) }
|
||||||
|
|
||||||
|
context 'when polling is disabled' do
|
||||||
|
before do
|
||||||
|
stub_application_setting(polling_interval_multiplier: 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets value to -1' do
|
||||||
|
polling_interval.set_header(response, interval: 10_000)
|
||||||
|
|
||||||
|
expect(headers['Poll-Interval']).to eq(-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when polling is enabled' do
|
||||||
|
before do
|
||||||
|
stub_application_setting(polling_interval_multiplier: 0.33333)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'applies modifier to base interval' do
|
||||||
|
polling_interval.set_header(response, interval: 10_000)
|
||||||
|
|
||||||
|
expect(headers['Poll-Interval']).to eq(3333)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue