Почтальон сохраняет почту несколько раз

Я включил самоцвет почтальона в мой проект rails. Он успешно получает письма из Gmail. В моем приложении есть модельное Сообщение для моих писем. Письма правильно сохранены как модель сообщения.

Проблема в том, что электронные письма иногда сохраняются несколько раз, и я не могу распознать шаблон. Некоторые электронные письма сохраняются один раз, некоторые два раза, а некоторые сохраняются три раза.

Но я не могу найти ошибку в моем коде.

Вот мой скрипт mailman_server:

Сценарий / mailman_server

#!/usr/bin/env ruby
# encoding: UTF-8
require "rubygems"
require "bundler/setup"
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
require 'mailman'

Mailman.config.ignore_stdin = true
#Mailman.config.logger = Logger.new File.expand_path("../../log/mailman_#{Rails.env}.log", __FILE__)

if Rails.env == 'test'
  Mailman.config.maildir = File.expand_path("../../tmp/test_maildir", __FILE__)
else
  Mailman.config.logger = Logger.new File.expand_path("../../log/mailman_#{Rails.env}.log", __FILE__)
  Mailman.config.poll_interval = 15
  Mailman.config.imap = {
    server: 'imap.gmail.com',
    port: 993,  # usually 995, 993 for gmail
    ssl: true,
    username: 'my@email.com',
    password: 'my_password'
  }
end

Mailman::Application.run do
  default do
    begin
      Message.receive_message(message)
    rescue Exception => e
      Mailman.logger.error "Exception occurred while receiving message:\n#{message}"
      Mailman.logger.error [e, *e.backtrace].join("\n")
    end
  end
end

Письмо обрабатывается в моем классе сообщений:

  def self.receive_message(message)
    if message.from.first == "my@email.com"
      Message.save_bcc_mail(message)
    else
      Message.save_incoming_mail(message)
    end
  end

  def self.save_incoming_mail(message)
    part_to_use = message.html_part || message.text_part || message
    if Kontakt.where(:email => message.from.first).empty?
      encoding = part_to_use.content_type_parameters['charset']
      Message.create topic: message.subject, message: part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8'), communication_partner: message.from.first, inbound: true, time: message.date
    else
      encoding = part_to_use.content_type_parameters['charset']
      Message.create topic: message.subject, message: part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8'), communication_partner: message.from.first, inbound: true, time: message.date, messageable_type: 'Company', messageable_id: Kontakt.where(:email => message.from.first).first.year.id
    end
  end

  def self.save_bcc_mail(message)
    part_to_use = message.html_part || message.text_part || message
    if Kontakt.where(:email => message.to.first).empty?
      encoding = part_to_use.content_type_parameters['charset']
      Message.create topic: message.subject, message: part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8'), communication_partner: message.to.first, inbound: false, time: message.date
    else
      encoding = part_to_use.content_type_parameters['charset']
      Message.create topic: message.subject, message: part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8'), communication_partner: message.to.first, inbound: false, time: message.date, messageable_type: 'Company', messageable_id: Kontakt.where(:email => message.to.first).first.year.id
    end
  end

Я демонизировал mailman_server с помощью этого скрипта:

Сценарий / mailman_daemon

#!/usr/bin/env ruby

require 'rubygems'  
require "bundler/setup"  
require 'daemons'

Daemons.run('script/mailman_server') 

Я использую Capistrano.

Это те части, которые отвечают за остановку, запуск и перезапуск моего mailman_server:

Сценарий / deploy.rb

set :rails_env, "production" #added for delayed job  
after "deploy:stop",    "delayed_job:stop"
after "deploy:start",   "delayed_job:start"
after "deploy:restart", "delayed_job:restart"
after "deploy:stop",    "mailman:stop"
after "deploy:start",   "mailman:start"
after "deploy:restart", "mailman:restart"

namespace :deploy do
  desc "mailman script ausfuehrbar machen"
  task :mailman_executable, :roles => :app do
   run "chmod +x #{current_path}/script/mailman_server"
  end

  desc "mailman daemon ausfuehrbar machen"
  task :mailman_daemon_executable, :roles => :app do
   run "chmod +x #{current_path}/script/mailman_daemon"
  end
end

namespace :mailman do  
  desc "Mailman::Start"
  task :start, :roles => [:app] do
   run "cd #{current_path};RAILS_ENV=#{fetch(:rails_env)} bundle exec script/mailman_daemon start"
  end

  desc "Mailman::Stop"
  task :stop, :roles => [:app] do
   run "cd #{current_path};RAILS_ENV=#{fetch(:rails_env)} bundle exec script/mailman_daemon stop"
  end

  desc "Mailman::Restart"
  task :restart, :roles => [:app] do
   mailman.stop
   mailman.start
  end
