Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/issue_templates/Geo Replicate a new Git repository type.md2
-rw-r--r--.gitlab/issue_templates/Geo Replicate a new blob type.md2
-rw-r--r--app/finders/merge_requests_finder.rb12
-rw-r--r--app/models/clusters/agent.rb2
-rw-r--r--app/models/design_management/repository.rb2
-rw-r--r--app/models/environment.rb1
-rw-r--r--app/models/project.rb4
-rw-r--r--app/workers/post_receive.rb15
-rw-r--r--config/application.rb2
-rw-r--r--config/feature_flags/development/enable_rails_cache_pipeline_patch.yml8
-rw-r--r--config/feature_flags/development/load_merge_request_via_links.yml8
-rw-r--r--config/initializers_before_autoloader/000_inflections.rb1
-rw-r--r--db/docs/design_management_repository_states.yml2
-rw-r--r--db/migrate/20230517151041_add_agent_id_column_to_environments.rb9
-rw-r--r--db/migrate/20230517151141_add_index_to_agent_id_column_environments.rb15
-rw-r--r--db/migrate/20230517151241_add_agent_id_foreign_key_to_environments.rb17
-rw-r--r--db/schema_migrations/202305171510411
-rw-r--r--db/schema_migrations/202305171511411
-rw-r--r--db/schema_migrations/202305171512411
-rw-r--r--db/structure.sql8
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md10
-rw-r--r--doc/administration/server_hooks.md2
-rw-r--r--doc/api/geo_nodes.md40
-rw-r--r--doc/api/graphql/reference/index.md66
-rw-r--r--doc/development/ai_features.md6
-rw-r--r--doc/development/feature_flags/index.md40
-rw-r--r--doc/development/sec/analyzer_development_guide.md6
-rw-r--r--lib/gitlab/gl_repository.rb2
-rw-r--r--lib/gitlab/import_export/project/import_export.yml1
-rw-r--r--lib/gitlab/import_export/project/relation_factory.rb1
-rw-r--r--lib/gitlab/instrumentation/redis_cluster_validator.rb8
-rw-r--r--lib/gitlab/patch/redis_cache_store.rb83
-rw-r--r--spec/finders/merge_requests_finder_spec.rb1
-rw-r--r--spec/lib/gitlab/gl_repository/repo_type_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml5
-rw-r--r--spec/lib/gitlab/patch/redis_cache_store_spec.rb177
-rw-r--r--spec/models/clusters/agent_spec.rb1
-rw-r--r--spec/models/environment_spec.rb1
-rw-r--r--spec/services/notes/create_service_spec.rb2
-rw-r--r--spec/services/projects/destroy_service_spec.rb12
-rw-r--r--spec/services/projects/transfer_service_spec.rb2
-rw-r--r--spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb6
-rw-r--r--spec/workers/post_receive_spec.rb24
44 files changed, 562 insertions, 50 deletions
diff --git a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
index 549100741d8..95f92430eea 100644
--- a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
@@ -588,7 +588,7 @@ Metrics are gathered by `Geo::MetricsUpdateWorker`, persisted in `GeoNodeStatus`
- `cool_widgets_synced_in_percentage`
- `cool_widgets_verified_in_percentage`
- [ ] Add the same fields to `GET /geo_nodes/status` example response in
- `ee/spec/fixtures/api/schemas/public_api/v4/geo_node_status.json`.
+ `ee/spec/fixtures/api/schemas/public_api/v4/geo_node_status.json` and `ee/spec/fixtures/api/schemas/public_api/v4/geo_site_status.json`.
- [ ] Add the following fields to the `Sidekiq metrics` table in `doc/administration/monitoring/prometheus/gitlab_metrics.md`:
```markdown
| `geo_cool_widgets` | Gauge | XX.Y | Number of Cool Widgets on primary | `url` |
diff --git a/.gitlab/issue_templates/Geo Replicate a new blob type.md b/.gitlab/issue_templates/Geo Replicate a new blob type.md
index fc454919cec..7bf8dd6526e 100644
--- a/.gitlab/issue_templates/Geo Replicate a new blob type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new blob type.md
@@ -555,7 +555,7 @@ Metrics are gathered by `Geo::MetricsUpdateWorker`, persisted in `GeoNodeStatus`
- `cool_widgets_synced_in_percentage`
- `cool_widgets_verified_in_percentage`
- [ ] Add the same fields to `GET /geo_nodes/status` example response in
- `ee/spec/fixtures/api/schemas/public_api/v4/geo_node_status.json`.
+ `ee/spec/fixtures/api/schemas/public_api/v4/geo_node_status.json` and `ee/spec/fixtures/api/schemas/public_api/v4/geo_site_status.json`.
- [ ] Add the following fields to the `Sidekiq metrics` table in `doc/administration/monitoring/prometheus/gitlab_metrics.md`:
```markdown
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index f1c5d5e08ad..f7ee90ab870 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -158,19 +158,15 @@ class MergeRequestsFinder < IssuableFinder
return items if draft_param.nil?
if draft_param
- items.where(wip_match(items.arel_table))
+ items.where(draft_match(items.arel_table))
else
- items.where.not(wip_match(items.arel_table))
+ items.where.not(draft_match(items.arel_table))
end
end
# rubocop: enable CodeReuse/ActiveRecord
- # WIP is deprecated in favor of Draft. Currently both options are supported
- def wip_match(table)
- table[:title].matches('WIP:%')
- .or(table[:title].matches('WIP %'))
- .or(table[:title].matches('[WIP]%'))
- .or(table[:title].matches('Draft - %'))
+ def draft_match(table)
+ table[:title].matches('Draft - %')
.or(table[:title].matches('Draft:%'))
.or(table[:title].matches('[Draft]%'))
.or(table[:title].matches('(Draft)%'))
diff --git a/app/models/clusters/agent.rb b/app/models/clusters/agent.rb
index 6980ec1c2d3..a03649cc41f 100644
--- a/app/models/clusters/agent.rb
+++ b/app/models/clusters/agent.rb
@@ -29,6 +29,8 @@ module Clusters
has_many :activity_events, -> { in_timeline_order }, class_name: 'Clusters::Agents::ActivityEvent', inverse_of: :agent
+ has_many :environments, class_name: '::Environment', inverse_of: :cluster_agent, foreign_key: :cluster_agent_id
+
scope :ordered_by_name, -> { order(:name) }
scope :with_name, -> (name) { where(name: name) }
scope :has_vulnerabilities, -> (value = true) { where(has_vulnerabilities: value) }
diff --git a/app/models/design_management/repository.rb b/app/models/design_management/repository.rb
index 33c5dc15fa4..39077fdbcb1 100644
--- a/app/models/design_management/repository.rb
+++ b/app/models/design_management/repository.rb
@@ -34,3 +34,5 @@ module DesignManagement
end
end
end
+
+DesignManagement::Repository.prepend_mod_with('DesignManagement::Repository')
diff --git a/app/models/environment.rb b/app/models/environment.rb
index f1de41674c6..e34b43d0a7c 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -15,6 +15,7 @@ class Environment < ApplicationRecord
belongs_to :project, optional: false
belongs_to :merge_request, optional: true
+ belongs_to :cluster_agent, class_name: 'Clusters::Agent', optional: true, inverse_of: :environments
use_fast_destroy :all_deployments
nullify_if_blank :external_url
diff --git a/app/models/project.rb b/app/models/project.rb
index bd341b83482..462a169ea95 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1205,8 +1205,8 @@ class Project < ApplicationRecord
@repository ||= Gitlab::GlRepository::PROJECT.repository_for(self)
end
- def design_management_repository
- super || create_design_management_repository
+ def find_or_create_design_management_repository
+ design_management_repository || create_design_management_repository
end
def design_repository
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 676a834d79d..4971dc3775f 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -36,6 +36,8 @@ class PostReceive
process_project_changes(post_received, container)
elsif repo_type.snippet?
process_snippet_changes(post_received, container)
+ elsif repo_type.design?
+ process_design_management_repository_changes(post_received, container)
else
# Other repos don't have hooks for now
end
@@ -89,10 +91,23 @@ class PostReceive
Snippets::UpdateStatisticsService.new(snippet).execute
end
+ def process_design_management_repository_changes(post_received, design_management_repository)
+ user = identify_user(post_received)
+
+ return false unless user
+
+ replicate_design_management_repository_changes(design_management_repository)
+ expire_caches(post_received, design_management_repository.repository)
+ end
+
def replicate_snippet_changes(snippet)
# Used by Gitlab Geo
end
+ def replicate_design_management_repository_changes(design_management_repository)
+ # Used by GitLab Geo
+ end
+
# Expire the repository status, branch, and tag cache once per push.
def expire_caches(post_received, repository)
repository.expire_status_cache if repository.empty?
diff --git a/config/application.rb b/config/application.rb
index e7bea556960..5aca9b999a7 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -70,6 +70,7 @@ module Gitlab
require_dependency Rails.root.join('lib/gitlab/middleware/rack_multipart_tempfile_factory')
require_dependency Rails.root.join('lib/gitlab/runtime')
require_dependency Rails.root.join('lib/gitlab/patch/database_config')
+ require_dependency Rails.root.join('lib/gitlab/patch/redis_cache_store')
require_dependency Rails.root.join('lib/gitlab/exceptions_app')
config.exceptions_app = Gitlab::ExceptionsApp.new(Gitlab.jh? ? Rails.root.join('jh/public') : Rails.public_path)
@@ -484,6 +485,7 @@ module Gitlab
end
# Use caching across all environments
+ ActiveSupport::Cache::RedisCacheStore.prepend(Gitlab::Patch::RedisCacheStore)
config.cache_store = :redis_cache_store, Gitlab::Redis::Cache.active_support_config
config.active_job.queue_adapter = :sidekiq
diff --git a/config/feature_flags/development/enable_rails_cache_pipeline_patch.yml b/config/feature_flags/development/enable_rails_cache_pipeline_patch.yml
new file mode 100644
index 00000000000..fdbd0eaaa76
--- /dev/null
+++ b/config/feature_flags/development/enable_rails_cache_pipeline_patch.yml
@@ -0,0 +1,8 @@
+---
+name: enable_rails_cache_pipeline_patch
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119724
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/410115
+milestone: '16.1'
+type: development
+group: group::scalability
+default_enabled: false
diff --git a/config/feature_flags/development/load_merge_request_via_links.yml b/config/feature_flags/development/load_merge_request_via_links.yml
new file mode 100644
index 00000000000..a9db587a7a9
--- /dev/null
+++ b/config/feature_flags/development/load_merge_request_via_links.yml
@@ -0,0 +1,8 @@
+---
+name: load_merge_request_via_links
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117321
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/412177
+milestone: '16.1'
+type: development
+group: group::threat insights
+default_enabled: false
diff --git a/config/initializers_before_autoloader/000_inflections.rb b/config/initializers_before_autoloader/000_inflections.rb
index 2bf98a38544..41ffa454b4f 100644
--- a/config/initializers_before_autoloader/000_inflections.rb
+++ b/config/initializers_before_autoloader/000_inflections.rb
@@ -19,6 +19,7 @@ ActiveSupport::Inflector.inflections do |inflect|
container_repository_registry
dependency_proxy_blob_registry
design_registry
+ design_management_repository_registry
dependency_proxy_manifest_registry
event_log
file_registry
diff --git a/db/docs/design_management_repository_states.yml b/db/docs/design_management_repository_states.yml
index 3a8fa59fb42..82378f692dd 100644
--- a/db/docs/design_management_repository_states.yml
+++ b/db/docs/design_management_repository_states.yml
@@ -1,7 +1,7 @@
---
table_name: design_management_repository_states
classes:
-classes: []
+- Geo::DesignManagementRepositoryState
feature_categories:
- geo_replication
description: Separate table for Design Repository verification states
diff --git a/db/migrate/20230517151041_add_agent_id_column_to_environments.rb b/db/migrate/20230517151041_add_agent_id_column_to_environments.rb
new file mode 100644
index 00000000000..bc042d361bc
--- /dev/null
+++ b/db/migrate/20230517151041_add_agent_id_column_to_environments.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddAgentIdColumnToEnvironments < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def change
+ add_column :environments, :cluster_agent_id, :bigint, null: true
+ end
+end
diff --git a/db/migrate/20230517151141_add_index_to_agent_id_column_environments.rb b/db/migrate/20230517151141_add_index_to_agent_id_column_environments.rb
new file mode 100644
index 00000000000..7a88ea7cfe6
--- /dev/null
+++ b/db/migrate/20230517151141_add_index_to_agent_id_column_environments.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddIndexToAgentIdColumnEnvironments < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_environments_cluster_agent_id'
+
+ def up
+ add_concurrent_index :environments, :cluster_agent_id, name: INDEX_NAME, where: 'cluster_agent_id IS NOT NULL'
+ end
+
+ def down
+ remove_concurrent_index_by_name :environments, name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20230517151241_add_agent_id_foreign_key_to_environments.rb b/db/migrate/20230517151241_add_agent_id_foreign_key_to_environments.rb
new file mode 100644
index 00000000000..c12bff0ec4e
--- /dev/null
+++ b/db/migrate/20230517151241_add_agent_id_foreign_key_to_environments.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddAgentIdForeignKeyToEnvironments < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :environments, :cluster_agents,
+ column: :cluster_agent_id,
+ on_delete: :nullify
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :environments, column: :cluster_agent_id
+ end
+ end
+end
diff --git a/db/schema_migrations/20230517151041 b/db/schema_migrations/20230517151041
new file mode 100644
index 00000000000..acd95a39933
--- /dev/null
+++ b/db/schema_migrations/20230517151041
@@ -0,0 +1 @@
+ce12aa5cb3f207f2bfa19a27a1765ad027f825b6d75e7029fee5141e3160e5cb \ No newline at end of file
diff --git a/db/schema_migrations/20230517151141 b/db/schema_migrations/20230517151141
new file mode 100644
index 00000000000..658a5798332
--- /dev/null
+++ b/db/schema_migrations/20230517151141
@@ -0,0 +1 @@
+06246b8d7df8d18c3d8e5308f1dcc8375e011f507a969c95678aa8d532a24644 \ No newline at end of file
diff --git a/db/schema_migrations/20230517151241 b/db/schema_migrations/20230517151241
new file mode 100644
index 00000000000..fc08cf92ae8
--- /dev/null
+++ b/db/schema_migrations/20230517151241
@@ -0,0 +1 @@
+f528675db7c4884930b4febc91aca6105189cd040416b92e21364ffe9d9e9bfd \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 4bd77584c6b..87510ecc9e7 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -15661,7 +15661,8 @@ CREATE TABLE environments (
auto_stop_at timestamp with time zone,
auto_delete_at timestamp with time zone,
tier smallint,
- merge_request_id bigint
+ merge_request_id bigint,
+ cluster_agent_id bigint
);
CREATE SEQUENCE environments_id_seq
@@ -30635,6 +30636,8 @@ CREATE INDEX index_emails_on_user_id ON emails USING btree (user_id);
CREATE INDEX index_enabled_clusters_on_id ON clusters USING btree (id) WHERE (enabled = true);
+CREATE INDEX index_environments_cluster_agent_id ON environments USING btree (cluster_agent_id) WHERE (cluster_agent_id IS NOT NULL);
+
CREATE INDEX index_environments_for_name_search_within_folder ON environments USING btree (project_id, lower(ltrim((name)::text, ((environment_type)::text || '/'::text))) varchar_pattern_ops, state);
CREATE INDEX index_environments_on_merge_request_id ON environments USING btree (merge_request_id);
@@ -35056,6 +35059,9 @@ ALTER TABLE ONLY index_statuses
ALTER TABLE ONLY cluster_agent_tokens
ADD CONSTRAINT fk_75008f3553 FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL;
+ALTER TABLE ONLY environments
+ ADD CONSTRAINT fk_75c2098045 FOREIGN KEY (cluster_agent_id) REFERENCES cluster_agents(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT fk_76bc5f5455 FOREIGN KEY (resolved_by_id) REFERENCES users(id) ON DELETE SET NULL;
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 6b6712cdd01..f92b408323c 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -372,6 +372,16 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `gitlab_memwd_violations_handled_total` | Counter | 15.9 | Total number of times Sidekiq process memory violations were handled | |
| `sidekiq_watchdog_running_jobs_total` | Counter | 15.9 | Current running jobs when RSS limit was reached | `worker_class` |
| `gitlab_maintenance_mode` | Gauge | 15.11 | Is GitLab Maintenance Mode enabled? | |
+| `geo_design_management_repositories` | Gauge | 16.1 | Number of design repositories on primary | `url` |
+| `geo_design_management_repositories_checksum_total` | Gauge | 16.1 | Number of design repositories tried to checksum on primary | `url` |
+| `geo_design_management_repositories_checksummed` | Gauge | 16.1 | Number of design repositories successfully checksummed on primary | `url` |
+| `geo_design_management_repositories_checksum_failed` | Gauge | 16.1 | Number of design repositories failed to calculate the checksum on primary | `url` |
+| `geo_design_management_repositories_synced` | Gauge | 16.1 | Number of syncable design repositories synced on secondary | `url` |
+| `geo_design_management_repositories_failed` | Gauge | 16.1 | Number of syncable design repositories failed to sync on secondary | `url` |
+| `geo_design_management_repositories_registry` | Gauge | 16.1 | Number of design repositories in the registry | `url` |
+| `geo_design_management_repositories_verification_total` | Gauge | 16.1 | Number of design repositories verifications tried on secondary | `url` |
+| `geo_design_management_repositories_verified` | Gauge | 16.1 | Number of design repositories verified on secondary | `url` |
+| `geo_design_management_repositories_verification_failed` | Gauge | 16.1 | Number of design repositories verifications failed on secondary | `url` |
## Database load balancing metrics **(PREMIUM SELF)**
diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md
index b167412075b..c6621c59c8f 100644
--- a/doc/administration/server_hooks.md
+++ b/doc/administration/server_hooks.md
@@ -201,7 +201,7 @@ The following GitLab environment variables are supported for all server hooks:
| Environment variable | Description |
|:---------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `GL_ID` | GitLab identifier of user that initiated the push. For example, `user-2234`. |
+| `GL_ID` | GitLab identifier of user or SSH key that initiated the push. For example, `user-2234` or `key-4`. |
| `GL_PROJECT_PATH` | (GitLab 13.2 and later) GitLab project path. |
| `GL_PROTOCOL` | (GitLab 13.2 and later) Protocol used for this change. One of: `http` (Git `push` using HTTP), `ssh` (Git `push` using SSH), or `web` (all other actions). |
| `GL_REPOSITORY` | `project-<id>` where `id` is the ID of the project. |
diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md
index 038b7d633a6..1445e9768c1 100644
--- a/doc/api/geo_nodes.md
+++ b/doc/api/geo_nodes.md
@@ -550,7 +550,19 @@ Example response:
"dependency_proxy_manifests_verified_count": 5,
"dependency_proxy_manifests_verification_failed_count": 5,
"dependency_proxy_manifests_synced_in_percentage": "100.00%",
- "dependency_proxy_manifests_verified_in_percentage": "100.00%"
+ "dependency_proxy_manifests_verified_in_percentage": "100.00%",
+ "design_management_repositories_count": 5,
+ "design_management_repositories_checksum_total_count": 5,
+ "design_management_repositories_checksummed_count": 5,
+ "design_management_repositories_checksum_failed_count": 5,
+ "design_management_repositories_synced_count": 5,
+ "design_management_repositories_failed_count": 0,
+ "design_management_repositories_registry_count": 5,
+ "design_management_repositories_verification_total_count": 5,
+ "design_management_repositories_verified_count": 5,
+ "design_management_repositories_verification_failed_count": 5,
+ "design_management_repositories_synced_in_percentage": "100.00%",
+ "design_management_repositories_verified_in_percentage": "100.00%"
},
{
"geo_node_id": 2,
@@ -580,6 +592,18 @@ Example response:
"design_repositories_synced_count": null,
"design_repositories_failed_count": null,
"design_repositories_synced_in_percentage": "0.00%",
+ "design_management_repositories_count": 5,
+ "design_management_repositories_synced_count": 5,
+ "design_management_repositories_failed_count": 5,
+ "design_management_repositories_synced_in_percentage": "100.00%",
+ "design_management_repositories_checksum_total_count": 5,
+ "design_management_repositories_checksummed_count": 5,
+ "design_management_repositories_checksum_failed_count": 5,
+ "design_management_repositories_registry_count": 5,
+ "design_management_repositories_verification_total_count": 5,
+ "design_management_repositories_verified_count": 5,
+ "design_management_repositories_verification_failed_count": 5,
+ "design_management_repositories_verified_in_percentage": "100.00%",
"projects_count": 41,
"repositories_count": 41,
"repositories_failed_count": 1,
@@ -966,7 +990,19 @@ Example response:
"dependency_proxy_manifests_verified_count": 5,
"dependency_proxy_manifests_verification_failed_count": 5,
"dependency_proxy_manifests_synced_in_percentage": "100.00%",
- "dependency_proxy_manifests_verified_in_percentage": "100.00%"
+ "dependency_proxy_manifests_verified_in_percentage": "100.00%",
+ "design_management_repositories_count": 5,
+ "design_management_repositories_checksum_total_count": 5,
+ "design_management_repositories_checksummed_count": 5,
+ "design_management_repositories_checksum_failed_count": 5,
+ "design_management_repositories_synced_count": 5,
+ "design_management_repositories_failed_count": 0,
+ "design_management_repositories_registry_count": 5,
+ "design_management_repositories_verification_total_count": 5,
+ "design_management_repositories_verified_count": 5,
+ "design_management_repositories_verification_failed_count": 5,
+ "design_management_repositories_synced_in_percentage": "100.00%",
+ "design_management_repositories_verified_in_percentage": "100.00%"
}
```
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 43fd2490b14..aba5ea4de71 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -8628,6 +8628,29 @@ The edge type for [`Design`](#design).
| <a id="designedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="designedgenode"></a>`node` | [`Design`](#design) | The item at the end of the edge. |
+#### `DesignManagementRepositoryRegistryConnection`
+
+The connection type for [`DesignManagementRepositoryRegistry`](#designmanagementrepositoryregistry).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="designmanagementrepositoryregistryconnectionedges"></a>`edges` | [`[DesignManagementRepositoryRegistryEdge]`](#designmanagementrepositoryregistryedge) | A list of edges. |
+| <a id="designmanagementrepositoryregistryconnectionnodes"></a>`nodes` | [`[DesignManagementRepositoryRegistry]`](#designmanagementrepositoryregistry) | A list of nodes. |
+| <a id="designmanagementrepositoryregistryconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `DesignManagementRepositoryRegistryEdge`
+
+The edge type for [`DesignManagementRepositoryRegistry`](#designmanagementrepositoryregistry).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="designmanagementrepositoryregistryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="designmanagementrepositoryregistryedgenode"></a>`node` | [`DesignManagementRepositoryRegistry`](#designmanagementrepositoryregistry) | The item at the end of the edge. |
+
#### `DesignVersionConnection`
The connection type for [`DesignVersion`](#designversion).
@@ -13868,6 +13891,25 @@ Returns [`DesignVersion`](#designversion).
| ---- | ---- | ----------- |
| <a id="designmanagementversionid"></a>`id` | [`DesignManagementVersionID!`](#designmanagementversionid) | Global ID of the version. |
+### `DesignManagementRepositoryRegistry`
+
+Represents the Geo replication and verification state of a Design Management Repository.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="designmanagementrepositoryregistrycreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp when the DesignManagementRepositoryRegistry was created. |
+| <a id="designmanagementrepositoryregistrydesignmanagementrepositoryid"></a>`designManagementRepositoryId` | [`ID!`](#id) | ID of the Design Management Repository. |
+| <a id="designmanagementrepositoryregistryid"></a>`id` | [`ID!`](#id) | ID of the DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistrylastsyncfailure"></a>`lastSyncFailure` | [`String`](#string) | Error message during sync of the DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistrylastsyncedat"></a>`lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the DesignManagementRepositoryRegistry is resynced. |
+| <a id="designmanagementrepositoryregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the DesignManagementRepositoryRegistry. |
+| <a id="designmanagementrepositoryregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the DesignManagementRepositoryRegistry is reverified. |
+| <a id="designmanagementrepositoryregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the DesignManagementRepositoryRegistry. |
+
### `DesignVersion`
A specific version in which designs were added, modified or deleted.
@@ -14880,6 +14922,29 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="geonodedependencyproxymanifestregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. |
| <a id="geonodedependencyproxymanifestregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. |
+##### `GeoNode.designManagementRepositoryRegistries`
+
+Find Design Repository registries on this Geo node. Ignored if `geo_design_management_repository_replication` feature flag is disabled.
+
+WARNING:
+**Introduced** in 16.1.
+This feature is an Experiment. It can be changed or removed at any time.
+
+Returns [`DesignManagementRepositoryRegistryConnection`](#designmanagementrepositoryregistryconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#connection-pagination-arguments):
+`before: String`, `after: String`, `first: Int`, `last: Int`.
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="geonodedesignmanagementrepositoryregistriesids"></a>`ids` | [`[ID!]`](#id) | Filters registries by their ID. |
+| <a id="geonodedesignmanagementrepositoryregistrieskeyword"></a>`keyword` | [`String`](#string) | Filters registries by their attributes using a keyword. |
+| <a id="geonodedesignmanagementrepositoryregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. |
+| <a id="geonodedesignmanagementrepositoryregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. |
+
##### `GeoNode.groupWikiRepositoryRegistries`
Find group wiki repository registries on this Geo node.
@@ -24562,6 +24627,7 @@ Geo registry class.
| <a id="georegistryclasscontainer_repository_registry"></a>`CONTAINER_REPOSITORY_REGISTRY` | Geo::ContainerRepositoryRegistry registry class. |
| <a id="georegistryclassdependency_proxy_blob_registry"></a>`DEPENDENCY_PROXY_BLOB_REGISTRY` | Geo::DependencyProxyBlobRegistry registry class. |
| <a id="georegistryclassdependency_proxy_manifest_registry"></a>`DEPENDENCY_PROXY_MANIFEST_REGISTRY` | Geo::DependencyProxyManifestRegistry registry class. |
+| <a id="georegistryclassdesign_management_repository_registry"></a>`DESIGN_MANAGEMENT_REPOSITORY_REGISTRY` | Geo::DesignManagementRepositoryRegistry registry class. |
| <a id="georegistryclassjob_artifact_registry"></a>`JOB_ARTIFACT_REGISTRY` | Geo::JobArtifactRegistry registry class. |
| <a id="georegistryclasslfs_object_registry"></a>`LFS_OBJECT_REGISTRY` | Geo::LfsObjectRegistry registry class. |
| <a id="georegistryclassmerge_request_diff_registry"></a>`MERGE_REQUEST_DIFF_REGISTRY` | Geo::MergeRequestDiffRegistry registry class. |
diff --git a/doc/development/ai_features.md b/doc/development/ai_features.md
index 25da99addc1..f8d19dc26bf 100644
--- a/doc/development/ai_features.md
+++ b/doc/development/ai_features.md
@@ -66,7 +66,9 @@ All AI features are experimental.
1. Enable **Experiment features**
1. Enable **Third-party AI services**
1. Enable the specific feature flag for the feature you want to test
-1. Set either the required access token `OpenAi` or `Vertex`. Ask in [`#ai_enablement_team`](https://gitlab.slack.com/archives/C051K31F30R) to receive an access token.
+1. Set the required access token. To receive an access token:
+ 1. For Vertex, follow the [instructions below](#configure-gcp-vertex-access).
+ 1. For all other providers, create an access request where `@m_gill`, `@wayne`, and `@timzallmann` are the tech stack owners.
### Set up the embedding database
@@ -102,7 +104,7 @@ tail -f log/llm.log
In order to obtain a GCP service key for local development, please follow the steps below:
-- Create a sandbox GCP environment by visiting [this page](https://about.gitlab.com/handbook/infrastructure-standards/#individual-environment) and following the instructions
+- Create a sandbox GCP environment by visiting [this page](https://about.gitlab.com/handbook/infrastructure-standards/#individual-environment) and following the instructions, or by requesting access to our existing group environment by using [this template](https://gitlab.com/gitlab-com/it/infra/issue-tracker/-/issues/new?issuable_template=gcp_group_account_iam_update_request). At this time, access to any endpoints outside of `text-bison` or `chat-bison` must be made through the group environment.
- In the GCP console, go to `IAM & Admin` > `Service Accounts` and click on the "Create new service account" button
- Name the service account something specific to what you're using it for. Select Create and Continue. Under `Grant this service account access to project`, select the role `Vertex AI User`. Select `Continue` then `Done`
- Select your new service account and `Manage keys` > `Add Key` > `Create new key`. This will download the **private** JSON credentials for your service account. Your full settings should then be:
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index c2026ab8966..4b0ad30e3ad 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -149,7 +149,8 @@ created using the [Experiment Tracking template](https://gitlab.com/gitlab-org/g
`worker` feature flags are used for controlling Sidekiq workers behavior, such as deferring Sidekiq jobs.
`worker` feature flags likely do not have any YAML definition as the name could be dynamically generated using
-the worker name itself, e.g. `defer_sidekiq_jobs:AuthorizedProjectsWorker`.
+the worker name itself, e.g. `defer_sidekiq_jobs:AuthorizedProjectsWorker`. Some examples for using `worker` type feature
+flags can be found in [deferring Sidekiq jobs](#deferring-sidekiq-jobs).
## Feature flag definition and validation
@@ -705,3 +706,40 @@ test is written to enable/disable a feature flag explicitly.
When a feature flag is changed on Staging or on GitLab.com, a Slack message will be posted to the `#qa-staging` or `#qa-production` channels to inform
the pipeline triage DRI so that they can more easily determine if any failures are related to a feature flag change. However, if you are working on a change you can
help to avoid unexpected failures by [confirming that the end-to-end tests pass with a feature flag enabled.](../testing_guide/end_to_end/feature_flags.md#confirming-that-end-to-end-tests-pass-with-a-feature-flag-enabled)
+
+## Controlling Sidekiq worker behavior with feature flags
+
+Feature flags with [`worker` type](#worker-type) can be used to control the behavior of a Sidekiq worker.
+
+### Deferring Sidekiq jobs
+
+Feature flags with the format of `defer_sidekiq_jobs:{WorkerName}` delay the execution of the worker
+by scheduling the job at a later time.
+Deferring jobs can be useful during an incident where contentious behavior from
+worker instances are saturating infrastructure resources (such as database and database connection pool).
+The implementation can be found at [DeferJobs Sidekiq server middleware](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/sidekiq_middleware/defer_jobs.rb).
+
+NOTE:
+Jobs are deferred indefinitely as long as the feature flag is enabled. It is important to disable the
+feature flag after the worker is deemed safe to continue processing.
+
+When set to true, 100% of the jobs are deferred. When you want processing to resume, you can
+use a **percentage of time** rollout. For example:
+
+```shell
+# defer 100% of the jobs
+/chatops run feature set defer_sidekiq_jobs:SlowRunningWorker true
+
+# defer 99% of the jobs, only letting 1% processed
+/chatops run feature set defer_sidekiq_jobs:SlowRunningWorker 99
+
+# defer 50% of the jobs
+/chatops run feature set defer_sidekiq_jobs:SlowRunningWorker 50
+
+# stop deferring the jobs, jobs are being processed normally
+/chatops run feature set defer_sidekiq_jobs:SlowRunningWorker false
+```
+
+NOTE:
+The percentage of time value denotes the percentage of time the jobs are being deferred (instead of being processed).
+For example, setting to `99` means only 1% of the jobs are being processed at random.
diff --git a/doc/development/sec/analyzer_development_guide.md b/doc/development/sec/analyzer_development_guide.md
index 31058427b55..3f1420b8d3d 100644
--- a/doc/development/sec/analyzer_development_guide.md
+++ b/doc/development/sec/analyzer_development_guide.md
@@ -361,3 +361,9 @@ This issue will guide you through the whole release process. In general, you hav
- Max role: `Developer`
- Scope of the associated `GITLAB_TOKEN`:
- Expiry Date of the associated `GITLAB_TOKEN`:
+
+#### Dependency updates
+
+All dependencies and upstream scanners (if any) used in the analyzer source are updated on a monthly cadence which primarily includes security fixes and non-breaking changes.
+
+- Static Analysis team uses a custom internal tool ([SastBot](https://gitlab.com/gitlab-org/security-products/analyzers/sast-analyzer-deps-bot#dependency-update-automation)) to automate dependency management of all the SAST analyzers. SastBot generates MRs on the **8th of each month** and distributes their assignment among Static Analysis team members to take them forward for review. For details on the process, see [Dependency Update Automation](https://gitlab.com/gitlab-org/security-products/analyzers/sast-analyzer-deps-bot#dependency-update-automation).
diff --git a/lib/gitlab/gl_repository.rb b/lib/gitlab/gl_repository.rb
index 268d5d3e564..9161a1a138f 100644
--- a/lib/gitlab/gl_repository.rb
+++ b/lib/gitlab/gl_repository.rb
@@ -34,7 +34,7 @@ module Gitlab
DESIGN = ::Gitlab::GlRepository::RepoType.new(
name: :design,
access_checker_class: ::Gitlab::GitAccessDesign,
- repository_resolver: -> (project) { project.design_management_repository.repository },
+ repository_resolver: -> (project) { project.find_or_create_design_management_repository.repository },
suffix: :design,
container_class: DesignManagement::Repository
).freeze
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index 36a3c73271b..56cbc5f1bb4 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -121,7 +121,6 @@ tree:
- label:
- :priorities
- :service_desk_setting
- - :design_management_repository
group_members:
- :user
diff --git a/lib/gitlab/import_export/project/relation_factory.rb b/lib/gitlab/import_export/project/relation_factory.rb
index 5d7e3ea9ed7..8c673acdd1a 100644
--- a/lib/gitlab/import_export/project/relation_factory.rb
+++ b/lib/gitlab/import_export/project/relation_factory.rb
@@ -23,6 +23,7 @@ module Gitlab
design: 'DesignManagement::Design',
designs: 'DesignManagement::Design',
design_management_repository: 'DesignManagement::Repository',
+ design_management_repository_state: 'Geo::DesignManagementRepositoryState',
design_versions: 'DesignManagement::Version',
actions: 'DesignManagement::Action',
labels: :project_labels,
diff --git a/lib/gitlab/instrumentation/redis_cluster_validator.rb b/lib/gitlab/instrumentation/redis_cluster_validator.rb
index 1567e54d8da..7b6200af66c 100644
--- a/lib/gitlab/instrumentation/redis_cluster_validator.rb
+++ b/lib/gitlab/instrumentation/redis_cluster_validator.rb
@@ -211,6 +211,10 @@ module Gitlab
Thread.current[:allow_cross_slot_commands] -= 1
end
+ def allow_cross_slot_commands?
+ Thread.current[:allow_cross_slot_commands].to_i > 0
+ end
+
private
def extract_keys(command)
@@ -226,10 +230,6 @@ module Gitlab
keys.map { |key| key_slot(key) }.uniq.many? # rubocop: disable CodeReuse/ActiveRecord
end
- def allow_cross_slot_commands?
- Thread.current[:allow_cross_slot_commands].to_i > 0
- end
-
def key_slot(key)
::Redis::Cluster::KeySlotConverter.convert(extract_hash_tag(key))
end
diff --git a/lib/gitlab/patch/redis_cache_store.rb b/lib/gitlab/patch/redis_cache_store.rb
new file mode 100644
index 00000000000..f71262896d1
--- /dev/null
+++ b/lib/gitlab/patch/redis_cache_store.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Patch
+ module RedisCacheStore
+ PATCH_INPUT_LIMIT = 100
+ PIPELINE_BATCH_SIZE = 100
+
+ # We will try keep patched code explicit and matching the original signature in
+ # https://github.com/rails/rails/blob/v6.1.7.2/activesupport/lib/active_support/cache/redis_cache_store.rb#L361
+ def read_multi_mget(*names) # rubocop:disable Style/ArgumentsForwarding
+ return super unless enable_rails_cache_pipeline_patch?
+ return super if names.size > PATCH_INPUT_LIMIT # avoid excessive apdex degradation during benchmarking exercise
+
+ patched_read_multi_mget(*names) # rubocop:disable Style/ArgumentsForwarding
+ end
+
+ # `delete_multi_entries` in Rails runs a multi-key `del` command
+ # patch will run pipelined single-key `del` for Redis Cluster compatibility
+ def delete_multi_entries(entries, **options)
+ return super unless enable_rails_cache_pipeline_patch?
+
+ delete_count = 0
+ redis.with do |conn|
+ entries.each_slice(PIPELINE_BATCH_SIZE) do |subset|
+ delete_count += conn.pipelined do |pipeline|
+ subset.each { |entry| pipeline.del(entry) }
+ end.sum
+ end
+ end
+ delete_count
+ end
+
+ # Copied from https://github.com/rails/rails/blob/v6.1.6.1/activesupport/lib/active_support/cache/redis_cache_store.rb
+ # re-implements `read_multi_mget` using a pipeline of `get`s rather than an `mget`
+ #
+ def patched_read_multi_mget(*names)
+ options = names.extract_options!
+ options = merged_options(options)
+ return {} if names == []
+
+ raw = options&.fetch(:raw, false)
+
+ keys = names.map { |name| normalize_key(name, options) }
+
+ values = failsafe(:patched_read_multi_mget, returning: {}) do
+ redis.with { |c| pipeline_mget(c, keys) }
+ end
+
+ names.zip(values).each_with_object({}) do |(name, value), results|
+ if value # rubocop:disable Style/Next
+ entry = deserialize_entry(value, raw: raw)
+ unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options))
+ results[name] = entry.value
+ end
+ end
+ end
+ end
+
+ def pipeline_mget(conn, keys)
+ conn.pipelined do |p|
+ keys.each { |key| p.get(key) }
+ end
+ end
+
+ private
+
+ def enable_rails_cache_pipeline_patch?
+ redis_cache? &&
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands? &&
+ ::Feature.enabled?(:enable_rails_cache_pipeline_patch) # rubocop:disable Cop/FeatureFlagUsage
+ end
+
+ def redis_cache?
+ # We do not want to risk cycles of feature code calling redis calling feature code.
+ # Also, we only want to benchmark redis-cache, hence repository-cache and rate-limiting are excluded.
+ !is_a?(Gitlab::Redis::FeatureFlag::FeatureFlagStore) &&
+ !is_a?(Gitlab::Redis::RepositoryCache::RepositoryCacheStore) &&
+ !is_a?(Gitlab::Redis::RateLimiting::RateLimitingStore)
+ end
+ end
+ end
+end
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 6d576bc8e38..9aa98189f30 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -295,7 +295,6 @@ RSpec.describe MergeRequestsFinder, feature_category: :code_review_workflow do
end
{
- wip: ["WIP:", "wip", "[wip]"],
draft: ["Draft:", "Draft -", "[Draft]", "(Draft)"]
}.each do |draft_param_key, title_prefixes|
title_prefixes.each do |title_prefix|
diff --git a/spec/lib/gitlab/gl_repository/repo_type_spec.rb b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
index 2ac2fc1fd4b..4345df1b018 100644
--- a/spec/lib/gitlab/gl_repository/repo_type_spec.rb
+++ b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe Gitlab::GlRepository::RepoType do
describe Gitlab::GlRepository::DESIGN do
it_behaves_like 'a repo type' do
let(:expected_repository) { project.design_repository }
- let(:expected_container) { project.design_management_repository }
+ let(:expected_container) { expected_repository.container }
let(:expected_id) { expected_container.id }
let(:expected_identifier) { "design-#{expected_id}" }
let(:expected_suffix) { '.design' }
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 34f9948b9dc..e6829363922 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -798,6 +798,7 @@ project:
- analytics_dashboards_configuration_project
- analytics_dashboards_pointer
- design_management_repository
+- design_management_repository_state
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index faf345e8f78..9e5b5ccd64a 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -932,11 +932,6 @@ DesignManagement::Version:
- created_at
- sha
- author_id
-DesignManagement::Repository:
-- id
-- project_id
-- created_at
-- updated_at
ZoomMeeting:
- id
- project_id
diff --git a/spec/lib/gitlab/patch/redis_cache_store_spec.rb b/spec/lib/gitlab/patch/redis_cache_store_spec.rb
new file mode 100644
index 00000000000..e1bf774d157
--- /dev/null
+++ b/spec/lib/gitlab/patch/redis_cache_store_spec.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Patch::RedisCacheStore, :use_clean_rails_redis_caching, feature_category: :scalability do
+ before do
+ Rails.cache.write('x', 1)
+ Rails.cache.write('y', 2)
+ Rails.cache.write('z', 3)
+
+ Rails.cache.write('{user1}:x', 1)
+ Rails.cache.write('{user1}:y', 2)
+ Rails.cache.write('{user1}:z', 3)
+ end
+
+ describe '#read_multi_mget' do
+ it 'runs multi-key command if no cross-slot command is expected' do
+ Rails.cache.redis.with do |redis|
+ expect(redis).not_to receive(:pipelined)
+ end
+
+ expect(
+ Rails.cache.fetch_multi('{user1}:x', '{user1}:y', '{user1}:z') { |key| key }
+ ).to eq({ '{user1}:x' => 1, '{user1}:y' => 2, '{user1}:z' => 3 })
+ end
+
+ it 'skips patch if input is above 100' do
+ Rails.cache.redis.with do |redis|
+ expect(redis).not_to receive(:pipelined)
+ end
+
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ Rails.cache.read_multi(*Array.new(101) { |i| i })
+ end
+ end
+
+ shared_examples "fetches multiple keys" do |patched|
+ it 'reads multiple keys' do
+ if patched
+ Rails.cache.redis.with do |redis|
+ expect(redis).to receive(:pipelined).once.and_call_original
+ end
+ end
+
+ expect(::Feature).to receive(:enabled?)
+ .with(:feature_flag_state_logs, { default_enabled_if_undefined: nil, type: :ops })
+ .exactly(:once)
+ .and_call_original
+
+ expect(::Feature).to receive(:enabled?)
+ .with(:enable_rails_cache_pipeline_patch)
+ .exactly(:once)
+ .and_call_original
+
+ expect(
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ # fetch_multi requires a block and we have to specifically test it
+ # as it is used in the Gitlab project
+ Rails.cache.fetch_multi('x', 'y', 'z') { |key| key }
+ end
+ ).to eq({ 'x' => 1, 'y' => 2, 'z' => 3 })
+ end
+ end
+
+ shared_examples 'reading using non redis cache stores' do |klass|
+ it 'does not affect non Redis::Cache cache stores' do
+ klass.cache_store.redis.with do |redis|
+ expect(redis).not_to receive(:pipelined)
+ end
+
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ klass.cache_store.fetch_multi('x', 'y', 'z') { |key| key }
+ end
+ end
+ end
+
+ context 'when reading from non redis-cache stores' do
+ it_behaves_like 'reading using non redis cache stores', Gitlab::Redis::RepositoryCache
+ it_behaves_like 'reading using non redis cache stores', Gitlab::Redis::FeatureFlag
+ it_behaves_like 'reading using non redis cache stores', Gitlab::Redis::RateLimiting
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(enable_rails_cache_pipeline_patch: false)
+ end
+
+ it_behaves_like 'fetches multiple keys'
+ end
+
+ it_behaves_like 'fetches multiple keys', true
+ end
+
+ describe '#delete_multi_entries' do
+ shared_examples "deletes multiple keys" do |patched|
+ it 'deletes multiple keys' do
+ if patched
+ Rails.cache.redis.with do |redis|
+ expect(redis).to receive(:pipelined).once.and_call_original
+ end
+ end
+
+ expect(::Feature).to receive(:enabled?)
+ .with(:feature_flag_state_logs, { default_enabled_if_undefined: nil, type: :ops })
+ .exactly(:once)
+ .and_call_original
+
+ expect(::Feature).to receive(:enabled?)
+ .with(:enable_rails_cache_pipeline_patch)
+ .exactly(:once)
+ .and_call_original
+
+ expect(
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ Rails.cache.delete_multi(%w[x y z])
+ end
+ ).to eq(3)
+ end
+ end
+
+ shared_examples 'deleting using non redis cache stores' do |klass|
+ it 'does not affect non Redis::Cache cache stores' do
+ klass.cache_store.redis.with do |redis|
+ expect(redis).not_to receive(:pipelined)
+ end
+
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ klass.cache_store.delete_multi(%w[x y z])
+ end
+ end
+ end
+
+ context 'when deleting from non redis-cache stores' do
+ it_behaves_like 'deleting using non redis cache stores', Gitlab::Redis::RepositoryCache
+ it_behaves_like 'deleting using non redis cache stores', Gitlab::Redis::FeatureFlag
+ it_behaves_like 'deleting using non redis cache stores', Gitlab::Redis::RateLimiting
+ end
+
+ context 'when deleting large amount of keys' do
+ before do
+ 200.times { |i| Rails.cache.write(i, i) }
+ end
+
+ it 'calls pipeline multiple times' do
+ Rails.cache.redis.with do |redis|
+ expect(redis).to receive(:pipelined).twice.and_call_original
+ end
+
+ expect(
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ Rails.cache.delete_multi(Array(0..199))
+ end
+ ).to eq(200)
+ end
+ end
+
+ it 'runs multi-key command if no cross-slot command is expected' do
+ Rails.cache.redis.with do |redis|
+ expect(redis).not_to receive(:pipelined)
+ end
+
+ expect(
+ Rails.cache.delete_multi(%w[{user1}:x {user1}:y {user1}:z])
+ ).to eq(3)
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(enable_rails_cache_pipeline_patch: false)
+ end
+
+ it_behaves_like 'deletes multiple keys'
+ end
+
+ it_behaves_like 'deletes multiple keys', true
+ end
+end
diff --git a/spec/models/clusters/agent_spec.rb b/spec/models/clusters/agent_spec.rb
index 10081b955f4..7c546f42d5d 100644
--- a/spec/models/clusters/agent_spec.rb
+++ b/spec/models/clusters/agent_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe Clusters::Agent, feature_category: :deployment_management do
it { is_expected.to have_many(:ci_access_authorized_groups).through(:ci_access_group_authorizations) }
it { is_expected.to have_many(:ci_access_project_authorizations).class_name('Clusters::Agents::Authorizations::CiAccess::ProjectAuthorization') }
it { is_expected.to have_many(:ci_access_authorized_projects).through(:ci_access_project_authorizations).class_name('::Project') }
+ it { is_expected.to have_many(:environments).class_name('::Environment') }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_length_of(:name).is_at_most(63) }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 87beba680d8..061291906a0 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ
it { is_expected.to belong_to(:project).required }
it { is_expected.to belong_to(:merge_request).optional }
+ it { is_expected.to belong_to(:cluster_agent).optional }
it { is_expected.to have_many(:deployments) }
it { is_expected.to have_many(:metrics_dashboard_annotations) }
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 240d81bb485..5a5e8691a76 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -482,7 +482,7 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
expect(noteable.target_branch == "fix").to eq(can_use_quick_action)
}
),
- # Set WIP status
+ # Set Draft status
QuickAction.new(
action_text: "/draft",
before_action: -> {
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 665f930a0a8..7da23a59a58 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -113,7 +113,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
destroy_project(project, user, {})
expect(project.reload.delete_error).to be_present
- expect(project.delete_error).to include(error_message)
+ expect(project.delete_error).to match(error_message)
end
end
@@ -287,7 +287,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
.to receive(:remove_legacy_registry_tags).and_return(false)
end
- it_behaves_like 'handles errors thrown during async destroy', "Failed to remove some tags"
+ it_behaves_like 'handles errors thrown during async destroy', /Failed to remove some tags/
end
context 'when `remove_repository` fails' do
@@ -296,7 +296,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
.to receive(:remove_repository).and_return(false)
end
- it_behaves_like 'handles errors thrown during async destroy', "Failed to remove project repository"
+ it_behaves_like 'handles errors thrown during async destroy', /Failed to remove/
end
context 'when `execute` raises expected error' do
@@ -305,7 +305,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
.to receive(:destroy!).and_raise(StandardError.new("Other error message"))
end
- it_behaves_like 'handles errors thrown during async destroy', "Other error message"
+ it_behaves_like 'handles errors thrown during async destroy', /Other error message/
end
context 'when `execute` raises unexpected error' do
@@ -456,6 +456,8 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
end
context 'repository removal' do
+ # 1. Project repository
+ # 2. Wiki repository
it 'removal of existing repos' do
expect_next_instances_of(Repositories::DestroyService, 2) do |instance|
expect(instance).to receive(:execute).and_return(status: :success)
@@ -529,7 +531,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
end
end
- it_behaves_like 'handles errors thrown during async destroy', "Failed to remove webhooks"
+ it_behaves_like 'handles errors thrown during async destroy', /Failed to remove webhooks/
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 48d5935f22f..37adc9e4867 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -716,7 +716,7 @@ RSpec.describe Projects::TransferService, feature_category: :projects do
end
def clear_design_repo_memoization
- project.design_management_repository.clear_memoization(:repository)
+ project&.design_management_repository&.clear_memoization(:repository)
project.clear_memoization(:design_repository)
end
diff --git a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
index 507bcd44ee8..14bbe837e31 100644
--- a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
@@ -74,7 +74,7 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests
:merge_request, :simple, author: user, assignees: [user2], reviewers: [user],
source_project: project2, target_project: project2,
state: 'locked',
- title: 'thing WIP thing'
+ title: 'thing Draft thing'
)
end
@@ -82,7 +82,7 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests
create(
:merge_request, :simple, author: user,
source_project: project3, target_project: project3,
- title: 'WIP thing'
+ title: 'Draft - thing'
)
end
@@ -90,7 +90,7 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests
create(
:merge_request, :simple, author: user,
source_project: project4, target_project: project4,
- title: '[WIP]'
+ title: '[Draft]'
)
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index bd1bfc46d53..5c8a75aca3f 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -491,16 +491,26 @@ RSpec.describe PostReceive, feature_category: :source_code_management do
end
end
- describe 'processing design changes' do
- let(:gl_repository) { "design-#{project.id}" }
+ describe '#process_design_management_repository_changes' do
+ let(:gl_repository) { "design-#{project.design_management_repository.id}" }
- it 'does not do anything' do
- worker = described_class.new
+ before do
+ project.create_design_management_repository
+ project.design_management_repository.repository.create_if_not_exists
+ end
- expect(worker).not_to receive(:process_wiki_changes)
- expect(worker).not_to receive(:process_project_changes)
+ it 'does not log an error' do
+ expect(Gitlab::GitLogger).not_to receive(:error)
+ expect(Gitlab::GitPostReceive).to receive(:new).and_call_original
+ expect_next(described_class).to receive(:process_design_management_repository_changes)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ perform
+ end
+
+ it 'expires cache' do
+ expect_next(described_class).to receive(:expire_caches).with(anything, project.design_management_repository.repository)
+
+ perform
end
it_behaves_like 'an idempotent worker'