Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion pkg/connector/backfill.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,29 @@ func (m *MetaClient) wrapBackfillEvents(ctx context.Context, portal *bridgev2.Po
if anchor != nil {
if forward {
upsert.Messages = slices.DeleteFunc(upsert.Messages, func(message *table.WrappedMessage) bool {
return message.TimestampMs <= anchor.Timestamp.UnixMilli()
if message.TimestampMs > anchor.Timestamp.UnixMilli() {
return false
}
// This message is older than the portal's latest bridged message, so it would
// normally be assumed to already exist. However, Meta can skip threads in a
// reconnect snapshot while advancing the sync cursor past their messages (see
// PLAT-36990), which leaves permanent mid-timeline gaps. Only drop the message
// if it actually exists in the bridge database, otherwise recover it.
existing, err := m.Main.Bridge.DB.Message.GetFirstPartByID(ctx, m.UserLogin.ID, metaid.MakeFBMessageID(message.MessageId))
if err != nil {
zerolog.Ctx(ctx).Err(err).
Str("message_id", message.MessageId).
Msg("Failed to check message existence during forward backfill dedup, dropping")
return true
}
if existing == nil {
zerolog.Ctx(ctx).Warn().
Str("message_id", message.MessageId).
Int64("timestamp_ms", message.TimestampMs).
Msg("Recovering pre-anchor message missing from database in forward backfill")
return false
}
return true
})
} else {
upsert.Messages = slices.DeleteFunc(upsert.Messages, func(message *table.WrappedMessage) bool {
Expand Down
26 changes: 21 additions & 5 deletions pkg/messagix/syncManager.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,12 @@ func (sm *SyncManager) SyncSocketData(ctx context.Context, databaseID int64, db
resp.Finish()

if len(resp.Table.LSHandleSyncFailure) > 0 {
// TODO handle these somehow?
sm.client.Logger.Warn().
// These failures mean the server-side sync for the database errored and no
// cursor was obtained, so the database stays unsynced until the next attempt.
// A common cause is empty sync params (handled in getSyncParams, see
// PLAT-36990); log at error level with the database ID so it's visible.
sm.client.Logger.Error().
Int64("database_id", databaseID).
Any("sync_failures", resp.Table.LSHandleSyncFailure).
Msg("Sync failures found")
}
Expand Down Expand Up @@ -236,18 +240,30 @@ func (sm *SyncManager) UpdateDatabaseSyncParams(dbs []*socket.QueryMetadata) err

var dbID7Params = `{"mnet_rank_types":[44]}`

// defaultLocaleSyncParams is the fallback sync params payload used when the page
// config doesn't provide LSPlatformMessengerSyncParams. messenger-lite sessions
// don't include these, so an empty string would make Meta's server fail with
// "Invalid argument supplied for foreach()" (see PLAT-36990), permanently
// breaking the affected sync database (e.g. Contact sync on database 2).
var defaultLocaleSyncParams = `{"locale":"en_US"}`

func (sm *SyncManager) getSyncParams(dbID int64, ch socket.SyncChannel) *string {
if dbID == 7 {
return &dbID7Params
}
var params *string
switch ch {
case socket.MailBox:
return &sm.syncParams.Mailbox
params = &sm.syncParams.Mailbox
case socket.Contact:
return &sm.syncParams.Contact
params = &sm.syncParams.Contact
default:
return &sm.syncParams.E2Ee
params = &sm.syncParams.E2Ee
}
if params == nil || *params == "" {
return &defaultLocaleSyncParams
}
return params
}

func (sm *SyncManager) GetCursor(db int64) string {
Expand Down
Loading