func (s *EmailServer) Run() { go s.queueToDb() batch := conf.Int(3, "email", "server", "batch") maxFails := conf.Int(3, "email", "server", "max-fails") maxTime := conf.String("1h", "email", "server", "max-time") dur, err := time.ParseDuration(maxTime) panik.On(err) maxTimeSec := int(dur.Seconds()) now := time.Now().Unix() var upd map[string]interface{} var msgs []Message sql := ` sent = 0 and num_fails < ? and ((send_after is null and ? - created_at between 0 and ?) or (send_after is not null and ? - send_after between 0 and ?))` for { s.dbo.Where(sql, maxFails, now, maxTimeSec, now, maxTimeSec). Order("priority desc, created_at asc"). Limit(batch). Find(&msgs) if len(msgs) == 0 { time.Sleep(5 * time.Second) } else { for i := 0; i < len(msgs); i++ { err := s.dispatchMessage(&msgs[i]) now = time.Now().Unix() if err == nil { upd = map[string]interface{}{ "sent": 1, "sent_at": &now, "updated_at": &now, } } else { upd = map[string]interface{}{ "num_fails": msgs[i].NumFails + 1, "last_failed_at": &now, "failure": err.Error(), "updated_at": &now, } } edb := s.dbo.Model(Message{}).Where("id = ?", msgs[i].Id).Updates(upd).Error panik.On2(edb, func() { log.Error("Failed to update message table", "email-err", err, "db-err", edb) }) } } } }
func NewEmailServer() *EmailServer { es := EmailServer{ dbo: orm.ReadConfig("email.server.database"), auth: smtp.PlainAuth( conf.String("", "email.server.smtp.auth", "identity"), conf.String("", "email.server.smtp.auth", "username"), conf.String("", "email.server.smtp.auth", "password"), conf.String("", "email.server.smtp.auth", "host"), ), } // testing es.dbo.LogMode(true) es.address = conf.String("", "email.server.smtp", "host") if conf.Exists("email.server.smtp", "port") { es.address = fmt.Sprintf("%s:%d", es.address, conf.Int(0, "email.server.smtp", "port")) } if conf.Exists("email.queue.default") { es.queue = que.NewQueue("email.queue.default") } // create relational tables if missing if !es.dbo.HasTable(Message{}) { panik.On(es.dbo.AutoMigrate(&Message{}).Error) fmt.Println("[message] table created.") } return &es }
func startLoop() { if mails != nil { return } mails = make(chan []byte, conf.Int(size, "email", "buffer")) panik.If(Queue == nil, "Queue is not initialized") go func() { for { select { case mb := <-mails: err := Queue.Push(mb) panik.On(err) } } }() }