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

Add a size hint attribute for fsreplace1 #20394

Merged

Conversation

allisonkarlitskaya
Copy link
Member

Plus some associated cleanups.

Not in this PR: sending the size attribute from cockpit.file().replace() — the way we handle iterating over string data (which may contain non-ascii characters) makes this quite difficult at present.

Vulture doesn't detect this because of the `_`.  There doesn't seem to
be a way to ignore locals prefixed with `_` while still catching such
instance variables.
I was so young and naive when I wrote these...
We call `asyncio.get_running_loop()` on every single `write()`.  Let's
cache the attribute on the channel from the start.  This will let us
avoid calling `.get_running_loop()` from some other users as well, in
the next commit.
There are a fair number of uses of .run_in_executor() sprinkled around
AsyncChannel implementations.  Let's add a trivial helper for those.

Getting the types right for this in a way that was backwards compatible
with Python 3.6 was fairly difficult, since 'typing.ParamSpec' doesn't
even exist there.
If we could depend on Python 3.12 and use delete_on_close=False then
using NamedTemporaryFile as a context manager would be really useful.
Since we can't, and already have to deal with the deletion manually,
let's also just deal with the close part manually.  Getting an fd and a
filename fits our uses a lot better anyway: most things we call want to
operate on an fd and having the name in a variable lets us easily flag
the cases where we don't want to try to delete it.

We move the `finally:` handling of closing the fd and (conditionally)
removing the temporary file to a separate internal `try:` block.  This
has nothing to do with the delete case (which does want to share the
error handling, which is now the only thing the outer `try:` does).
The entire `fsreplace1` channel is implemented in a single function, and
it's getting a bit complicated (and is about to get even more
complicated).

Split things up a bit.
@allisonkarlitskaya allisonkarlitskaya requested review from martinpitt and jelly and removed request for martinpitt April 29, 2024 08:10
src/cockpit/channel.py Dismissed Show dismissed Hide dismissed
src/cockpit/channel.py Dismissed Show dismissed Hide dismissed
src/cockpit/channel.py Dismissed Show dismissed Hide dismissed
@allisonkarlitskaya
Copy link
Member Author

Why can't we have nice things?

>       assert 'No space left on device' in get_str(err, 'message')
E       assert 'No space left on device' in '[Errno 27] File too large'

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! Just Kleinzeug..

src/cockpit/channels/filesystem.py Outdated Show resolved Hide resolved
if size is not None:
logger.debug('fallocate(%s.tmp, %d)', path, size)
if size: # posix_fallocate() of 0 bytes is EINVAL
await self.in_thread(os.posix_fallocate, fd, 0, size)
Copy link
Member

Choose a reason for hiding this comment

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

While at it, does it also make sense to add posix_fadvise(os.POSIX_FADV_DONTNEED) to avoid cache trashing? (This can be a follow-up of course)

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure we know that as a general rule — it may well be the case that the user wants to use this immediately after uploading it.

test/pytest/test_bridge.py Outdated Show resolved Hide resolved
This calls posix_fallocate() before starting to write the data to the
disk.  This has two benefits:

 - allocating the data in one go is better for fragmentation.  Without a
   size hint given in advance, the kernel has no idea how big of a block
   it needs.  With a slow upload, the kernel will start committing data
   to disk long before it knows the total size.

 - allocating the data at the start means that we can detect "out of
   space" early, before we waste time uploading 90% of a huge file
Instead of taking a ReadableStream, take a Blob.

The reason for this is to gain access to the `.size` attribute on the
Blob, allowing us to send a `size` hint on the `fsreplace1` channel.

I considered giving the possibility of accepting either, but this API is
totally new (only appearing in the last release), there are no known
existing users, and is bundled, anyway.
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.

Cheers!

@allisonkarlitskaya allisonkarlitskaya merged commit 63cd368 into cockpit-project:main Apr 29, 2024
82 checks passed
@allisonkarlitskaya allisonkarlitskaya deleted the fsreplace-size-hint branch April 29, 2024 18:09
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

2 participants