提交 43b74afd 编写于 作者: A Andreas Brandl

Rename to UserInteractedProjects.

This is to avoid a mix-up with the existing concept of 'user
contributions'. See `User#contributed_projects` or
`Event#contributions`.
上级 986f470d
...@@ -65,7 +65,7 @@ class Event < ActiveRecord::Base ...@@ -65,7 +65,7 @@ class Event < ActiveRecord::Base
# Callbacks # Callbacks
after_create :reset_project_activity after_create :reset_project_activity
after_create :set_last_repository_updated_at, if: :push? after_create :set_last_repository_updated_at, if: :push?
after_create :track_user_contributed_projects after_create :track_user_interacted_projects
# Scopes # Scopes
scope :recent, -> { reorder(id: :desc) } scope :recent, -> { reorder(id: :desc) }
...@@ -391,10 +391,10 @@ class Event < ActiveRecord::Base ...@@ -391,10 +391,10 @@ class Event < ActiveRecord::Base
.update_all(last_repository_updated_at: created_at) .update_all(last_repository_updated_at: created_at)
end end
def track_user_contributed_projects def track_user_interacted_projects
# Note the call to .available? is due to earlier migrations # Note the call to .available? is due to earlier migrations
# that would otherwise conflict with the call to .track # that would otherwise conflict with the call to .track
# (because the table does not exist yet). # (because the table does not exist yet).
UserContributedProjects.track(self) if UserContributedProjects.available? UserInteractedProjects.track(self) if UserInteractedProjects.available?
end end
end end
class UserContributedProjects < ActiveRecord::Base class UserInteractedProjects < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :project belongs_to :project
...@@ -52,7 +52,7 @@ class UserContributedProjects < ActiveRecord::Base ...@@ -52,7 +52,7 @@ class UserContributedProjects < ActiveRecord::Base
private private
def cached_exists?(project_id:, user_id:, &block) def cached_exists?(project_id:, user_id:, &block)
cache_key = "user_contributed_projects:#{project_id}:#{user_id}" cache_key = "user_interacted_projects:#{project_id}:#{user_id}"
Rails.cache.fetch(cache_key, expires_in: CACHE_EXPIRY_TIME, &block) Rails.cache.fetch(cache_key, expires_in: CACHE_EXPIRY_TIME, &block)
end end
end end
......
--- ---
title: Keep track of projects a user contributed to. title: Keep track of projects a user interacted with.
merge_request: 17327 merge_request: 17327
author: author:
type: other type: other
class CreateUserContributedProjectsTable < ActiveRecord::Migration class CreateUserInteractedProjectsTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
DOWNTIME = false DOWNTIME = false
...@@ -6,16 +6,16 @@ class CreateUserContributedProjectsTable < ActiveRecord::Migration ...@@ -6,16 +6,16 @@ class CreateUserContributedProjectsTable < ActiveRecord::Migration
disable_ddl_transaction! disable_ddl_transaction!
def up def up
create_table :user_contributed_projects, id: false do |t| create_table :user_interacted_projects, id: false do |t|
t.references :user, null: false t.references :user, null: false
t.references :project, null: false t.references :project, null: false
end end
add_concurrent_foreign_key :user_contributed_projects, :users, column: :user_id, on_delete: :cascade add_concurrent_foreign_key :user_interacted_projects, :users, column: :user_id, on_delete: :cascade
add_concurrent_foreign_key :user_contributed_projects, :projects, column: :project_id, on_delete: :cascade add_concurrent_foreign_key :user_interacted_projects, :projects, column: :project_id, on_delete: :cascade
end end
def down def down
drop_table :user_contributed_projects drop_table :user_interacted_projects
end end
end end
class BuildUserContributedProjectsTable < ActiveRecord::Migration class BuildUserInteractedProjectsTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime. # Set this constant to true if this migration requires downtime.
...@@ -13,13 +13,13 @@ class BuildUserContributedProjectsTable < ActiveRecord::Migration ...@@ -13,13 +13,13 @@ class BuildUserContributedProjectsTable < ActiveRecord::Migration
MysqlStrategy.new MysqlStrategy.new
end.up end.up
add_concurrent_index :user_contributed_projects, [:project_id, :user_id], unique: true add_concurrent_index :user_interacted_projects, [:project_id, :user_id], unique: true
end end
def down def down
execute "TRUNCATE user_contributed_projects" execute "TRUNCATE user_interacted_projects"
remove_concurrent_index_by_name :user_contributed_projects, 'index_user_contributed_projects_on_project_id_and_user_id' remove_concurrent_index_by_name :user_interacted_projects, 'index_user_interacted_projects_on_project_id_and_user_id'
end end
private private
...@@ -31,22 +31,22 @@ class BuildUserContributedProjectsTable < ActiveRecord::Migration ...@@ -31,22 +31,22 @@ class BuildUserContributedProjectsTable < ActiveRecord::Migration
SLEEP_TIME = 5 SLEEP_TIME = 5
def up def up
with_index(:events, [:author_id, :project_id], name: 'events_user_contributions_temp', where: 'project_id IS NOT NULL') do with_index(:events, [:author_id, :project_id], name: 'events_user_interactions_temp', where: 'project_id IS NOT NULL') do
iteration = 0 iteration = 0
records = 0 records = 0
begin begin
Rails.logger.info "Building user_contributed_projects table, batch ##{iteration}" Rails.logger.info "Building user_interacted_projects table, batch ##{iteration}"
result = execute <<~SQL result = execute <<~SQL
INSERT INTO user_contributed_projects (user_id, project_id) INSERT INTO user_interacted_projects (user_id, project_id)
SELECT e.user_id, e.project_id SELECT e.user_id, e.project_id
FROM (SELECT DISTINCT author_id AS user_id, project_id FROM events WHERE project_id IS NOT NULL) AS e FROM (SELECT DISTINCT author_id AS user_id, project_id FROM events WHERE project_id IS NOT NULL) AS e
LEFT JOIN user_contributed_projects ucp USING (user_id, project_id) LEFT JOIN user_interacted_projects ucp USING (user_id, project_id)
WHERE ucp.user_id IS NULL WHERE ucp.user_id IS NULL
LIMIT #{BATCH_SIZE} LIMIT #{BATCH_SIZE}
SQL SQL
iteration += 1 iteration += 1
records += result.cmd_tuples records += result.cmd_tuples
Rails.logger.info "Building user_contributed_projects table, batch ##{iteration} complete, created #{records} overall" Rails.logger.info "Building user_interacted_projects table, batch ##{iteration} complete, created #{records} overall"
Kernel.sleep(SLEEP_TIME) if result.cmd_tuples > 0 Kernel.sleep(SLEEP_TIME) if result.cmd_tuples > 0
rescue ActiveRecord::InvalidForeignKey => e rescue ActiveRecord::InvalidForeignKey => e
Rails.logger.info "Retry on InvalidForeignKey: #{e}" Rails.logger.info "Retry on InvalidForeignKey: #{e}"
...@@ -54,7 +54,7 @@ class BuildUserContributedProjectsTable < ActiveRecord::Migration ...@@ -54,7 +54,7 @@ class BuildUserContributedProjectsTable < ActiveRecord::Migration
end while result.cmd_tuples > 0 end while result.cmd_tuples > 0
end end
execute "ANALYZE user_contributed_projects" execute "ANALYZE user_interacted_projects"
end end
...@@ -73,10 +73,10 @@ class BuildUserContributedProjectsTable < ActiveRecord::Migration ...@@ -73,10 +73,10 @@ class BuildUserContributedProjectsTable < ActiveRecord::Migration
def up def up
execute <<~SQL execute <<~SQL
INSERT INTO user_contributed_projects (user_id, project_id) INSERT INTO user_interacted_projects (user_id, project_id)
SELECT e.user_id, e.project_id SELECT e.user_id, e.project_id
FROM (SELECT DISTINCT author_id AS user_id, project_id FROM events WHERE project_id IS NOT NULL) AS e FROM (SELECT DISTINCT author_id AS user_id, project_id FROM events WHERE project_id IS NOT NULL) AS e
LEFT JOIN user_contributed_projects ucp USING (user_id, project_id) LEFT JOIN user_interacted_projects ucp USING (user_id, project_id)
WHERE ucp.user_id IS NULL WHERE ucp.user_id IS NULL
SQL SQL
end end
......
...@@ -1815,13 +1815,6 @@ ActiveRecord::Schema.define(version: 20180304204842) do ...@@ -1815,13 +1815,6 @@ ActiveRecord::Schema.define(version: 20180304204842) do
add_index "user_callouts", ["user_id", "feature_name"], name: "index_user_callouts_on_user_id_and_feature_name", unique: true, using: :btree add_index "user_callouts", ["user_id", "feature_name"], name: "index_user_callouts_on_user_id_and_feature_name", unique: true, using: :btree
add_index "user_callouts", ["user_id"], name: "index_user_callouts_on_user_id", using: :btree add_index "user_callouts", ["user_id"], name: "index_user_callouts_on_user_id", using: :btree
create_table "user_contributed_projects", id: false, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "project_id", null: false
end
add_index "user_contributed_projects", ["project_id", "user_id"], name: "index_user_contributed_projects_on_project_id_and_user_id", unique: true, using: :btree
create_table "user_custom_attributes", force: :cascade do |t| create_table "user_custom_attributes", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false t.datetime_with_timezone "updated_at", null: false
...@@ -1833,6 +1826,13 @@ ActiveRecord::Schema.define(version: 20180304204842) do ...@@ -1833,6 +1826,13 @@ ActiveRecord::Schema.define(version: 20180304204842) do
add_index "user_custom_attributes", ["key", "value"], name: "index_user_custom_attributes_on_key_and_value", using: :btree add_index "user_custom_attributes", ["key", "value"], name: "index_user_custom_attributes_on_key_and_value", using: :btree
add_index "user_custom_attributes", ["user_id", "key"], name: "index_user_custom_attributes_on_user_id_and_key", unique: true, using: :btree add_index "user_custom_attributes", ["user_id", "key"], name: "index_user_custom_attributes_on_user_id_and_key", unique: true, using: :btree
create_table "user_interacted_projects", id: false, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "project_id", null: false
end
add_index "user_interacted_projects", ["project_id", "user_id"], name: "index_user_interacted_projects_on_project_id_and_user_id", unique: true, using: :btree
create_table "user_synced_attributes_metadata", force: :cascade do |t| create_table "user_synced_attributes_metadata", force: :cascade do |t|
t.boolean "name_synced", default: false t.boolean "name_synced", default: false
t.boolean "email_synced", default: false t.boolean "email_synced", default: false
...@@ -2101,9 +2101,9 @@ ActiveRecord::Schema.define(version: 20180304204842) do ...@@ -2101,9 +2101,9 @@ ActiveRecord::Schema.define(version: 20180304204842) do
add_foreign_key "trending_projects", "projects", on_delete: :cascade add_foreign_key "trending_projects", "projects", on_delete: :cascade
add_foreign_key "u2f_registrations", "users" add_foreign_key "u2f_registrations", "users"
add_foreign_key "user_callouts", "users", on_delete: :cascade add_foreign_key "user_callouts", "users", on_delete: :cascade
add_foreign_key "user_contributed_projects", "projects", name: "fk_6fe26e92ae", on_delete: :cascade
add_foreign_key "user_contributed_projects", "users", name: "fk_285db038d3", on_delete: :cascade
add_foreign_key "user_custom_attributes", "users", on_delete: :cascade add_foreign_key "user_custom_attributes", "users", on_delete: :cascade
add_foreign_key "user_interacted_projects", "projects", name: "fk_722ceba4f7", on_delete: :cascade
add_foreign_key "user_interacted_projects", "users", name: "fk_0894651f08", on_delete: :cascade
add_foreign_key "user_synced_attributes_metadata", "users", on_delete: :cascade add_foreign_key "user_synced_attributes_metadata", "users", on_delete: :cascade
add_foreign_key "users_star_projects", "projects", name: "fk_22cd27ddfc", on_delete: :cascade add_foreign_key "users_star_projects", "projects", name: "fk_22cd27ddfc", on_delete: :cascade
add_foreign_key "web_hook_logs", "web_hooks", on_delete: :cascade add_foreign_key "web_hook_logs", "web_hooks", on_delete: :cascade
......
...@@ -50,18 +50,18 @@ describe Event do ...@@ -50,18 +50,18 @@ describe Event do
end end
end end
describe 'after_create :track_user_contributed_projects' do describe 'after_create :track_user_interacted_projects' do
let(:event) { build(:push_event, project: project, author: project.owner) } let(:event) { build(:push_event, project: project, author: project.owner) }
it 'passes event to UserContributedProjects.track' do it 'passes event to UserInteractedProjects.track' do
expect(UserContributedProjects).to receive(:available?).and_return(true) expect(UserInteractedProjects).to receive(:available?).and_return(true)
expect(UserContributedProjects).to receive(:track).with(event) expect(UserInteractedProjects).to receive(:track).with(event)
event.save event.save
end end
it 'does not call UserContributedProjects.track if its not yet available' do it 'does not call UserInteractedProjects.track if its not yet available' do
expect(UserContributedProjects).to receive(:available?).and_return(false) expect(UserInteractedProjects).to receive(:available?).and_return(false)
expect(UserContributedProjects).not_to receive(:track) expect(UserInteractedProjects).not_to receive(:track)
event.save event.save
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe UserContributedProjects do describe UserInteractedProjects do
describe '.track' do describe '.track' do
subject { described_class.track(event) } subject { described_class.track(event) }
let(:event) { build(:event) } let(:event) { build(:event) }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册