| @@ -4,8 +4,9 @@ | ||
| 4 | 4 | |
| 5 | 5 | use FluentCommunity\App\Functions\Utility; |
| 6 | 6 | use FluentCommunity\App\Models\NotificationPreference; |
| 7 | 7 | use FluentCommunity\App\Models\Space; |
| 8 | +use FluentCommunity\Database\Migrations\NotificationPrefMigrator; | |
| 8 | 9 | use FluentCommunity\Framework\Support\Arr; |
| 9 | 10 | |
| 10 | 11 | /** |
| 11 | 12 | * Read/write layer over a member's notification preferences. |
| @@ -157,8 +158,23 @@ | ||
| 157 | 158 | return $prefs; |
| 158 | 159 | } |
| 159 | 160 | |
| 160 | 161 | /** |
| 162 | + * Is fcom_notification_prefs the whole truth yet? | |
| 163 | + * | |
| 164 | + * False while the backfill still has rows to copy. On a large site that is a | |
| 165 | + * normal state, not an error one: maybeBackfillFromLegacy() gives up after 15 | |
| 166 | + * seconds and resumes through Action Scheduler, so the table can sit partly | |
| 167 | + * filled for minutes while the migration is working correctly. | |
| 168 | + * | |
| 169 | + * @return bool | |
| 170 | + */ | |
| 171 | + private static function backfillIsComplete() | |
| 172 | + { | |
| 173 | + return (bool)get_option(NotificationPrefMigrator::DONE_OPTION); | |
| 174 | + } | |
| 175 | + | |
| 176 | + /** | |
| 161 | 177 | * Load the flat pref arrays for many members in one query. |
| 162 | 178 | * |
| 163 | 179 | * @param array $userIds |
| 164 | 180 | * @return array user id => [flat key => value] |
| @@ -509,9 +525,23 @@ | ||
| 509 | 525 | ->where('value', 1) |
| 510 | 526 | ->exists(); |
| 511 | 527 | } |
| 512 | 528 | |
| 513 | - update_option(self::AGGREGATE_OPTION, $aggregates, false); | |
| 529 | + /* | |
| 530 | + * Persisted only once the table is whole. | |
| 531 | + * | |
| 532 | + * This option is a cache with no expiry and one writer - the preference | |
| 533 | + * write path - so whatever lands here is not revisited until some member | |
| 534 | + * happens to save their preferences. Computed mid-backfill it says "nobody | |
| 535 | + * has the digest on", and Scheduler::checkDailyDigestSchedule() unschedules | |
| 536 | + * the digest on that answer - an unschedule that would then outlive the | |
| 537 | + * migration that made it wrong. Skipping the write costs one indexed | |
| 538 | + * EXISTS per call for the duration of the backfill; markComplete() clears | |
| 539 | + * the option, so the first read afterwards recomputes and stores. | |
| 540 | + */ | |
| 541 | + if (self::backfillIsComplete()) { | |
| 542 | + update_option(self::AGGREGATE_OPTION, $aggregates, false); | |
| 543 | + } | |
| 514 | 544 | |
| 515 | 545 | return $aggregates; |
| 516 | 546 | } |
| 517 | 547 | } |