Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b131b6f680
commit
134681224f
|
|
@ -4,3 +4,5 @@ export const TRACK_CLICK_TRAINING_LINK_ACTION = 'click_security_training_link';
|
|||
export const TRACK_PROVIDER_LEARN_MORE_CLICK_ACTION = 'click_link';
|
||||
export const TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL = 'security_training_provider';
|
||||
export const TRACK_TRAINING_LOADED_ACTION = 'security_training_link_loaded';
|
||||
export const TRACK_PROMOTION_BANNER_CTA_CLICK_ACTION = 'click_button';
|
||||
export const TRACK_PROMOTION_BANNER_CTA_CLICK_LABEL = 'security_training_promotion_cta';
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353125
|
|||
milestone: '14.9'
|
||||
type: development
|
||||
group: group::threat insights
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
|||
|
|
@ -407,3 +407,49 @@ click the `+` button and input the issue reference number or the full URL of the
|
|||
The issues then appear in the related feature flag and the other way round.
|
||||
|
||||
This feature is similar to the [linked issues](../user/project/issues/related_issues.md) feature.
|
||||
|
||||
## Performance factors
|
||||
|
||||
In general, GitLab Feature Flags can be used in any applications,
|
||||
however, if it's a large application, it could require an additional configuration in advance.
|
||||
This section explains the performance factors to help your organization to identify
|
||||
what's needed to be done before using the feature.
|
||||
Please read [How it works](#how-it-works) section before diving into the details.
|
||||
|
||||
### Maximum supported clients in application nodes
|
||||
|
||||
GitLab accepts client requests as much as possible until it hits the [rate limiting](../security/rate_limits.md).
|
||||
At the moment, the Feature Flag API falls into **Unauthenticated traffic (from a given IP address)**
|
||||
in the [GitLab.com specific limits](../user/gitlab_com/index.md),
|
||||
so it's **500 requests per minute**.
|
||||
|
||||
Please note that the polling rate is configurable in SDKs. Provided that all clients are requesting from the same IP:
|
||||
|
||||
- Request once per minute ... 500 clients can be supported.
|
||||
- Request once per 15 sec ... 125 clients can be supported.
|
||||
|
||||
For applications looking for more scalable solution, we recommend to use [Unleash Proxy](#unleash-proxy-example).
|
||||
This proxy server sits between the server and clients. It requests to the server as a behalf of the client groups,
|
||||
so the nubmer of outbound requests can be greatly reduced.
|
||||
|
||||
There is also an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/295472) to give more
|
||||
capacity to the current rate limit.
|
||||
|
||||
### Recovering from network errors
|
||||
|
||||
In general, [Unleash clients](https://github.com/Unleash/unleash#unleash-sdks) have
|
||||
a fall-back mechanism when the server returns an error code.
|
||||
For example, `unleash-ruby-client` reads flag data from the local backup so that
|
||||
application can keep running in the current state.
|
||||
|
||||
Please reads the documentation in a SDK project for more information.
|
||||
|
||||
### Self-managed GitLab
|
||||
|
||||
Functionality-wise, there are no differences. Both SaaS and self-managed behave the same.
|
||||
|
||||
In terms of scalability, it's up to the spec of the GitLab instance.
|
||||
For example, GitLab.com runs on HA architecture so that it can handle a lot of requests concurrently,
|
||||
however, a self-managed instance runs on a low spec machine can't expect the same result.
|
||||
Please see [Reference architectures](../administration/reference_architectures/index.md)
|
||||
for more information.
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ To support the following package managers, the GitLab analyzers proceed in two s
|
|||
<li>
|
||||
<a id="exported-dependency-information-notes-3"></a>
|
||||
<p>
|
||||
These tests confirms that if a <code>gradlew</code> file does not exist, the version of <code>Gradle</code> pre-installed in the analyzer image is used.
|
||||
These tests confirm that if a <code>gradlew</code> file does not exist, the version of <code>Gradle</code> pre-installed in the analyzer image is used.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ module Gitlab
|
|||
attr_reader :json_data, :report, :validate
|
||||
|
||||
def valid?
|
||||
if Feature.enabled?(:show_report_validation_warnings)
|
||||
if Feature.enabled?(:show_report_validation_warnings, default_enabled: :yaml)
|
||||
# We want validation to happen regardless of VALIDATE_SCHEMA CI variable
|
||||
schema_validation_passed = schema_validator.valid?
|
||||
|
||||
|
|
|
|||
|
|
@ -72,8 +72,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def async_index_creation_available?
|
||||
ApplicationRecord.connection.table_exists?(:postgres_async_indexes) &&
|
||||
Feature.enabled?(:database_async_index_creation, type: :ops)
|
||||
connection.table_exists?(:postgres_async_indexes)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,45 +16,29 @@ RSpec.describe Gitlab::Database::AsyncIndexes::MigrationHelpers do
|
|||
describe '#unprepare_async_index' do
|
||||
let!(:async_index) { create(:postgres_async_index, name: index_name) }
|
||||
|
||||
context 'when the flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(database_async_index_creation: true)
|
||||
end
|
||||
it 'destroys the record' do
|
||||
expect do
|
||||
migration.unprepare_async_index(table_name, 'id')
|
||||
end.to change { index_model.where(name: index_name).count }.by(-1)
|
||||
end
|
||||
|
||||
context 'when an explicit name is given' do
|
||||
let(:index_name) { 'my_test_async_index' }
|
||||
|
||||
it 'destroys the record' do
|
||||
expect do
|
||||
migration.unprepare_async_index(table_name, 'id')
|
||||
migration.unprepare_async_index(table_name, 'id', name: index_name)
|
||||
end.to change { index_model.where(name: index_name).count }.by(-1)
|
||||
end
|
||||
|
||||
context 'when an explicit name is given' do
|
||||
let(:index_name) { 'my_test_async_index' }
|
||||
|
||||
it 'destroys the record' do
|
||||
expect do
|
||||
migration.unprepare_async_index(table_name, 'id', name: index_name)
|
||||
end.to change { index_model.where(name: index_name).count }.by(-1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the async index table does not exist' do
|
||||
it 'does not raise an error' do
|
||||
connection.drop_table(:postgres_async_indexes)
|
||||
|
||||
expect(index_model).not_to receive(:find_by)
|
||||
|
||||
expect { migration.unprepare_async_index(table_name, 'id') }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the feature flag is disabled' do
|
||||
it 'does not destroy the record' do
|
||||
stub_feature_flags(database_async_index_creation: false)
|
||||
context 'when the async index table does not exist' do
|
||||
it 'does not raise an error' do
|
||||
connection.drop_table(:postgres_async_indexes)
|
||||
|
||||
expect do
|
||||
migration.unprepare_async_index(table_name, 'id')
|
||||
end.not_to change { index_model.where(name: index_name).count }
|
||||
expect(index_model).not_to receive(:find_by)
|
||||
|
||||
expect { migration.unprepare_async_index(table_name, 'id') }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -63,35 +47,19 @@ RSpec.describe Gitlab::Database::AsyncIndexes::MigrationHelpers do
|
|||
let(:index_name) { "index_#{table_name}_on_id" }
|
||||
let!(:async_index) { create(:postgres_async_index, name: index_name) }
|
||||
|
||||
context 'when the flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(database_async_index_creation: true)
|
||||
end
|
||||
|
||||
it 'destroys the record' do
|
||||
expect do
|
||||
migration.unprepare_async_index_by_name(table_name, index_name)
|
||||
end.to change { index_model.where(name: index_name).count }.by(-1)
|
||||
end
|
||||
|
||||
context 'when the async index table does not exist' do
|
||||
it 'does not raise an error' do
|
||||
connection.drop_table(:postgres_async_indexes)
|
||||
|
||||
expect(index_model).not_to receive(:find_by)
|
||||
|
||||
expect { migration.unprepare_async_index_by_name(table_name, index_name) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
it 'destroys the record' do
|
||||
expect do
|
||||
migration.unprepare_async_index_by_name(table_name, index_name)
|
||||
end.to change { index_model.where(name: index_name).count }.by(-1)
|
||||
end
|
||||
|
||||
context 'when the feature flag is disabled' do
|
||||
it 'does not destroy the record' do
|
||||
stub_feature_flags(database_async_index_creation: false)
|
||||
context 'when the async index table does not exist' do
|
||||
it 'does not raise an error' do
|
||||
connection.drop_table(:postgres_async_indexes)
|
||||
|
||||
expect do
|
||||
migration.unprepare_async_index_by_name(table_name, index_name)
|
||||
end.not_to change { index_model.where(name: index_name).count }
|
||||
expect(index_model).not_to receive(:find_by)
|
||||
|
||||
expect { migration.unprepare_async_index_by_name(table_name, index_name) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -101,14 +69,23 @@ RSpec.describe Gitlab::Database::AsyncIndexes::MigrationHelpers do
|
|||
connection.create_table(table_name)
|
||||
end
|
||||
|
||||
context 'when the feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(database_async_index_creation: true)
|
||||
end
|
||||
it 'creates the record for the async index' do
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id')
|
||||
end.to change { index_model.where(name: index_name).count }.by(1)
|
||||
|
||||
it 'creates the record for the async index' do
|
||||
record = index_model.find_by(name: index_name)
|
||||
|
||||
expect(record.table_name).to eq(table_name)
|
||||
expect(record.definition).to match(/CREATE INDEX CONCURRENTLY "#{index_name}"/)
|
||||
end
|
||||
|
||||
context 'when an explicit name is given' do
|
||||
let(:index_name) { 'my_async_index_name' }
|
||||
|
||||
it 'creates the record with the given name' do
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id')
|
||||
migration.prepare_async_index(table_name, 'id', name: index_name)
|
||||
end.to change { index_model.where(name: index_name).count }.by(1)
|
||||
|
||||
record = index_model.find_by(name: index_name)
|
||||
|
|
@ -116,78 +93,53 @@ RSpec.describe Gitlab::Database::AsyncIndexes::MigrationHelpers do
|
|||
expect(record.table_name).to eq(table_name)
|
||||
expect(record.definition).to match(/CREATE INDEX CONCURRENTLY "#{index_name}"/)
|
||||
end
|
||||
|
||||
context 'when an explicit name is given' do
|
||||
let(:index_name) { 'my_async_index_name' }
|
||||
|
||||
it 'creates the record with the given name' do
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id', name: index_name)
|
||||
end.to change { index_model.where(name: index_name).count }.by(1)
|
||||
|
||||
record = index_model.find_by(name: index_name)
|
||||
|
||||
expect(record.table_name).to eq(table_name)
|
||||
expect(record.definition).to match(/CREATE INDEX CONCURRENTLY "#{index_name}"/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the index already exists' do
|
||||
it 'does not create the record' do
|
||||
connection.add_index(table_name, 'id', name: index_name)
|
||||
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id')
|
||||
end.not_to change { index_model.where(name: index_name).count }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the record already exists' do
|
||||
it 'does attempt to create the record' do
|
||||
create(:postgres_async_index, table_name: table_name, name: index_name)
|
||||
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id')
|
||||
end.not_to change { index_model.where(name: index_name).count }
|
||||
end
|
||||
|
||||
it 'updates definition if changed' do
|
||||
index = create(:postgres_async_index, table_name: table_name, name: index_name, definition: '...')
|
||||
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id', name: index_name)
|
||||
end.to change { index.reload.definition }
|
||||
end
|
||||
|
||||
it 'does not update definition if not changed' do
|
||||
definition = "CREATE INDEX CONCURRENTLY \"index_#{table_name}_on_id\" ON \"#{table_name}\" (\"id\")"
|
||||
index = create(:postgres_async_index, table_name: table_name, name: index_name, definition: definition)
|
||||
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id', name: index_name)
|
||||
end.not_to change { index.reload.updated_at }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the async index table does not exist' do
|
||||
it 'does not raise an error' do
|
||||
connection.drop_table(:postgres_async_indexes)
|
||||
|
||||
expect(index_model).not_to receive(:safe_find_or_create_by!)
|
||||
|
||||
expect { migration.prepare_async_index(table_name, 'id') }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the feature flag is disabled' do
|
||||
context 'when the index already exists' do
|
||||
it 'does not create the record' do
|
||||
stub_feature_flags(database_async_index_creation: false)
|
||||
connection.add_index(table_name, 'id', name: index_name)
|
||||
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id')
|
||||
end.not_to change { index_model.where(name: index_name).count }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the record already exists' do
|
||||
it 'does attempt to create the record' do
|
||||
create(:postgres_async_index, table_name: table_name, name: index_name)
|
||||
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id')
|
||||
end.not_to change { index_model.where(name: index_name).count }
|
||||
end
|
||||
|
||||
it 'updates definition if changed' do
|
||||
index = create(:postgres_async_index, table_name: table_name, name: index_name, definition: '...')
|
||||
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id', name: index_name)
|
||||
end.to change { index.reload.definition }
|
||||
end
|
||||
|
||||
it 'does not update definition if not changed' do
|
||||
definition = "CREATE INDEX CONCURRENTLY \"index_#{table_name}_on_id\" ON \"#{table_name}\" (\"id\")"
|
||||
index = create(:postgres_async_index, table_name: table_name, name: index_name, definition: definition)
|
||||
|
||||
expect do
|
||||
migration.prepare_async_index(table_name, 'id', name: index_name)
|
||||
end.not_to change { index.reload.updated_at }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the async index table does not exist' do
|
||||
it 'does not raise an error' do
|
||||
connection.drop_table(:postgres_async_indexes)
|
||||
|
||||
expect(index_model).not_to receive(:safe_find_or_create_by!)
|
||||
|
||||
expect { migration.prepare_async_index(table_name, 'id') }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue