Skip to content
Snippets Groups Projects
Commit 3cd08f4b authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent dd4bee69
No related branches found
No related tags found
No related merge requests found
Showing
with 542 additions and 99 deletions
---
title: Add functionality to revoke a X509Certificate and update related X509CommitSignatures
merge_request: 24889
author: Roger Meier
type: added
---
title: Remove promoted notes temporary index
merge_request: 26896
author:
type: other
---
title: Use ReplicateRepository when moving repo storage
merge_request: 26550
author:
type: changed
Loading
Loading
@@ -546,6 +546,9 @@ Gitlab.ee do
Settings.cron_jobs['elastic_index_bulk_cron_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['elastic_index_bulk_cron_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['elastic_index_bulk_cron_worker']['job_class'] ||= 'ElasticIndexBulkCronWorker'
Settings.cron_jobs['sync_seat_link_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['sync_seat_link_worker']['cron'] ||= "#{rand(60)} 0 * * *"
Settings.cron_jobs['sync_seat_link_worker']['job_class'] = 'SyncSeatLinkWorker'
end
 
#
Loading
Loading
Loading
Loading
@@ -133,6 +133,9 @@ Rails.application.routes.draw do
get :kill
end
end
# Notification settings
resources :notification_settings, only: [:create, :update]
end
 
concern :clusterable do
Loading
Loading
@@ -181,9 +184,6 @@ Rails.application.routes.draw do
# Spam reports
resources :abuse_reports, only: [:new, :create]
 
# Notification settings
resources :notification_settings, only: [:create, :update]
resources :groups, only: [:index, :new, :create] do
post :preview_markdown
end
Loading
Loading
Loading
Loading
@@ -232,6 +232,8 @@
- 2
- - service_desk_email_receiver
- 1
- - sync_seat_link_request
- 1
- - system_hook_push
- 1
- - todos_destroyer
Loading
Loading
@@ -248,3 +250,5 @@
- 1
- - web_hook
- 1
- - x509_certificate_revoke
- 1
# frozen_string_literal: true
class CleanupEmptyCommitUserMentions < ActiveRecord::Migration[5.2]
DOWNTIME = false
BATCH_SIZE = 10_000
class CommitUserMention < ActiveRecord::Base
include EachBatch
self.table_name = 'commit_user_mentions'
end
def up
# cleanup commit user mentions with no actual mentions,
# re https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24586#note_285982468
CommitUserMention
.where(mentioned_users_ids: nil)
.where(mentioned_groups_ids: nil)
.where(mentioned_projects_ids: nil)
.each_batch(of: BATCH_SIZE) do |batch|
batch.delete_all
end
end
def down
# no-op
end
end
# frozen_string_literal: true
class MigrateCommitNotesMentionsToDb < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
DELAY = 3.minutes.to_i
BATCH_SIZE = 1_000
MIGRATION = 'UserMentions::CreateResourceUserMention'
QUERY_CONDITIONS = "note LIKE '%@%'::text AND notes.noteable_type = 'Commit' AND commit_user_mentions.commit_id IS NULL"
JOIN = 'LEFT JOIN commit_user_mentions ON notes.id = commit_user_mentions.note_id'
class Note < ActiveRecord::Base
include EachBatch
self.table_name = 'notes'
end
def up
Note
.joins(JOIN)
.where(QUERY_CONDITIONS)
.each_batch(of: BATCH_SIZE) do |batch, index|
range = batch.pluck(Arel.sql('MIN(notes.id)'), Arel.sql('MAX(notes.id)')).first
migrate_in(index * DELAY, MIGRATION, ['Commit', JOIN, QUERY_CONDITIONS, true, *range])
end
end
def down
# no-op
# temporary index is to be dropped in a different migration in an upcoming release:
# https://gitlab.com/gitlab-org/gitlab/issues/196842
end
end
# frozen_string_literal: true
class RemoveIssueStateIndexes < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
# issues state column is ignored since 12.6 and will be removed on a following migration
def up
remove_concurrent_index_by_name :issues, 'index_issues_on_state'
remove_concurrent_index_by_name :issues, 'index_issues_on_project_id_and_created_at_and_id_and_state'
remove_concurrent_index_by_name :issues, 'idx_issues_on_project_id_and_due_date_and_id_and_state_partial'
remove_concurrent_index_by_name :issues, 'index_issues_on_project_id_and_rel_position_and_state_and_id'
remove_concurrent_index_by_name :issues, 'index_issues_on_project_id_and_updated_at_and_id_and_state'
end
def down
add_concurrent_index :issues, :state, name: 'index_issues_on_state'
add_concurrent_index :issues,
[:project_id, :created_at, :id, :state],
name: 'index_issues_on_project_id_and_created_at_and_id_and_state'
add_concurrent_index :issues,
[:project_id, :due_date, :id, :state],
where: 'due_date IS NOT NULL',
name: 'idx_issues_on_project_id_and_due_date_and_id_and_state_partial'
add_concurrent_index :issues,
[:project_id, :relative_position, :state, :id],
order: { id: :desc },
name: 'index_issues_on_project_id_and_rel_position_and_state_and_id'
add_concurrent_index :issues,
[:project_id, :updated_at, :id, :state],
name: 'index_issues_on_project_id_and_updated_at_and_id_and_state'
end
end
# frozen_string_literal: true
class RemoveMergeRequestStateIndexes < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
# merge_requests state column is ignored since 12.6 and will be removed on a following migration
def up
remove_concurrent_index_by_name :merge_requests, 'index_merge_requests_on_id_and_merge_jid'
remove_concurrent_index_by_name :merge_requests, 'index_merge_requests_on_source_project_and_branch_state_opened'
remove_concurrent_index_by_name :merge_requests, 'index_merge_requests_on_state_and_merge_status'
remove_concurrent_index_by_name :merge_requests, 'index_merge_requests_on_target_project_id_and_iid_opened'
end
def down
add_concurrent_index :merge_requests,
[:id, :merge_jid],
where: "merge_jid IS NOT NULL and state = 'locked'",
name: 'index_merge_requests_on_id_and_merge_jid'
add_concurrent_index :merge_requests,
[:source_project_id, :source_branch],
where: "state = 'opened'",
name: 'index_merge_requests_on_source_project_and_branch_state_opened'
add_concurrent_index :merge_requests,
[:state, :merge_status],
where: "state = 'opened' AND merge_status = 'can_be_merged'",
name: 'index_merge_requests_on_state_and_merge_status'
add_concurrent_index :merge_requests,
[:target_project_id, :iid],
where: "state = 'opened'",
name: 'index_merge_requests_on_target_project_id_and_iid_opened'
end
end
# frozen_string_literal: true
class RemoveStateFromIssues < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
return unless issue_state_column_exists?
# Ignored in 12.6 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19574
with_lock_retries do
remove_column :issues, :state, :string
end
end
def down
return if issue_state_column_exists?
with_lock_retries do
add_column :issues, :state, :string # rubocop:disable Migration/AddLimitToStringColumns
end
end
private
def issue_state_column_exists?
column_exists?(:issues, :state)
end
end
# frozen_string_literal: true
class RemoveStateFromMergeRequests < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
return unless merge_requests_state_column_exists?
# Ignored in 12.6 - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19574
with_lock_retries do
remove_column :merge_requests, :state, :string
end
end
def down
return if merge_requests_state_column_exists?
with_lock_retries do
add_column :merge_requests, :state, :string # rubocop:disable Migration/AddLimitToStringColumns
end
end
private
def merge_requests_state_column_exists?
column_exists?(:merge_requests, :state)
end
end
# frozen_string_literal: true
# Removes temporary index to fix orphan promoted issues.
# For more information check: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23916
class RemoveTemporaryPromotedNotesIndex < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
remove_concurrent_index_by_name :notes, 'tmp_idx_on_promoted_notes'
end
def down
add_concurrent_index :notes,
:note,
where: "noteable_type = 'Issue' AND system IS TRUE AND note LIKE 'promoted to epic%'",
name: 'tmp_idx_on_promoted_notes'
end
end
Loading
Loading
@@ -2184,7 +2184,6 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.datetime "updated_at"
t.text "description"
t.integer "milestone_id"
t.string "state"
t.integer "iid"
t.integer "updated_by_id"
t.integer "weight"
Loading
Loading
@@ -2216,18 +2215,13 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["lock_version"], name: "index_issues_on_lock_version", where: "(lock_version IS NULL)"
t.index ["milestone_id"], name: "index_issues_on_milestone_id"
t.index ["moved_to_id"], name: "index_issues_on_moved_to_id", where: "(moved_to_id IS NOT NULL)"
t.index ["project_id", "created_at", "id", "state"], name: "index_issues_on_project_id_and_created_at_and_id_and_state"
t.index ["project_id", "created_at", "id", "state_id"], name: "idx_issues_on_project_id_and_created_at_and_id_and_state_id"
t.index ["project_id", "due_date", "id", "state"], name: "idx_issues_on_project_id_and_due_date_and_id_and_state_partial", where: "(due_date IS NOT NULL)"
t.index ["project_id", "due_date", "id", "state_id"], name: "idx_issues_on_project_id_and_due_date_and_id_and_state_id", where: "(due_date IS NOT NULL)"
t.index ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true
t.index ["project_id", "relative_position", "state", "id"], name: "index_issues_on_project_id_and_rel_position_and_state_and_id", order: { id: :desc }
t.index ["project_id", "relative_position", "state_id", "id"], name: "idx_issues_on_project_id_and_rel_position_and_state_id_and_id", order: { id: :desc }
t.index ["project_id", "updated_at", "id", "state"], name: "index_issues_on_project_id_and_updated_at_and_id_and_state"
t.index ["project_id", "updated_at", "id", "state_id"], name: "idx_issues_on_project_id_and_updated_at_and_id_and_state_id"
t.index ["promoted_to_epic_id"], name: "index_issues_on_promoted_to_epic_id", where: "(promoted_to_epic_id IS NOT NULL)"
t.index ["relative_position"], name: "index_issues_on_relative_position"
t.index ["state"], name: "index_issues_on_state"
t.index ["state_id"], name: "idx_issues_on_state_id"
t.index ["title"], name: "index_issues_on_title_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["updated_at"], name: "index_issues_on_updated_at"
Loading
Loading
@@ -2597,7 +2591,6 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.datetime "created_at"
t.datetime "updated_at"
t.integer "milestone_id"
t.string "state", default: "opened", null: false
t.string "merge_status", default: "unchecked", null: false
t.integer "target_project_id", null: false
t.integer "iid"
Loading
Loading
@@ -2633,7 +2626,6 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["description"], name: "index_merge_requests_on_description_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["head_pipeline_id"], name: "index_merge_requests_on_head_pipeline_id"
t.index ["id", "merge_jid"], name: "idx_merge_requests_on_id_and_merge_jid", where: "((merge_jid IS NOT NULL) AND (state_id = 4))"
t.index ["id", "merge_jid"], name: "index_merge_requests_on_id_and_merge_jid", where: "((merge_jid IS NOT NULL) AND ((state)::text = 'locked'::text))"
t.index ["id"], name: "merge_request_mentions_temp_index", where: "((description ~~ '%@%'::text) OR ((title)::text ~~ '%@%'::text))"
t.index ["latest_merge_request_diff_id"], name: "index_merge_requests_on_latest_merge_request_diff_id"
t.index ["lock_version"], name: "index_merge_requests_on_lock_version", where: "(lock_version IS NULL)"
Loading
Loading
@@ -2641,15 +2633,12 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["milestone_id"], name: "index_merge_requests_on_milestone_id"
t.index ["source_branch"], name: "index_merge_requests_on_source_branch"
t.index ["source_project_id", "source_branch"], name: "idx_merge_requests_on_source_project_and_branch_state_opened", where: "(state_id = 1)"
t.index ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_and_branch_state_opened", where: "((state)::text = 'opened'::text)"
t.index ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_id_and_source_branch"
t.index ["state", "merge_status"], name: "index_merge_requests_on_state_and_merge_status", where: "(((state)::text = 'opened'::text) AND ((merge_status)::text = 'can_be_merged'::text))"
t.index ["state_id", "merge_status"], name: "idx_merge_requests_on_state_id_and_merge_status", where: "((state_id = 1) AND ((merge_status)::text = 'can_be_merged'::text))"
t.index ["target_branch"], name: "index_merge_requests_on_target_branch"
t.index ["target_project_id", "created_at"], name: "index_merge_requests_target_project_id_created_at"
t.index ["target_project_id", "iid"], name: "idx_merge_requests_on_target_project_id_and_iid_opened", where: "(state_id = 1)"
t.index ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true
t.index ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid_opened", where: "((state)::text = 'opened'::text)"
t.index ["target_project_id", "merge_commit_sha", "id"], name: "index_merge_requests_on_tp_id_and_merge_commit_sha_and_id"
t.index ["target_project_id", "target_branch"], name: "index_merge_requests_on_target_project_id_and_target_branch", where: "((state_id = 1) AND (merge_when_pipeline_succeeds = true))"
t.index ["title"], name: "index_merge_requests_on_title"
Loading
Loading
@@ -2844,7 +2833,6 @@ ActiveRecord::Schema.define(version: 2020_03_11_165635) do
t.index ["id", "noteable_type"], name: "note_mentions_temp_index", where: "(note ~~ '%@%'::text)"
t.index ["line_code"], name: "index_notes_on_line_code"
t.index ["note"], name: "index_notes_on_note_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["note"], name: "tmp_idx_on_promoted_notes", where: "(((noteable_type)::text = 'Issue'::text) AND (system IS TRUE) AND (note ~~ 'promoted to epic%'::text))"
t.index ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type"
t.index ["project_id", "id"], name: "index_notes_on_project_id_and_id_and_system_false", where: "(NOT system)"
t.index ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type"
Loading
Loading
Loading
Loading
@@ -8,4 +8,4 @@ link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#links
level: error
scope: raw
raw:
- '\[.+\]\((https?:){0}[\w\/\.]+(\.html).*\)'
- '\[.+\]\((https?:){0}[\w\/\.-]+(\.html).*\)'
Loading
Loading
@@ -67,7 +67,7 @@ This configuration is supported in [GitLab Premium and Ultimate](https://about.g
 
References:
 
- [Geo Documentation](../../gitlab-geo/README.html)
- [Geo Documentation](../geo/replication/index.md)
- [GitLab Geo with a highly available configuration](../geo/replication/high_availability.md)
 
## Recommended setups based on number of users
Loading
Loading
Loading
Loading
@@ -258,6 +258,134 @@ export default {
};
```
 
### Working with pagination
GitLab's GraphQL API uses [Relay-style cursor pagination](https://www.apollographql.com/docs/react/data/pagination/#cursor-based)
for connection types. This means a "cursor" is used to keep track of where in the data
set the next items should be fetched from.
Every connection type (for example, `DesignConnection` and `DiscussionConnection`) has a field `pageInfo` that contains an information required for pagination:
```javascript
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
```
Here:
- `startCursor` and `endCursor` display the cursor of the first and last items
respectively.
- `hasPreviousPage` and `hasNextPage` allow us to check if there are more pages
available before or after the current page.
When we fetch data with a connection type, we can pass cursor as `after` or `before`
parameter, indicating a starting or ending point of our pagination. They should be
followed with `first` or `last` parameter respectively to indicate _how many_ items
we want to fetch after or before a given endpoint.
For example, here we're fetching 10 designs after a cursor:
```javascript
query {
project(fullPath: "root/my-project") {
id
issue(iid: "42") {
designCollection {
designs(atVersion: null, after: "Ihwffmde0i", first: 10) {
edges {
node {
id
}
}
}
}
}
}
}
```
#### Using `fetchMore` method in components
When making an initial fetch, we usually want to start a pagination from the beginning.
In this case, we can either:
- Skip passing a cursor.
- Pass `null` explicitly to `after`.
After data is fetched, we should save a `pageInfo` object. Let's assume we're storing
it to Vue component `data`:
```javascript
data() {
return {
pageInfo: null,
}
},
apollo: {
designs: {
query: projectQuery,
variables() {
return {
// rest of design variables
...
first: 10,
};
},
result(res) {
this.pageInfo = res.data?.project?.issue?.designCollection?.designs?.pageInfo;
},
},
},
```
When we want to move to the next page, we use an Apollo `fetchMore` method, passing a
new cursor (and, optionally, new variables) there. In the `updateQuery` hook, we have
to return a result we want to see in the Apollo cache after fetching the next page.
```javascript
fetchNextPage() {
// as a first step, we're checking if we have more pages to move forward
if (this.pageInfo?.hasNextPage) {
this.$apollo.queries.designs.fetchMore({
variables: {
// rest of design variables
...
first: 10,
after: this.pageInfo?.endCursor,
},
updateQuery(previousResult, { fetchMoreResult }) {
// here we can implement the logic of adding new designs to fetched one (for example, if we use infinite scroll)
// or replacing old result with the new one if we use numbered pages
const newDesigns = fetchMoreResult.project.issue.designCollection.designs;
previousResult.project.issue.designCollection.designs.push(...newDesigns)
return previousResult;
},
});
}
}
```
Please note we don't have to save `pageInfo` one more time; `fetchMore` triggers a query
`result` hook as well.
#### Limitations
Currently, bidirectional pagination doesn't work:
- `hasNextPage` returns a correct value only when we paginate forward using `endCursor`
and `first` parameters.
- `hasPreviousPage` returns a correct value only when we paginate backward using
`startCursor` and `last` parameters.
This should be resolved in the scope of the issue
[Bi-directional Pagination in GraphQL doesn't work as expected](https://gitlab.com/gitlab-org/gitlab/-/issues/208301).
### Testing
 
#### Mocking response as component data
Loading
Loading
Loading
Loading
@@ -45,49 +45,73 @@ This could lead to false successes where subsequent "requests" could have querie
 
## Finding the source of the query
 
It may be useful to identify the source of the queries by looking at the call backtrace.
To enable this, run the specs with the `QUERY_RECORDER_DEBUG` environment variable set. For example:
```shell
QUERY_RECORDER_DEBUG=1 bundle exec rspec spec/requests/api/projects_spec.rb
```
This will log calls to QueryRecorder into the `test.log`. For example:
```plaintext
QueryRecorder SQL: SELECT COUNT(*) FROM "issues" WHERE "issues"."deleted_at" IS NULL AND "issues"."project_id" = $1 AND ("issues"."state" IN ('opened')) AND "issues"."confidential" = $2
--> /home/user/gitlab/gdk/gitlab/spec/support/query_recorder.rb:19:in `callback'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/fanout.rb:127:in `finish'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/fanout.rb:46:in `block in finish'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/fanout.rb:46:in `each'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/fanout.rb:46:in `finish'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/instrumenter.rb:36:in `finish'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/instrumenter.rb:25:in `instrument'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract_adapter.rb:478:in `log'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/postgresql_adapter.rb:601:in `exec_cache'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/postgresql_adapter.rb:585:in `execute_and_clear'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/postgresql/database_statements.rb:160:in `exec_query'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/database_statements.rb:356:in `select'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/query_cache.rb:68:in `block in select_all'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/query_cache.rb:83:in `cache_sql'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/query_cache.rb:68:in `select_all'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/relation/calculations.rb:270:in `execute_simple_calculation'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/relation/calculations.rb:227:in `perform_calculation'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/relation/calculations.rb:133:in `calculate'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/relation/calculations.rb:48:in `count'
--> /home/user/gitlab/gdk/gitlab/app/services/base_count_service.rb:20:in `uncached_count'
--> /home/user/gitlab/gdk/gitlab/app/services/base_count_service.rb:12:in `block in count'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:299:in `block in fetch'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:585:in `block in save_block_result_to_cache'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:547:in `block in instrument'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications.rb:166:in `instrument'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:547:in `instrument'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:584:in `save_block_result_to_cache'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:299:in `fetch'
--> /home/user/gitlab/gdk/gitlab/app/services/base_count_service.rb:12:in `count'
--> /home/user/gitlab/gdk/gitlab/app/models/project.rb:1296:in `open_issues_count'
```
There are multiple ways to find the source of queries.
1. The `QueryRecorder` `data` attribute stores queries by `file_name:line_number:method_name`.
Each entry is a `hash` with the following fields:
- `count`: the number of times a query from this `file_name:line_number:method_name` was called
- `occurrences`: the actual `SQL` of each call
- `backtrace`: the stack trace of each call (if either of the two following options were enabled)
`QueryRecorder#find_query` allows filtering queries by their `file_name:line_number:method_name` and
`count` attributes. For example:
```ruby
control = ActiveRecord::QueryRecorder.new(skip_cached: false) { visit_some_page }
control.find_query(/.*note.rb.*/, 0, first_only: true)
```
`QueryRecorder#occurrences_by_line_method` returns a sorted array based on `data`, sorted by `count`.
1. You can output the call backtrace for the specific `QueryRecorder` instance you want
by using `ActiveRecord::QueryRecorder.new(query_recorder_debug: true)`. The output
will be in `test.log`
1. Using the environment variable `QUERY_RECORDER_DEBUG`, the call backtrace will be output for all tests.
To enable this, run the specs with the `QUERY_RECORDER_DEBUG` environment variable set. For example:
```shell
QUERY_RECORDER_DEBUG=1 bundle exec rspec spec/requests/api/projects_spec.rb
```
This will log calls to QueryRecorder into the `test.log` file. For example:
```plaintext
QueryRecorder SQL: SELECT COUNT(*) FROM "issues" WHERE "issues"."deleted_at" IS NULL AND "issues"."project_id" = $1 AND ("issues"."state" IN ('opened')) AND "issues"."confidential" = $2
--> /home/user/gitlab/gdk/gitlab/spec/support/query_recorder.rb:19:in `callback'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/fanout.rb:127:in `finish'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/fanout.rb:46:in `block in finish'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/fanout.rb:46:in `each'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/fanout.rb:46:in `finish'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/instrumenter.rb:36:in `finish'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications/instrumenter.rb:25:in `instrument'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract_adapter.rb:478:in `log'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/postgresql_adapter.rb:601:in `exec_cache'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/postgresql_adapter.rb:585:in `execute_and_clear'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/postgresql/database_statements.rb:160:in `exec_query'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/database_statements.rb:356:in `select'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/query_cache.rb:68:in `block in select_all'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/query_cache.rb:83:in `cache_sql'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/connection_adapters/abstract/query_cache.rb:68:in `select_all'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/relation/calculations.rb:270:in `execute_simple_calculation'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/relation/calculations.rb:227:in `perform_calculation'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/relation/calculations.rb:133:in `calculate'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activerecord-4.2.8/lib/active_record/relation/calculations.rb:48:in `count'
--> /home/user/gitlab/gdk/gitlab/app/services/base_count_service.rb:20:in `uncached_count'
--> /home/user/gitlab/gdk/gitlab/app/services/base_count_service.rb:12:in `block in count'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:299:in `block in fetch'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:585:in `block in save_block_result_to_cache'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:547:in `block in instrument'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/notifications.rb:166:in `instrument'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:547:in `instrument'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:584:in `save_block_result_to_cache'
--> /home/user/.rbenv/versions/2.3.5/lib/ruby/gems/2.3.0/gems/activesupport-4.2.8/lib/active_support/cache.rb:299:in `fetch'
--> /home/user/gitlab/gdk/gitlab/app/services/base_count_service.rb:12:in `count'
--> /home/user/gitlab/gdk/gitlab/app/models/project.rb:1296:in `open_issues_count'
```
 
## See also
 
Loading
Loading
Loading
Loading
@@ -64,13 +64,13 @@ source projects, GitLab grants access to **Gold** features for all GitLab.com
 
#### Self-managed
 
A self-managed subscription uses a hybrid model. You pay for a subscription according to the maximum number of users enabled during the subscription period. At the end of the subscription period, the maximum number of simultaneous users in the self-managed installation is checked. If the number of users is higher than your subscription, you are billed for the extra users. The maximum number of simultaneous users is also used to calculate the cost of subscription renewal.
A self-managed subscription uses a hybrid model. You pay for a subscription according to the maximum number of users enabled during the subscription period. For instances that aren't air-gapped or on a closed network, the maximum number of simultaneous users in the self-managed installation is checked each quarter, using [Seat Link](#seat-link).
 
Every occupied seat, whether by person, job, or bot is counted in the subscription, with the following exceptions:
 
- Blocked users who are blocked prior to the renewal of a subscription won't be counted as active users for the renewal subscription. They may count as active users in the subscription period in which they were originally added.
- Members with Guest permissions on an Ultimate subscription.
- Special internal GitLab accounts: `Ghost User` and `Support Bot`.
- GitLab-created service accounts: `Ghost User` and `Support Bot`.
 
NOTE: **Note:**
If you have LDAP integration enabled, anyone in the configured domain can sign up for a GitLab account. This can result in an unexpected bill at time of renewal. Consider [disabling new signups](../user/admin_area/settings/sign_up_restrictions.md) and managing new users manually instead.
Loading
Loading
@@ -237,6 +237,65 @@ The following will be emailed to you:
- A payment receipt. You can also access this information in the Customers Portal under **Payment History**.
- A new license. [Upload this license](../user/admin_area/license.md#uploading-your-license) to your instance to use it.
 
### Seat Link
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/208832) in [GitLab Starter](https://about.gitlab.com/pricing) 12.9.
Seat Link allows us to provide our self-managed customers with prorated charges for user growth throughout the year using a quarterly reconciliation process.
Seat Link sends to GitLab daily a count of all users in connected self-managed instances. That information is used to automate prorated reconciliations. The data is sent securely through an encrypted HTTPS connection.
Seat Link is mandatory because we need the user count data to enable prorated billing. Seat Link provides **only** the following information to GitLab:
- Date
- Historical maximum user count
- License key
Here is an example of the POST request:
```plaintext
{
date: '2020-01-29',
license_key: 'ZXlKa1lYUmhJam9pWm5WNmVsTjVZekZ2YTJoV2NucDBh
RXRxTTA5amQxcG1VMVZqDQpXR3RwZEc5SGIyMVhibmxuZDJ0NWFrNXJTVzVH
UzFCT1hHNVRiVFIyT0ZaUFlVSm1OV1ZGV0VObE1uVk4NCk4xY3ZkM1F4Y2to
MFFuVklXSFJvUWpSM01VdE9SVE5rYkVjclZrdDJORkpOTlhka01qaE5aalpj
YmxSMg0KWVd3MFNFTldTRmRtV1ZGSGRDOUhPR05oUVZvNUsxVnRXRUZIZFU1
U1VqUm5aVFZGZUdwTWIxbDFZV1EyDQphV1JTY1V4c1ZYSjNPVGhrYVZ4dVlu
TkpWMHRJZUU5dmF6ZEJRVVkxTlVWdFUwMTNSMGRHWm5SNlJFcFYNClQyVkJl
VXc0UzA0NWFFb3ZlSFJrZW0xbVRqUlZabkZ4U1hWcWNXRnZYRzVaTm5GSmVW
UnJVR1JQYTJKdA0KU0ZZclRHTmFPRTVhZEVKMUt6UjRkSE15WkRCT1UyNWlS
MGRJZDFCdmRFWk5Za2h4Tm5sT1VsSktlVlYyDQpXRmhjYmxSeU4wRnRNMU5q
THpCVWFGTmpTMnh3UWpOWVkyc3pkbXBST1dnelZHY3hUV3hxVDIwdlZYRlQN
Ck9EWTJSVWx4WlVOT01EQXhVRlZ3ZGs1Rk0xeHVSVEJTTDFkMWJUQTVhV1ZK
WjBORFdWUktaRXNyVnpsTw0KTldkWWQwWTNZa05VWlZBMmRUVk9kVUpxT1hV
Mk5VdDFTUzk0TUU5V05XbFJhWGh0WEc1cVkyWnhaeTlXDQpTMEpyZWt0cmVY
bzBOVGhFVG1oU1oxSm5WRFprY0Uwck0wZEdhVUpEV1d4a1RXZFRjVU5tYTB0
a2RteEQNCmNWTlFSbFpuWlZWY2JpdFVVbXhIV0d4MFRuUnRWbkJKTkhwSFJt
TnRaMGsyV0U1MFFUUXJWMUJVTWtOSA0KTVhKUWVGTkxPVTkzV1VsMlVUUldk
R3hNTWswNU1USlNjRnh1U1UxTGJTdHRRM1l5YTFWaWJtSlBTMkUxDQplRkpL
SzJSckszaG1hVXB1ZVRWT1UwdHZXV0ZOVG1WamMyVjRPV0pSUlZkUU9UUnpU
VWh2Wlc5cFhHNUgNClNtRkdVMDUyY1RGMWNGTnhVbU5JUkZkeGVWcHVRMnBh
VTBSUGR6VnRNVGhvWTFBM00zVkZlVzFOU0djMA0KY1ZFM1FWSlplSFZ5UzFS
aGIxTmNia3BSUFQxY2JpSXNJbxRsZVNJNkltZFhiVzFGVkRZNWNFWndiV2Rt
DQpNWEIyY21SbFFrdFNZamxaYURCdVVHcHhiRlV3Tm1WQ2JGSlFaSFJ3Y0Rs
cFMybGhSMnRPTkZOMWNVNU0NClVGeHVTa3N6TUUxcldVOTVWREl6WVVWdk5U
ZGhWM1ZvVjJkSFRtZFBZVXRJTkVGcE55dE1NRE5dWnpWeQ0KWlV0aWJsVk9T
RmRzVVROUGRHVXdWR3hEWEc1MWjWaEtRMGQ2YTAxWFpUZHJURTVET0doV00w
ODRWM0V2DQphV2M1YWs5cWFFWk9aR3BYTm1aVmJXNUNaazlXVUVRMWRrMXpj
bTFDV0V4dldtRmNibFpTTWpWU05VeFMNClEwTjRNMWxWCUtSVGEzTTJaV2xE
V0hKTFRGQmpURXRsZFVaQlNtRnJTbkpPZGtKdlUyUmlNVWxNWWpKaQ0KT0dw
c05YbE1kVnh1YzFWbk5VZDFhbU56ZUM5Tk16TXZUakZOVW05cVpsVTNObEo0
TjJ4eVlVUkdkWEJtDQpkSHByYWpreVJrcG9UVlo0Y0hKSU9URndiV2RzVFdO
VlhHNXRhVmszTkV0SVEzcEpNMWRyZEVoRU4ydHINCmRIRnFRVTlCVUVVM1pV
SlRORE4xUjFaYVJGb3JlWGM5UFZ4dUlpd2lhWFlpt2lKV00yRnNVbk5RTjJk
Sg0KU1hNMGExaE9SVGR2V2pKQlBUMWNiaUo5DQo=',
max_historical_user_count: 10
}
```
For air-gapped or closed network customers, the existing [true-up model](#users-over-license) will be used. Prorated charges are not possible without user count data.
### Renew or change a GitLab.com subscription
 
To renew for more users than are currently active in your GitLab.com system, contact our sales team via `renewals@gitlab.com` for assistance as this can't be done in the Customers Portal.
Loading
Loading
Loading
Loading
@@ -35,43 +35,39 @@ export REGION=us-central1 # the GCP region where the GKE cluster is provisioned.
 
## Configure RBAC permissions
 
- For a non-GitLab managed cluster(s), ensure that the service account for the token provided can manage resources in the `database.crossplane.io` API group.
Manually grant GitLab's service account the ability to manage resources in the
`database.crossplane.io` API group. The Aggregated ClusterRole allows us to do that.
NOTE: **Note:**
For a non-GitLab managed cluster, ensure that the service account for the token provided can manage resources in the `database.crossplane.io` API group.
​1. Save the following YAML as `crossplane-database-role.yaml`:
```shell
cat > crossplane-database-role.yaml <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: crossplane-database-role
labels:
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups:
- database.crossplane.io
resources:
- postgresqlinstances
verbs:
- get
- list
- create
- update
- delete
- patch
- watch
EOF
```
Once the file is created, apply it with the following command in order to create the necessary role:
```shell
kubectl apply -f crossplane-database-role.yaml
```
- For GitLab-managed clusters, RBAC is configured automatically.
- For non-GitLab managed clusters, ensure that the service account for the token provided can manage resources in the `database.crossplane.io` API group:
1. Save the following YAML as `crossplane-database-role.yaml`:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: crossplane-database-role
labels:
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups:
- database.crossplane.io
resources:
- postgresqlinstances
verbs:
- get
- list
- create
- update
- delete
- patch
- watch
```
1. Apply the cluster role to the cluster:
```shell
kubectl apply -f crossplane-database-role.yaml
```
 
## Configure Crossplane with a cloud provider
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment