Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix reading the local database state just after the initial write #3373

Merged
merged 5 commits into from
Aug 15, 2024

Conversation

laevandus
Copy link
Contributor

@laevandus laevandus commented Aug 13, 2024

🔗 Issue Links

Resolves PBE-5519

🎯 Goal

  • Enable reading the local database state within synchronize()'s completion block when the initial DB state was empty

📝 Summary

  • Remove the additional OperationQueue from the BackgroundDatabaseObserver (less latency)
  • Process DB changes immediately on the managed context queue (less latency)
  • Fix an issue where the initial observer state was set to empty although data was just written to the DB
  • Fix a timing issue with will change callback what returned changed data (timing related)

🛠 Implementation

BackgroundDatabaseObserver tries to load the initial state as soon as observing starts using a background thread (less blocking of the main thread). Since it is an async operation, there is a data race between loading the initial state and the managed object context changing at the same time. For avoiding it, the observer keeps an internal state to figure out when it can use the cached items array. This internal state was incorrect if the DB was empty and there just was a DB write as well. The observer's implementation is similar to the StateLayerDatabaseObserver now where we use NSManagedObjectContext's thread for processing and accessing the state (better data consistency).

Account Chewbacca in the demo app:

// ComposerVC.swift:925
    quotedMessageId: content.quotingMessage?.id,
    skipEnrichUrl: content.skipEnrichUrl,
    extraData: content.extraData
  )
  test()
}

func test() {
  guard let chatClient = channelController?.client else { return }
  let identifier = ChannelId(type: .messaging, id: "D65B357E-8")
  let query = ChannelQuery(cid: identifier, pageSize: 20)
  let channelController = chatClient.channelController(for: query)
        
  channelController.synchronize { error in
    if let error {
        log.error("Failed to get messages: \(error)")
        return
     }
     // The last 20 messages should be in channelController.messages
     let messages = channelController.messages
     print(messages.count)
  }
}

🧪 Manual Testing Notes

Account: Chewbacca

  1. Paste that snippet to somewhere which gets triggered after connecting (I used ComposerVC.swift line 925 to call this snippet, this is triggered when sending a message)
  2. Logout and login with Chewbacca
  3. Send a message which triggers the snippet
  4. Observe the message count, it should return 20 (checking console log, best is to put a breakpoint after the print)

Exploratory testing (because DB observers affect all the controllers)

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change should be manually QAed
  • Changelog is updated with client-facing changes
  • Changelog is updated with new localization keys
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (docusaurus, tutorial, CMS)

@laevandus laevandus added 🐞 Bug An issue or PR related to a bug 🌐 SDK: StreamChat (LLC) Tasks related to the StreamChat LLC SDK 🤞 Ready For QA A PR that is Ready for QA labels Aug 13, 2024
@laevandus laevandus requested a review from a team as a code owner August 13, 2024 12:08
@laevandus laevandus force-pushed the fix/reading-db-state-after-initial-write branch from b6511ca to eef353b Compare August 13, 2024 12:10
@Stream-SDK-Bot
Copy link
Collaborator

Stream-SDK-Bot commented Aug 13, 2024

SDK Size

title develop branch diff status
StreamChat 6.73MB 6.73MB 0.0MB 🟢
StreamChatUI 4.41MB 4.41MB 0.0MB 🟢

Comment on lines -57 to -60
let expectation1 = expectation(description: "onWillChange is called")
observer.onWillChange = {
expectation1.fulfill()
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because data is accessible as soon as startObserving is called, I removed the will change delegate because it does not really make sense anymore (we allow immediate access). I kept the didChange because we still do async loading.

@laevandus laevandus force-pushed the fix/reading-db-state-after-initial-write branch from eef353b to a768177 Compare August 13, 2024 12:18
@Stream-SDK-Bot
Copy link
Collaborator

SDK Performance

target metric benchmark branch performance status
MessageList Hitches total duration 10 ms 0.0 ms 100.0% 🔼 🟢
Duration 2.6 s 2.56 s 1.54% 🔼 🟢
Hitch time ratio 4 ms per s 0.0 ms per s 100.0% 🔼 🟢
Frame rate 75 fps 78.18 fps 4.24% 🔼 🟢
Number of hitches 1 0.0 100.0% 🔼 🟢
ChannelList Hitches total duration 12.5 ms 27.53 ms -120.24% 🔽 🔴
Duration 2.6 s 2.54 s 2.31% 🔼 🟢
Hitch time ratio 5 ms per s 10.83 ms per s -116.6% 🔽 🔴
Frame rate 72 fps 73.89 fps 2.63% 🔼 🟢
Number of hitches 1.2 2.4 -100.0% 🔽 🔴

@Stream-SDK-Bot
Copy link
Collaborator

Stream-SDK-Bot commented Aug 13, 2024

SDK Size

title develop branch diff status
StreamChat 6.76 MB 6.75 MB -18 KB 🚀
StreamChatUI 4.41 MB 4.41 MB 0 KB 🟢

@laevandus laevandus force-pushed the fix/reading-db-state-after-initial-write branch from a768177 to cc447e1 Compare August 13, 2024 12:59
Copy link
Member

@nuno-vieira nuno-vieira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Wise looks good. But we need to make sure the benchmark passes and also we should do some smoke testing CC @testableapple

CHANGELOG.md Show resolved Hide resolved
Copy link
Contributor

@martinmitrevski martinmitrevski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good ✅ Please check the failed performance benchmarks before merging

@laevandus laevandus force-pushed the fix/reading-db-state-after-initial-write branch from cc447e1 to 44b2f48 Compare August 14, 2024 08:02
@testableapple testableapple added 🧪 QAing and removed 🤞 Ready For QA A PR that is Ready for QA labels Aug 14, 2024
Copy link

@laevandus
Copy link
Contributor Author

Manually triggered performance job here

@testableapple testableapple added 🟢 QAed A PR that was QAed and removed 🧪 QAing labels Aug 15, 2024
@laevandus laevandus merged commit e40df65 into develop Aug 15, 2024
15 of 16 checks passed
@laevandus laevandus deleted the fix/reading-db-state-after-initial-write branch August 15, 2024 10:49
@Stream-SDK-Bot Stream-SDK-Bot mentioned this pull request Aug 15, 2024
glennposadas added a commit to fotonotes/stream-chat-swift that referenced this pull request Sep 7, 2024
* main: (154 commits)
  Unblock release on workflow dispatch (GetStream#3389)
  [CI] Run release action on workflow dispatch (GetStream#3386)
  [CI] Do not unshallow branches on CI
  [CI] Do not unshallow branches on CI
  [CI] Update git config on merging develop and release branches
  [CI] Update git config on merging develop and release branches
  [CI] Pull origin release branch on release
  [CI] Pull origin release branch on release
  [CI] Fetch all branches on release (GetStream#3385)
  [CI] Fetch all branches on release (GetStream#3385)
  Bump 4.62.0
  Store pending message along with regular messages (GetStream#3378)
  [CI] Set up a default base branch when automatically creating PRs (GetStream#3383)
  Fix reading the local database state just after the initial write (GetStream#3373)
  [CI] Add space to SDK size badge (GetStream#3379)
  Update channel type documentation to reflect allowed characters (GetStream#3377)
  Reuse CurrentChatUserController instead of recreating it when sending typing events (GetStream#3372)
  Run offline state sync in the background (GetStream#3367)
  [CI] Bump fastlane plugin version (GetStream#3376)
  [CI] Bump fastlane actions plugin (GetStream#3375)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐞 Bug An issue or PR related to a bug 🟢 QAed A PR that was QAed 🌐 SDK: StreamChat (LLC) Tasks related to the StreamChat LLC SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants