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

Fixed len computation when size just goes beyond 14 bits #668

Merged
merged 12 commits into from
May 16, 2018

Conversation

pierresouchay
Copy link
Contributor

Fixes #663

Since offset of compression has to start within the first 14 bits, we don't want to take it into account before trying to compress but once the length computation has been performed

@miekg
Copy link
Owner

miekg commented Apr 10, 2018

Fun times trying to make 2 diff. algorithms do the same thing: This now fails in travis:

-- FAIL: TestMsgCompressLengthLargeRecords (0.03s)
	length_test.go:172: predicted compressed length is wrong: predicted my.service.acme. (len=250) 22833, actual 22850

You PR looks sane btw.

@pierresouchay
Copy link
Contributor Author

Ok, I undestand fully the problem and have a fix:

Basically, the function compressionLenHelper should take as parameter the current len of message to guess whether compression is possible or not:

When you arrive around 16384, lets say 16380, if you plan to add those entries:

let's say you already added those labels:
dom8.example.com

  • x1.dom9.example.com => should be compressed fully and re-usable, meaning that dom9.example.com can be re-used later
  • long_name.dom9.example.com => will only re-use .example.com later, not dom9.example.com because long_name overflows 16384, thus dom9.example.com won't be accessible to next entries (since it would be outside the 16384 bytes range)

I made a patch, but I am a bit lost regarding the overhead of DNS structure to compute really the overhead of labels...

You probably know the answer better than I do and how to fix my offsets :-)

I you can have a look

Regards

@pierresouchay
Copy link
Contributor Author

@miekg I added a new test that does padding at begin of DNS query to ensure we properly handle all cases, it should fail for now

@miekg
Copy link
Owner

miekg commented Apr 11, 2018

Ah, I see, because compressionLenHelper adds the labels that can be used for compression and this can also step over the boundary.

I wonder if all that extra complexity is worth it though, right now Len() will over estimate, so this boils down to X missing RRs (compared to Pack())

@miekg
Copy link
Owner

miekg commented Apr 11, 2018

Of course in the range 16K <-> 64K this adds up. hmm

@pierresouchay
Copy link
Contributor Author

