Skip to content

Commit a9d7b15

Browse files
Merge pull request #1161 from nextcloud/techdebt/noid/group-queries-on-pushing
Group DB queries on pushing multiple notifications until we flush()
2 parents 002c1c5 + 7024894 commit a9d7b15

File tree

1 file changed

+113
-7
lines changed

1 file changed

+113
-7
lines changed

lib/Push.php

Lines changed: 113 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,24 @@ class Push {
7272
protected $output;
7373
/** @var array */
7474
protected $payloadsToSend = [];
75+
76+
/** @var bool */
77+
protected $deferPreparing = false;
7578
/** @var bool */
7679
protected $deferPayloads = false;
80+
/** @var array[] */
81+
protected $deletesToPush = [];
82+
/** @var INotification[] */
83+
protected $notificationsToPush = [];
84+
85+
/** @var null[]|IUserStatus[] */
86+
protected $userStatuses = [];
87+
/** @var array[] */
88+
protected $userDevices = [];
89+
/** @var string[] */
90+
protected $loadDevicesForUsers = [];
91+
/** @var string[] */
92+
protected $loadStatusForUsers = [];
7793

7894
public function __construct(IDBConnection $connection,
7995
INotificationManager $notificationManager,
@@ -112,10 +128,47 @@ public function isDeferring(): bool {
112128
}
113129

114130
public function deferPayloads(): void {
131+
$this->deferPreparing = true;
115132
$this->deferPayloads = true;
116133
}
117134

118135
public function flushPayloads(): void {
136+
$this->deferPreparing = false;
137+
138+
if (!empty($this->loadDevicesForUsers)) {
139+
$this->loadDevicesForUsers = array_unique($this->loadDevicesForUsers);
140+
$missingDevicesFor = array_diff($this->loadDevicesForUsers, array_keys($this->userDevices));
141+
$newUserDevices = $this->getDevicesForUsers($missingDevicesFor);
142+
foreach ($missingDevicesFor as $userId) {
143+
$this->userDevices[$userId] = $newUserDevices[$userId] ?? [];
144+
}
145+
$this->loadDevicesForUsers = [];
146+
}
147+
148+
if (!empty($this->loadStatusForUsers)) {
149+
$this->loadStatusForUsers = array_unique($this->loadStatusForUsers);
150+
$missingStatusFor = array_diff($this->loadStatusForUsers, array_keys($this->userStatuses));
151+
$newUserStatuses = $this->userStatusManager->getUserStatuses($missingStatusFor);
152+
foreach ($missingStatusFor as $userId) {
153+
$this->userStatuses[$userId] = $newUserStatuses[$userId] ?? null;
154+
}
155+
$this->loadStatusForUsers = [];
156+
}
157+
158+
if (!empty($this->notificationsToPush)) {
159+
foreach ($this->notificationsToPush as $id => $notification) {
160+
$this->pushToDevice($id, $notification);
161+
}
162+
$this->notificationsToPush = [];
163+
}
164+
165+
if (!empty($this->deletesToPush)) {
166+
foreach ($this->deletesToPush as $id => $data) {
167+
$this->pushDeleteToDevice($data['userId'], $id, $data['app']);
168+
}
169+
$this->deletesToPush = [];
170+
}
171+
119172
$this->deferPayloads = false;
120173
$this->sendNotificationsToProxies();
121174
}
@@ -154,21 +207,38 @@ public function pushToDevice(int $id, INotification $notification, ?OutputInterf
154207
return;
155208
}
156209

210+
if ($this->deferPreparing) {
211+
$this->notificationsToPush[$id] = clone $notification;
212+
$this->loadDevicesForUsers[] = $notification->getUser();
213+
$this->loadStatusForUsers[] = $notification->getUser();
214+
return;
215+
}
216+
157217
$user = $this->createFakeUserObject($notification->getUser());
158218

159-
$userStatus = $this->userStatusManager->getUserStatuses([
160-
$notification->getUser(),
161-
]);
219+
if (!array_key_exists($notification->getUser(), $this->userStatuses)) {
220+
$userStatus = $this->userStatusManager->getUserStatuses([
221+
$notification->getUser(),
222+
]);
162223

163-
if (isset($userStatus[$notification->getUser()])) {
164-
$userStatus = $userStatus[$notification->getUser()];
224+
$this->userStatuses[$notification->getUser()] = $userStatus[$notification->getUser()] ?? null;
225+
}
226+
227+
if (isset($this->userStatuses[$notification->getUser()])) {
228+
$userStatus = $this->userStatuses[$notification->getUser()];
165229
if ($userStatus->getStatus() === IUserStatus::DND) {
166230
$this->printInfo('<error>User status is set to DND - no push notifications will be sent</error>');
167231
return;
168232
}
169233
}
170234

171-
$devices = $this->getDevicesForUser($notification->getUser());
235+
if (!array_key_exists($notification->getUser(), $this->userDevices)) {
236+
$devices = $this->getDevicesForUser($notification->getUser());
237+
$this->userDevices[$notification->getUser()] = $devices;
238+
} else {
239+
$devices = $this->userDevices[$notification->getUser()];
240+
}
241+
172242
if (empty($devices)) {
173243
$this->printInfo('No devices found for user');
174244
return;
@@ -237,9 +307,21 @@ public function pushDeleteToDevice(string $userId, int $notificationId, string $
237307
return;
238308
}
239309

310+
if ($this->deferPreparing) {
311+
$this->deletesToPush[$notificationId] = ['userId' => $userId, 'app' => $app];
312+
$this->loadDevicesForUsers[] = $userId;
313+
return;
314+
}
315+
240316
$user = $this->createFakeUserObject($userId);
241317

242-
$devices = $this->getDevicesForUser($userId);
318+
if (!array_key_exists($userId, $this->userDevices)) {
319+
$devices = $this->getDevicesForUser($userId);
320+
$this->userDevices[$userId] = $devices;
321+
} else {
322+
$devices = $this->userDevices[$userId];
323+
}
324+
243325
if ($notificationId !== 0 && $app !== '') {
244326
// Only filter when it's not a single delete
245327
$devices = $this->filterDeviceList($devices, $app);
@@ -516,6 +598,30 @@ protected function getDevicesForUser(string $uid): array {
516598
return $devices;
517599
}
518600

601+
/**
602+
* @param string[] $userIds
603+
* @return array[]
604+
*/
605+
protected function getDevicesForUsers(array $userIds): array {
606+
$query = $this->db->getQueryBuilder();
607+
$query->select('*')
608+
->from('notifications_pushhash')
609+
->where($query->expr()->in('uid', $query->createNamedParameter($userIds, IQueryBuilder::PARAM_STR_ARRAY)));
610+
611+
$devices = [];
612+
$result = $query->executeQuery();
613+
while ($row = $result->fetch()) {
614+
if (!isset($devices[$row['uid']])) {
615+
$devices[$row['uid']] = [];
616+
}
617+
$devices[$row['uid']][] = $row;
618+
}
619+
620+
$result->closeCursor();
621+
622+
return $devices;
623+
}
624+
519625
/**
520626
* @param int $tokenId
521627
* @return bool

0 commit comments

Comments
 (0)