Skip to content

Commit 52fbc5f

Browse files
Merge pull request #12303 from nextcloud/perf/noid/improved-call-notifications
fix(notifications): Preparse call notifications for improved performance
2 parents c4e027d + a0f9ceb commit 52fbc5f

File tree

2 files changed

+61
-13
lines changed

2 files changed

+61
-13
lines changed

lib/Notification/Listener.php

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
use OCP\EventDispatcher\Event;
2525
use OCP\EventDispatcher\IEventDispatcher;
2626
use OCP\EventDispatcher\IEventListener;
27+
use OCP\IConfig;
2728
use OCP\IDBConnection;
2829
use OCP\IUser;
2930
use OCP\IUserSession;
3031
use OCP\Notification\IManager;
32+
use OCP\Notification\INotification;
3133
use Psr\Log\LoggerInterface;
3234

3335
/**
@@ -36,10 +38,14 @@
3638
class Listener implements IEventListener {
3739

3840
protected bool $shouldSendCallNotification = false;
41+
/** @var array<string, INotification> $preparedCallNotifications Map of language => parsed notification in that language */
42+
protected array $preparedCallNotifications = [];
3943

4044
public function __construct(
45+
protected IConfig $serverConfig,
4146
protected IDBConnection $connection,
4247
protected IManager $notificationManager,
48+
protected Notifier $notificationProvider,
4349
protected ParticipantService $participantsService,
4450
protected IEventDispatcher $dispatcher,
4551
protected IUserSession $userSession,
@@ -273,17 +279,50 @@ protected function sendCallNotifications(Room $room): void {
273279
return;
274280
}
275281

282+
$this->preparedCallNotifications = [];
276283
$userIds = $this->participantsService->getParticipantUserIdsForCallNotifications($room);
284+
// Room name depends on the notification user for one-to-one,
285+
// so we avoid pre-parsing it there. Also, it comes with some base load,
286+
// so we only do it for "big enough" calls.
287+
$preparseNotificationForPush = count($userIds) > 10;
288+
if ($preparseNotificationForPush) {
289+
$fallbackLang = $this->serverConfig->getSystemValue('force_language', null);
290+
if (is_string($fallbackLang)) {
291+
/** @psalm-var array<string, string> $userLanguages */
292+
$userLanguages = [];
293+
} else {
294+
$fallbackLang = $this->serverConfig->getSystemValueString('default_language', 'en');
295+
/** @psalm-var array<string, string> $userLanguages */
296+
$userLanguages = $this->serverConfig->getUserValueForUsers('core', 'lang', $userIds);
297+
}
298+
}
299+
277300
$this->connection->beginTransaction();
278301
try {
279302
foreach ($userIds as $userId) {
280303
if ($actorId === $userId) {
281304
continue;
282305
}
283306

307+
if ($preparseNotificationForPush) {
308+
// Get the settings for this particular user, then check if we have notifications to email them
309+
$languageCode = $userLanguages[$userId] ?? $fallbackLang;
310+
311+
if (!isset($this->preparedCallNotifications[$languageCode])) {
312+
$translatedNotification = clone $notification;
313+
314+
$this->notificationManager->setPreparingPushNotification(true);
315+
$this->preparedCallNotifications[$languageCode] = $this->notificationProvider->prepare($translatedNotification, $languageCode);
316+
$this->notificationManager->setPreparingPushNotification(false);
317+
}
318+
$userNotification = $this->preparedCallNotifications[$languageCode];
319+
} else {
320+
$userNotification = $notification;
321+
}
322+
284323
try {
285-
$notification->setUser($userId);
286-
$this->notificationManager->notify($notification);
324+
$userNotification->setUser($userId);
325+
$this->notificationManager->notify($userNotification);
287326
} catch (\InvalidArgumentException $e) {
288327
$this->logger->error($e->getMessage(), ['exception' => $e]);
289328
}

lib/Notification/Notifier.php

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,12 @@ public function prepare(INotification $notification, string $languageCode): INot
184184
throw new UnknownNotificationException('Incorrect app');
185185
}
186186

187-
$userId = $notification->getUser();
188-
$user = $this->userManager->get($userId);
189-
if (!$user instanceof IUser || $this->config->isDisabledForUser($user)) {
190-
throw new AlreadyProcessedException();
187+
if (!($this->notificationManager->isPreparingPushNotification() && $notification->getSubject() === 'call')) {
188+
$userId = $notification->getUser();
189+
$user = $this->userManager->get($userId);
190+
if (!$user instanceof IUser || $this->config->isDisabledForUser($user)) {
191+
throw new AlreadyProcessedException();
192+
}
191193
}
192194

193195
$l = $this->lFactory->get(Application::APP_ID, $languageCode);
@@ -204,20 +206,27 @@ public function prepare(INotification $notification, string $languageCode): INot
204206
return $this->parseCertificateExpiration($notification, $l);
205207
}
206208

207-
try {
208-
$room = $this->getRoom($notification->getObjectId(), $userId);
209-
} catch (RoomNotFoundException $e) {
210-
// Room does not exist
211-
throw new AlreadyProcessedException();
212-
}
213-
214209
if ($this->notificationManager->isPreparingPushNotification() && $notification->getSubject() === 'call') {
210+
try {
211+
$room = $this->manager->getRoomByToken($notification->getObjectId());
212+
} catch (RoomNotFoundException) {
213+
// Room does not exist
214+
throw new AlreadyProcessedException();
215+
}
216+
215217
// Skip the participant check when we generate push notifications
216218
// we just looped over the participants to create the notification,
217219
// they can not be removed between these 2 steps, but we can save
218220
// n queries.
219221
$participant = null;
220222
} else {
223+
try {
224+
$room = $this->getRoom($notification->getObjectId(), $userId);
225+
} catch (RoomNotFoundException $e) {
226+
// Room does not exist
227+
throw new AlreadyProcessedException();
228+
}
229+
221230
try {
222231
$participant = $this->getParticipant($room, $userId);
223232
} catch (ParticipantNotFoundException $e) {

0 commit comments

Comments
 (0)