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

rec: dedup records #14617

Merged
merged 10 commits into from
Jan 10, 2025
Merged

rec: dedup records #14617

merged 10 commits into from
Jan 10, 2025

Conversation

omoerbeek
Copy link
Member

@omoerbeek omoerbeek commented Sep 3, 2024

Short description

This deduplicaties records in two places:

  1. Records received from authoritative servers (disabled later and replaces by specific dedup call)
  2. Records sent out to clients (only on non-packet cache hits) (disabled later).

When testing this, I encountered a few cases where auths sent duplicate records.

dig @ns4.dnsv5.com getui.com txt 
dig @ord.geons.ftw.jiveip.net api.geoip.jive.com txt
dig @dnsd10.chatango.com chatango.com
dig @ns.sotoon53.com yektanet.com txt
dig @ns.viettelidc.com.vn ns9.viettelidc.com.vn
dig @ns1.cdnetdns.net flypgs.com txt

Nothing actually breaks if we don't dedup afaik. So it is questionable of we want this part.

But on the client side, this fixes #14120 and maybe other cases I do not know.

The big question is if we want this. Dedupping is fundamentally not cheap, although I tried to optimize the dedup code.

Originally I played with the idea to change the data structure building the reply vector to avoid duplicates, but that requires changes in many places. Still the idea is not completely off the table.

On the receiving side the dedup internals could also be weaved into the sanitize code, at the cost of increasing complexity. That would avoid the separate dedup() call.

This will remain a draft until I have some some speed measurements and pondered the alternative approaches some more. This PR is mainly to share thoughts.

The test changes are needed as a few of them use duplicate records.

Update: I removed the general dedup calls and I'm now only using dedup where it makes sense to solve a specific case of potential duplciate records. The general purpose (quite performant) dedup code remains and is used, in one case replacing existing ad-hoc dedup code.

Checklist

I have:

  • read the CONTRIBUTING.md document
  • compiled this code
  • tested this code
  • included documentation (including possible behaviour changes)
  • documented the code
  • added or modified regression test(s)
  • added or modified unit test(s)

@omoerbeek omoerbeek added the rec label Sep 3, 2024
@coveralls
Copy link

coveralls commented Sep 3, 2024

Pull Request Test Coverage Report for Build 12705938523

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 75 of 75 (100.0%) changed or added relevant lines in 8 files are covered.
  • 41 unchanged lines in 12 files lost coverage.
  • Overall coverage decreased (-0.009%) to 64.835%

Files with Coverage Reduction New Missed Lines %
modules/gpgsqlbackend/gpgsqlbackend.cc 1 88.62%
pdns/misc.cc 1 64.74%
pdns/validate.cc 1 68.24%
pdns/backends/gsql/gsqlbackend.hh 1 97.71%
pdns/pollmplexer.cc 1 83.66%
pdns/packethandler.cc 3 72.48%
pdns/iputils.cc 3 55.91%
pdns/dnsrecords.cc 3 80.2%
pdns/recursordist/rec-main.cc 3 62.4%
pdns/recursordist/pdns_recursor.cc 4 72.42%
Totals Coverage Status
Change from base Build 12690664929: -0.009%
Covered Lines: 126273
Relevant Lines: 163958

💛 - Coveralls

Copy link
Member

@rgacogne rgacogne left a comment

Choose a reason for hiding this comment

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

The logic looks good to me. I guess we could try a few more heuristics before actually serializing the content, like checking if we have several records with the same (qtype, qname), but it might not be worth it. Having real-world numbers would indeed be useful.

pdns/dnsparser.hh Show resolved Hide resolved
pdns/shuffle.hh Outdated Show resolved Hide resolved
@omoerbeek omoerbeek self-assigned this Oct 25, 2024
@omoerbeek
Copy link
Member Author

Rebased to fix conflict.

@omoerbeek
Copy link
Member Author

Some observations:
quad1 does dedup in some cases (only when the dups are adjacent?)
quad8 does not dedup
OpenDNS does dedup

Software tested in default config
unbound does not do dedup
bind does dedup
knot-resolver does dedup

@omoerbeek
Copy link
Member Author

Speedtest results:

