Allow sending messages offline (non-datasync messages)
This commit is contained in:
parent
83c3849899
commit
3564630c33
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue