apollo-backend/internal/worker/stuck_notifications.go

267 lines
6.3 KiB
Go
Raw Permalink Normal View History

2021-10-17 14:17:41 +00:00
package worker
import (
"context"
"fmt"
"os"
"strconv"
"github.com/DataDog/datadog-go/statsd"
"github.com/adjust/rmq/v4"
"github.com/go-redis/redis/v8"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/sirupsen/logrus"
"github.com/christianselig/apollo-backend/internal/domain"
"github.com/christianselig/apollo-backend/internal/reddit"
"github.com/christianselig/apollo-backend/internal/repository"
)
type stuckNotificationsWorker struct {
context.Context
2021-10-17 14:17:41 +00:00
logger *logrus.Logger
statsd *statsd.Client
db *pgxpool.Pool
redis *redis.Client
queue rmq.Connection
reddit *reddit.Client
consumers int
accountRepo domain.AccountRepository
}
func NewStuckNotificationsWorker(ctx context.Context, logger *logrus.Logger, statsd *statsd.Client, db *pgxpool.Pool, redis *redis.Client, queue rmq.Connection, consumers int) Worker {
2021-10-17 14:17:41 +00:00
reddit := reddit.NewClient(
os.Getenv("REDDIT_CLIENT_ID"),
os.Getenv("REDDIT_CLIENT_SECRET"),
statsd,
2022-03-12 17:50:05 +00:00
redis,
2021-10-17 14:17:41 +00:00
consumers,
)
return &stuckNotificationsWorker{
ctx,
2021-10-17 14:17:41 +00:00
logger,
statsd,
db,
redis,
queue,
reddit,
consumers,
repository.NewPostgresAccount(db),
}
}
func (snw *stuckNotificationsWorker) Start() error {
queue, err := snw.queue.OpenQueue("stuck-notifications")
if err != nil {
return err
}
snw.logger.WithFields(logrus.Fields{
"numConsumers": snw.consumers,
}).Info("starting up stuck notifications worker")
prefetchLimit := int64(snw.consumers * 2)
if err := queue.StartConsuming(prefetchLimit, pollDuration); err != nil {
return err
}
host, _ := os.Hostname()
for i := 0; i < snw.consumers; i++ {
name := fmt.Sprintf("consumer %s-%d", host, i)
consumer := NewStuckNotificationsConsumer(snw, i)
if _, err := queue.AddConsumer(name, consumer); err != nil {
return err
}
}
return nil
}
func (snw *stuckNotificationsWorker) Stop() {
<-snw.queue.StopAllConsuming() // wait for all Consume() calls to finish
}
type stuckNotificationsConsumer struct {
*stuckNotificationsWorker
tag int
}
func NewStuckNotificationsConsumer(snw *stuckNotificationsWorker, tag int) *stuckNotificationsConsumer {
return &stuckNotificationsConsumer{
snw,
tag,
}
}
func (snc *stuckNotificationsConsumer) Consume(delivery rmq.Delivery) {
snc.logger.WithFields(logrus.Fields{
"account#id": delivery.Payload(),
}).Debug("starting job")
id, err := strconv.ParseInt(delivery.Payload(), 10, 64)
if err != nil {
snc.logger.WithFields(logrus.Fields{
"account#id": delivery.Payload(),
"err": err,
}).Error("failed to parse account ID")
_ = delivery.Reject()
return
}
defer func() { _ = delivery.Ack() }()
account, err := snc.accountRepo.GetByID(snc, id)
2021-10-17 14:17:41 +00:00
if err != nil {
snc.logger.WithFields(logrus.Fields{
"err": err,
}).Error("failed to fetch account from database")
return
}
if account.LastMessageID == "" {
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
}).Debug("account has no messages, returning")
return
}
2022-03-12 17:50:05 +00:00
rac := snc.reddit.NewAuthenticatedClient(account.AccountID, account.RefreshToken, account.AccessToken)
2021-10-17 14:17:41 +00:00
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
"thing#id": account.LastMessageID,
}).Debug("fetching last thing")
2021-10-17 14:47:43 +00:00
kind := account.LastMessageID[:2]
var things *reddit.ListingResponse
if kind == "t4" {
2021-10-17 14:17:41 +00:00
snc.logger.WithFields(logrus.Fields{
2021-10-17 14:47:43 +00:00
"account#username": account.NormalizedUsername(),
"thing#id": account.LastMessageID,
}).Debug("checking last thing via inbox")
2021-10-17 14:17:41 +00:00
things, err = rac.MessageInbox(snc)
2021-10-17 14:47:43 +00:00
if err != nil {
2022-05-19 15:51:56 +00:00
if err != reddit.ErrRateLimited {
snc.logger.WithFields(logrus.Fields{
"err": err,
}).Error("failed to fetch last thing via inbox")
}
2021-10-17 14:17:41 +00:00
return
}
2021-10-17 14:47:43 +00:00
} else {
things, err = rac.AboutInfo(snc, account.LastMessageID)
2021-10-17 14:47:43 +00:00
if err != nil {
snc.logger.WithFields(logrus.Fields{
"err": err,
}).Error("failed to fetch last thing")
return
}
}
2021-10-17 14:53:14 +00:00
if things.Count > 0 {
2021-10-17 14:47:43 +00:00
for _, thing := range things.Children {
if thing.FullName() != account.LastMessageID {
continue
}
2022-05-19 16:37:03 +00:00
if thing.IsDeleted() {
break
}
2022-05-19 17:02:16 +00:00
if kind == "t4" {
return
}
sthings, err := rac.MessageInbox(snc)
2022-05-19 16:37:03 +00:00
if err != nil {
snc.logger.WithFields(logrus.Fields{
"err": err,
}).Error("failed to check inbox")
return
}
2022-05-19 17:02:16 +00:00
found := false
for _, sthing := range sthings.Children {
if sthing.FullName() == account.LastMessageID {
found = true
}
}
if !found {
2021-10-17 14:47:43 +00:00
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
"thing#id": account.LastMessageID,
2022-05-19 16:37:03 +00:00
}).Debug("thing exists, but not in inbox, marking as deleted")
break
2021-10-17 14:47:43 +00:00
}
2022-05-19 16:37:03 +00:00
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
"thing#id": account.LastMessageID,
}).Debug("thing exists, returning")
return
2021-10-17 14:47:43 +00:00
}
2021-10-17 14:17:41 +00:00
}
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
"thing#id": account.LastMessageID,
2021-10-17 14:20:07 +00:00
}).Info("thing got deleted, resetting")
2021-10-17 14:17:41 +00:00
2021-10-17 14:47:43 +00:00
if kind != "t4" {
2021-10-17 15:48:41 +00:00
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
}).Debug("getting message inbox to determine last good thing")
2021-10-17 15:48:41 +00:00
things, err = rac.MessageInbox(snc)
2021-10-17 14:47:43 +00:00
if err != nil {
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
"err": err,
}).Error("failed to get message inbox")
return
}
2021-10-17 14:17:41 +00:00
}
account.LastMessageID = ""
2021-10-17 15:48:41 +00:00
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
}).Debug("calculating last good thing")
2021-10-17 14:47:43 +00:00
for _, thing := range things.Children {
2021-10-17 15:27:52 +00:00
if thing.IsDeleted() {
2021-10-17 15:48:41 +00:00
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
"thing#id": thing.FullName(),
}).Debug("thing deleted, next")
2021-10-17 15:27:52 +00:00
continue
}
2021-10-17 15:48:41 +00:00
account.LastMessageID = thing.FullName()
break
2021-10-17 14:17:41 +00:00
}
2021-10-17 15:27:52 +00:00
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
"thing#id": account.LastMessageID,
}).Debug("updating last good thing")
if err := snc.accountRepo.Update(snc, &account); err != nil {
2021-10-17 14:17:41 +00:00
snc.logger.WithFields(logrus.Fields{
"account#username": account.NormalizedUsername(),
"err": err,
}).Error("failed to update account's message id")
}
}