From dffcdc3f51ccf392664ee40f97712fddc310a38e Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Wed, 20 Sep 2017 17:11:46 +0200 Subject: [PATCH] SONAR-7024 SQL optimization if multiple channels when checking permission --- .../DefaultNotificationManager.java | 93 +++++++++++++------ 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java b/server/sonar-server/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java index 52f9b80e441..20998f7e012 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java +++ b/server/sonar-server/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java @@ -20,16 +20,17 @@ package org.sonar.server.notification; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Multimap; -import com.google.common.collect.SetMultimap; import java.io.IOException; import java.io.InvalidClassException; import java.util.Arrays; -import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.sonar.api.notifications.Notification; import org.sonar.api.notifications.NotificationChannel; import org.sonar.api.utils.SonarException; @@ -120,27 +121,72 @@ public class DefaultNotificationManager implements NotificationManager { requireNonNull(projectUuid, "ProjectUUID is mandatory"); String dispatcherKey = dispatcher.getKey(); - SetMultimap recipients = HashMultimap.create(); - for (NotificationChannel channel : notificationChannels) { - String channelKey = channel.getKey(); - - // Find users subscribed globally to the dispatcher (i.e. not on a specific project) - // And users subscribed to the dispatcher specifically for the project - Set subscribedUsers = dbClient.propertiesDao().findUsersForNotification(dispatcherKey, channelKey, projectUuid); - - if (!subscribedUsers.isEmpty()) { - try (DbSession dbSession = dbClient.openSession(false)) { - Set filteredSubscribedUsers = dbClient.authorizationDao().keepAuthorizedLoginsOnProject( - dbSession, - subscribedUsers.stream().map(Subscriber::getLogin).collect(Collectors.toSet()), - projectUuid, - UserRole.USER); - addUsersToRecipientListForChannel(filteredSubscribedUsers, recipients, channel); - } + Set subscriberAndChannels = Arrays.stream(notificationChannels) + .flatMap(notificationChannel -> toSubscriberAndChannels(dispatcherKey, projectUuid, notificationChannel)) + .collect(Collectors.toSet()); + + if (subscriberAndChannels.isEmpty()) { + return ImmutableMultimap.of(); + } + + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + try (DbSession dbSession = dbClient.openSession(false)) { + Set authorizedLogins = keepAuthorizedLogins(projectUuid, subscriberAndChannels, dbSession); + subscriberAndChannels.stream() + .filter(subscriberAndChannel -> authorizedLogins.contains(subscriberAndChannel.getSubscriber().getLogin())) + .forEach(subscriberAndChannel -> builder.put(subscriberAndChannel.getSubscriber().getLogin(), subscriberAndChannel.getChannel())); + } + return builder.build(); + } + + private Stream toSubscriberAndChannels(String dispatcherKey, String projectUuid, NotificationChannel notificationChannel) { + return dbClient.propertiesDao().findUsersForNotification(dispatcherKey, notificationChannel.getKey(), projectUuid) + .stream() + .map(login -> new SubscriberAndChannel(login, notificationChannel)); + } + + private Set keepAuthorizedLogins(String projectUuid, Set subscriberAndChannels, DbSession dbSession) { + Set logins = subscriberAndChannels.stream() + .map(DefaultNotificationManager.SubscriberAndChannel::getSubscriber) + .map(Subscriber::getLogin) + .collect(Collectors.toSet()); + return dbClient.authorizationDao().keepAuthorizedLoginsOnProject(dbSession, logins, projectUuid, UserRole.USER); + } + + private final class SubscriberAndChannel { + private final Subscriber subscriber; + private final NotificationChannel channel; + + private SubscriberAndChannel(Subscriber subscriber, NotificationChannel channel) { + this.subscriber = subscriber; + this.channel = channel; + } + + public Subscriber getSubscriber() { + return subscriber; + } + + public NotificationChannel getChannel() { + return channel; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; } + SubscriberAndChannel that = (SubscriberAndChannel) o; + return Objects.equals(subscriber, that.subscriber) && + Objects.equals(channel, that.channel); } - return recipients; + @Override + public int hashCode() { + return Objects.hash(subscriber, channel); + } } @VisibleForTesting @@ -148,9 +194,4 @@ public class DefaultNotificationManager implements NotificationManager { return Arrays.asList(notificationChannels); } - private static void addUsersToRecipientListForChannel(Collection users, SetMultimap recipients, NotificationChannel channel) { - for (String username : users) { - recipients.put(username, channel); - } - } } -- 2.39.5