fix: avoid reading external plugin stdout to memory#4234
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! 🚀 New features to boost your workflow:
|
Robot Results
|
albinsuresh
left a comment
There was a problem hiding this comment.
LGTM. Just a few minor remarks/queries.
| tempfile::NamedTempFile::new_in(self.tmp_dir.as_std_path()).map_err(|err| { | ||
| self.plugin_error(format!( | ||
| "Failed to create temporary file in {}: {err}", | ||
| tempfile::env::temp_dir().to_string_lossy() |
There was a problem hiding this comment.
| tempfile::env::temp_dir().to_string_lossy() | |
| self.tmp_dir.as_str() |
I see that it was inherited from crates/extensions/tedge_log_manager/src/plugin.rs in 5fda661.
| let child = command.spawn().map_err(|err| self.plugin_error(err))?; | ||
| let output = child | ||
| .wait_with_output(command_log) |
There was a problem hiding this comment.
Wondering why you took this "spawn child wait for output" approach here as opposed to the command.status() approach taken in crates/extensions/tedge_log_manager/src/plugin.rs. The command.status() approach felt more straightforward, especially about the intent of not caring about the stdout or stderr. I understand that the output.stdout/stderr would be None in this case as well, as they are redirected to the files. But, its' not as direct as the other.
Since you only wrote the simpler status based version for the log_manager/plugin.rs, there must have been a specific reason why you took a different approach here. Just trying to understand what that is.
There was a problem hiding this comment.
Okay, the problem is that log plugin's get operation isn't logged, but config plugin's get operation is logged.
To verify that config plugin's get operation keeps being logged, I added a check to the config plugin's test in 782d86b.
The spawn + wait_with_output method here was chosen to pass the command_log to log stdout to the operation log, however this just doesn't work because stdout is routed to file, not pipe.
So to avoid reading all of stdout to memory and also log stdout to the operation log, we'll have to do something like this:
- create tempfile for stdout
- route process stdout to tempfile
- call
.status()to wait for process to exit - open both stdout and stderr tempfiles and copy their contents to operation logfile
- persist operation logfile
- then open and read stdout file again for upload
We write the file, then read it twice, keeping all its contents in a logfile, but we don't load all stdout into memory, so it's okay. I will implement these changes.
d28787a to
8053ad6
Compare
albinsuresh
left a comment
There was a problem hiding this comment.
Approving again with some minor suggestions.
| } | ||
|
|
||
| /// A command output that allows reading from it, no matter if output was captured to memory or attached to a file. | ||
| pub struct CommandOutput<'a> { |
There was a problem hiding this comment.
That's a nice abstraction.
| logger.write_all(b"EOF\n").await?; | ||
| let mut stdout = output.stdout; | ||
| if !stdout.fill_buf().await?.is_empty() { | ||
| logger.write_all(b"stderr <<EOF\n").await?; |
There was a problem hiding this comment.
| logger.write_all(b"stderr <<EOF\n").await?; | |
| logger.write_all(b"stdout <<EOF\n").await?; |
There was a problem hiding this comment.
I think both this and below comments are referring to old commits. These don't exist now and also when I reviewed it.
| logger.write_all(b"EOF\n\n").await?; | ||
| } else { | ||
| logger.write_all(b"stdout (EMPTY)\n").await?; | ||
| logger.write_all(b"stderr (EMPTY)\n\n").await?; |
There was a problem hiding this comment.
| logger.write_all(b"stderr (EMPTY)\n\n").await?; | |
| logger.write_all(b"stdout (EMPTY)\n\n").await?; |
Signed-off-by: Marcel Guzik <marcel.guzik@cumulocity.com>
Signed-off-by: Marcel Guzik <marcel.guzik@cumulocity.com>
Similar to 5fda661, stream result of external config plugin instead of loading everything to memory. Not as impactful as log plugin, because usually people won't be having config files bigger than available RAM, but nevertheless behaviour was changed to keep consistency between the two plugins. Signed-off-by: Marcel Guzik <marcel.guzik@cumulocity.com>
8053ad6 to
adba130
Compare
Proposed changes
Similar to 5fda661, stream result of external config plugin instead of loading everything to memory. Not as impactful as log plugin, because usually people won't be having config files bigger than available RAM, but nevertheless behaviour was changed to keep consistency between the two plugins.
Types of changes
Paste Link to the issue
#4222 (review)
Checklist
just prepare-devonce)just formatas mentioned in CODING_GUIDELINESjust checkas mentioned in CODING_GUIDELINESFurther comments