diff options
author | Timothy Andrew <mail@timothyandrew.net> | 2016-09-15 14:29:36 +0530 |
---|---|---|
committer | Timothy Andrew <mail@timothyandrew.net> | 2016-09-15 14:53:02 +0530 |
commit | ba25e2f1ac61b47940f939a2d9f1d0ad417e1de2 (patch) | |
tree | e30ce100687fde731bb738c95a8ccd12033f9c42 /db | |
parent | 798b17a35311d60fe18440bfc53dba3aadd7b099 (diff) | |
download | gitlab-ce-ba25e2f1ac61b47940f939a2d9f1d0ad417e1de2.tar.gz |
Improve performance of the cycle analytics page.
1. These changes bring down page load time for 100 issues from more than
a minute to about 1.5 seconds.
2. This entire commit is composed of these types of performance
enhancements:
- Cache relevant data in `IssueMetrics` wherever possible.
- Cache relevant data in `MergeRequestMetrics` wherever possible.
- Preload metrics
3. Given these improvements, we now only need to make 4 SQL calls:
- Load all issues
- Load all merge requests
- Load all metrics for the issues
- Load all metrics for the merge requests
4. A list of all the data points that are now being pre-calculated:
a. The first time an issue is mentioned in a commit
- In `GitPushService`, find all issues mentioned by the given commit
using `ReferenceExtractor`. Set the `first_mentioned_in_commit_at`
flag for each of them.
- There seems to be a (pre-existing) bug here - files (and
therefore commits) created using the Web CI don't have
cross-references created, and issues are not closed even when
the commit title is "Fixes #xx".
b. The first time a merge request is deployed to production
When a `Deployment` is created, find all merge requests that
were merged in before the deployment, and set the
`first_deployed_to_production_at` flag for each of them.
c. The start / end time for a merge request pipeline
Hook into the `Pipeline` state machine. When the `status` moves to
`running`, find the merge requests whose tip commit matches the
pipeline, and record the `latest_build_started_at` time for each
of them. When the `status` moves to `success`, record the
`latest_build_finished_at` time.
d. The merge requests that close an issue
- This was a big cause of the performance problems we were having
with Cycle Analytics. We need to use `ReferenceExtractor` to make
this calculation, which is slow when we have to run it on a large
number of merge requests.
- When a merge request is created, updated, or refreshed, find the
issues it closes, and create an instance of
`MergeRequestsClosingIssues`, which acts as a join model between
merge requests and issues.
- If a `MergeRequestsClosingIssues` instance links a merge request
and an issue, that issue closes that merge request.
5. The `Queries` module was changed into a class, so we can cache the
results of `issues` and `merge_requests_closing_issues` across
various cycle analytics stages.
6. The code added in this commit is untested. Tests will be added in the
next commit.
Diffstat (limited to 'db')
6 files changed, 155 insertions, 5 deletions
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb index 6d5a28c2e81..46cf0fbd805 100644 --- a/db/fixtures/development/17_cycle_analytics.rb +++ b/db/fixtures/development/17_cycle_analytics.rb @@ -24,30 +24,37 @@ class Gitlab::Seeder::CycleAnalytics def seed! Sidekiq::Testing.inline! do issues = create_issues(@project) + print '.' # Stage 1 Timecop.travel 5.days.from_now add_milestones_and_list_labels(issues) + print '.' # Stage 2 Timecop.travel 5.days.from_now branches = mention_in_commits(issues) + print '.' # Stage 3 Timecop.travel 5.days.from_now merge_requests = create_merge_requests_closing_issues(issues, branches) + print '.' # Stage 4 Timecop.travel 5.days.from_now run_builds(merge_requests) + print '.' # Stage 5 Timecop.travel 5.days.from_now merge_merge_requests(merge_requests) + print '.' # Stage 6 / 7 Timecop.travel 5.days.from_now deploy_to_production(merge_requests) + print '.' end print '.' @@ -101,8 +108,14 @@ class Gitlab::Seeder::CycleAnalytics } commit_sha = Gitlab::Git::Blob.commit(issue.project.repository, options) - commit = issue.project.repository.commit(commit_sha) - commit.create_cross_references! + issue.project.repository.commit(commit_sha) + + + GitPushService.new(issue.project, + @user, + oldrev: issue.project.repository.commit("master").sha, + newrev: commit_sha, + ref: 'refs/heads/master').execute branch_name end @@ -162,10 +175,12 @@ end Gitlab::Seeder.quiet do if ENV['SEED_CYCLE_ANALYTICS'] - seeder = Gitlab::Seeder::CycleAnalytics.new(Project.find(1)) + Project.all.each do |project| + seeder = Gitlab::Seeder::CycleAnalytics.new(project) seeder.seed! + end elsif ENV['CYCLE_ANALYTICS_PERF_TEST'] - seeder = Gitlab::Seeder::CycleAnalytics.new(Project.first, perf: true) + seeder = Gitlab::Seeder::CycleAnalytics.new(Project.order(:id).first, perf: true) seeder.seed! else puts "Not running the cycle analytics seed file. Use the `SEED_CYCLE_ANALYTICS` environment variable to enable it." diff --git a/db/migrate/20160915042921_create_merge_requests_closing_issues.rb b/db/migrate/20160915042921_create_merge_requests_closing_issues.rb new file mode 100644 index 00000000000..bc0814eeaeb --- /dev/null +++ b/db/migrate/20160915042921_create_merge_requests_closing_issues.rb @@ -0,0 +1,34 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class CreateMergeRequestsClosingIssues < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = true + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + DOWNTIME_REASON = 'Adding foreign keys' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + create_table :merge_requests_closing_issues do |t| + t.references :merge_request, foreign_key: true, null: false + t.references :issue, foreign_key: true, null: false + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20160915061248_add_production_deploy_time_to_merge_request_metrics.rb b/db/migrate/20160915061248_add_production_deploy_time_to_merge_request_metrics.rb new file mode 100644 index 00000000000..34dacfc994b --- /dev/null +++ b/db/migrate/20160915061248_add_production_deploy_time_to_merge_request_metrics.rb @@ -0,0 +1,29 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddProductionDeployTimeToMergeRequestMetrics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + add_column :merge_request_metrics, :first_deployed_to_production_at, :datetime + end +end diff --git a/db/migrate/20160915072217_add_first_mentioned_in_commit_time_to_issue_metrics.rb b/db/migrate/20160915072217_add_first_mentioned_in_commit_time_to_issue_metrics.rb new file mode 100644 index 00000000000..6c1a388ff3f --- /dev/null +++ b/db/migrate/20160915072217_add_first_mentioned_in_commit_time_to_issue_metrics.rb @@ -0,0 +1,29 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddFirstMentionedInCommitTimeToIssueMetrics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + add_column :issue_metrics, :first_mentioned_in_commit_at, :datetime + end +end diff --git a/db/migrate/20160915081353_add_latest_build_time_to_merge_request_metrics.rb b/db/migrate/20160915081353_add_latest_build_time_to_merge_request_metrics.rb new file mode 100644 index 00000000000..b413e6103c9 --- /dev/null +++ b/db/migrate/20160915081353_add_latest_build_time_to_merge_request_metrics.rb @@ -0,0 +1,30 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddLatestBuildTimeToMergeRequestMetrics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + add_column :merge_request_metrics, :latest_build_started_at, :datetime + add_column :merge_request_metrics, :latest_build_finished_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 2c380de3d50..26d49e7b261 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160901141443) do +ActiveRecord::Schema.define(version: 20160915081353) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -442,6 +442,7 @@ ActiveRecord::Schema.define(version: 20160901141443) do t.datetime "first_added_to_board_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.datetime "first_mentioned_in_commit_at" end add_index "issue_metrics", ["issue_id"], name: "index_issue_metrics", using: :btree @@ -596,6 +597,9 @@ ActiveRecord::Schema.define(version: 20160901141443) do t.datetime "first_closed_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.datetime "first_deployed_to_production_at" + t.datetime "latest_build_started_at" + t.datetime "latest_build_finished_at" end add_index "merge_request_metrics", ["merge_request_id"], name: "index_merge_request_metrics", using: :btree @@ -641,6 +645,13 @@ ActiveRecord::Schema.define(version: 20160901141443) do add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} + create_table "merge_requests_closing_issues", force: :cascade do |t| + t.integer "merge_request_id", null: false + t.integer "issue_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "milestones", force: :cascade do |t| t.string "title", null: false t.integer "project_id", null: false @@ -1170,6 +1181,8 @@ ActiveRecord::Schema.define(version: 20160901141443) do add_foreign_key "lists", "boards" add_foreign_key "lists", "labels" add_foreign_key "merge_request_metrics", "merge_requests" + add_foreign_key "merge_requests_closing_issues", "issues" + add_foreign_key "merge_requests_closing_issues", "merge_requests" add_foreign_key "personal_access_tokens", "users" add_foreign_key "protected_branch_merge_access_levels", "protected_branches" add_foreign_key "protected_branch_push_access_levels", "protected_branches" |