Skip to content

Commit

Permalink
Email sender: move PersistentConnection message subscription into the…
Browse files Browse the repository at this point in the history
… designated zone. (#8072)
  • Loading branch information
isoos authored Sep 24, 2024
1 parent b343a91 commit 3026a95
Showing 1 changed file with 25 additions and 15 deletions.
40 changes: 25 additions & 15 deletions app/lib/frontend/email_sender.dart
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,16 @@ class _GmailSmtpRelay implements EmailSender {
// closing the old connection if there was any, ignoring errors
await old?.close();

return _GmailConnection(
_parentZone,
PersistentConnection(
// PersistentConnection needs to be created in its designated zone, as its
// internal message subscription starts inside the constructor.
final connectionZone = _ConnectionZone(_parentZone);
final connection = await connectionZone._zone.run(
() async => PersistentConnection(
await _getSmtpServer(sender),
timeout: Duration(seconds: 15),
),
);
return _GmailConnection(connectionZone, connection);
});
_connectionsBySender[sender] = newConnectionFuture;
return newConnectionFuture;
Expand Down Expand Up @@ -281,28 +284,34 @@ class _GmailSmtpRelay implements EmailSender {
}
}

class _GmailConnection {
final DateTime created;
final PersistentConnection _connection;
class _ConnectionZone {
final Zone _parentZone;
DateTime _lastUsed;
var _sentCount = 0;
Object? _uncaughtError;

_GmailConnection(this._parentZone, this._connection)
: created = clock.now(),
_lastUsed = clock.now();
_ConnectionZone(this._parentZone);

late final _zone = _parentZone.fork(specification: ZoneSpecification(
handleUncaughtError: (self, parent, zone, error, stackTrace) {
_uncaughtError = error;
_logger.severe('Uncaught error while sending email', error, stackTrace);
},
));
}

class _GmailConnection {
final DateTime created;
final PersistentConnection _connection;
final _ConnectionZone _connectionZone;
DateTime _lastUsed;
var _sentCount = 0;

_GmailConnection(this._connectionZone, this._connection)
: created = clock.now(),
_lastUsed = clock.now();

bool get isExpired {
// The connection is in an unknown state, better not use it.
if (_uncaughtError != null) {
if (_connectionZone._uncaughtError != null) {
return true;
}
// There is a 100-recipient limit per SMTP transaction for smtp-relay.gmail.com.
Expand All @@ -325,8 +334,9 @@ class _GmailConnection {
Future<SendReport> send(Message message) async {
_sentCount += message.recipients.length + message.ccRecipients.length;
try {
final r = await _zone.run(() async => await _connection.send(message));
if (_uncaughtError != null) {
final r = await _connectionZone._zone
.run(() async => await _connection.send(message));
if (_connectionZone._uncaughtError != null) {
throw EmailSenderException.failed();
}
return r;
Expand All @@ -337,7 +347,7 @@ class _GmailConnection {

Future<void> close() async {
try {
await _zone.run(() async {
await _connectionZone._zone.run(() async {
await _connection.close();
});
} catch (e, st) {
Expand Down

0 comments on commit 3026a95

Please sign in to comment.