Blogging Prompts: Fix Core Data concurrency issues in scheduler#20854
Blogging Prompts: Fix Core Data concurrency issues in scheduler#20854dvdchr merged 8 commits intorelease/22.6from
Conversation
…ion settings UNNotificationCenter's `getNotificationSettings` method does not guarantee that the completion block is called in the original queue. This ensures that the completion block code that touches Core Data is run within the context's queue, as obtained from the managed object.
Generated by 🚫 dangerJS |
|
| App Name | Jetpack Alpha |
|
| Configuration | Release-Alpha | |
| Build Number | pr20854-a9d5782 | |
| Version | 22.6 | |
| Bundle ID | com.jetpack.alpha | |
| Commit | a9d5782 | |
| App Center Build | jetpack-installable-builds #5025 |
|
| App Name | WordPress Alpha |
|
| Configuration | Release-Alpha | |
| Build Number | pr20854-a9d5782 | |
| Version | 22.6 | |
| Bundle ID | org.wordpress.alpha | |
| Commit | a9d5782 | |
| App Center Build | WPiOS - One-Offs #5997 |
crazytonyli
left a comment
There was a problem hiding this comment.
Pattern like blog.managedObjectContext.perform { doSomething(with: blog)} feels like a patch to me. We probably should use it sparsely. Because there is no guarantee the doSomething function only uses the passed-in blog instance in its calling thread.
For instance, a couple of call sites of processSchedule is changed to be called from the blog context thread. But the function itself accesses the blog in an API callback which I believe is called from the main thread.
I think, yes, this change would fix the particular crash shown in #20848, but it may cause other issues.
One solution on top of my head is refactoring the Blog reminder classes to not use Blog type. It can declare a value type struct BlogData (with a better name probably), which includes a few property the Blog Reminder component really cares about: objectID, site id, titile, etc. So that it's not restricted by Core Data's programming model, but also has the capability to change the Blog model with the objectID if needed.
WordPress/Classes/Utility/Blogging Prompts/PromptRemindersScheduler.swift
Show resolved
Hide resolved
Refactor `addLocalNotification` and `addStaticNotifications` to operate with value types instead of dealing with `Blog` directly. There are still loose property reads to `Blog` within the method, but `processSchedule` is private and its callers are guaranteeing that the method is called from the Blog's context queue. Further improvements will be made in the next iteration.
|
Thanks for the input, @crazytonyli!
Great idea, and I agree. I was considering this yesterday but realized that there are going to be a lot of changes. Since I'm targeting 22.6 (which is in code freeze), I'm thinking of keeping the impact of this PR minimal and doing a larger refactor separately in the next iteration. However, it doesn't make sense if the improvement is flawed; as you've pointed out:
Thanks for catching that. To address this, I've made some improvements to the In the (near) future, the larger refactor will remove the Anyways, can you give this a second pass, please? Thank you! |
crazytonyli
left a comment
There was a problem hiding this comment.
Thanks for addressing my feedback. This PR looks good to me. 👍
It would be nice to see a proper refactor done to the Blog Reminder part of the app soon though 😺
| maxDays > 0, | ||
| let maxDate = Calendar.current.date(byAdding: .day, value: maxDays, to: afterDate), | ||
| blog.dotComID != nil else { | ||
| let maxDate = Calendar.current.date(byAdding: .day, value: maxDays, to: afterDate) else { |
There was a problem hiding this comment.
Not related to this PR. But I didn't realise that Calendar.current—the calendar that user picked in the device's System Settings—is used a lot in the app. In most cases, we want the Gregorian calendar, not the calendar the user likes their phone to display.
There was a problem hiding this comment.
Hmm, yeah, I think I didn't really think about it and just followed how it's done across the codebase. I'll consider fixing this during the refactor later. Thanks!
…prompts-scheduler-concurrency
|
@crazytonyli I ended up here while looking into something else, just wanted to say that this sounds like a great idea:
|


Fixes #20842
Description
When scheduling or rescheduling prompts, the related methods make a call to
UNNotificationCenterto either request for push authorization or fetch notification settings. However, the notification center does not guarantee that the completion block will be executed in the caller's queue. Since the completion block code involves Core Data, this raises a multithreading violation when run in debug mode.This PR fixes the issue by wrapping the Core Data-related code within these methods in the managed object context's queue.
Additionally, this also improves the thread-safety of
BloggingPromptsService's init method by ensuring that any access to Core Data properties is performed within that object's context queue.To test
-com.apple.CoreData.ConcurrencyDebug 1launch argument.Additionally, please also smoke test Blogging Reminders — creating, updating, or removing schedules; with and without prompts.
Regression Notes
Potential unintended areas of impact
Should be none. The logic is kept as close to the original as possible to minimize impact.
What I did to test those areas of impact (or what existing automated tests I relied on)
Manually tested the changes, and the existing unit tests are passing.
What automated tests I added (or what prevented me from doing so)
N/A
PR submission checklist:
RELEASE-NOTES.txtif necessary.UI Changes testing checklist:
N/A — this PR doesn't contain UI-related changes.