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

rfe: multithread splitstream creation #62

Open
allisonkarlitskaya opened this issue Jan 27, 2025 · 3 comments
Open

rfe: multithread splitstream creation #62

allisonkarlitskaya opened this issue Jan 27, 2025 · 3 comments

Comments

@allisonkarlitskaya
Copy link
Collaborator

cfsctl oci pull is kinda slow right now.

When converting the container image .tar.gz into splitstreams it does the gzip decompression, sha256 verification (of the underlying .tar), fs-verity calculations (of the split objects), fdatasync() (of the split objects), zstd compression of the splitstream, and finally fs-verity and fdatasync() of the splitstream, all in a single thread.

mkcomposefs is smarter here: it spawns a bunch of worker threads to do the fs-verity and fdatasync() on the split objects.

This would be pretty easy for us to do, and Rust would help us make sure we're doing it right, except the way that splitstreams are currently constructed is pretty linear: you need to get the object ID of each object to write into the stream before you can proceed to the next one. But: we're just writing to a Vec<u8> anyway, so we can easily go back in fill those parts in, or come up with some kind of a "chunks" model where we merge all the chunks into the linear stream at the end, or any other design. We could then make some of the chunks be promises or something — I'm really not sure how this works in Rust...

@allisonkarlitskaya
Copy link
Collaborator Author

This also needs to work for cfsctl create-image which scans the filesystem. We might also try to opportunistically use reflinking in that case.

@allisonkarlitskaya
Copy link
Collaborator Author

For the splitstream case: I think some sort of model like this:

  • we have two queues:
    • some kind of a "work queue" which is handled by workers
    • some kind of an "output" queue which the splitstream segments get written to
  • one thread does the download/decompress/tar decoding
  • for each inline chunk, write it directly to the output queue
  • for each external chunk:
    • create a new ephemeral queue (as a sort of promise mechanism)
    • add an item to the worker queue containing:
      • the data
      • a promise queue Sender side
    • write the promise queue Receiver side to the "output" queue
  • the workers receive (data, Sender) pairs and write the data to the repo, sending the result (the fs-verity digest) to the Sender.
  • finally, have another thread responsible for composing the output. It reads inline chunks and promises and writes the splitstream.

@allisonkarlitskaya
Copy link
Collaborator Author

allisonkarlitskaya commented Jan 29, 2025

For the cfsctl create-image case it's going to be harder. We could imagine a filesystem tree where external files contain promises for fs-verity digests instead of the fs-verity digests but this is a serious complication of the existing structure. We could model it, instead, as a queue of (filename, fd) and move the read/checksum/write of the fd to a worker, producing another queue of (filename, checksum). Ensuring ordering would be annoying in this case, though...

edit: although maybe we don't have to worry about this so much. When creating from the filesystem we aren't doing multiple layers or anything, so the order of operations is actually not very important...

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

No branches or pull requests

1 participant