summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Niedzielski <adamsunday@gmail.com>2017-03-30 17:36:12 +0200
committerAdam Niedzielski <adamsunday@gmail.com>2017-03-30 17:36:12 +0200
commit6d882dca64176e8b61d7c878bd8230fab094a904 (patch)
tree03776e83775d13b657785e0a81cee603c61cefe1
parentbf0590b3e2799963494c0144275445a81bbc97af (diff)
downloadgitlab-ce-introduce-base-polling-interval.tar.gz
Introduce "polling_interval_multiplier" as application settingintroduce-base-polling-interval
Implement module for setting "Poll-Interval" response header. Return 429 in ETag caching middleware when polling is disabled.
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/models/application_setting.rb7
-rw-r--r--app/views/admin/application_settings/_form.html.haml14
-rw-r--r--changelogs/unreleased/introduce-polling-interval-multiplier.yml4
-rw-r--r--db/migrate/20170329124448_add_polling_interval_multiplier_to_application_settings.rb33
-rw-r--r--db/schema.rb3
-rw-r--r--doc/api/settings.md7
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/settings.rb3
-rw-r--r--lib/gitlab/etag_caching/middleware.rb11
-rw-r--r--lib/gitlab/polling_interval.rb22
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb13
-rw-r--r--spec/lib/gitlab/polling_interval_spec.rb34
13 files changed, 146 insertions, 7 deletions
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 0bfbe47eb4f..515d8e1523b 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -134,6 +134,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:unique_ips_limit_enabled,
:version_check_enabled,
:terminal_max_session_time,
+ :polling_interval_multiplier,
disabled_oauth_sign_in_sources: [],
import_sources: [],
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 671a0fe98cc..2961e16f5e0 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -131,6 +131,10 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
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|
value&.each do |level|
unless Gitlab::VisibilityLevel.options.has_value?(level)
@@ -233,7 +237,8 @@ class ApplicationSetting < ActiveRecord::Base
signup_enabled: Settings.gitlab['signup_enabled'],
terminal_max_session_time: 0,
two_factor_grace_period: 48,
- user_default_external: false
+ user_default_external: false,
+ polling_interval_multiplier: 1
}
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 3eab065bb9f..5d51a2b5cbc 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -558,5 +558,19 @@
Maximum time for web terminal websocket connection (in seconds).
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
= f.submit 'Save', class: 'btn btn-save'
diff --git a/changelogs/unreleased/introduce-polling-interval-multiplier.yml b/changelogs/unreleased/introduce-polling-interval-multiplier.yml
new file mode 100644
index 00000000000..3ccae8e327f
--- /dev/null
+++ b/changelogs/unreleased/introduce-polling-interval-multiplier.yml
@@ -0,0 +1,4 @@
+---
+title: Introduce "polling_interval_multiplier" as application setting
+merge_request: 10280
+author:
diff --git a/db/migrate/20170329124448_add_polling_interval_multiplier_to_application_settings.rb b/db/migrate/20170329124448_add_polling_interval_multiplier_to_application_settings.rb
new file mode 100644
index 00000000000..a8affd19a0b
--- /dev/null
+++ b/db/migrate/20170329124448_add_polling_interval_multiplier_to_application_settings.rb
@@ -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
diff --git a/db/schema.rb b/db/schema.rb
index dba242548c1..19aca4b941e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# 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
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_time_window"
t.boolean "unique_ips_limit_enabled", default: false, null: false
+ t.decimal "polling_interval_multiplier", default: 1.0, null: false
end
create_table "audit_events", force: :cascade do |t|
diff --git a/doc/api/settings.md b/doc/api/settings.md
index ad975e2e325..d99695ca986 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -48,7 +48,8 @@ Example response:
"koding_url": null,
"plantuml_enabled": false,
"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_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. |
+| `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling. |
```bash
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,
"plantuml_enabled": false,
"plantuml_url": null,
- "terminal_max_session_time": 0
+ "terminal_max_session_time": 0,
+ "polling_interval_multiplier": 1.0
}
```
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 5954aea8041..01ae2dbd583 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -581,6 +581,7 @@ module API
expose :plantuml_enabled
expose :plantuml_url
expose :terminal_max_session_time
+ expose :polling_interval_multiplier
end
class Release < Grape::Entity
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index d4d3229f0d1..ff03ec9b054 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -110,6 +110,7 @@ module API
requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
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 :polling_interval_multiplier, type: Integer, 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,
:default_group_visibility, :restricted_visibility_levels, :import_sources,
:enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit,
@@ -125,7 +126,7 @@ module API
:akismet_enabled, :admin_notification_email, :sentry_enabled,
:repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_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
put "application/settings" do
attrs = declared_params(include_missing: false)
diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb
index ffbc6e17dc5..9c98f0d1a30 100644
--- a/lib/gitlab/etag_caching/middleware.rb
+++ b/lib/gitlab/etag_caching/middleware.rb
@@ -18,8 +18,7 @@ module Gitlab
if_none_match = env['HTTP_IF_NONE_MATCH']
if if_none_match == etag
- Gitlab::Metrics.add_event(:etag_caching_cache_hit)
- [304, { 'ETag' => etag }, ['']]
+ handle_cache_hit(etag)
else
track_cache_miss(if_none_match, cached_value_present)
@@ -52,6 +51,14 @@ module Gitlab
%Q{W/"#{value}"}
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)
if if_none_match.blank?
Gitlab::Metrics.add_event(:etag_caching_header_missing)
diff --git a/lib/gitlab/polling_interval.rb b/lib/gitlab/polling_interval.rb
new file mode 100644
index 00000000000..c44bb1cd14d
--- /dev/null
+++ b/lib/gitlab/polling_interval.rb
@@ -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
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index 8b5bfc4dbb0..6ec4360adc2 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -99,6 +99,19 @@ describe Gitlab::EtagCaching::Middleware do
middleware.call(build_env(path, if_none_match))
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
context 'when If-None-Match header does not match ETag in store' do
diff --git a/spec/lib/gitlab/polling_interval_spec.rb b/spec/lib/gitlab/polling_interval_spec.rb
new file mode 100644
index 00000000000..56c2847e26a
--- /dev/null
+++ b/spec/lib/gitlab/polling_interval_spec.rb
@@ -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