blob: ae6778e49be261291561308597fc95751ca3e854 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
# frozen_string_literal: true
class UserInteractedProject < ActiveRecord::Base
belongs_to :user
belongs_to :project
validates :project_id, presence: true
validates :user_id, presence: true
CACHE_EXPIRY_TIME = 1.day
# Schema version required for this model
REQUIRED_SCHEMA_VERSION = 20180223120443
class << self
def track(event)
# For events without a project, we simply don't care.
# An example of this is the creation of a snippet (which
# is not related to any project).
return unless event.project_id
attributes = {
project_id: event.project_id,
user_id: event.author_id
}
cached_exists?(attributes) do
transaction(requires_new: true) do
begin
where(attributes).select(1).first || create!(attributes)
true # not caching the whole record here for now
rescue ActiveRecord::RecordNotUnique
# Note, above queries are not atomic and prone
# to race conditions (similar like #find_or_create!).
# In the case where we hit this, the record we want
# already exists - shortcut and return.
true
end
end
end
end
# Check if we can safely call .track (table exists)
def available?
@available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION # rubocop:disable Gitlab/PredicateMemoization
end
# Flushes cached information about schema
def reset_column_information
@available_flag = nil
super
end
private
def cached_exists?(project_id:, user_id:, &block)
cache_key = "user_interacted_projects:#{project_id}:#{user_id}"
Rails.cache.fetch(cache_key, expires_in: CACHE_EXPIRY_TIME, &block)
end
end
end
|