Commit f646cf500aef1d37aeb28a4a11f5bf2a4930837c
1 parent
816110d7
Exists in
master
and in
1 other branch
reduce number of queries on insert
These changes reduce the typical number of queries needed to insert an error from nine to five.
Showing
5 changed files
with
81 additions
and
71 deletions
Show diff stats
app/models/app.rb
@@ -57,8 +57,10 @@ class App | @@ -57,8 +57,10 @@ class App | ||
57 | def find_or_create_err!(attrs) | 57 | def find_or_create_err!(attrs) |
58 | Err.where( | 58 | Err.where( |
59 | :fingerprint => attrs[:fingerprint] | 59 | :fingerprint => attrs[:fingerprint] |
60 | - ).first || | ||
61 | - problems.create!(attrs.slice(:error_class, :environment)).errs.create!(attrs.slice(:fingerprint, :problem_id)) | 60 | + ).first || ( |
61 | + problem = problems.create!(attrs.slice(:error_class, :environment)) | ||
62 | + problem.errs.create!(attrs.slice(:fingerprint, :problem_id)) | ||
63 | + ) | ||
62 | end | 64 | end |
63 | 65 | ||
64 | # Mongoid Bug: find(id) on association proxies returns an Enumerator | 66 | # Mongoid Bug: find(id) on association proxies returns an Enumerator |
app/models/backtrace.rb
@@ -14,12 +14,13 @@ class Backtrace | @@ -14,12 +14,13 @@ class Backtrace | ||
14 | 14 | ||
15 | delegate :app, :to => :notice | 15 | delegate :app, :to => :notice |
16 | 16 | ||
17 | - def self.find_or_create(attributes = {}) | ||
18 | - new(attributes).similar.find_and_modify({ '$setOnInsert' => attributes }) | ||
19 | - end | 17 | + def self.find_or_create(lines) |
18 | + fingerprint = generate_fingerprint(lines) | ||
20 | 19 | ||
21 | - def similar | ||
22 | - Backtrace.where(:fingerprint => fingerprint) | 20 | + where(fingerprint: generate_fingerprint(lines)). |
21 | + find_one_and_update( | ||
22 | + { '$setOnInsert' => { fingerprint: fingerprint, lines: lines } }, | ||
23 | + { return_document: :after, upsert: true }) | ||
23 | end | 24 | end |
24 | 25 | ||
25 | def raw=(raw) | 26 | def raw=(raw) |
@@ -30,6 +31,6 @@ class Backtrace | @@ -30,6 +31,6 @@ class Backtrace | ||
30 | 31 | ||
31 | private | 32 | private |
32 | def generate_fingerprint | 33 | def generate_fingerprint |
33 | - self.fingerprint = Digest::SHA1.hexdigest(lines.map(&:to_s).join) | 34 | + self.fingerprint = self.class.generate_fingerprint(lines) |
34 | end | 35 | end |
35 | end | 36 | end |
app/models/error_report.rb
@@ -40,24 +40,88 @@ class ErrorReport | @@ -40,24 +40,88 @@ class ErrorReport | ||
40 | end | 40 | end |
41 | 41 | ||
42 | def backtrace | 42 | def backtrace |
43 | - @normalized_backtrace ||= Backtrace.find_or_create(raw: @backtrace) | 43 | + @normalized_backtrace ||= Backtrace.find_or_create(@backtrace) |
44 | end | 44 | end |
45 | 45 | ||
46 | def generate_notice! | 46 | def generate_notice! |
47 | return unless valid? | 47 | return unless valid? |
48 | return @notice if @notice | 48 | return @notice if @notice |
49 | + | ||
50 | + make_notice | ||
51 | + error.notices << @notice | ||
52 | + cache_attributes_on_problem | ||
53 | + email_notification | ||
54 | + services_notification | ||
55 | + @notice | ||
56 | + end | ||
57 | + | ||
58 | + def make_notice | ||
49 | @notice = Notice.new( | 59 | @notice = Notice.new( |
50 | message: message, | 60 | message: message, |
51 | error_class: error_class, | 61 | error_class: error_class, |
52 | - backtrace_id: backtrace.id, | 62 | + backtrace: backtrace, |
53 | request: request, | 63 | request: request, |
54 | server_environment: server_environment, | 64 | server_environment: server_environment, |
55 | notifier: notifier, | 65 | notifier: notifier, |
56 | user_attributes: user_attributes, | 66 | user_attributes: user_attributes, |
57 | framework: framework | 67 | framework: framework |
58 | ) | 68 | ) |
59 | - error.notices << @notice | ||
60 | - @notice | 69 | + end |
70 | + | ||
71 | + # Update problem cache with information about this notice | ||
72 | + def cache_attributes_on_problem | ||
73 | + # increment notice count | ||
74 | + message_digest = Digest::MD5.hexdigest(@notice.message) | ||
75 | + host_digest = Digest::MD5.hexdigest(@notice.host) | ||
76 | + user_agent_digest = Digest::MD5.hexdigest(@notice.user_agent_string) | ||
77 | + | ||
78 | + @problem = Problem.where("_id" => @error.problem_id).find_one_and_update( | ||
79 | + '$set' => { | ||
80 | + 'app_name' => app.name, | ||
81 | + 'environment' => @notice.environment_name, | ||
82 | + 'error_class' => @notice.error_class, | ||
83 | + 'last_notice_at' => @notice.created_at, | ||
84 | + 'message' => @notice.message, | ||
85 | + 'resolved' => false, | ||
86 | + 'resolved_at' => nil, | ||
87 | + 'where' => @notice.where, | ||
88 | + "messages.#{message_digest}.value" => @notice.message, | ||
89 | + "hosts.#{host_digest}.value" => @notice.host, | ||
90 | + "user_agents.#{user_agent_digest}.value" => @notice.user_agent_string, | ||
91 | + }, | ||
92 | + '$inc' => { | ||
93 | + 'notices_count' => 1, | ||
94 | + "messages.#{message_digest}.count" => 1, | ||
95 | + "hosts.#{host_digest}.count" => 1, | ||
96 | + "user_agents.#{user_agent_digest}.count" => 1, | ||
97 | + } | ||
98 | + ) | ||
99 | + end | ||
100 | + | ||
101 | + def similar_count | ||
102 | + @similar_count ||= @problem.notices_count | ||
103 | + end | ||
104 | + | ||
105 | + # Send email notification if needed | ||
106 | + def email_notification | ||
107 | + return false unless app.emailable? | ||
108 | + return false unless app.email_at_notices.include?(similar_count) | ||
109 | + Mailer.err_notification(@notice).deliver | ||
110 | + rescue => e | ||
111 | + HoptoadNotifier.notify(e) | ||
112 | + end | ||
113 | + | ||
114 | + def should_notify? | ||
115 | + app.notification_service.notify_at_notices.include?(0) || | ||
116 | + app.notification_service.notify_at_notices.include?(similar_count) | ||
117 | + end | ||
118 | + | ||
119 | + # Launch all notification define on the app associate to this notice | ||
120 | + def services_notification | ||
121 | + return true unless app.notification_service_configured? and should_notify? | ||
122 | + app.notification_service.create_notification(problem) | ||
123 | + rescue => e | ||
124 | + HoptoadNotifier.notify(e) | ||
61 | end | 125 | end |
62 | 126 | ||
63 | ## | 127 | ## |
app/models/notice.rb
@@ -20,13 +20,10 @@ class Notice | @@ -20,13 +20,10 @@ class Notice | ||
20 | index(:created_at => 1) | 20 | index(:created_at => 1) |
21 | index(:err_id => 1, :created_at => 1, :_id => 1) | 21 | index(:err_id => 1, :created_at => 1, :_id => 1) |
22 | 22 | ||
23 | - after_create :cache_attributes_on_problem, :unresolve_problem | ||
24 | - after_create :email_notification | ||
25 | - after_create :services_notification | ||
26 | before_save :sanitize | 23 | before_save :sanitize |
27 | before_destroy :decrease_counter_cache, :remove_cached_attributes_from_problem | 24 | before_destroy :decrease_counter_cache, :remove_cached_attributes_from_problem |
28 | 25 | ||
29 | - validates_presence_of :backtrace, :server_environment, :notifier | 26 | + validates_presence_of :backtrace_id, :server_environment, :notifier |
30 | 27 | ||
31 | scope :ordered, ->{ order_by(:created_at.asc) } | 28 | scope :ordered, ->{ order_by(:created_at.asc) } |
32 | scope :reverse_ordered, ->{ order_by(:created_at.desc) } | 29 | scope :reverse_ordered, ->{ order_by(:created_at.desc) } |
@@ -110,22 +107,6 @@ class Notice | @@ -110,22 +107,6 @@ class Notice | ||
110 | backtrace_lines.in_app | 107 | backtrace_lines.in_app |
111 | end | 108 | end |
112 | 109 | ||
113 | - def similar_count | ||
114 | - problem.notices_count | ||
115 | - end | ||
116 | - | ||
117 | - def emailable? | ||
118 | - app.email_at_notices.include?(similar_count) | ||
119 | - end | ||
120 | - | ||
121 | - def should_email? | ||
122 | - app.emailable? && emailable? | ||
123 | - end | ||
124 | - | ||
125 | - def should_notify? | ||
126 | - app.notification_service.notify_at_notices.include?(0) || app.notification_service.notify_at_notices.include?(similar_count) | ||
127 | - end | ||
128 | - | ||
129 | ## | 110 | ## |
130 | # TODO: Move on decorator maybe | 111 | # TODO: Move on decorator maybe |
131 | # | 112 | # |
@@ -151,21 +132,12 @@ class Notice | @@ -151,21 +132,12 @@ class Notice | ||
151 | problem.remove_cached_notice_attributes(self) if err | 132 | problem.remove_cached_notice_attributes(self) if err |
152 | end | 133 | end |
153 | 134 | ||
154 | - def unresolve_problem | ||
155 | - problem.update_attributes!(:resolved => false, :resolved_at => nil, :notices_count => 1) if problem.resolved? | ||
156 | - end | ||
157 | - | ||
158 | - def cache_attributes_on_problem | ||
159 | - ProblemUpdaterCache.new(problem, self).update | ||
160 | - end | ||
161 | - | ||
162 | def sanitize | 135 | def sanitize |
163 | [:server_environment, :request, :notifier].each do |h| | 136 | [:server_environment, :request, :notifier].each do |h| |
164 | send("#{h}=",sanitize_hash(send(h))) | 137 | send("#{h}=",sanitize_hash(send(h))) |
165 | end | 138 | end |
166 | end | 139 | end |
167 | 140 | ||
168 | - | ||
169 | def sanitize_hash(h) | 141 | def sanitize_hash(h) |
170 | h.recurse do |h| | 142 | h.recurse do |h| |
171 | h.inject({}) do |h,(k,v)| | 143 | h.inject({}) do |h,(k,v)| |
@@ -178,25 +150,4 @@ class Notice | @@ -178,25 +150,4 @@ class Notice | ||
178 | end | 150 | end |
179 | end | 151 | end |
180 | end | 152 | end |
181 | - | ||
182 | - private | ||
183 | - | ||
184 | - ## | ||
185 | - # Send email notification if needed | ||
186 | - def email_notification | ||
187 | - return true unless should_email? | ||
188 | - Mailer.err_notification(self).deliver | ||
189 | - rescue => e | ||
190 | - HoptoadNotifier.notify(e) | ||
191 | - end | ||
192 | - | ||
193 | - ## | ||
194 | - # Launch all notification define on the app associate to this notice | ||
195 | - def services_notification | ||
196 | - return true unless app.notification_service_configured? and should_notify? | ||
197 | - app.notification_service.create_notification(problem) | ||
198 | - rescue => e | ||
199 | - HoptoadNotifier.notify(e) | ||
200 | - end | ||
201 | - | ||
202 | end | 153 | end |
app/models/problem.rb
@@ -132,15 +132,7 @@ class Problem | @@ -132,15 +132,7 @@ class Problem | ||
132 | def cache_app_attributes | 132 | def cache_app_attributes |
133 | if app | 133 | if app |
134 | self.app_name = app.name | 134 | self.app_name = app.name |
135 | - self.last_deploy_at = if (last_deploy = app.deploys.where(:environment => self.environment).last) | ||
136 | - last_deploy.created_at.utc | ||
137 | - end | ||
138 | - collection. | ||
139 | - find('_id' => self.id). | ||
140 | - update_one({'$set' => { | ||
141 | - 'app_name' => self.app_name, | ||
142 | - 'last_deploy_at' => self.last_deploy_at.try(:utc) | ||
143 | - }}) | 135 | + self.last_deploy_at = app.last_deploy_at |
144 | end | 136 | end |
145 | end | 137 | end |
146 | 138 |