diff options
author | Luca Milanesio <luca.milanesio@gmail.com> | 2022-04-13 00:03:47 +0100 |
---|---|---|
committer | Luca Milanesio <luca.milanesio@gmail.com> | 2022-04-29 13:05:22 +0000 |
commit | 71e4ce3d069507f6186e1112112ed2bfd45fbccd (patch) | |
tree | 8efd8e90a923c02bd7ccae50334eeff1bd4851e4 | |
parent | b8e9d684c28402d9888573d5ea79a1985c1b3253 (diff) |
Set PerThreadCache as readonly when formatting change e-mails
Apply the same optimisation made for Change-Id: I1b71bfcf to
the formatting the change e-mails body, which is done
synchronously to the incoming REST API and thus impacts
the latency perceived by the end user.
The composition of the e-mail is using multiple information
coming from multiple fields from NoteDb and calls the /meta
refs lookups multiple times for the same change.
The same considerations of performance improvement and reduction
of /meta refs lookup are valid in this use-case.
Release-Notes: enable the read-only cache of /meta when producing JSON
Change-Id: I993d4ddc37cf27b66ea9d7323963e761564603fa
3 files changed, 128 insertions, 118 deletions
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java index 924ab2def9..e0378741e9 100644 --- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java +++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java @@ -85,7 +85,7 @@ public abstract class ChangeEmail extends NotificationEmail { protected boolean emailOnlyAuthors; protected ChangeEmail(EmailArguments ea, String mc, ChangeData cd) throws OrmException { - super(ea, mc, cd.change().getDest()); + super(ea, mc, cd); changeData = cd; change = cd.change(); emailOnlyAuthors = false; diff --git a/java/com/google/gerrit/server/mail/send/NotificationEmail.java b/java/com/google/gerrit/server/mail/send/NotificationEmail.java index be593ff9f2..09c6bf7acb 100644 --- a/java/com/google/gerrit/server/mail/send/NotificationEmail.java +++ b/java/com/google/gerrit/server/mail/send/NotificationEmail.java @@ -24,7 +24,10 @@ import com.google.gerrit.mail.MailHeader; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.server.account.ProjectWatches.NotifyType; +import com.google.gerrit.server.cache.PerThreadCache; +import com.google.gerrit.server.cache.PerThreadCache.ReadonlyRequestWindow; import com.google.gerrit.server.mail.send.ProjectWatch.Watchers; +import com.google.gerrit.server.query.change.ChangeData; import com.google.gwtorm.server.OrmException; import java.util.HashMap; import java.util.Map; @@ -35,9 +38,11 @@ public abstract class NotificationEmail extends OutgoingEmail { protected Branch.NameKey branch; - protected NotificationEmail(EmailArguments ea, String mc, Branch.NameKey branch) { + protected NotificationEmail(EmailArguments ea, String mc, ChangeData cd) throws OrmException { super(ea, mc); - this.branch = branch; + try (ReadonlyRequestWindow window = PerThreadCache.openReadonlyRequestWindow()) { + this.branch = cd.change().getDest(); + } } @Override diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java index cd6882fc0e..608057309b 100644 --- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java +++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java @@ -35,6 +35,8 @@ import com.google.gerrit.mail.MailHeader; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.UserIdentity; import com.google.gerrit.server.account.AccountState; +import com.google.gerrit.server.cache.PerThreadCache; +import com.google.gerrit.server.cache.PerThreadCache.ReadonlyRequestWindow; import com.google.gerrit.server.permissions.PermissionBackendException; import com.google.gerrit.server.validators.OutgoingEmailValidationListener; import com.google.gerrit.server.validators.ValidationException; @@ -99,138 +101,141 @@ public abstract class OutgoingEmail { * @throws EmailException */ public void send() throws EmailException { - if (!args.emailSender.isEnabled()) { - // Server has explicitly disabled email sending. - // - logger.atFine().log( - "Not sending '%s': Email sending is disabled by server config", messageClass); - return; - } + try (ReadonlyRequestWindow window = PerThreadCache.openReadonlyRequestWindow()) { + if (!args.emailSender.isEnabled()) { + // Server has explicitly disabled email sending. + // + logger.atFine().log( + "Not sending '%s': Email sending is disabled by server config", messageClass); + return; + } - if (NotifyHandling.NONE.equals(notify) && accountsToNotify.isEmpty()) { - logger.atFine().log("Not sending '%s': Notify handling is NONE", messageClass); - return; - } + if (NotifyHandling.NONE.equals(notify) && accountsToNotify.isEmpty()) { + logger.atFine().log("Not sending '%s': Notify handling is NONE", messageClass); + return; + } - init(); - if (useHtml()) { - appendHtml(soyHtmlTemplate("HeaderHtml")); - } - format(); - appendText(textTemplate("Footer")); - if (useHtml()) { - appendHtml(soyHtmlTemplate("FooterHtml")); - } + init(); + if (useHtml()) { + appendHtml(soyHtmlTemplate("HeaderHtml")); + } + format(); + appendText(textTemplate("Footer")); + if (useHtml()) { + appendHtml(soyHtmlTemplate("FooterHtml")); + } - Set<Address> smtpRcptToPlaintextOnly = new HashSet<>(); - if (shouldSendMessage()) { - if (fromId != null) { - Optional<AccountState> fromUser = args.accountCache.get(fromId); - if (fromUser.isPresent()) { - GeneralPreferencesInfo senderPrefs = fromUser.get().getGeneralPreferences(); - if (senderPrefs != null && senderPrefs.getEmailStrategy() == CC_ON_OWN_COMMENTS) { - // If we are impersonating a user, make sure they receive a CC of - // this message so they can always review and audit what we sent - // on their behalf to others. - // - add(RecipientType.CC, fromId); - } else if (!accountsToNotify.containsValue(fromId) && rcptTo.remove(fromId)) { - // If they don't want a copy, but we queued one up anyway, - // drop them from the recipient lists. - // - removeUser(fromUser.get().getAccount()); + Set<Address> smtpRcptToPlaintextOnly = new HashSet<>(); + if (shouldSendMessage()) { + if (fromId != null) { + Optional<AccountState> fromUser = args.accountCache.get(fromId); + if (fromUser.isPresent()) { + GeneralPreferencesInfo senderPrefs = fromUser.get().getGeneralPreferences(); + if (senderPrefs != null && senderPrefs.getEmailStrategy() == CC_ON_OWN_COMMENTS) { + // If we are impersonating a user, make sure they receive a CC of + // this message so they can always review and audit what we sent + // on their behalf to others. + // + add(RecipientType.CC, fromId); + } else if (!accountsToNotify.containsValue(fromId) && rcptTo.remove(fromId)) { + // If they don't want a copy, but we queued one up anyway, + // drop them from the recipient lists. + // + removeUser(fromUser.get().getAccount()); + } } } - } - // Check the preferences of all recipients. If any user has disabled - // his email notifications then drop him from recipients' list. - // In addition, check if users only want to receive plaintext email. - for (Account.Id id : rcptTo) { - Optional<AccountState> thisUser = args.accountCache.get(id); - if (thisUser.isPresent()) { - Account thisUserAccount = thisUser.get().getAccount(); - GeneralPreferencesInfo prefs = thisUser.get().getGeneralPreferences(); - if (prefs == null || prefs.getEmailStrategy() == DISABLED) { - removeUser(thisUserAccount); - } else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) { - removeUser(thisUserAccount); - smtpRcptToPlaintextOnly.add( - new Address(thisUserAccount.getFullName(), thisUserAccount.getPreferredEmail())); + // Check the preferences of all recipients. If any user has disabled + // his email notifications then drop him from recipients' list. + // In addition, check if users only want to receive plaintext email. + for (Account.Id id : rcptTo) { + Optional<AccountState> thisUser = args.accountCache.get(id); + if (thisUser.isPresent()) { + Account thisUserAccount = thisUser.get().getAccount(); + GeneralPreferencesInfo prefs = thisUser.get().getGeneralPreferences(); + if (prefs == null || prefs.getEmailStrategy() == DISABLED) { + removeUser(thisUserAccount); + } else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) { + removeUser(thisUserAccount); + smtpRcptToPlaintextOnly.add( + new Address(thisUserAccount.getFullName(), thisUserAccount.getPreferredEmail())); + } + } + if (smtpRcptTo.isEmpty() && smtpRcptToPlaintextOnly.isEmpty()) { + logger.atFine().log("Not sending '%s': No SMTP recipients", messageClass); + return; } } - if (smtpRcptTo.isEmpty() && smtpRcptToPlaintextOnly.isEmpty()) { - logger.atFine().log("Not sending '%s': No SMTP recipients", messageClass); - return; - } - } - // Set Reply-To only if it hasn't been set by a child class - // Reply-To will already be populated for the message types where Gerrit supports - // inbound email replies. - if (!headers.containsKey(FieldName.REPLY_TO)) { - StringJoiner j = new StringJoiner(", "); - if (fromId != null) { - Address address = toAddress(fromId); - if (address != null) { - j.add(address.getEmail()); + // Set Reply-To only if it hasn't been set by a child class + // Reply-To will already be populated for the message types where Gerrit supports + // inbound email replies. + if (!headers.containsKey(FieldName.REPLY_TO)) { + StringJoiner j = new StringJoiner(", "); + if (fromId != null) { + Address address = toAddress(fromId); + if (address != null) { + j.add(address.getEmail()); + } } + smtpRcptTo.stream().forEach(a -> j.add(a.getEmail())); + smtpRcptToPlaintextOnly.stream().forEach(a -> j.add(a.getEmail())); + setHeader(FieldName.REPLY_TO, j.toString()); } - smtpRcptTo.stream().forEach(a -> j.add(a.getEmail())); - smtpRcptToPlaintextOnly.stream().forEach(a -> j.add(a.getEmail())); - setHeader(FieldName.REPLY_TO, j.toString()); - } - - String textPart = textBody.toString(); - OutgoingEmailValidationListener.Args va = new OutgoingEmailValidationListener.Args(); - va.messageClass = messageClass; - va.smtpFromAddress = smtpFromAddress; - va.smtpRcptTo = smtpRcptTo; - va.headers = headers; - va.body = textPart; - if (useHtml()) { - va.htmlBody = htmlBody.toString(); - } else { - va.htmlBody = null; - } + String textPart = textBody.toString(); + OutgoingEmailValidationListener.Args va = new OutgoingEmailValidationListener.Args(); + va.messageClass = messageClass; + va.smtpFromAddress = smtpFromAddress; + va.smtpRcptTo = smtpRcptTo; + va.headers = headers; + va.body = textPart; + + if (useHtml()) { + va.htmlBody = htmlBody.toString(); + } else { + va.htmlBody = null; + } - for (OutgoingEmailValidationListener validator : args.outgoingEmailValidationListeners) { - try { - validator.validateOutgoingEmail(va); - } catch (ValidationException e) { - logger.atFine().log( - "Not sending '%s': Rejected by outgoing email validator: %s", - messageClass, e.getMessage()); - return; + for (OutgoingEmailValidationListener validator : args.outgoingEmailValidationListeners) { + try { + validator.validateOutgoingEmail(va); + } catch (ValidationException e) { + logger.atFine().log( + "Not sending '%s': Rejected by outgoing email validator: %s", + messageClass, e.getMessage()); + return; + } } - } - Set<Address> intersection = Sets.intersection(va.smtpRcptTo, smtpRcptToPlaintextOnly); - if (!intersection.isEmpty()) { - logger.atSevere().log("Email '%s' will be sent twice to %s", messageClass, intersection); - } + Set<Address> intersection = Sets.intersection(va.smtpRcptTo, smtpRcptToPlaintextOnly); + if (!intersection.isEmpty()) { + logger.atSevere().log("Email '%s' will be sent twice to %s", messageClass, intersection); + } - if (!va.smtpRcptTo.isEmpty()) { - // Send multipart message - logger.atFine().log("Sending multipart '%s'", messageClass); - args.emailSender.send(va.smtpFromAddress, va.smtpRcptTo, va.headers, va.body, va.htmlBody); - } + if (!va.smtpRcptTo.isEmpty()) { + // Send multipart message + logger.atFine().log("Sending multipart '%s'", messageClass); + args.emailSender.send( + va.smtpFromAddress, va.smtpRcptTo, va.headers, va.body, va.htmlBody); + } - if (!smtpRcptToPlaintextOnly.isEmpty()) { - logger.atFine().log("Sending plaintext '%s'", messageClass); - // Send plaintext message - Map<String, EmailHeader> shallowCopy = new HashMap<>(); - shallowCopy.putAll(headers); - // Remove To and Cc - shallowCopy.remove(FieldName.TO); - shallowCopy.remove(FieldName.CC); - for (Address a : smtpRcptToPlaintextOnly) { - // Add new To - EmailHeader.AddressList to = new EmailHeader.AddressList(); - to.add(a); - shallowCopy.put(FieldName.TO, to); + if (!smtpRcptToPlaintextOnly.isEmpty()) { + logger.atFine().log("Sending plaintext '%s'", messageClass); + // Send plaintext message + Map<String, EmailHeader> shallowCopy = new HashMap<>(); + shallowCopy.putAll(headers); + // Remove To and Cc + shallowCopy.remove(FieldName.TO); + shallowCopy.remove(FieldName.CC); + for (Address a : smtpRcptToPlaintextOnly) { + // Add new To + EmailHeader.AddressList to = new EmailHeader.AddressList(); + to.add(a); + shallowCopy.put(FieldName.TO, to); + } + args.emailSender.send(va.smtpFromAddress, smtpRcptToPlaintextOnly, shallowCopy, va.body); } - args.emailSender.send(va.smtpFromAddress, smtpRcptToPlaintextOnly, shallowCopy, va.body); } } } |