-
Notifications
You must be signed in to change notification settings - Fork 31
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
feat(archives): gzip archived recordings #1112
base: main
Are you sure you want to change the base?
Conversation
@andrewazores I had to let the compress method as |
Thanks for checking. I think that work can be included in this PR. |
13a9400
to
2538e26
Compare
@andrewazores I am unsure about the tests that I can create for this feature. Should it be an integration test to test when the flag |
Our itest setup doesn't handle setting env vars for the Cryostat container itself right now. I think the updated itests that check that the recordings can be downloaded with the |
src/main/java/io/cryostat/recordings/RecordingArchiveHelper.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good and working well. Only "broken" thing is that as it stands before this PR, I can download a recording .jfr
from archives and re-upload that directly into the archives again. With this PR, the file I download will be a .jfr.gz
(unless it is an old uncompressed file from before this PR), but I cannot upload a .jfr.gz
because the RecordingsPostHandler
tries to strip the .jfr
filename extension, which it incorrectly handles when it ends in .jfr.gz
and then it fails to match the filename against the expected pattern.
Maybe we can/should just drop the filename pattern requirement altogether. It was always an arbitrary requirement that didn't really do anything except make it annoying for a user to try to use Cryostat to analyze a JFR file they already collected from elsewhere.
There is a validation in the upload checking if the file is a valid flight recording. I will decompress the uploaded file for the validation.
I agree to do not use the extension, less classes and tests will be affected by this change this way. |
7ea4d96
to
4ae7559
Compare
Working mostly well and code looks well-written. However, I notice that ex.
but, if I actually download the To preserve compatibility I think decompressing the file first actually makes the most sense to implement right now. Later on someone can circle back around to implement content-based negotiation so that the server will serve the compressed asset if the client requests it or says they can accept it, otherwise it should fall back to serving the uncompressed asset. |
@andrewazores I was trying to find a solution that does not let temp files, but I could not find any other solution. In this implementation, I am replacing the non-compressed file with the compressed file. To decompress the file for downloading, I was thinking to create it as a temp file as it is not be possible to delete it immediately after the file is requested and the download can take time. Any thoughts? Do you have any suggestion? |
If there is a compressed file on disk, you can return it uncompressed to the client by 1) decompressing it to a temp file and serving the temp file to the client, 2) decompressing the file and streaming this as a response to the client.
|
fca5360
to
a3278f5
Compare
Test image available:
|
if (!env.hasEnv(Variables.DISABLE_ARCHIVE_COMPRESS) && isGzip(recordingPath)) { | ||
return unGzip(recordingPath); | ||
} | ||
recordingFile = Files.readAllBytes(recordingPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will load the entire recording file into RAM within Cryostat's process, which is probably within a container with a relatively small memory allocation. If the requested recording file is close to that same size or larger then this operation will fail because there is not enough memory available to allocate. I think we need to find a way to return a Stream from this method and pump that Stream out to the HTTP response in the endpoint handler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am trying to use the pipeFromInput
as TargetRecordingGetHandler
but it is not returning response and always returning an empty file. Would you mind looking and help me to find what is wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I'll see if I can find a bit of time to look into this tomorrow or Friday. If not then I am on PTO the week after, but I can take a look the week after that if you're still blocked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think I see what might be going wrong. The TargetRecordingGetHandler
implementation needs an open JMX connection to the target since it wants to stream the recording data over JMX, through Cryostat, and back into a pipe to the requesting client over HTTP. Therefore, it creates a ConnectionDescriptor
object that represents the JMX Service URL of the application and any required JMX credentials. It gets the JMX Service URL from the :targetId
path parameter of the request.
In this case, we want to do something similar with streaming an InputStream
to the client over HTTP, but it's just backed by a simple file on disk rather than by a JMX connection to a target application. So we don't actually need or want a JMX connection to be opened. But, because the OutputToReadStream
class was created with that JMX-to-HTTP piping in mind, it requires a TargetConnectionManager
and ConnectionDescriptor
. So in the modified handler you are also creating a ConnectionDescriptor
, but this actually is an invalid descriptor because the request URL has no :targetId
path parameter - instead it has a :sourceTarget
that would be equivalent, but again it's unnecessary. I think what happens is that this invalid ConnectionDescriptor
gets passed to the TargetConnectionManager
, and then inside OutputToReadStream
any time the method checkConnection
is called it silently fails because the descriptor is invalid.
So in the end I think the fix will be to create another class very similar to OutputToReadStream
, but without the dependency on a ConnectionDescriptor
and the TargetConnectionManager
. I will see if I can commit directly to your PR branch here and help out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lkonno I think I got it working, at least the immediate bug we were just talking about. I was able to download an archived recording through the web-client UI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Fixes #732
.gz
extension to the compressed files.CRYOSTAT_DISABLE_ARCHIVE_COMPRESS
totrue
to disable the compression.