Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flow control for file uploads #20315

Merged

Conversation

allisonkarlitskaya
Copy link
Member

@allisonkarlitskaya allisonkarlitskaya commented Apr 15, 2024

  • add a flow control mechanism to the bridge (sending 'ack' control messages to confirm that data was received)
  • add a helper api (upload()) to pkg/lib to allow uploading large amounts of data (from a ReadableStream) using flow control, with progress reporting

This is my take on #20160

The watch channel can emit events even after `.close()` is called, which
can cause issues in the `kdump/test-config-client.js` QUnit test if the
timing of the file write changes (which it is about to do, in a upcoming
commit which reimplements fsreplace1 asynchronously).

Removing the event listener is a sure way to ensure we won't get further
events.
Comment on lines 23 to 24
if (window.debugging == 'all' || window.debugging?.includes('upload'))
console.debug('debug[upload]', ...args);
Copy link
Member

Choose a reason for hiding this comment

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

Normally this is:

pkg/systemd/services.jsx:        console.debug.apply(console, arguments);

Can we, keep things consistent :)

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

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

This got force-pushed underneath me, so sending the first half of my pending review.

test/common/testlib.py Outdated Show resolved Hide resolved
test/common/testlib.py Outdated Show resolved Hide resolved
doc/protocol.md Outdated Show resolved Hide resolved
src/cockpit/channel.py Show resolved Hide resolved
src/cockpit/channel.py Outdated Show resolved Hide resolved
pkg/lib/cockpit-upload-helper.ts Outdated Show resolved Hide resolved
pkg/lib/cockpit-upload-helper.ts Outdated Show resolved Hide resolved
pkg/lib/cockpit-upload-helper.ts Outdated Show resolved Hide resolved
pkg/lib/cockpit-upload-helper.ts Outdated Show resolved Hide resolved
pkg/lib/cockpit-upload-helper.ts Outdated Show resolved Hide resolved
Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

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

... and the rest of the review. Thanks!

pkg/playground/react-demo-file-upload.tsx Outdated Show resolved Hide resolved
test/verify/check-upload-component Outdated Show resolved Hide resolved
test/verify/check-upload-component Outdated Show resolved Hide resolved
test/verify/check-upload-component Outdated Show resolved Hide resolved
test/verify/check-upload-component Outdated Show resolved Hide resolved
Our previous reason for avoiding tempfile.NamedTemporaryFile() was that
we didn't want to chown() after the fact, but we've crossed that bridge
already since 72027c1, so we may as
well go all the way.

Rewrite things as a single run() function using NamedTemporaryFile as a
context manager.  This is a fairly substantial simplification.  We also
make the channel properly asynchronous by dispatching the actual write
to an executor.  We also add fdatasync(), which we were missing before.

We need to make a minor adjustment to AsyncChannel to support sending
close arguments, similar to the way we handle them on GeneratorChannel.
allisonkarlitskaya and others added 2 commits April 15, 2024 19:12
382ed70 changed the EOF return value of
`AsyncChannel.read()` from `b''` to `None` to account for the
possibility that the user might have sent an empty message (which has a
semantic meaning in at least one other channel — `fsreplace1`).
Unfortunately, it also introduced a comment documenting the old return
value behaviour.

Update the comment to reflect the new reality.
In Cockpit Navigator we want to support uploading large files and sent
data in chunks. As fsreplace1 lacks flow control navigator itself has to
make sure it doesn't flood the channel with data but before this commit
couldn't determine when to back off.

Add a new channel-level option — `send-acks` — for sending "ack" control
messages for each data frame we receive.  At first, this new option
supports a single value, `bytes`, which means to send ack messages with
the number of bytes acknowledged.  With this option enabled, the bridge
will send:

```
  {"command": "ack", "bytes": 16384}
```

for a received data frame containing 16384 bytes.  The bridge is also
free to combine acknowledgements (ie: the "bytes" field can be the sum
from multiple messages).

We can support this for all channels at the outset, even if the support
is somewhat preliminary: if do_data() returns None (as it does for all
existing channels) we send the acknowledgement immediately.  Channels
can decide to handle deferred sending on their own by returning True
(which is a new possibility).  We do that for AsyncChannel.

The implementation for ProtocolChannel is sufficient if the bottleneck
is the network upload speed but will fall over if the cause of
throttling is the command (or socket) not accepting data.  This is not
going to be trivial to fix, and will be handled in a later version.

Co-authored-by: Jelle van der Waa <jvanderwaa@redhat.com>
Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

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

Thanks! I like the Waiter approach, that's quite explicit and easier to read. I only have one strong nack here about bringing back the "bigfile" upload test. The rest is totally ignorable noise. Cheers!

pkg/lib/cockpit-upload-helper.ts Outdated Show resolved Hide resolved
pkg/lib/cockpit-upload-helper.ts Outdated Show resolved Hide resolved
pkg/playground/react-demo-file-upload.tsx Outdated Show resolved Hide resolved
pkg/playground/react-demo-file-upload.tsx Show resolved Hide resolved
test/verify/check-pages Show resolved Hide resolved
test/verify/check-pages Show resolved Hide resolved
Introduces a helper function which handles uploading a ReadableStream to
a server with `fsreplace1`, sending data in chunks, with proper flow
control and progress reporting.  This is made to be used for
cockpit-files but is re-usable for other plugins.

Add a React component to the playground as a proof of concept, and add a
couple of integration tests based on it.

Co-authored-by: Allison Karlitskaya <allison.karlitskaya@redhat.com>
Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

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

Me happy if bots are. Thanks for pushing this through and get past all our mutual bickering!

But please let @jelly review this as well, given how many sweat and emotions went in here 😅

@martinpitt martinpitt requested a review from jelly April 16, 2024 08:53
Comment on lines +27 to +28
if (window.debugging == 'all' || window.debugging?.includes('upload'))
console.debug('upload', ...args);
Copy link
Contributor

Choose a reason for hiding this comment

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

These 2 added lines are not executed by any test.

Comment on lines +42 to +44
const handleClick = () => {
if (ref.current) {
ref.current.click();
Copy link
Contributor

Choose a reason for hiding this comment

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

These 3 added lines are not executed by any test.

<Alert variant={alert.variant}
title={alert.title}
timeout={3000}
actionClose={<AlertActionCloseButton onClose={() => setAlert(null)} />}
Copy link
Contributor

Choose a reason for hiding this comment

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

This added line is not executed by any test.

@allisonkarlitskaya allisonkarlitskaya merged commit cb6da26 into cockpit-project:main Apr 16, 2024
74 of 76 checks passed
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.

None yet

4 participants