Description
Add the following code to a file in the test folder and run it to reproduce the issue (or read the comments in the code):
import 'package:postgres/postgres_v3_experimental.dart';
import 'package:test/test.dart';
import 'docker.dart';
void main() {
// NOTE: The Docker Container will not close after stopping this test so that needs to be done manually.
usePostgresDocker();
// ignore: unused_local_variable
late final PgConnection conn1;
late final PgConnection conn2;
group('description', () {
setUpAll(() async {
conn1 = await PgConnection.open(
PgEndpoint(
host: 'localhost',
database: 'dart_test',
username: 'dart',
password: 'dart',
),
sessionSettings: PgSessionSettings(
onBadSslCertificate: (cert) => true,
),
);
conn2 = await PgConnection.open(
PgEndpoint(
host: 'localhost',
database: 'dart_test',
username: 'postgres',
password: 'postgres',
),
sessionSettings: PgSessionSettings(
onBadSslCertificate: (cert) => true,
),
);
});
test('produce error', () async {
// get conn1 PID
final res = await conn2
.execute("SELECT pid FROM pg_stat_activity where usename = 'dart';");
final conn1PID = res.first.first as int;
// Simulate issue by terminating a connection during a query
// ignore: unawaited_futures
conn1.execute('select * from pg_stat_activity;'); // comment this out and a different error will appear
// Terminate the conn1 while the query is running
await conn2.execute(
'select pg_terminate_backend($conn1PID) from pg_stat_activity;');
// this will cause the following exception:
// PostgreSQLException (PostgreSQLSeverity.fatal 57P01: terminating connection due to administrator command )
expect(true, true);
});
tearDownAll(() async {
print('closing conn1');
await conn1.close(); // this will never close & execution will hang here
print('closing conn2');
await conn2.close();
});
});
}
I think currently there's no direct and easy way for the user to handle such Exceptions (maybe a callback would help?). I also think PgConnection.close
should check if the connection was closed/terminated/errored-out before trying a graceful termination process.
Additional Context
I initially encountered a simple issue where await conn.close();
would hang forever due to a connection blocking query used in logical replication (i.e. START_REPLICATION ......
). I tried to force close the connection but there was no way around it. So I opened a separate connection to kill the replication process in the database in the testing teardown
code as a workaround. That's when I came across the issue which is presented in the code snippet above.
While this issue appeared with me in an edge case, I still think there should be a way to handle such severe/fatal PostgreSQL errors because they may appear in other cases. Also, as mentioned earlier, the PgConnection.close
should account for such scenarios and maybe have a flag for ungraceful termination (e.g. PgConnection.close(force: true)
) in addition to a timeout logic (honestly i didn't try to add timeout settings yet).