summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuca Milanesio <luca.milanesio@gmail.com>2022-04-13 00:03:47 +0100
committerLuca Milanesio <luca.milanesio@gmail.com>2022-04-29 13:05:22 +0000
commit71e4ce3d069507f6186e1112112ed2bfd45fbccd (patch)
tree8efd8e90a923c02bd7ccae50334eeff1bd4851e4
parentb8e9d684c28402d9888573d5ea79a1985c1b3253 (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
-rw-r--r--java/com/google/gerrit/server/mail/send/ChangeEmail.java2
-rw-r--r--java/com/google/gerrit/server/mail/send/NotificationEmail.java9
-rw-r--r--java/com/google/gerrit/server/mail/send/OutgoingEmail.java235
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);
}
}
}