我在生产环境中面临一个非常讨厌的错误,这引起了一些混乱。我增加了对生产的记录以缩小问题范围,现在能够在我们的本地环境中重现它。因此,我们在Ubuntu机器上运行Rails 5。该应用程序使用ElasticSearch 5.4,我们将图像存储在Amazon S3上。
处理:用户可以上传图片。 Elasticsearch中有一个用户索引,它还将信息存储到相关的Photo模型中。
问题是指用户上传照片后,有时会在索引页面或详细用户页面上出现错误,其中照片以某种方式被访问。并且要显示的结果不是从数据库读取的,而是Elasticsearch。
解决方法(不是一个)。一旦索引被重新导入,错误就不再发生了 - 最初让我相信它与ElasticSearch有关。
用户模型
class User < ApplicationRecord
include UserSearchable
extend FriendlyId
require "redis"
friendly_id :slug_candidates, use: :slugged
has_many :photos
after_update { self.photos.each(&:touch) }
...
端
用户可搜索的关注
模块UserSearchable 扩展ActiveSupport :: Concern
included do
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
index_name Rails.application.class.parent_name.underscore
document_type self.name.downcase
settings index: { number_of_shards: 1 } do
mapping dynamic: false do
indexes :description, analyzer: 'english'
indexes :tagline, analyzer: 'english'
indexes :username
...
end
end
after_touch() { __elasticsearch__.index_document }
def as_indexed_json(_options = {})
self.as_json(
except: [:email, :lat, :lng, :status, :termsofuse, :v_code],
include: {
photos: { only: [:name, :caption, :active, :image_data, :downloadable, :uploader_id, :public] },
).merge(
location: {lat: lat.to_f, lon: lng.to_f},
age: birthday.nil? ? 18 : ((Date.today - birthday.to_date) / 365.25).floor
)
end
def home_search(searcher, order = nil, how_many = nil)
how_many = 400 unless how_many.is_a?(Integer)
order = 1 unless order.is_a?(Integer)
if self.radius < 30
use_radius = 30
else
use_radius = self.radius
end
search_definition = Jbuilder.encode do |json|
json.sort do
if order == 1
json.array! [{'_geo_distance' => { :location => {:lat => lat, :lon => lng} }}, '_score']
else
json.array! ['_score', {'_geo_distance' => { :location => {:lat => lat, :lon => lng} }}]
end
end
json.query do
json.bool do
json.filter do
json.bool do
json.must do
json.array! [:geo_distance => { :distance => use_radius, :unit => "mi", :location => {:lat => self.lat, :lon => self.lng}, :boost => 5.0}]
end
...
unless searcher.id.nil?
json.must_not do
json.array! [ {:term => { 'id' => self.id }} ]
end
end
end
end
...
end
end
# json.size how_many
end
self.class.__elasticsearch__.search(search_definition)
end
end
end
照片模型
class Photo < ApplicationRecord
include ImageUploader[:image]
include ActiveModel::Validations
acts_as_taggable
before_create :set_name
belongs_to :user, touch: true
after_update { self.user(&:touch) }
private
def set_name
self.name = "Photo"
end
end
在控制台中测试问题:
此处,这是问题的键。如果我评价这张照片有时它是零,有时它不是!我无法弄清楚为什么?如果我在控制台上运行此命令可以说10次,那么10次中只有2次。这就是当生产系统脱离其轨道并且向用户呈现错误时。由于系统是内联网和新系统,我们实际上向用户显示例外情况(所以不要因为错误的异常处理而感到警惕 - 如果你不清楚,用户就不会说出一件事;)
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
!! #<NoMethodError: undefined method `[]' for nil:NilClass>
追加追踪:
>> Settings.s3 + eval(user.photos.sample.image_data)[:small][:id]
!! #<NoMethodError: undefined method `[]' for nil:NilClass>
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
!! #<NoMethodError: undefined method `[]' for nil:NilClass>
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
!! #<NoMethodError: undefined method `[]' for nil:NilClass>
>> eval(user.photos.sample.image_data)[:small][:id]
!! #<NoMethodError: undefined method `[]' for nil:NilClass>
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>>
=> nil
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
!! #<NoMethodError: undefined method `[]' for nil:NilClass>
>> user.photos.sample.image_data
=> "{\"original\":{\"id\":\"photo/47/image/original-114c9db755b25afe0398f5b25aed5bef.jpg\",\"storage\":\"store\",\"metadata\":{\"size\":61357,\"filename\":\"London-Escort-Angelina (5).jpg\",\"mime_type\":\"image/jpeg\",\"width\":500,\"height\":500}},\"large\":{\"id\":\"photo/47/image/large-c3985d412ee05495594caa659feca371.jpg\",\"storage\":\"store\",\"metadata\":{\"filename\":\"shrine-s320170627-29702-17km6c5.jpg\",\"size\":61356,\"mime_type\":\"image/jpeg\",\"width\":500,\"height\":500}},\"small\":{\"id\":\"photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg\",\"storage\":\"store\",\"metadata\":{\"filename\":\"shrine-s320170627-29702-ouo63f.jpg\",\"size\":25642,\"mime_type\":\"image/jpeg\",\"width\":300,\"height\":300}}}"
>> user.photos.sample.image_data[:small]
!! #<TypeError: no implicit conversion of Symbol into Integer>
>> eval(user.photos.sample.image_data)[:small]
=> {:id=>"photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg", :storage=>"store", :metadata=>{:filename=>"shrine-s320170627-29702-ouo63f.jpg", :size=>25642, :mime_type=>"image/jpeg", :width=>300, :height=>300}}
>> eval(user.photos.sample.image_data)[:small][:id]
=> "photo/47/image/small-2cd0928d02826f0614086a01ee97ef32.jpg"
>> eval(user.photos.sample.image_data)[:small][:id]
!! #<NoMethodError: undefined method `[]' for nil:NilClass>
非常感谢任何帮助!!!
答案 0 :(得分:0)
您的结果有所不同,因为您使用sample方法,从user.photos
中选择一个随机元素。
为防止出现undefined method [] for nil:NilClass
错误,您可以将&.dig用于ruby&gt; = 2.3或try
(如果更少)
nil&amp; .dig(:small,:id)
=&GT;零
nil.try(:dig,:small,:id)
=&GT;零
答案 1 :(得分:0)
因此,我可以通过在上传或删除照片时重新索引更新的用户文档来解决问题(workaround-ish)。之前忘了破坏部分。由于必须在ES中删除整个记录并重新编制索引,但是流量很低并且暂时可行,因此它看起来并不正确,因为它很麻烦。如果我弄清楚这个问题,我会回到这个问题。