end

Может ли быть так, что несколько экземпляров почтового сервера запускаются во время моего развертывания почти одновременно, а затем каждый экземпляр опрашивает почти одновременно? Пулы второго и третьего экземпляров перед первым экземпляром помечают письмо как прочитанное, а также опрашивают и обрабатывают письмо?

Обновление 30.01.

Я установил интервал опроса до 60 секунд. но это ничего не меняет.

Я проверил папку, в которой хранится pid-файл почтальона. есть только один файл pid почтальона. Таким образом, определенно работает только один почтовый сервер. Я проверил лог-файл и вижу, что сообщения выбираются несколько раз:

Mailman v0.7.0 started
IMAP receiver enabled (my@email.com).
Polling enabled. Checking every 60 seconds.
Got new message from 'my.other@email.com' with subject 'Test nr 0'.
Got new message from 'my.other@email.com' with subject 'Test nr 1'.
Got new message from 'my.other@email.com' with subject 'test nr 2'.
Got new message from 'my.other@email.com' with subject 'test nr 2'.
Got new message from 'my.other@email.com' with subject 'test nr 3'.
Got new message from 'my.other@email.com' with subject 'test nr 4'.
Got new message from 'my.other@email.com' with subject 'test nr 4'.
Got new message from 'my.other@email.com' with subject 'test nr 4'.

Так что мне кажется, что проблема определенно в коде моего почтового сервера.

Обновление 31.1.

Мне кажется, это как-то связано с моей производственной машиной. когда я тестирую это в разработке с точно такой же конфигурацией (изменил мою локальную базу данных с sqlite на mysql этим утром, чтобы протестировать ее), так как на рабочей машине я не получаю дубликаты. Вероятно, все в порядке с моим кодом, но есть проблема с рабочей машиной. Спросите моего хостера, могут ли они найти решение для этого. Чтобы исправить это, я пойду с предложением Арьеяна.

Решение: я нашел проблему. Я развернул на машине, где каталог tmp является общим для всех выпусков. Я забыл определить путь, по которому должен быть сохранен pid-файл mailman_daemon. Поэтому он был сохранен в каталоге сценариев вместо каталога /tmp/pids. Из-за этого старый mailman_daemon не может быть остановлен после нового развертывания. Это привело к армии рабочих mailman_daemon s, которые опрашивали мой почтовый аккаунт... После уничтожения всех этих процессов все прошло хорошо! Нет больше дубликатов!

2 ответа

Решение

Я нашел проблему. Я развернул на машине, где каталог tmp является общим для всех выпусков. Я забыл определить путь, по которому должен быть сохранен pid-файл mailman_daemon. Поэтому он был сохранен в каталоге сценариев вместо каталога /tmp/pids. Из-за этого старый mailman_daemon не может быть остановлен после нового развертывания. Это привело к армии рабочих mailman_daemon s, которые опрашивали мой почтовый аккаунт... После уничтожения всех этих процессов все прошло хорошо! Нет больше дубликатов!

Это может быть проблемой параллелизма / синхронизации. Например, новые письма импортируются до того, как те, которые в настоящее время обрабатываются, будут сохранены.


Изменить: только что заметил, что у вас есть Mailman.config.poll_interval установить на 15. Это означает, что он будет проверять наличие новых сообщений каждые 15 секунд. Попробуйте увеличить это значение до значения по умолчанию 60 секунд. Независимо от этого параметра, было бы неплохо добавить код дедупликации, о котором я упоминал ниже.


Мой совет будет также хранить message_id с каждого письма, так что вы можете легко найти дубликаты.

Вместо:

Message.create(...)

делать:

# This makes sure you have the latest pulled version.
message = Message.find_or_create(message_id: message.message_id)
message.update_attributes(...)

# This makes sure you only import it once, then ignore further duplicates.
if !Message.where(message_id: message.message_id).exists?
  Message.create(...)
end

Для получения дополнительной информации о message_id: http://rdoc.info/github/mikel/mail/Mail/Message

Помните, что электронная почта и imap не должны быть согласованными хранилищами данных, как вы ожидаете от Postgres или Mysql. Надеюсь, это поможет вам разобраться с дубликатами писем.

Другие вопросы по тегам