diff --git a/app/models/virtual_registries/packages/maven/cache/entry.rb b/app/models/virtual_registries/packages/maven/cache/entry.rb index 7a5b4a4a9b00a2be87f940699fd90fe2758ed8ff..da698c0c41777a0685cf7308724329f8096712fc 100644 --- a/app/models/virtual_registries/packages/maven/cache/entry.rb +++ b/app/models/virtual_registries/packages/maven/cache/entry.rb @@ -28,6 +28,7 @@ class Entry < ApplicationRecord enum :status, default: 0, processing: 1, pending_destruction: 2, error: 3 ignore_column :downloaded_at, remove_with: '17.9', remove_after: '2025-01-23' + ignore_column :file_final_path, remove_with: '17.11', remove_after: '2025-03-23' sha_attribute :file_sha1 sha_attribute :file_md5 @@ -39,18 +40,19 @@ class Entry < ApplicationRecord :file_sha1, presence: true validates :upstream_etag, :content_type, length: { maximum: 255 } - validates :relative_path, :object_storage_key, :file_final_path, length: { maximum: 1024 } + validates :relative_path, :object_storage_key, length: { maximum: 1024 } validates :file_md5, length: { is: 32 }, allow_nil: true validates :file_sha1, length: { is: 40 } validates :relative_path, uniqueness: { scope: [:upstream_id, :status] }, if: :default? + validates :object_storage_key, uniqueness: { scope: :relative_path } validates :file, presence: true mount_file_store_uploader ::VirtualRegistries::Cache::EntryUploader before_validation :set_object_storage_key, - if: -> { object_storage_key.blank? && relative_path && upstream && upstream.registry } + if: -> { object_storage_key.blank? && upstream && upstream.registry } attr_readonly :object_storage_key scope :search_by_relative_path, ->(query) do @@ -106,18 +108,7 @@ def mark_as_pending_destruction private def set_object_storage_key - self.object_storage_key = Gitlab::HashedPath.new( - 'virtual_registries', - 'packages', - 'maven', - upstream.registry.id, - 'upstream', - upstream.id, - 'cache', - 'entry', - OpenSSL::Digest::SHA256.hexdigest(relative_path), - root_hash: upstream.registry.id - ).to_s + self.object_storage_key = upstream.object_storage_key_for(registry_id: upstream.registry.id) end end end diff --git a/app/models/virtual_registries/packages/maven/upstream.rb b/app/models/virtual_registries/packages/maven/upstream.rb index ed02de1cc19372b46facff910a7703787c60e354..bfcdacbd19e827742312a3c591c343cda91a8255 100644 --- a/app/models/virtual_registries/packages/maven/upstream.rb +++ b/app/models/virtual_registries/packages/maven/upstream.rb @@ -59,6 +59,24 @@ def default_cache_entries cache_entries.default end + def object_storage_key_for(registry_id:) + hash = Digest::SHA2.hexdigest(SecureRandom.uuid) + Gitlab::HashedPath.new( + 'virtual_registries', + 'packages', + 'maven', + registry_id.to_s, + 'upstream', + id.to_s, + 'cache', + 'entry', + hash[0..1], + hash[2..3], + hash[4..], + root_hash: registry_id + ).to_s + end + private def reset_credentials diff --git a/app/services/ci/job_artifacts/create_service.rb b/app/services/ci/job_artifacts/create_service.rb index 82f2c22adadc3979d1e054608efe8a4297a08ba3..6b1d8a71db1ab8f82c137f739de838ade699748f 100644 --- a/app/services/ci/job_artifacts/create_service.rb +++ b/app/services/ci/job_artifacts/create_service.rb @@ -27,7 +27,7 @@ def authorize(artifact_type:, filesize: nil) has_length: false, maximum_size: max_size(artifact_type), use_final_store_path: true, - final_store_path_root_id: project.id + final_store_path_config: { root_hash: project.id } ) if lsif?(artifact_type) diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb index 7f0ac00d0bc9ae4f3a07d3eab625271186d5e69a..5c0fc1a6c01c1e516e2b8753f8933f757f2c21bd 100644 --- a/app/uploaders/object_storage.rb +++ b/app/uploaders/object_storage.rb @@ -177,7 +177,7 @@ def generate_remote_id [CarrierWave.generate_cache_id, SecureRandom.hex].join('-') end - def generate_final_store_path(root_id:) + def generate_final_store_path(root_hash:) hash = Digest::SHA2.hexdigest(SecureRandom.uuid) # We prefix '@final' to prevent clashes and make the files easily recognizable @@ -186,21 +186,26 @@ def generate_final_store_path(root_id:) # We generate a hashed path of the root ID (e.g. Project ID) to distribute directories instead of # filling up one root directory with a bunch of files. - Gitlab::HashedPath.new(sub_path, root_hash: root_id).to_s + Gitlab::HashedPath.new(sub_path, root_hash: root_hash).to_s end + # final_store_path_config is only used if use_final_store_path is set to true + # Two keys are available: + # - :root_hash. The root hash used in Gitlab::HashedPath for the path generation. + # - :override_path. If set, the path generation is skipped and this value is used instead. + # Make sure that this value is unique for each upload. def workhorse_authorize( has_length:, maximum_size: nil, use_final_store_path: false, - final_store_path_root_id: nil) + final_store_path_config: {}) {}.tap do |hash| if self.direct_upload_to_object_store? hash[:RemoteObject] = workhorse_remote_upload_options( has_length: has_length, maximum_size: maximum_size, use_final_store_path: use_final_store_path, - final_store_path_root_id: final_store_path_root_id + final_store_path_config: final_store_path_config ) else hash[:TempPath] = workhorse_local_upload_path @@ -231,13 +236,18 @@ def workhorse_remote_upload_options( has_length:, maximum_size: nil, use_final_store_path: false, - final_store_path_root_id: nil) + final_store_path_config: {}) return unless direct_upload_to_object_store? if use_final_store_path - raise MissingFinalStorePathRootId unless final_store_path_root_id.present? + id = if final_store_path_config[:override_path].present? + final_store_path_config[:override_path] + else + raise MissingFinalStorePathRootId unless final_store_path_config[:root_hash].present? + + generate_final_store_path(root_hash: final_store_path_config[:root_hash]) + end - id = generate_final_store_path(root_id: final_store_path_root_id) upload_path = with_bucket_prefix(id) prepare_pending_direct_upload(id) else diff --git a/app/uploaders/virtual_registries/cache/entry_uploader.rb b/app/uploaders/virtual_registries/cache/entry_uploader.rb index 92e936be3136c4ccd473c8564fc6e910303187c2..a2c5ca9d189976a1dadc942decc3d7178bfbeae2 100644 --- a/app/uploaders/virtual_registries/cache/entry_uploader.rb +++ b/app/uploaders/virtual_registries/cache/entry_uploader.rb @@ -28,6 +28,11 @@ def sync_model_object_store? true end + override :direct_upload_final_path_attribute_name + def direct_upload_final_path_attribute_name + :object_storage_key + end + private def set_content_type(file) diff --git a/db/post_migrate/20250128153614_add_object_storage_key_unique_to_v_regs_packages_maven_cache_entries.rb b/db/post_migrate/20250128153614_add_object_storage_key_unique_to_v_regs_packages_maven_cache_entries.rb new file mode 100644 index 0000000000000000000000000000000000000000..bf4047e4bb43027bf3705489a5123935c0cdcee2 --- /dev/null +++ b/db/post_migrate/20250128153614_add_object_storage_key_unique_to_v_regs_packages_maven_cache_entries.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class AddObjectStorageKeyUniqueToVRegsPackagesMavenCacheEntries < Gitlab::Database::Migration[2.2] + include Gitlab::Database::PartitioningMigrationHelpers + + milestone '17.9' + + disable_ddl_transaction! + + TABLE_NAME = :virtual_registries_packages_maven_cache_entries + COLUMNS = [:relative_path, :object_storage_key] + INDEX_NAME = :idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key + + def up + truncate_tables!(TABLE_NAME.to_s) + add_concurrent_partitioned_index TABLE_NAME, COLUMNS, unique: true, name: INDEX_NAME + end + + def down + remove_concurrent_partitioned_index_by_name TABLE_NAME, INDEX_NAME + end +end diff --git a/db/schema_migrations/20250128153614 b/db/schema_migrations/20250128153614 new file mode 100644 index 0000000000000000000000000000000000000000..a54f2bbe26fc2b1fc900b7ab75d12de5c55e6eeb --- /dev/null +++ b/db/schema_migrations/20250128153614 @@ -0,0 +1 @@ +d43f2fa892bb1a4b268197cd6c83b6f1437c80677fae1caadc61b52caf45f2e5 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 6517d9917006e5a8b00d75b9bf6e8fb9c3f94e63..591c15ae407fea6dd99cbd87211d63395fa6e09f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -28872,6 +28872,10 @@ CREATE INDEX index_issue_stage_events_project_duration ON ONLY analytics_cycle_a CREATE INDEX index_000925dbd7 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_11 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ON ONLY virtual_registries_packages_maven_cache_entries USING btree (relative_path, object_storage_key); + +CREATE UNIQUE INDEX index_0051c4d20c ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_15 USING btree (relative_path, object_storage_key); + CREATE INDEX index_merge_request_stage_events_project_duration ON ONLY analytics_cycle_analytics_merge_request_stage_events USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); CREATE INDEX index_006f943df6 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_16 USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); @@ -28880,6 +28884,8 @@ CREATE INDEX index_issue_stage_events_for_consistency_check ON ONLY analytics_cy CREATE INDEX index_009e6c1133 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_26 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id); +CREATE UNIQUE INDEX index_0264b93cfb ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_11 USING btree (relative_path, object_storage_key); + CREATE INDEX index_02749b504c ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_11 USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); CREATE INDEX index_merge_request_stage_events_group_duration ON ONLY analytics_cycle_analytics_merge_request_stage_events USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); @@ -28962,6 +28968,8 @@ CREATE INDEX index_1f6c3faabe ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_1f8af04ed1 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_19 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); +CREATE UNIQUE INDEX index_1fa613e160 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_08 USING btree (relative_path, object_storage_key); + CREATE INDEX index_201c5ddbe9 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_00 USING btree (stage_event_hash_id, group_id, start_event_timestamp, merge_request_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); CREATE INDEX index_20353089e0 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_20 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); @@ -28996,6 +29004,10 @@ CREATE INDEX index_23783dc748 ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_241e9a574c ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_08 USING btree (stage_event_hash_id, project_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); +CREATE UNIQUE INDEX index_2439930f8c ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_01 USING btree (relative_path, object_storage_key); + +CREATE UNIQUE INDEX index_2442d1fbd9 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_05 USING btree (relative_path, object_storage_key); + CREATE INDEX index_24ac321751 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_02 USING btree (stage_event_hash_id, project_id, start_event_timestamp, merge_request_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); CREATE INDEX index_25e2aaee9b ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_12 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); @@ -29004,6 +29016,8 @@ CREATE INDEX index_2653e7eeb8 ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_2745f5a388 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_31 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX index_27739b516b ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_09 USING btree (relative_path, object_storage_key); + CREATE INDEX index_27759556bc ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_01 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); CREATE INDEX index_27d7ad78d8 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_31 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); @@ -29080,6 +29094,8 @@ CREATE INDEX index_435802dd01 ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_436fa9ad5f ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_13 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX index_43aff761b5 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_00 USING btree (relative_path, object_storage_key); + CREATE INDEX index_453a659cb6 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_16 USING btree (stage_event_hash_id, project_id, start_event_timestamp, merge_request_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); CREATE INDEX index_46b989b294 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_02 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); @@ -29116,6 +29132,8 @@ CREATE INDEX index_4e6ce1c371 ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_4ea50d3a5b ON gitlab_partitions_static.issue_search_data_24 USING btree (namespace_id); +CREATE UNIQUE INDEX index_4efb1529af ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_10 USING btree (relative_path, object_storage_key); + CREATE INDEX index_4f2eb7a06b ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_08 USING btree (stage_event_hash_id, group_id, start_event_timestamp, merge_request_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); CREATE INDEX index_4f6fc34e57 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_31 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); @@ -29216,6 +29234,8 @@ CREATE INDEX index_6bf2b9282c ON gitlab_partitions_static.issue_search_data_22 U CREATE INDEX index_6cfb391b86 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_21 USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX index_6daa12da84 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_03 USING btree (relative_path, object_storage_key); + CREATE INDEX index_6e560c1a4d ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_23 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id); CREATE INDEX index_6e64aa1646 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_18 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id); @@ -29324,6 +29344,8 @@ CREATE INDEX index_8b1b6b03b4 ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_8b9f9a19a4 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_18 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX index_8c8835ac5e ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_12 USING btree (relative_path, object_storage_key); + CREATE INDEX index_8fb48e72ce ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_26 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); CREATE INDEX index_907e12b7ba ON gitlab_partitions_static.issue_search_data_54 USING btree (namespace_id); @@ -29372,6 +29394,8 @@ CREATE INDEX index_a46b7b7f26 ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_a4f5106804 ON gitlab_partitions_static.issue_search_data_11 USING btree (namespace_id); +CREATE UNIQUE INDEX index_a5d8ab0218 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_06 USING btree (relative_path, object_storage_key); + CREATE INDEX index_a6999c65c9 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_09 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); CREATE INDEX index_a6c68d16b2 ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_14 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); @@ -29438,6 +29462,8 @@ CREATE INDEX index_b7f21460bb ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_b83fe1306b ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_03 USING btree (stage_event_hash_id, group_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX index_bb41d5837a ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_02 USING btree (relative_path, object_storage_key); + CREATE INDEX index_bb6defaa27 ON gitlab_partitions_static.issue_search_data_34 USING btree (namespace_id); CREATE INDEX index_bc189e47ab ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_11 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); @@ -29490,6 +29516,8 @@ CREATE INDEX index_c6ea8a0e26 ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_c7ac8595d3 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_00 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id); +CREATE UNIQUE INDEX index_c7fa6f402d ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_04 USING btree (relative_path, object_storage_key); + CREATE INDEX index_c8bbf2b334 ON gitlab_partitions_static.issue_search_data_26 USING btree (namespace_id); CREATE INDEX index_c8c4219c0a ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_26 USING btree (stage_event_hash_id, group_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); @@ -29498,6 +29526,8 @@ CREATE INDEX index_c971e6c5ce ON gitlab_partitions_static.analytics_cycle_analyt CREATE INDEX index_c9b14a3d9f ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_08 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX index_cb0e4510aa ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_07 USING btree (relative_path, object_storage_key); + CREATE INDEX index_cb222425ed ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_29 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id); CREATE INDEX index_cbb61ea269 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_12 USING btree (stage_event_hash_id, project_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); @@ -29554,6 +29584,8 @@ CREATE INDEX index_db6477916f ON gitlab_partitions_static.issue_search_data_28 U CREATE INDEX index_dc571ba649 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_01 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX index_dc7ca9eb1d ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_13 USING btree (relative_path, object_storage_key); + CREATE INDEX index_de0334da63 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_12 USING btree (stage_event_hash_id, group_id, end_event_timestamp, issue_id); CREATE INDEX index_df62a8c50e ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_07 USING btree (stage_event_hash_id, project_id, start_event_timestamp, issue_id) WHERE ((end_event_timestamp IS NULL) AND (state_id = 1)); @@ -29624,6 +29656,8 @@ CREATE INDEX index_f415dc2abd ON gitlab_partitions_static.issue_search_data_18 U CREATE INDEX index_f47327ec1f ON gitlab_partitions_static.analytics_cycle_analytics_merge_request_stage_events_27 USING btree (stage_event_hash_id, project_id, end_event_timestamp, merge_request_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); +CREATE UNIQUE INDEX index_f586c952e6 ON gitlab_partitions_static.virtual_registries_packages_maven_cache_entries_14 USING btree (relative_path, object_storage_key); + CREATE INDEX index_f5f0e8eefd ON gitlab_partitions_static.issue_search_data_37 USING btree (namespace_id); CREATE INDEX index_f6b0d458a3 ON gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_15 USING btree (stage_event_hash_id, project_id, end_event_timestamp, issue_id, start_event_timestamp) WHERE (end_event_timestamp IS NOT NULL); @@ -35702,10 +35736,14 @@ ALTER INDEX analytics_cycle_analytics_merge_request_stage_events_pkey ATTACH PAR ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_000925dbd7; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_0051c4d20c; + ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_006f943df6; ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_009e6c1133; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_0264b93cfb; + ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_02749b504c; ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_0287f5ba09; @@ -35774,6 +35812,8 @@ ALTER INDEX index_issue_stage_events_project_in_progress_duration ATTACH PARTITI ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_1f8af04ed1; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_1fa613e160; + ALTER INDEX index_merge_request_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_201c5ddbe9; ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_20353089e0; @@ -35806,6 +35846,10 @@ ALTER INDEX index_merge_request_stage_events_group_in_progress_duration ATTACH P ALTER INDEX index_issue_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_241e9a574c; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_2439930f8c; + +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_2442d1fbd9; + ALTER INDEX index_merge_request_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_24ac321751; ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_25e2aaee9b; @@ -35814,6 +35858,8 @@ ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION g ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_2745f5a388; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_27739b516b; + ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_27759556bc; ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_27d7ad78d8; @@ -35890,6 +35936,8 @@ ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_436fa9ad5f; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_43aff761b5; + ALTER INDEX index_merge_request_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_453a659cb6; ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_46b989b294; @@ -35926,6 +35974,8 @@ ALTER INDEX index_merge_request_stage_events_project_in_progress_duration ATTACH ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_4ea50d3a5b; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_4efb1529af; + ALTER INDEX index_merge_request_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_4f2eb7a06b; ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_4f6fc34e57; @@ -36026,6 +36076,8 @@ ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_part ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_6cfb391b86; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_6daa12da84; + ALTER INDEX index_mr_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_6e560c1a4d; ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_6e64aa1646; @@ -36134,6 +36186,8 @@ ALTER INDEX index_mr_stage_events_for_consistency_check ATTACH PARTITION gitlab_ ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_8b9f9a19a4; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_8c8835ac5e; + ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_8fb48e72ce; ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_907e12b7ba; @@ -36182,6 +36236,8 @@ ALTER INDEX index_mr_stage_events_for_consistency_check ATTACH PARTITION gitlab_ ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_a4f5106804; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_a5d8ab0218; + ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_a6999c65c9; ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_a6c68d16b2; @@ -36248,6 +36304,8 @@ ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION ALTER INDEX index_merge_request_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_b83fe1306b; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_bb41d5837a; + ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_bb6defaa27; ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_bc189e47ab; @@ -36300,6 +36358,8 @@ ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_c7ac8595d3; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_c7fa6f402d; + ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_c8bbf2b334; ALTER INDEX index_issue_stage_events_group_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_c8c4219c0a; @@ -36308,6 +36368,8 @@ ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_part ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_c9b14a3d9f; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_cb0e4510aa; + ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_cb222425ed; ALTER INDEX index_issue_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_cbb61ea269; @@ -36364,6 +36426,8 @@ ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_part ALTER INDEX index_issue_stage_events_group_duration ATTACH PARTITION gitlab_partitions_static.index_dc571ba649; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_dc7ca9eb1d; + ALTER INDEX index_issue_stage_events_for_consistency_check ATTACH PARTITION gitlab_partitions_static.index_de0334da63; ALTER INDEX index_issue_stage_events_project_in_progress_duration ATTACH PARTITION gitlab_partitions_static.index_df62a8c50e; @@ -36434,6 +36498,8 @@ ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_part ALTER INDEX index_merge_request_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_f47327ec1f; +ALTER INDEX idx_vregs_pkgs_mvn_cache_entries_on_uniq_object_storage_key ATTACH PARTITION gitlab_partitions_static.index_f586c952e6; + ALTER INDEX index_issue_search_data_on_namespace_id ATTACH PARTITION gitlab_partitions_static.index_f5f0e8eefd; ALTER INDEX index_issue_stage_events_project_duration ATTACH PARTITION gitlab_partitions_static.index_f6b0d458a3; diff --git a/lib/api/concerns/virtual_registries/packages/endpoint.rb b/lib/api/concerns/virtual_registries/packages/endpoint.rb index e0474b94d9958b968c48a3fdb36d648d379900f2..adff635b930a4f76bc3c8a9dc0e8850bcad4b823 100644 --- a/lib/api/concerns/virtual_registries/packages/endpoint.rb +++ b/lib/api/concerns/virtual_registries/packages/endpoint.rb @@ -77,7 +77,9 @@ def authorized_upload_response has_length: true, maximum_size: MAX_FILE_SIZE, use_final_store_path: true, - final_store_path_root_id: registry.id + final_store_path_config: { + override_path: upstream.object_storage_key_for(registry_id: registry.id) + } ) end diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb index 479811e510cd53cd95a1e451413c7599dd793369..620753d9765bd06e1be8b9a92c711eef77ca564b 100644 --- a/lib/api/helpers/packages_helpers.rb +++ b/lib/api/helpers/packages_helpers.rb @@ -60,7 +60,7 @@ def authorize_workhorse!( params = { has_length: has_length, use_final_store_path: use_final_store_path } params[:maximum_size] = maximum_size unless has_length - params[:final_store_path_root_id] = subject.id if use_final_store_path + params[:final_store_path_config] = { root_hash: subject.id } if use_final_store_path ::Packages::PackageFileUploader.workhorse_authorize(**params) end diff --git a/spec/factories/virtual_registries/packages/maven/cache/entries.rb b/spec/factories/virtual_registries/packages/maven/cache/entries.rb index b39c7b530f207abf8ae8cc899e3a107c3a3e5b1a..feffe61f7cfe5e8259632c8747de2c807fc91532 100644 --- a/spec/factories/virtual_registries/packages/maven/cache/entries.rb +++ b/spec/factories/virtual_registries/packages/maven/cache/entries.rb @@ -9,7 +9,6 @@ size { 1.kilobyte } upstream_etag { OpenSSL::Digest.hexdigest('SHA256', 'test') } content_type { 'text/plain' } - file_final_path { '5f/9c/5f9c/@final/c7/4c/240c' } file_md5 { 'd8e8fca2dc0f896fd7cb4cb0031ba249' } file_sha1 { '4e1243bd22c66e76c2ba9eddc1f91394e57f9f83' } status { :default } diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb index cd516e2abe94dbd2338634900dcf131564e35d94..b1f241d0e3ff8da4afd58508e5556efa60196e9e 100644 --- a/spec/lib/api/helpers/packages_helpers_spec.rb +++ b/spec/lib/api/helpers/packages_helpers_spec.rb @@ -161,7 +161,7 @@ let(:params) { super().merge(use_final_store_path: true) } it_behaves_like 'workhorse authorize' do - let(:workhorse_authorize_params) { { has_length: true, use_final_store_path: true, final_store_path_root_id: project.id } } + let(:workhorse_authorize_params) { { has_length: true, use_final_store_path: true, final_store_path_config: { root_hash: project.id } } } end end end diff --git a/spec/models/virtual_registries/packages/maven/cache/entry_spec.rb b/spec/models/virtual_registries/packages/maven/cache/entry_spec.rb index c37951a7cc4dc018d732fe78729f9056676a2450..a2aa801e95ae1006bc20ae54628b9673ff049c0c 100644 --- a/spec/models/virtual_registries/packages/maven/cache/entry_spec.rb +++ b/spec/models/virtual_registries/packages/maven/cache/entry_spec.rb @@ -22,7 +22,7 @@ it { is_expected.to validate_length_of(attr).is_at_most(255) } end - %i[relative_path object_storage_key file_final_path].each do |attr| + %i[relative_path object_storage_key].each do |attr| it { is_expected.to validate_length_of(attr).is_at_most(1024) } end @@ -35,6 +35,7 @@ end it { is_expected.to validate_uniqueness_of(:relative_path).scoped_to(:upstream_id, :status) } + it { is_expected.to validate_uniqueness_of(:object_storage_key).scoped_to(:relative_path) } context 'with a similar cached response in a different status' do let!(:cache_entry_in_error) do @@ -103,6 +104,7 @@ it 'can not be null' do cache_entry.object_storage_key = nil cache_entry.relative_path = nil + cache_entry.upstream = nil expect(cache_entry).to be_invalid expect(cache_entry.errors.full_messages).to include("Object storage key can't be blank") diff --git a/spec/models/virtual_registries/packages/maven/upstream_spec.rb b/spec/models/virtual_registries/packages/maven/upstream_spec.rb index 13454398a53beba07286bf1577d3156a81e0241a..60ffb25dab4b821057061ea0176c8850c9c904cc 100644 --- a/spec/models/virtual_registries/packages/maven/upstream_spec.rb +++ b/spec/models/virtual_registries/packages/maven/upstream_spec.rb @@ -247,4 +247,23 @@ it { is_expected.to contain_exactly(default_cache_entry) } end + + describe '#object_storage_key_for' do + let_it_be(:upstream) { build_stubbed(:virtual_registries_packages_maven_upstream) } + + let(:registry_id) { '555' } + + subject { upstream.object_storage_key_for(registry_id: registry_id) } + + it 'contains the expected terms' do + is_expected.to include("virtual_registries/packages/maven/#{registry_id}/upstream/#{upstream.id}/cache/entry") + end + + it 'does not return the same value when called twice' do + first_value = upstream.object_storage_key_for(registry_id: registry_id) + second_value = upstream.object_storage_key_for(registry_id: registry_id) + + expect(first_value).not_to eq(second_value) + end + end end diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb index 23a45bc0b7f61e3be387d41ef43b34e4cea89ac0..948c0692c258ba4af3d42ad6ef5fb90142425d36 100644 --- a/spec/requests/api/generic_packages_spec.rb +++ b/spec/requests/api/generic_packages_spec.rb @@ -195,7 +195,7 @@ def deploy_token_header(value) it 'sends use_final_store_path with true' do expect(::Packages::PackageFileUploader).to receive(:workhorse_authorize).with( - hash_including(use_final_store_path: true, final_store_path_root_id: project.id) + hash_including(use_final_store_path: true, final_store_path_config: { root_hash: project.id }) ).and_call_original authorize_upload_file(workhorse_headers.merge(personal_access_token_header)) diff --git a/spec/requests/api/terraform/modules/v1/project_packages_spec.rb b/spec/requests/api/terraform/modules/v1/project_packages_spec.rb index eea4bd9d1580e8d89383f9bf0938e7a70e311c11..e7c2ac1bbd6b26effb73deb3eed4d2f9ab6e9091 100644 --- a/spec/requests/api/terraform/modules/v1/project_packages_spec.rb +++ b/spec/requests/api/terraform/modules/v1/project_packages_spec.rb @@ -89,7 +89,7 @@ it 'sends use_final_store_path with true' do expect(::Packages::PackageFileUploader).to receive(:workhorse_authorize).with( - hash_including(use_final_store_path: true, final_store_path_root_id: project.id) + hash_including(use_final_store_path: true, final_store_path_config: { root_hash: project.id }) ).and_call_original api_request diff --git a/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb b/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb index 2defc354f72c2e5b9460b4c3cc8cbcd78608ae9e..bd6c7882135839455de60563edb7c862453db5c0 100644 --- a/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb +++ b/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb @@ -39,7 +39,7 @@ expect(::VirtualRegistries::Cache::EntryUploader).to receive(:workhorse_authorize).with( a_hash_including( use_final_store_path: true, - final_store_path_root_id: registry.id + final_store_path_config: { override_path: be_instance_of(String) } ) ).and_call_original diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb index 3bfd6ed6bed91366971fec5bd67271f0b1fe0e07..2601eb714dedd75dcffaf455a176918d8b515ad1 100644 --- a/spec/services/ci/job_artifacts/create_service_spec.rb +++ b/spec/services/ci/job_artifacts/create_service_spec.rb @@ -97,7 +97,7 @@ allow(JobArtifactUploader) .to receive(:generate_final_store_path) - .with(root_id: project.id) + .with(root_hash: project.id) .and_return(final_store_path) end diff --git a/spec/support/helpers/orphan_final_artifacts_cleanup_helpers.rb b/spec/support/helpers/orphan_final_artifacts_cleanup_helpers.rb index ce67cf0e6b6d9cc72dc937b5f77ad815f49cf31d..7a0d9f7f172011b1c2c2458c57c2062ef3fe2fbc 100644 --- a/spec/support/helpers/orphan_final_artifacts_cleanup_helpers.rb +++ b/spec/support/helpers/orphan_final_artifacts_cleanup_helpers.rb @@ -3,7 +3,7 @@ module OrphanFinalArtifactsCleanupHelpers def create_fog_file(final: true) path = if final - JobArtifactUploader.generate_final_store_path(root_id: 123) + JobArtifactUploader.generate_final_store_path(root_hash: 123) else JobArtifactUploader.generate_remote_id end diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 20fa810b86b8a8c7ac436bb6ced7204e911a7a39..4da6aa48fa65e154c9fc39ce8d80ef2664ae2837 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -552,14 +552,15 @@ def when_file_is_in_use let(:has_length) { true } let(:maximum_size) { nil } let(:use_final_store_path) { false } - let(:final_store_path_root_id) { nil } + let(:final_store_path_root_hash) { nil } + let(:final_store_path_config) { { root_hash: final_store_path_root_hash } } subject do uploader_class.workhorse_authorize( has_length: has_length, maximum_size: maximum_size, use_final_store_path: use_final_store_path, - final_store_path_root_id: final_store_path_root_id + final_store_path_config: final_store_path_config ) end @@ -644,24 +645,24 @@ def when_file_is_in_use shared_examples 'handling object storage final upload path' do |multipart| context 'when use_final_store_path is true' do let(:use_final_store_path) { true } - let(:final_store_path_root_id) { 12345 } + let(:final_store_path_root_hash) { 12345 } let(:final_store_path) { File.join('@final', 'myprefix', 'abc', '123', 'somefilename') } let(:escaped_path) { escape_path(final_store_path) } - context 'and final_store_path_root_id was not given' do - let(:final_store_path_root_id) { nil } + context 'and final_store_path_root_hash was not given' do + let(:final_store_path_root_hash) { nil } it 'raises an error' do expect { subject }.to raise_error(ObjectStorage::MissingFinalStorePathRootId) end end - context 'and final_store_path_root_id was given' do + context 'and final_store_path_root_hash was given' do before do stub_object_storage_multipart_init_with_final_store_path("#{storage_url}#{final_store_path}") if multipart allow(uploader_class).to receive(:generate_final_store_path) - .with(root_id: final_store_path_root_id) + .with(root_hash: final_store_path_root_hash) .and_return(final_store_path) end @@ -720,6 +721,35 @@ def when_file_is_in_use end end end + + context 'and override_path was given' do + let(:override_path) { 'test_override_path' } + let(:final_store_path_config) { { override_path: override_path } } + + before do + stub_object_storage_multipart_init_with_final_store_path("#{storage_url}#{override_path}") if multipart + end + + it 'uses the override instead of generating a path' do + expect(uploader_class).not_to receive(:generate_final_store_path) + + expect(subject[:RemoteObject][:ID]).to eq(override_path) + expect(subject[:RemoteObject][:GetURL]).to include(override_path) + expect(subject[:RemoteObject][:StoreURL]).to include(override_path) + + if multipart + expect(subject[:RemoteObject][:MultipartUpload][:PartURLs]).to all(include(override_path)) + expect(subject[:RemoteObject][:MultipartUpload][:CompleteURL]).to include(override_path) + expect(subject[:RemoteObject][:MultipartUpload][:AbortURL]).to include(override_path) + end + + expect(subject[:RemoteObject][:SkipDelete]).to eq(true) + + expect( + ObjectStorage::PendingDirectUpload.exists?(uploader_class.storage_location_identifier, override_path) + ).to eq(true) + end + end end def escape_path(path) @@ -1334,10 +1364,10 @@ def escape_path(path) end describe '.generate_final_store_path' do - let(:root_id) { 12345 } - let(:expected_root_hashed_path) { Gitlab::HashedPath.new(root_hash: root_id) } + let(:root_hash) { 12345 } + let(:expected_root_hashed_path) { Gitlab::HashedPath.new(root_hash: root_hash) } - subject(:final_path) { uploader_class.generate_final_store_path(root_id: root_id) } + subject(:final_path) { uploader_class.generate_final_store_path(root_hash: root_hash) } before do allow(Digest::SHA2).to receive(:hexdigest).and_return('somehash1234')