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 | 57 | def find_or_create_err!(attrs) |
| 58 | 58 | Err.where( |
| 59 | 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 | 64 | end |
| 63 | 65 | |
| 64 | 66 | # Mongoid Bug: find(id) on association proxies returns an Enumerator | ... | ... |
app/models/backtrace.rb
| ... | ... | @@ -14,12 +14,13 @@ class Backtrace |
| 14 | 14 | |
| 15 | 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 | 24 | end |
| 24 | 25 | |
| 25 | 26 | def raw=(raw) |
| ... | ... | @@ -30,6 +31,6 @@ class Backtrace |
| 30 | 31 | |
| 31 | 32 | private |
| 32 | 33 | def generate_fingerprint |
| 33 | - self.fingerprint = Digest::SHA1.hexdigest(lines.map(&:to_s).join) | |
| 34 | + self.fingerprint = self.class.generate_fingerprint(lines) | |
| 34 | 35 | end |
| 35 | 36 | end | ... | ... |
app/models/error_report.rb
| ... | ... | @@ -40,24 +40,88 @@ class ErrorReport |
| 40 | 40 | end |
| 41 | 41 | |
| 42 | 42 | def backtrace |
| 43 | - @normalized_backtrace ||= Backtrace.find_or_create(raw: @backtrace) | |
| 43 | + @normalized_backtrace ||= Backtrace.find_or_create(@backtrace) | |
| 44 | 44 | end |
| 45 | 45 | |
| 46 | 46 | def generate_notice! |
| 47 | 47 | return unless valid? |
| 48 | 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 | 59 | @notice = Notice.new( |
| 50 | 60 | message: message, |
| 51 | 61 | error_class: error_class, |
| 52 | - backtrace_id: backtrace.id, | |
| 62 | + backtrace: backtrace, | |
| 53 | 63 | request: request, |
| 54 | 64 | server_environment: server_environment, |
| 55 | 65 | notifier: notifier, |
| 56 | 66 | user_attributes: user_attributes, |
| 57 | 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 | 125 | end |
| 62 | 126 | |
| 63 | 127 | ## | ... | ... |
app/models/notice.rb
| ... | ... | @@ -20,13 +20,10 @@ class Notice |
| 20 | 20 | index(:created_at => 1) |
| 21 | 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 | 23 | before_save :sanitize |
| 27 | 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 | 28 | scope :ordered, ->{ order_by(:created_at.asc) } |
| 32 | 29 | scope :reverse_ordered, ->{ order_by(:created_at.desc) } |
| ... | ... | @@ -110,22 +107,6 @@ class Notice |
| 110 | 107 | backtrace_lines.in_app |
| 111 | 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 | 111 | # TODO: Move on decorator maybe |
| 131 | 112 | # |
| ... | ... | @@ -151,21 +132,12 @@ class Notice |
| 151 | 132 | problem.remove_cached_notice_attributes(self) if err |
| 152 | 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 | 135 | def sanitize |
| 163 | 136 | [:server_environment, :request, :notifier].each do |h| |
| 164 | 137 | send("#{h}=",sanitize_hash(send(h))) |
| 165 | 138 | end |
| 166 | 139 | end |
| 167 | 140 | |
| 168 | - | |
| 169 | 141 | def sanitize_hash(h) |
| 170 | 142 | h.recurse do |h| |
| 171 | 143 | h.inject({}) do |h,(k,v)| |
| ... | ... | @@ -178,25 +150,4 @@ class Notice |
| 178 | 150 | end |
| 179 | 151 | end |
| 180 | 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 | 153 | end | ... | ... |
app/models/problem.rb
| ... | ... | @@ -132,15 +132,7 @@ class Problem |
| 132 | 132 | def cache_app_attributes |
| 133 | 133 | if app |
| 134 | 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 | 136 | end |
| 145 | 137 | end |
| 146 | 138 | ... | ... |