'2 DedupRecords (generate only)' 0.10 seconds: 1230169.1 runs/s, 0.81 us/run
'2 DedupRecords' 0.10 seconds: 504897.9 runs/s, 1.98 us/run
'2 DedupRecords (with dup)' 0.10 seconds: 628577.4 runs/s, 1.59 us/run
'256 DedupRecords (generate only)' 0.10 seconds: 9248.3 runs/s, 108.13 us/run
'256 DedupRecords' 0.10 seconds: 3867.5 runs/s, 258.57 us/run
'256 DedupRecords (with dup)' 0.10 seconds: 3823.4 runs/s, 261.55 us/run
'4096 DedupRecords (generate only)' 0.11 seconds: 561.7 runs/s, 1780.35 us/run
'4096 DedupRecords' 0.10 seconds: 241.7 runs/s, 4137.49 us/run
'4096 DedupRecords (with dup)' 0.10 seconds: 239.3 runs/s, 4178.26 us/run

The measured slowdown is about 2.5 and is uniform over the various test case sizes.

So the dedupping takes time as expected, but for the already pretty extreme case of 256, records, its absolute value is not a lot compared to the expected network latency. For the 4096 case we spent time that comes closer to the expected network latency.

@paddg
Copy link
Contributor

paddg commented Oct 25, 2024

Can we rule out that deduping is an attack vector?

@omoerbeek
Copy link
Member Author

Can we rule out that deduping is an attack vector?

Not completely, in the extreme case spending even a few CPU ms on a single auth result is quite a lot.

@paddg
Copy link
Contributor

paddg commented Oct 25, 2024

Not completely, in the extreme case spending even a few CPU ms on a single auth result is quite a lot.

Are you considering an on/off switch for it?

@omoerbeek
Copy link
Member Author

Not completely, in the extreme case spending even a few CPU ms on a single auth result is quite a lot.

Are you considering an on/off switch for it?

Yes, that would be one of the options. Another alternative would be to not do the dedupping on large answers as we already refuse to cache them anyway.

@omoerbeek
Copy link
Member Author

The logic looks good to me. I guess we could try a few more heuristics before actually serializing the content, like checking if we have several records with the same (qtype, qname), but it might not be worth it. Having real-world numbers would indeed be useful.

I played a bit with a pre-scan on qtype and name only, but saw no speedup

@omoerbeek omoerbeek force-pushed the rec-dedup-recs branch 2 times, most recently from 274b82b to e26c334 Compare November 19, 2024 11:47
@omoerbeek omoerbeek added this to the rec-5.3.0 milestone Dec 16, 2024
@omoerbeek omoerbeek requested a review from rgacogne December 16, 2024 11:25
@omoerbeek omoerbeek marked this pull request as ready for review December 16, 2024 11:25
Copy link
Member

@rgacogne rgacogne left a comment

Choose a reason for hiding this comment

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

The general logic and the places were it is applied look good to me.

@@ -1527,6 +1512,9 @@ void startDoResolve(void* arg) // NOLINT(readability-function-cognitive-complexi
}

if (!ret.empty()) {
#ifdef notyet
Copy link
Member

Choose a reason for hiding this comment

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

This might warrant a comment, at the very least :)

pdns/shuffle.cc Outdated Show resolved Hide resolved
pdns/shuffle.cc Outdated Show resolved Hide resolved
Comment on lines 1199 to 1213
std::vector<DNSRecord> vec;
vec.reserve(d_howmany);
std::string name("some.name.in.some.domain");
auto count = d_howmany;
if (d_withdup) {
count--;
}
for (size_t i = 0; i < count; i++) {
auto content = DNSRecordContent::make(QType::TXT, QClass::IN, "\"a text " + std::to_string(i) + "\"");
DNSRecord rec(name, content, QType::TXT);
if (i == 0 && d_withdup) {
vec.emplace_back(rec);
}
vec.emplace_back(std::move(rec));
}
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to do this in the constructor instead, to get the initial steps out of the timed computation?

Copy link
Member Author

@omoerbeek omoerbeek Jan 7, 2025

Choose a reason for hiding this comment

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

I initially did not do that as operator()() must be const, and dedup modifies the vector. A middle ground would be to copy the vec constructed in the ct and operate on that one.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, right! The middle ground looks good indeed.

Copy link
Member

Choose a reason for hiding this comment

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

The new version looks good, note that I still have a few comments that have not been addressed as far as I can tell.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I could have sworn I did the other ones as well, will fix.

@omoerbeek omoerbeek merged commit 1ec2bb1 into PowerDNS:master Jan 10, 2025
79 checks passed
@omoerbeek omoerbeek deleted the rec-dedup-recs branch January 10, 2025 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

rec: Duplicate NSEC and RRSIG in foo.cname-exists.phicoh.nl
4 participants