github.com/status-im/status-go@v1.1.0/protocol/messenger_raw_message_resend.go (about) 1 package protocol 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "time" 8 9 "github.com/status-im/status-go/protocol/protobuf" 10 11 "github.com/pkg/errors" 12 "go.uber.org/zap" 13 14 "github.com/status-im/status-go/protocol/common" 15 ) 16 17 // watchExpiredMessages regularly checks for expired emojis and invoke their resending 18 func (m *Messenger) watchExpiredMessages() { 19 m.logger.Debug("watching expired messages") 20 go func() { 21 for { 22 select { 23 case <-time.After(time.Second): 24 if m.Online() { 25 err := m.resendExpiredMessages() 26 if err != nil { 27 m.logger.Debug("failed to resend expired message", zap.Error(err)) 28 } 29 } 30 case <-m.quit: 31 return 32 } 33 } 34 }() 35 } 36 37 func (m *Messenger) resendExpiredMessages() error { 38 if m.connectionState.Offline { 39 return errors.New("offline") 40 } 41 42 ids, err := m.persistence.ExpiredMessagesIDs(m.config.messageResendMaxCount) 43 if err != nil { 44 return errors.Wrapf(err, "Can't get expired reactions from db") 45 } 46 47 for _, id := range ids { 48 message, shouldResend, err := m.processMessageID(id) 49 if err != nil { 50 m.logger.Error("Error processing message ID when trying resend raw message", zap.String("id", id), zap.Error(err)) 51 } else if shouldResend { 52 m.logger.Debug("Resent raw message", 53 zap.String("id", id), 54 zap.Any("message type", message.MessageType), 55 zap.Int("send count", message.SendCount), 56 ) 57 } 58 } 59 return nil 60 } 61 62 func (m *Messenger) processMessageID(id string) (*common.RawMessage, bool, error) { 63 rawMessage, err := m.persistence.RawMessageByID(id) 64 if err != nil { 65 return nil, false, errors.Wrap(err, "Can't get raw message by ID") 66 } 67 68 shouldResend := m.shouldResendMessage(rawMessage, m.getTimesource()) 69 if !shouldResend { 70 return rawMessage, false, nil 71 } 72 73 switch rawMessage.ResendMethod { 74 case common.ResendMethodSendCommunityMessage: 75 err = m.handleSendCommunityMessage(rawMessage) 76 case common.ResendMethodSendPrivate: 77 err = m.handleSendPrivateMessage(rawMessage) 78 case common.ResendMethodDynamic: 79 shouldResend, err = m.handleOtherResendMethods(rawMessage) 80 default: 81 err = errors.New("Unknown resend method") 82 } 83 return rawMessage, shouldResend, err 84 } 85 86 func (m *Messenger) handleSendCommunityMessage(rawMessage *common.RawMessage) error { 87 _, err := m.sender.SendCommunityMessage(context.TODO(), rawMessage) 88 if err != nil { 89 err = errors.Wrap(err, "Can't resend message with SendCommunityMessage") 90 } 91 m.upsertRawMessageToWatch(rawMessage) 92 return err 93 } 94 95 func (m *Messenger) handleSendPrivateMessage(rawMessage *common.RawMessage) error { 96 if len(rawMessage.Recipients) == 0 { 97 m.logger.Error("No recipients to resend message", zap.String("id", rawMessage.ID)) 98 m.upsertRawMessageToWatch(rawMessage) 99 return errors.New("No recipients to resend message with SendPrivate") 100 } 101 102 var err error 103 for _, r := range rawMessage.Recipients { 104 _, err = m.sender.SendPrivate(context.TODO(), r, rawMessage) 105 if err != nil { 106 err = errors.Wrap(err, fmt.Sprintf("Can't resend message with SendPrivate to %s", common.PubkeyToHex(r))) 107 } 108 } 109 110 m.upsertRawMessageToWatch(rawMessage) 111 return err 112 } 113 114 func (m *Messenger) handleOtherResendMethods(rawMessage *common.RawMessage) (bool, error) { 115 chat, ok := m.allChats.Load(rawMessage.LocalChatID) 116 if !ok { 117 m.logger.Error("Can't find chat with id", zap.String("id", rawMessage.LocalChatID)) 118 return false, nil // Continue with next message if chat not found 119 } 120 121 if !(chat.Public() || chat.CommunityChat()) { 122 return false, nil // Only resend for public or community chats 123 } 124 125 if ok { 126 err := m.persistence.SaveRawMessage(rawMessage) 127 if err != nil { 128 m.logger.Error("Can't save raw message marked as expired", zap.Error(err)) 129 return true, err 130 } 131 } 132 return true, m.reSendRawMessage(context.Background(), rawMessage.ID) 133 } 134 135 func (m *Messenger) shouldResendMessage(message *common.RawMessage, t common.TimeSource) bool { 136 if m.featureFlags.ResendRawMessagesDisabled { 137 return false 138 } 139 //exponential backoff depends on how many attempts to send message already made 140 power := math.Pow(2, float64(message.SendCount-1)) 141 backoff := uint64(power) * uint64(m.config.messageResendMinDelay.Milliseconds()) 142 backoffElapsed := t.GetCurrentTime() > (message.LastSent + backoff) 143 144 return backoffElapsed 145 } 146 147 // pull a message from the database and send it again 148 func (m *Messenger) reSendRawMessage(ctx context.Context, messageID string) error { 149 message, err := m.persistence.RawMessageByID(messageID) 150 if err != nil { 151 return err 152 } 153 154 chat, ok := m.allChats.Load(message.LocalChatID) 155 if !ok { 156 return errors.New("chat not found") 157 } 158 159 _, err = m.dispatchMessage(ctx, common.RawMessage{ 160 LocalChatID: chat.ID, 161 Payload: message.Payload, 162 PubsubTopic: message.PubsubTopic, 163 MessageType: message.MessageType, 164 Recipients: message.Recipients, 165 ResendType: message.ResendType, 166 SendCount: message.SendCount, 167 }) 168 return err 169 } 170 171 // UpsertRawMessageToWatch insert/update the rawMessage to the database, resend it if necessary. 172 // relate watch method: Messenger#watchExpiredMessages 173 func (m *Messenger) UpsertRawMessageToWatch(rawMessage *common.RawMessage) (*common.RawMessage, error) { 174 rawMessage.SendCount++ 175 rawMessage.LastSent = m.getTimesource().GetCurrentTime() 176 err := m.persistence.SaveRawMessage(rawMessage) 177 if err != nil { 178 return nil, err 179 } 180 return rawMessage, nil 181 } 182 183 // AddRawMessageToWatch check if RawMessage is correct and insert the rawMessage to the database 184 // relate watch method: Messenger#watchExpiredMessages 185 func (m *Messenger) AddRawMessageToWatch(rawMessage *common.RawMessage) (*common.RawMessage, error) { 186 if err := m.sender.ValidateRawMessage(rawMessage); err != nil { 187 m.logger.Error("Can't add raw message to watch", zap.String("messageID", rawMessage.ID), zap.Error(err)) 188 return nil, err 189 } 190 191 return m.UpsertRawMessageToWatch(rawMessage) 192 } 193 194 func (m *Messenger) upsertRawMessageToWatch(rawMessage *common.RawMessage) { 195 _, err := m.UpsertRawMessageToWatch(rawMessage) 196 if err != nil { 197 // this is unlikely to happen, but we should log it 198 m.logger.Error("Can't upsert raw message after SendCommunityMessage", zap.Error(err), zap.String("id", rawMessage.ID)) 199 } 200 } 201 202 func (m *Messenger) RawMessagesIDsByType(t protobuf.ApplicationMetadataMessage_Type) ([]string, error) { 203 return m.persistence.RawMessagesIDsByType(t) 204 } 205 206 func (m *Messenger) RawMessageByID(id string) (*common.RawMessage, error) { 207 return m.persistence.RawMessageByID(id) 208 } 209 210 func (m *Messenger) UpdateRawMessageSent(id string, sent bool) error { 211 return m.persistence.UpdateRawMessageSent(id, sent) 212 } 213 214 func (m *Messenger) UpdateRawMessageLastSent(id string, lastSent uint64) error { 215 return m.persistence.UpdateRawMessageLastSent(id, lastSent) 216 }