Conversation
| self.total_written += msg.data.len(); | ||
| // for a gzipped file, we can't use `msg.data.len()` to | ||
| // determine the number of bytes written, so we have to | ||
| // manually do a `write_all()` type loop |
There was a problem hiding this comment.
i'm not understanding this and i'm hoping you can help me
if what we're tracking here is the number of uncompressed bytes, why can't we still use write_all and msg.data.len()?
There was a problem hiding this comment.
ah you're totally right, this is an outdated comment/implementation from when i was trying to track total_bytes_written rather than total_uncompressed_bytes
There was a problem hiding this comment.
ah ok. that makes sense
i do want to flag tho that when i try changing this back to use write_all, the tests fail so there may be another reason to use this approach which i also don't understand right now lol
There was a problem hiding this comment.
i wager it's due to a missing .flush() call -- i'll push a commit w/ working tests in a sec
bmw
left a comment
There was a problem hiding this comment.
i didn't make the time to really test this (at least not yet), but i wanted to share the comments i had after reading the code and playing with things just a little bit so you could continue working on this
for my benefit at least as much as yours for when i come back to this, my comment above about continuing to use write_all in write_container has not been fully addressed yet
|
@bmw @untitaker sorry about the protracted delay, but i've substantially reworked this based on some of your comments. since you last reviewed it, here are the major changes:
i'll mark your previous review comments as resolved since i believe i addressed them all. thanks for your patience! |
|
oops, lemme resolve the merge conflicts first |
Major changes: * QmdlWriter now outputs gzipped QMDL files by default * QmdlReader renamed to QmdlMessageReader, and reads both compressed and uncompressed QMDL. It no longer requires bounding to avoid reading partially written files.
|
sorry for the force push, but rebasing with so many old commits that i majorly reworked was just untenable. this is review-ready again! |
|
just posting this in case it helps you to see it, but it looks like clippy is still mad here |
|
bleh, i guess my clippy was slightly outdated and missed a suggestion. |
bmw
left a comment
There was a problem hiding this comment.
i still want to manually test this a bit more before merging, but i think we're getting close!
nice work here wgreenberg. this strikes me as a tricky and thorny project
| } | ||
| } | ||
|
|
||
| impl From<MessagesContainer> for Bytes { |
There was a problem hiding this comment.
do we need or want this? when i delete this and the use bytes::Bytes; above and run tests on my fork everything passes
There was a problem hiding this comment.
good catch, i think this is leftover from either a test i deleted, or possibly from serializing to an axum Body. in any case, you're right that it's superfluous
| self.writer.flush().await?; | ||
| } |
There was a problem hiding this comment.
a small thing i noticed playing with GzipEncoder is the compression gets better if you call flush less often. because of that, what do you think about making a change like this?
| self.writer.flush().await?; | |
| } | |
| } | |
| self.writer.flush().await?; |
i played with this change modifying test_compressed_reading_and_writing to assert the size of the buffer. in the version in this PR, the buffer is 173 or 183 bytes depending on the value of do_close. after making the modification here, it's 146 or 156 bytes
that's not a huge savings, but it's something and i feel like we might as well. what do you think?
There was a problem hiding this comment.
deleting this causes run_compressed_reading_and_writing_tests(false) to fail for me, does it not for you?
it seems like when the writer isn't close()'d, without flushes we don't successfully persist all the messages to the disk. i'm not sure how regularly flushing happens if it's not called manually, but i do worry about losing unflushed messages on a crash
There was a problem hiding this comment.
to be clear, i'm not proposing we remove the flush calls. i'm just proposing we move it out of the loop to give GzipEncoder a little more context to work with before flushing. the tests still pass for me after applying my suggestion here
this is definitely a nitpick tho so if you don't like this idea for some reason, feel free to ignore it
There was a problem hiding this comment.
oh i totally misread your initial comment, sorry! i'm happy to make the change, though fwiw in production i think pretty much all MessagesContainers are of length 1
| hdlcs.iter().map(|hdlc| hdlc.data.len()).collect(), | ||
| ) | ||
| }; | ||
| for truncated_hdlc_i in 1..hdlcs.len() - 1 { |
There was a problem hiding this comment.
any reason not to do one more iteration here? the tests still pass with this change
| for truncated_hdlc_i in 1..hdlcs.len() - 1 { | |
| for truncated_hdlc_i in 1..hdlcs.len() { |
There was a problem hiding this comment.
nope, just an off-by-one on my part
|
|
||
| let headers = [ | ||
| (CONTENT_TYPE, "application/octet-stream"), | ||
| (CONTENT_LENGTH, &entry.qmdl_size_bytes.to_string()), |
There was a problem hiding this comment.
i think this content length is going to be wrong because qmdl_size_bytes is the compressed size
there are multiple ways to fix this, but i think we should just remove this header. it's inclusion comes from #253 and while having a progress bar is nice, i personally think it's more trouble than it's worth here, especially in the initial version of this
| // thread. | ||
| if file_kind == FileKind::Qmdl { | ||
| copy(&mut file.take(qmdl_size_bytes as u64), &mut entry_writer).await?; | ||
| copy(&mut file.take(qmdl_file_size as u64), &mut entry_writer).await?; |
There was a problem hiding this comment.
at least when trying to make a zip file with the current recording, this will always generate an invalid gzipped file because it's missing the footer. when i try to decompress this file with gzip, i get:
$ gunzip 1781018397.qmdl.gz
gzip: 1781018397.qmdl.gz: unexpected end of file
gzip: 1781018397.qmdl.gz: uncompress failed
can we use an approach similar to what was done for get_qmdl above and use into_qmdl_stream on the QmdlMessageReader here?
| .close() | ||
| .await | ||
| .expect("failed to close analysis writer"); | ||
| match (qmdl_writer.close().await, analysis_writer.close().await) { |
There was a problem hiding this comment.
calling close causes the qmdl_writer to add the gzip footer but qmdl_size_bytes is never updated. this is especially bad with how qmdl_size_bytes is currently used when generating a zip file because it means every .qmdl.gz file created there will be missing its footer
i also unfortunately don't think that calling qmdl_writer.size() here will work because close() will have shut down the stream. probably the quickest fix would be to check the size of the file on disk here after calling close and calling update_entry_qmdl_size with that value
what do you think?
There was a problem hiding this comment.
actually, i ended up going in a slightly different direction, where close() returns the final size of the file
A few minor refactors, and a more major one that renames RecordingStore's update_entry_qmdl_size to update_current_entry_qmdl_size, since the only time we're ever updating an entry's QMDL size is when it's the current one.
bmw
left a comment
There was a problem hiding this comment.
this lgtm!
on top of reviewing the new commit, i manually played with this a bit more and couldn't find a way to break it
this is prerequisite work for #81, since the diag logs for things like RSSI massively increase the size of QMDL files. in my experience, simply gzipping the qmdls reduces their size by 4-5x, which i think should be sufficient for our purposes.
this PR reworks QmdlWriter to output gzipped QMDL files by default, and allows QmdlReader to operate on either compressed or uncompressed QMDLs.
QmdlReader has been significantly rewritten to expose a single AsyncRead interface to both compressed and uncompressed QMDL sources.
i'd still like to do some more in-depth testing of this, but in the meantime i'd love a review on it