Allow sending messages offline (non-datasync messages)

This commit is contained in:
Roman Volosovskyi 2021-11-17 11:11:51 +02:00
parent 83c3849899
commit 3564630c33
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
5 changed files with 75 additions and 50 deletions

View File

@ -1 +1 @@
0.92.0
0.92.1

View File

@ -64,8 +64,8 @@ const (
privateChat chatContext = "private-chat"
)
const emojiResendMinDelay = 30
const emojiResendMaxCount = 3
const messageResendMinDelay = 30
const messageResendMaxCount = 3
var communityAdvertiseIntervalSecond int64 = 60 * 60
@ -140,9 +140,16 @@ func (interceptor EnvelopeEventsInterceptor) EnvelopeSent(identifiers [][]byte)
err := interceptor.Messenger.processSentMessages(ids)
if err != nil {
interceptor.Messenger.logger.Info("Messenger failed to process sent messages", zap.Error(err))
} else {
interceptor.EnvelopeEventsHandler.EnvelopeSent(identifiers)
}
} else {
// NOTE(rasom): In case if interceptor.Messenger is not nil and
// some error occurred on processing sent message we don't want
// to send envelop.sent signal to the client, thus `else` cause
// is necessary.
interceptor.EnvelopeEventsHandler.EnvelopeSent(identifiers)
}
interceptor.EnvelopeEventsHandler.EnvelopeSent(identifiers)
}
// EnvelopeExpired triggered when envelope is expired but wasn't delivered to any peer.
@ -404,6 +411,10 @@ func NewMessenger(
}
func (m *Messenger) processSentMessages(ids []string) error {
if m.connectionState.Offline {
return errors.New("Can't mark message as sent while offline")
}
for _, id := range ids {
rawMessage, err := m.persistence.RawMessageByID(id)
if err != nil {
@ -416,32 +427,42 @@ func (m *Messenger) processSentMessages(ids []string) error {
if err != nil {
return errors.Wrapf(err, "Can't save raw message marked as sent")
}
err = m.UpdateMessageOutgoingStatus(id, "sent")
if err != nil {
return err
}
}
return nil
}
func shouldResendEmojiReaction(message *common.RawMessage, t common.TimeSource) (bool, error) {
if message.MessageType != protobuf.ApplicationMetadataMessage_EMOJI_REACTION {
return false, errors.New("Should resend only emoji reactions")
func shouldResendMessage(message *common.RawMessage, t common.TimeSource) (bool, error) {
if !(message.MessageType == protobuf.ApplicationMetadataMessage_EMOJI_REACTION ||
message.MessageType == protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) {
return false, errors.Errorf("Should resend only specific types of messages, can't resend %v", message.MessageType)
}
if message.Sent {
return false, errors.New("Should resend only non-sent messages")
}
if message.SendCount > emojiResendMaxCount {
if message.SendCount > messageResendMaxCount {
return false, nil
}
//exponential backoff depends on how many attempts to send message already made
backoff := uint64(math.Pow(2, float64(message.SendCount-1))) * emojiResendMinDelay * uint64(time.Second)
backoff := uint64(math.Pow(2, float64(message.SendCount-1))) * messageResendMinDelay * uint64(time.Second.Milliseconds())
backoffElapsed := t.GetCurrentTime() > (message.LastSent + backoff)
return backoffElapsed, nil
}
func (m *Messenger) resendExpiredEmojiReactions() error {
ids, err := m.persistence.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
func (m *Messenger) resendExpiredMessages() error {
if m.connectionState.Offline {
return errors.New("offline")
}
ids, err := m.persistence.ExpiredMessagesIDs(messageResendMaxCount)
if err != nil {
return errors.Wrapf(err, "Can't get expired reactions from db")
}
@ -452,7 +473,21 @@ func (m *Messenger) resendExpiredEmojiReactions() error {
return errors.Wrapf(err, "Can't get raw message with id %v", id)
}
if ok, err := shouldResendEmojiReaction(rawMessage, m.getTimesource()); ok {
chat, ok := m.allChats.Load(rawMessage.LocalChatID)
if !ok {
return ErrChatNotFound
}
if !(chat.Public() || chat.CommunityChat()) {
return errors.New("Only public chats and community chats messages are resent")
}
ok, err = shouldResendMessage(rawMessage, m.getTimesource())
if err != nil {
return err
}
if ok {
err = m.persistence.SaveRawMessage(rawMessage)
if err != nil {
return errors.Wrapf(err, "Can't save raw message marked as non-expired")
@ -462,8 +497,6 @@ func (m *Messenger) resendExpiredEmojiReactions() error {
if err != nil {
return errors.Wrapf(err, "Can't resend expired message with id %v", rawMessage.ID)
}
} else {
return err
}
}
return nil
@ -524,7 +557,7 @@ func (m *Messenger) Start() (*MessengerResponse, error) {
m.handleConnectionChange(m.online())
m.handleENSVerificationSubscription(ensSubscription)
m.watchConnectionChange()
m.watchExpiredEmojis()
m.watchExpiredMessages()
m.watchIdentityImageChanges()
m.broadcastLatestUserStatus()
m.startBackupLoop()
@ -998,15 +1031,15 @@ func (m *Messenger) watchConnectionChange() {
}()
}
// watchExpiredEmojis regularly checks for expired emojis and invoke their resending
func (m *Messenger) watchExpiredEmojis() {
m.logger.Debug("watching expired emojis")
// watchExpiredMessages regularly checks for expired emojis and invoke their resending
func (m *Messenger) watchExpiredMessages() {
m.logger.Debug("watching expired messages")
go func() {
for {
select {
case <-time.After(time.Second):
if m.online() {
err := m.resendExpiredEmojiReactions()
err := m.resendExpiredMessages()
if err != nil {
m.logger.Debug("Error when resending expired emoji reactions", zap.Error(err))
}

View File

@ -2242,7 +2242,7 @@ func (s *MessengerSuite) TestLastSentField() {
func (s *MessengerSuite) TestShouldResendEmoji() {
// shouldn't try to resend non-emoji messages.
ok, err := shouldResendEmojiReaction(&common.RawMessage{
ok, err := shouldResendMessage(&common.RawMessage{
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE,
Sent: false,
SendCount: 2,
@ -2251,7 +2251,7 @@ func (s *MessengerSuite) TestShouldResendEmoji() {
s.False(ok)
// shouldn't try to resend already sent message
ok, err = shouldResendEmojiReaction(&common.RawMessage{
ok, err = shouldResendMessage(&common.RawMessage{
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
Sent: true,
SendCount: 1,
@ -2260,50 +2260,50 @@ func (s *MessengerSuite) TestShouldResendEmoji() {
s.False(ok)
// messages that already sent to many times shouldn't be resend
ok, err = shouldResendEmojiReaction(&common.RawMessage{
ok, err = shouldResendMessage(&common.RawMessage{
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
Sent: false,
SendCount: emojiResendMaxCount + 1,
SendCount: messageResendMaxCount + 1,
}, s.m.getTimesource())
s.NoError(err)
s.False(ok)
// message sent one time CAN'T be resend in 15 seconds (only after 30)
ok, err = shouldResendEmojiReaction(&common.RawMessage{
ok, err = shouldResendMessage(&common.RawMessage{
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
Sent: false,
SendCount: 1,
LastSent: s.m.getTimesource().GetCurrentTime() - 15*uint64(time.Second),
LastSent: s.m.getTimesource().GetCurrentTime() - 15*uint64(time.Second.Milliseconds()),
}, s.m.getTimesource())
s.NoError(err)
s.False(ok)
// message sent one time CAN be resend in 35 seconds
ok, err = shouldResendEmojiReaction(&common.RawMessage{
ok, err = shouldResendMessage(&common.RawMessage{
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
Sent: false,
SendCount: 1,
LastSent: s.m.getTimesource().GetCurrentTime() - 35*uint64(time.Second),
LastSent: s.m.getTimesource().GetCurrentTime() - 35*uint64(time.Second.Milliseconds()),
}, s.m.getTimesource())
s.NoError(err)
s.True(ok)
// message sent three times CAN'T be resend in 100 seconds (only after 120)
ok, err = shouldResendEmojiReaction(&common.RawMessage{
ok, err = shouldResendMessage(&common.RawMessage{
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
Sent: false,
SendCount: 3,
LastSent: s.m.getTimesource().GetCurrentTime() - 100*uint64(time.Second),
LastSent: s.m.getTimesource().GetCurrentTime() - 100*uint64(time.Second.Milliseconds()),
}, s.m.getTimesource())
s.NoError(err)
s.False(ok)
// message sent tow times CAN be resend in 65 seconds
ok, err = shouldResendEmojiReaction(&common.RawMessage{
ok, err = shouldResendMessage(&common.RawMessage{
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
Sent: false,
SendCount: 3,
LastSent: s.m.getTimesource().GetCurrentTime() - 125*uint64(time.Second),
LastSent: s.m.getTimesource().GetCurrentTime() - 125*uint64(time.Second.Milliseconds()),
}, s.m.getTimesource())
s.NoError(err)
s.True(ok)
@ -2359,7 +2359,7 @@ func (s *MessengerSuite) TestResendExpiredEmojis() {
s.Equal(1, rawMessage.SendCount)
//imitate that more than 30 seconds passed since message was sent
rawMessage.LastSent = rawMessage.LastSent - 35*uint64(time.Second)
rawMessage.LastSent = rawMessage.LastSent - 35*uint64(time.Second.Milliseconds())
err = s.m.persistence.SaveRawMessage(rawMessage)
s.NoError(err)
time.Sleep(2 * time.Second)

View File

@ -643,7 +643,7 @@ func (db sqlitePersistence) SaveContactChatIdentity(contactID string, chatIdenti
return
}
func (db sqlitePersistence) ExpiredEmojiReactionsIDs(maxSendCount int) ([]string, error) {
func (db sqlitePersistence) ExpiredMessagesIDs(maxSendCount int) ([]string, error) {
ids := []string{}
rows, err := db.db.Query(`
@ -652,8 +652,11 @@ func (db sqlitePersistence) ExpiredEmojiReactionsIDs(maxSendCount int) ([]string
FROM
raw_messages
WHERE
message_type = ? AND sent = ? AND send_count <= ?`,
protobuf.ApplicationMetadataMessage_EMOJI_REACTION, false, maxSendCount)
message_type IN (?, ?) AND sent = ? AND send_count <= ?`,
protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
false,
maxSendCount)
if err != nil {
return ids, err
}

View File

@ -533,23 +533,12 @@ func TestMessagesIDsByType(t *testing.T) {
require.Equal(t, "emoji-message-id", ids[0])
}
func TestExpiredEmojiReactionsIDs(t *testing.T) {
func TestExpiredMessagesIDs(t *testing.T) {
db, err := openTestDB()
require.NoError(t, err)
p := NewSQLitePersistence(db)
ids, err := p.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
require.NoError(t, err)
require.Empty(t, ids)
//save expired chat message
rawChatMessage := minimalRawMessage("chat-message-id", protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
rawChatMessage.Sent = false
err = p.SaveRawMessage(rawChatMessage)
require.NoError(t, err)
//make sure it doesn't apper in an expired emoji reactions list
ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
ids, err := p.ExpiredMessagesIDs(messageResendMaxCount)
require.NoError(t, err)
require.Empty(t, ids)
@ -560,7 +549,7 @@ func TestExpiredEmojiReactionsIDs(t *testing.T) {
require.NoError(t, err)
//make sure it appered in expired emoji reactions list
ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
ids, err = p.ExpiredMessagesIDs(messageResendMaxCount)
require.NoError(t, err)
require.Equal(t, 1, len(ids))
@ -571,7 +560,7 @@ func TestExpiredEmojiReactionsIDs(t *testing.T) {
require.NoError(t, err)
//make sure it didn't appear in expired emoji reactions list
ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount)
ids, err = p.ExpiredMessagesIDs(messageResendMaxCount)
require.NoError(t, err)
require.Equal(t, 1, len(ids))
}