Reduce denormalization of dast_site_tokens table
What does this MR do and why?
we discovered we could end up with orphaned dast_site_tokens
, so we added an application-level callback to ensure that no new orphaned records could be created. this merge request simplifies this by promoting the association between a dast_site_token
and a dast_site
to the database-level via a fk
rather than a common, unique,url
.
Related Issue(s)
Database
there are currently 212
dast_site_token
and 895
dast_sites
on gitlab.com
.
Migrations
$ rails db:migrate:up VERSION=20211011040203 && rails db:migrate:up VERSION=20211011044147 && rails db:migrate:up VERSION=20211012000408 && rails db:migrate:up VERSION=20211012001017 && rails db:migrate:up VERSION=20211012004556
== 20211011040203 AddDastSiteIdToDastSiteTokens: migrating ====================
-- add_column(:dast_site_tokens, :dast_site_id, :bigint, {:null=>false, :default=>0})
-> 0.0039s
== 20211011040203 AddDastSiteIdToDastSiteTokens: migrated (0.0039s) ===========
== 20211011044147 CleanUpOrphanedDastSiteTokens: migrating ====================
== 20211011044147 CleanUpOrphanedDastSiteTokens: migrated (0.0359s) ===========
== 20211012000408 RemoveDefaultFromDastSiteTokensDastSiteId: migrating ========
-- change_column_default(:dast_site_tokens, :dast_site_id, nil)
-> 0.0039s
== 20211012000408 RemoveDefaultFromDastSiteTokensDastSiteId: migrated (0.0039s)
== 20211012001017 AddIndexAndFkToDastSiteTokensDastSiteId: migrating ==========
-- transaction_open?()
-> 0.0000s
-- index_exists?(:dast_site_tokens, :dast_site_id, {:name=>:index_dast_site_tokens_on_dast_site_id, :unique=>true, :algorithm=>:concurrently})
-> 0.0035s
-- execute("SET statement_timeout TO 0")
-> 0.0006s
-- add_index(:dast_site_tokens, :dast_site_id, {:name=>:index_dast_site_tokens_on_dast_site_id, :unique=>true, :algorithm=>:concurrently})
-> 0.0050s
-- execute("RESET statement_timeout")
-> 0.0006s
-- add_foreign_key(:dast_site_tokens, :dast_sites, {:on_delete=>:cascade, :validate=>false})
-> 0.0047s
== 20211012001017 AddIndexAndFkToDastSiteTokensDastSiteId: migrated (0.0166s) =
== 20211012004556 DropNotNullConstraintFromUrlOnDastSiteTokens: migrating =====
-- change_column_null(:dast_site_tokens, :url, true)
-> 0.0022s
== 20211012004556 DropNotNullConstraintFromUrlOnDastSiteTokens: migrated (0.0022s)
rails db:migrate:down VERSION=20211012004556 && rails db:migrate:down VERSION=20211012001017 && rails db:migrate:down VERSION=20211012000408 && rails db:migrate:down VERSION=20211011044147 && rails db:migrate:down VERSION=20211011040203
== 20211012004556 DropNotNullConstraintFromUrlOnDastSiteTokens: reverting =====
-- change_column_null(:dast_site_tokens, :url, false)
-> 0.0024s
== 20211012004556 DropNotNullConstraintFromUrlOnDastSiteTokens: reverted (0.0041s)
== 20211012001017 AddIndexAndFkToDastSiteTokensDastSiteId: reverting ==========
-- foreign_keys(:dast_site_tokens)
-> 0.0039s
-- remove_foreign_key(:dast_site_tokens, {:column=>:dast_site_id})
-> 0.0043s
-- transaction_open?()
-> 0.0000s
-- index_exists?(:dast_site_tokens, :dast_site_id, {:name=>:index_dast_site_tokens_on_dast_site_id, :algorithm=>:concurrently})
-> 0.0039s
-- execute("SET statement_timeout TO 0")
-> 0.0007s
-- remove_index(:dast_site_tokens, {:name=>:index_dast_site_tokens_on_dast_site_id, :algorithm=>:concurrently, :column=>:dast_site_id})
-> 0.0050s
-- execute("RESET statement_timeout")
-> 0.0006s
== 20211012001017 AddIndexAndFkToDastSiteTokensDastSiteId: reverted (0.0206s) =
== 20211012000408 RemoveDefaultFromDastSiteTokensDastSiteId: reverting ========
-- change_column_default(:dast_site_tokens, :dast_site_id, 0)
-> 0.0043s
== 20211012000408 RemoveDefaultFromDastSiteTokensDastSiteId: reverted (0.0044s)
== 20211011044147 CleanUpOrphanedDastSiteTokens: reverting ====================
== 20211011044147 CleanUpOrphanedDastSiteTokens: reverted (0.0000s) ===========
== 20211011040203 AddDastSiteIdToDastSiteTokens: reverting ====================
-- remove_column(:dast_site_tokens, :dast_site_id)
-> 0.0024s
== 20211011040203 AddDastSiteIdToDastSiteTokens: reverted (0.0025s) ===========
Screenshots or screen recordings
Console
[15] pry(main)> validation = DastSiteValidation.find(222)
DastSiteValidation Load (0.4ms) SELECT "dast_site_validations".* FROM "dast_site_validations" WHERE "dast_site_validations"."id" = 222 LIMIT 1 /*application:console,db_config_name:main,line:/lib/gitlab/database/load_balancing/connection_proxy.rb:103:in `block in read_using_load_balancer'*/
=> #<DastSiteValidation:0x00007fe1a7edc368
id: 222,
dast_site_token_id: 111,
created_at: Thu, 14 Oct 2021 02:36:12.781913000 UTC +00:00,
updated_at: Thu, 14 Oct 2021 02:37:55.200085000 UTC +00:00,
validation_started_at: Thu, 14 Oct 2021 02:36:33.566351000 UTC +00:00,
validation_passed_at: nil,
validation_failed_at: Thu, 14 Oct 2021 02:37:55.198366000 UTC +00:00,
validation_last_retried_at: Thu, 14 Oct 2021 02:37:54.666518000 UTC +00:00,
validation_strategy: "text_file",
url_base: "https://d53425329c4917ed6776b75057001272.com:443",
url_path: "GitLab-DAST-Site-Validation-2f6cf4db-f8ab-41ea-85f8-87b5bcbd5b18.txt",
state: "failed">
[16] pry(main)> dast_site_token = validation.dast_site_token
DastSiteToken Load (0.4ms) SELECT "dast_site_tokens"."id", "dast_site_tokens"."project_id", "dast_site_tokens"."created_at", "dast_site_tokens"."updated_at", "dast_site_tokens"."expired_at", "dast_site_tokens"."token", "dast_site_tokens"."dast_site_id" FROM "dast_site_tokens" WHERE "dast_site_tokens"."id" = 111 LIMIT 1 /*application:console,db_config_name:main,line:/lib/gitlab/database/load_balancing/connection_proxy.rb:103:in `block in read_using_load_balancer'*/
=> #<DastSiteToken:0x00007fe1b5199058 id: 111, project_id: 9, created_at: Mon, 11 Oct 2021 22:04:00.565819000 UTC +00:00, updated_at: Mon, 11 Oct 2021 22:04:00.565819000 UTC +00:00, expired_at: nil, token: "[FILTERED]", dast_site_id: 201>
[17] pry(main)> dast_site_token.dast_site
DastSite Load (0.6ms) SELECT "dast_sites".* FROM "dast_sites" WHERE "dast_sites"."id" = 201 LIMIT 1 /*application:console,db_config_name:main,line:/lib/gitlab/database/load_balancing/connection_proxy.rb:103:in `block in read_using_load_balancer'*/
=> #<DastSite:0x00007fe1a7e91c50 id: 201, project_id: 9, created_at: Mon, 11 Oct 2021 22:04:00.528744000 UTC +00:00, updated_at: Thu, 14 Oct 2021 02:36:12.873871000 UTC +00:00, url: "https://d53425329c4917ed6776b75057001272.com", dast_site_validation_id: 222>
Analysis
Data Migration
How to set up and validate locally
Numbered steps to set up and validate the change are strongly suggested.
- start your
gdk
gdk start
- seed the database
bundle exec rake db:seed_fu FILTER=dast_profiles
-
navigate to a project in the browser e.g.
/root/air-gap
-
navigate to list of site profiles e.g.
/root/air-gap/-/security/configuration/dast_scans#site-profiles
-
start validation
-
navigate to pipelines view
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.