Skip to content

Removed soft cancellation for the reader's sequential methods#3276

Merged
roji merged 10 commits into
npgsql:mainfrom
vonzshik:sequential-reader-timeout-fix
Oct 28, 2020
Merged

Removed soft cancellation for the reader's sequential methods#3276
roji merged 10 commits into
npgsql:mainfrom
vonzshik:sequential-reader-timeout-fix

Conversation

@vonzshik
Copy link
Copy Markdown
Contributor

Closes #3275

Comment thread src/Npgsql/HardCancellationBlock.cs Outdated
Comment thread src/Npgsql/HardCancellationBlock.cs Outdated
@vonzshik vonzshik requested a review from YohDeadfall October 27, 2020 05:28
Comment thread src/Npgsql/HardCancellationBlock.cs Outdated
Comment thread src/Npgsql/HardCancellationBlock.cs Outdated
Comment thread src/Npgsql/HardCancellationBlock.cs Outdated
Comment thread src/Npgsql/NpgsqlReadBuffer.cs Outdated
Copy link
Copy Markdown
Member

@roji roji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @vonzshik.

I was thinking about these cases in the last few days (WaitAsync, COPY, sequential read), and how we can deal with them. Here's a summary of scenarios:

Scenario Attempt PG cancellation? Break on timeout?
Normal command Yes Yes
Sequential read inside message No Yes
Reading from any type handler No Yes
COPY export Yes (only for CopyData) Yes
COPY import No Yes
WaitAsync No No

Some notes on this table:

  • Type handler reads are basically sequential reads - since type handlers read inside a column, everything is buffered except when we're doing sequential.
  • Basically, whenever we're accessing the buffer directly (not via NpgsqlConnector.ReadMessage), we never want PG cancellation (this is the case of sequential and type handler reads).
  • When we're reading messages (via NpgsqlConnector.ReadMessage), we want PG cancellation, except for Wait.
  • For COPY export, things align nicely with the above: when reading CopyData we want cancellation, when reading within CopyData we don't.

Re implementation... for break vs. don't break on timeout, we pass a readingNotifications flag to NpgsqlReadBuffer.Ensure - it makes sense to me to do the same for whether PG cancellation should be attempted or not (e.g. attemptPostgresCancellation. This PR basically does the same thing, except that it passes that flag through AsyncLocal, which I think is complicates our flow/logic (which is already complicated), and spreads out state in another place.

As per the above, the default overload of Ensure - the one called by everyone except NpgsqlConnector.ReadMessage - should not attempt PG cancellation. So ReadMessage can call a special overload which does attempt it.

Comment thread test/Npgsql.Tests/ReaderTests.cs Outdated
Comment thread src/Npgsql/NpgsqlReadBuffer.cs Outdated
@vonzshik vonzshik requested review from YohDeadfall and roji October 27, 2020 12:25
@vonzshik
Copy link
Copy Markdown
Contributor Author

@roji okay, I think this is it. COPY operations for now are as is (this will be done as a part of #3151).

Copy link
Copy Markdown
Member

@roji roji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, see some comments below.

Comment thread src/Npgsql/NpgsqlCommand.cs Outdated
Comment thread test/Npgsql.Tests/ReaderTests.cs Outdated
Comment thread src/Npgsql/NpgsqlConnector.cs Outdated
@vonzshik vonzshik requested a review from roji October 28, 2020 11:03
Copy link
Copy Markdown
Member

@roji roji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks almost ready to merge, there's still some questions of when to do PG cancellation, see comments below.


CopyOutResponseMessage copyOutResponse;
var msg = _connector.ReadMessage();
var msg = _connector.ReadMessageWithoutCancellation();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't we say we want PG cancellation when reading messages when exporting?

Thinking about this again, we can say that we don't think PG cancellation makes sense with COPY, because we don't think PG will hang for any reason (after all it's just pushing out data), so we only hang because of network issues. If this is true (it makes sense to me), we can just do hard cancellation. Interestingly we still allow sending PG cancellation via the Cancel method, but that wouldn't be because of hanging, just because the user decided mid-way to stop the long process.

I'm still not completely sure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we're not reading here a CopyDataMessage. Is there a reason you think, that reading a CopyOutResponseMessage may hang?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, just like RawCopyStream , here we're initializing the stream. Is it really OK to cancel, when we do not even know if the copy operation has really started?

Copy link
Copy Markdown
Member

@roji roji Oct 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, I have no idea...

Let's go with your approach, and attempt cancellation only when reading CopyData. We can always adjust this later based on user feedback if we get any.

{
Expect<ParseCompleteMessage>(connector.ReadMessage(), connector);
var paramTypeOIDs = Expect<ParameterDescriptionMessage>(connector.ReadMessage(), connector).TypeOIDs;
Expect<ParseCompleteMessage>(connector.ReadMessageWithoutCancellation(), connector);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason this shouldn't do PG cancellation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is... actually, I'm not really sure about. Does it really can timeout because it was executing for too long?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not - we're just asking the server to parse and describe the parameters. I think in my mind I wanted to default to trying cancellation, but like with COPY we can go with this and revisit later (this API specifically is in very low usage.

Comment thread src/Npgsql/NpgsqlConnector.cs
Comment thread src/Npgsql/NpgsqlConnector.cs
Comment thread src/Npgsql/NpgsqlRawCopyStream.cs
var task = reader.GetFieldValueAsync<byte[]>(0, cts.Token);
cts.Cancel();

var exception = Assert.ThrowsAsync<OperationCanceledException>(async () => await task);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For completeness we can assert that no PG cancellation request reached the server (also in the other relevant tests).

Copy link
Copy Markdown
Member

@roji roji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, you've pretty much convinced me :)

Am going to merge as-is, we'll revisit in the future based on feedback.

@roji roji merged commit f07465d into npgsql:main Oct 28, 2020
@vonzshik vonzshik deleted the sequential-reader-timeout-fix branch October 30, 2020 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cancellation/timeout for NpgsqlDataReader.{GetFieldValueAsync,GetIsDBNull}

3 participants