Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions conceptual/Npgsql/release-notes/11.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ Npgsql version 11.0 is in development.

Previous versions of Npgsql supported a high-performance mode called "multiplexing". While this mode could increase throughput in certain extremely-high-performance scenarios, it did not scale in highly-concurrent situations with many CPU cores, and added quite a bit of complexity to the codebase. We'll evaluate re-introducing a similar implementation that's more scalable and better-designed.

### Replication fixes that change behavior

Npgsql 11.0 fixes a number of bugs in the replication APIs. Some of these fixes change behavior that existing code may depend on:

* Npgsql now quotes slot and output plugin names, and escapes quotes in option names, option values and publication names, so all valid names work (e.g. slot names starting with a digit) and no name can change the replication command. Slot and plugin names are still folded to lower case, as before. If you escaped quotes in option values or publication names yourself, stop doing so.
* The low-level `StartLogicalReplication()` now throws `ArgumentException` if pgoutput's `proto_version` or `publication_names` option is given without a value, which crashes the walsender and with it every other session of the server (PostgreSQL bug #19795).
* Ending a replication enumeration early (e.g. with `break` or because of an exception) now stops replication, so that the connection can be used again. Before, the connection stayed in replication mode and every other command failed. Physical replication stops by ending copy mode; logical replication stops by cancelling the replication command, which, like any error, drops the temporary replication slots of the session. Since the connection no longer replicates after that, `SendStatusUpdate()` now throws `InvalidOperationException` after the loop; send the status update before leaving the loop, or pass the position you processed as `walLocation` when you start replication again. On PostgreSQL 17 and earlier, a pgoutput stream that you start again on the same connection may lack the `RelationMessage`s, so use a new connection for it.
* Starting replication while another replication stream is active on the same connection now throws `NpgsqlOperationInProgressException`, without disturbing the active stream.
* A physical replication stream now ends normally when the server reaches the end of the timeline it streams (e.g. because the standby you stream from got promoted); before, the connection broke. Use `IdentifySystem()` and `TimelineHistory()` to continue on the new timeline. When the server ends a stream because it shuts down, Npgsql now throws `NpgsqlException`.
* In physical replication, the write position Npgsql reports to the server is now the end of the WAL it received. Before, it reported the server's WAL end, which made `synchronous_commit = remote_write` unsafe.
* `ReplicationConnection.CommandTimeout` now applies to replication commands and can be set before opening the connection. `Show("all")` now throws `NotSupportedException`.
* A `WalReceiverStatusInterval` or `WalReceiverTimeout` of zero now disables the corresponding timer, like in PostgreSQL, instead of flooding the server with status updates. Negative values other than `Timeout.InfiniteTimeSpan` now throw `ArgumentOutOfRangeException`.
* `SendStatusUpdate()` now throws when the status update couldn't be sent, instead of only logging the failure.
* Malformed pgoutput data (unknown message or tuple types, or changes for relations without a preceding `RelationMessage`) now breaks the connection with an exception, instead of leaving it in the middle of the stream. Column values can no longer be read after they were consumed (this throws `InvalidOperationException`), and `TupleEnumerator.Current` throws before the first `MoveNextAsync()`.

### COPY fixes that change behavior

* When an error ends a COPY export (e.g. at a row, or after the data, from an `AFTER` trigger or a deferred constraint), later calls on the export now throw `InvalidOperationException` ("The COPY operation ended with an error.") with the original exception as `InnerException`, until it is disposed. This applies to the reading methods of `NpgsqlBinaryExporter` (`StartRow()`, `Read()`, `IsNull`, `Skip()` and their async variants), the stream from `BeginRawBinaryCopy()` and the reader from `BeginTextExport()`. Before, these calls reported misleading errors or waited until the command timeout broke the connection, and disposing the export could wait the same way.

### Removed deprecated large object APIs

Npgsql 11.0 removes some APIs to support PostgreSQL [large objects](https://www.postgresql.org/docs/current/largeobjects.html); these were obsolete since Npgsql 8.0. The APIs were old and had some design issues, and were also not necessary, as the corresponding PostgreSQL functions can simply be called directly.
6 changes: 3 additions & 3 deletions conceptual/Npgsql/replication.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ Enable logical replication in your `postgresql.conf` file:
wal_level = logical
```

Set up a replication user in your [`pg_hba.conf`](https://www.postgresql.org/docs/current/auth-pg-hba-conf.html) file:
Allow a replication user to connect in your [`pg_hba.conf`](https://www.postgresql.org/docs/current/auth-pg-hba-conf.html) file. Logical replication connections connect to a database, so the `replication` keyword, which only matches physical replication connections, doesn't apply to them:

```text
host replication repuser 0.0.0.0/0 md5
host all repuser 0.0.0.0/0 md5
```

The user `repuser` must exist in your cluster and either be a superuser or have the replication attribute set. See [CREATE ROLE docs](https://www.postgresql.org/docs/current/sql-createrole.html).
Expand Down Expand Up @@ -58,7 +58,7 @@ var slot = new PgOutputReplicationSlot("blog_slot");
// The following will loop until the cancellation token is triggered, and will print message types coming from PostgreSQL:
var cancellationTokenSource = new CancellationTokenSource();
await foreach (var message in conn.StartReplication(
slot, new PgOutputReplicationOptions("blog_pub", 1), cancellationTokenSource.Token))
slot, new PgOutputReplicationOptions("blog_pub", PgOutputProtocolVersion.V1), cancellationTokenSource.Token))
{
Console.WriteLine($"Received message type: {message.GetType().Name}");

Expand Down