@miekg yes... :(

@miekg
Copy link
Owner

miekg commented Apr 11, 2018

Ok lbs gives you the index of the dots in the name, so if you have the current len in l, then
l + lbs < compressionOffset can be used for compression and not the rest.

I similar fix may or may not be needed in compressionLenHelperType.

@andrewtj
Copy link
Collaborator

andrewtj commented Apr 11, 2018 via email

@miekg
Copy link
Owner

miekg commented Apr 11, 2018 via email

@codecov-io
Copy link

codecov-io commented Apr 16, 2018

Codecov Report

Merging #668 into master will increase coverage by 0.01%.
The diff coverage is 51.94%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #668      +/-   ##
==========================================
+ Coverage   58.05%   58.07%   +0.01%     
==========================================
  Files          37       37              
  Lines        9999    10072      +73     
==========================================
+ Hits         5805     5849      +44     
- Misses       3144     3174      +30     
+ Partials     1050     1049       -1
Impacted Files Coverage Δ
msg.go 78.19% <100%> (+0.66%) ⬆️
zcompress.go 25.69% <26%> (+0.69%) ⬆️
dns.go 62.5% <0%> (-7.5%) ⬇️
dnssec.go 59.1% <0%> (ø) ⬆️
privaterr.go 70.83% <0%> (+0.97%) ⬆️
server.go 61.78% <0%> (+1.78%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 01d5935...33f7d9f. Read the comment docs.

msg.go Outdated
func compressionLenHelper(c map[string]int, s string) {
// Put the parts of the name in the compression map, return the size added in payload
func compressionLenHelper(c map[string]int, s string, currentLen int) int {
addedSize := 0
Copy link
Owner

Choose a reason for hiding this comment

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

I wonder if we can optimize this more; if we have seen s here, there shouldn't be a need a spit it at all. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, probably, split might be reused... but since the review is already big (I mean for the bug we consider), I did prefer to limit as much as possible the changes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in next patch version

msg.go Outdated
func compressionLenHelper(c map[string]int, s string) {
// Put the parts of the name in the compression map, return the size added in payload
func compressionLenHelper(c map[string]int, s string, currentLen int) int {
addedSize := 0
Copy link
Owner

Choose a reason for hiding this comment

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

addedLen ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, my comment is probably not clear: compressionLenHelper return the size added to global DNS response (thus the addedSize name).

Considering adding "host1.example.com" with a compression map having already:
"host0.example.com" (and thus "example.com" as well as "com")

it is supposed to return 2+ len("host0") + 2 (pointer to "example.com" label

Do you have a suggestion to have clearer name?

msg.go Outdated
c[pref] = len(pref)
lenAdded := len(pref)
numLabelsBefore := len(lbs) - j - 1
offsetOfLabel := currentLen + len(s) - len(pref) + numLabelsBefore*2
Copy link
Owner

Choose a reason for hiding this comment

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

don't fully understand this yet... why the *2 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

numLabelsBefore*2 => size of label pointers (since their size is 2 bytes => 2 times)

@miekg
Copy link
Owner

miekg commented Apr 16, 2018 via email

@pierresouchay
Copy link
Contributor Author

@miekg Ok for the constants...

About the change, I tried several approaches (including adding another function within compressionLenSlice that did check whether currentLen + r.len() did overflow 14bits, but the code was even more unreadable.

The idea I took is that when we add compression, we also add some data in the global payload, so, all function adding compression (compressionLenHelper / compressionLenSearch should return how much data size they added themselves.

The algorithm is weird, because there are actually 2 parts:

  • one that does decrease the size of x (with the += (1 - k) ) Note that the decrease is weird as well (why += 1 - k ? => took me a while to understand why)
  • the other that increase it (sound like a more common approach since the size is in bytes)

I first tried to decompose it, but it was not easy, hence the addedSize stuff => but I agree having both ways is crap. (But method for length computation were not present - hence the +6 from nowhere)

Of course, you are the boss, so decide what you really want, but for now, among all my tries, this sounds like the easier to understand

@miekg
Copy link
Owner

miekg commented Apr 16, 2018 via email

@miekg
Copy link
Owner

miekg commented Apr 23, 2018

I don't think I can come up with something better - this seems to work :)

I give a quick glance over one final time and then we should be good to go.

@pierresouchay
Copy link
Contributor Author

@miekg Great, I'll update Consul dependency with it as soon as a release is done to re-enable compression in it! Thank you

msg.go Outdated
for _, r := range rs {
if r == nil {
continue
}
// track this length, and the global length in len, while taking compression into account for both.
// TmpLen is to track len of record at 14bits boudaries, 6 bytes is the raw overhead
tmpLen := lenp + 6
Copy link
Owner

Choose a reason for hiding this comment

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

I still have trouble understanding where this +6 comes from; what's it this? What bytes of the message are we skipping over here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Don't have access to the code easily from my phone, but I would say the signaling of a new record? (I am not at all an expert in DNS, I just read RFC part related to compression for this patch)

@pierresouchay
Copy link
Contributor Author

@miekg I had not the time to look further, but I think I know why we have to add 6 and not 10 or something similar.

Actually 6 is a constant regarding the start of message, erased at each iteration since we take x (we discard 6 and all added len at each iteration)

What it could mean is just that we already added some len of the DNS message (lets say 4 bytes if you expected the constant to be 10) before entering this section of the code and those bytes are actually supposed to be AFTER this computation. I'll try to have a look tomorrow

@pierresouchay
Copy link
Contributor Author

@miekg I finally go this right I think

I changed the compression map used by Len() to have the same semantics (aka the offset of label) as the one used in Pack() to be able to add unit tests properly.

With this one, I change a few things, but everything is well tested now and "Fake" (aka used by Len()) has the same values as Pack() -> so I am confident compression will work the same way around 14bits

@miekg
Copy link
Owner

miekg commented Apr 30, 2018 via email

@pierresouchay
Copy link
Contributor Author

@miekg have a nice kubecon!

@pierresouchay
Copy link
Contributor Author

@miekg does the patch is OK for you ?

Since now compression map contains the same exact information as the one used for real packing, I am quite confident the result will always be the same since I test many permutations with it in test TestCompareCompressionMapsForSRV(), we now are for sure that any change in computation is equivalent in both Pack() and Len() since offset are all equal. In order to test this, I had to create a new packBufferWithCompressionMap() function that takes into parameter the compression map so we can compare it easily both maps easily.

The patch now does not take any magic offset anymore and is tested with a very large combination of lengths and offset, so I am pretty confident it works well.

Copy link
Owner

@miekg miekg left a comment

Choose a reason for hiding this comment

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

This looks very sane. Actual code changes (apart from tests) are not too massive. Some nits that i'll brush up.

Thanks for pushing this through!

@miekg miekg merged commit 0964911 into miekg:master May 16, 2018
miekg added a commit that referenced this pull request May 16, 2018
Some post #668 comments nits.
@miekg miekg mentioned this pull request May 16, 2018
@pierresouchay
Copy link
Contributor Author

@miekg Thank you, I'll be able to fully re-enable compression in Consul then once a new release is created :)

miekg added a commit that referenced this pull request May 16, 2018
Some post #668 comments nits.
@miekg
Copy link
Owner

miekg commented May 16, 2018 via email

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.

4 participants