-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
builtins: add crdb_internal.fingerprint builtin #91124
Conversation
cc0d368
to
89cac0f
Compare
89cac0f
to
be88a29
Compare
Pushing this out to get some initial comments, and ideas for more interesting cases to test. |
be88a29
to
084cc68
Compare
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.
Overall the test you wrote is about what I expected to see.
I suppose we could generate range tombstones through actual sql operations and then assert that splits and merges don't affect the fingerprint.
If we modified this to create tenant-agonostic fingerprints by default, we could also integrate it into a bunch (all?) of the tenant to tenant tests in a follow-up PR.
084cc68
to
de7237f
Compare
de7237f
to
67047b8
Compare
friendly ping @stevendanna @erikgrinaker, if there are no blocking comments then I'd like to start using this in our C2C tests to shake out bugs/issues.
The test does have a case where it issues an admin split and ensures that we see two ExportRequests that are then combined by distsender. I think we'll also see more coverage once C2C in the face of SQL operations that write tombstones start comparing fingerprints. |
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.
Overall this looks reasonable to me. Thanks for working on it!
Sorry, I'm running a bit behind on code reviews, will have a quick look now. |
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.
A few issues that should be straightforward to fix (and some nits that you can ignore at will), feel free to merge once resolved.
} else if !ok { | ||
break | ||
} | ||
hasPoint, _ := iter.HasPointAndRange() |
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 likely doesn't matter here at all, but combined point/range key iteration is usually a fair bit more expensive than iterating over them separately. We could set up a point-only iterator at the start of the function, and check if a seek lands on a valid position (found a point key), and then use range-only iteration for the fingerprinting.
This isn't going to matter unless a span has a bunch of range keys though, which we don't really expect to see, but I suppose it could e.g. in the case of import cancellations of coarsely interleaved data. Feel free to ignore this or leave a comment for later.
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.
ahh TIL, changed to first use a point iter to assert we don't have any point keys.
pkg/storage/fingerprint_writer.go
Outdated
if err := fw.hash(fw.timestampBuf); err != nil { | ||
return 0, err | ||
} | ||
if err := fw.hashValue(v.Value); err != nil { |
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.
Here, we're fingerprinting the encoded value including the MVCCValueHeader
(if any), while MVCCExportToFingerprint
fingerprints the inner roachpb.Value
contained in MVCCValue.Value.RawBytes
. We should do the same here, by decoding the MVCCValue
first.
This is particularly important because the MVCCValueHeader
may contain data that isn't guaranteed to be the same across clusters or datasets, such as the MVCCValueHeader.LocalTimestamp
, even though the SQL user data is identical.
This deserves a test case, where datasets with differing (or empty/non-empty) value headers yield identical fingerprints, both for point keys and range keys. TestMVCCHistories
can generate this by passing localTs
with a value below ts
to put
or del_range_ts
.
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.
Good catch!
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.
nice catch indeed, i think we already have the test you outlined for point keys over here -
put k=/b ts=2 v=b localTs=4 tenant-prefix=11 init-checksum |
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.
Now that we have FingerprintRangekeys
I think I can teach TestMVCCHistories to also fingerprint rangekeys instead of printing out the rangekeys. Let me try that.
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.
Okay, tweaked the datadriven driver to compute the rangekey fingerprint and XOR'ing it with the point key fingerprint instead of printing rangekeys. I also added a test to export_fingerprint_tenant
where the rangekey in tenant 10 has a localTS but an identical rangekey in tenant 11 doesn't. The fingerprints continue to match which proves we are discarding the MVCCValueHeader before fingerprinting.
}, | ||
tree.Overload{ | ||
Types: tree.ArgTypes{ | ||
{"span", types.BytesArray}, |
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.
Don't we usually pass the start/end keys separately to functions like these? No idea what the convention or recommendation is, just wondering.
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 think crdb_internal.scan
has an overload for both passing in a span or a start/end key. I was optimizing for the use case of crdb_internal.fingerprint(crdb_internal.tenant_span(<id>))
or crdb_internal.fingerprint(crdb_internal.table_span(...))
but its likely we'll add the other overload soon enough. I'll leave it as a follow up when we need it.
4a43407
to
4e57fb8
Compare
TFTRs! bors r=stevendanna,erikgrinaker |
Build failed: |
Unsure what happened here. bors retry |
Build failed: |
oh, rebasing: |
This change adds a `crdb_internal.fingerprint` builtin that accepts a `startTime`, `endTime`, `startKey` and `endKey` to define the interval the user wants to fingerprint. The builtin is powered by sending an ExportRequest with the defined intervals but with the `ExportFingerprint` option set to true. Setting this option on the ExportRequest means that instead of writing all point and rangekeys to an SST and sending them back to the client, command evaluation will use the newly introduced `fingerprintWriter` (cockroachdb#90848) when exporting keys. This writer computes an `fnv64` hash of the key/timestamp, value for each point key and maintains a running XOR aggregate of all the point keys processed as part of the ExportRequest. Rangekeys are not fingerprinted during command evaluation, but instead returned to the client in a pebble SST. This is because range keys do not have a stable, discrete identity and so it is up to the caller to define a deterministic ingerprinting scheme across all returned range keys. The ExportRequest sent as part of this builtin does not set any DistSender limit, thereby allowing concurrent execution across ranges. We are not concerned about the ExportResponses growing too large since the SSTs will only contain rangekeys that should be few in number. If this assumption is proved incorrect in the future, we can revisit setting a `TargetBytes` to the header of the BatchRequest. Fixes: cockroachdb#89336 Release note: None
4e57fb8
to
1acfcc8
Compare
bors r+ |
Build succeeded: |
This change adds a
crdb_internal.fingerprint
builtinthat accepts a
startTime
,endTime
,startKey
andendKey
to define the interval the user wants to fingerprint. The builtin
is powered by sending an ExportRequest with the defined intervals
but with the
ExportFingerprint
option set to true.Setting this option on the ExportRequest means that instead of
writing all point and rangekeys to an SST and sending them back to
the client, command evaluation will use the newly introduced
fingerprintWriter
(#90848) when exporting keys. This writercomputes an
fnv64
hash of the key/timestamp, value for each point keyand maintains a running XOR aggregate of all the point keys processed
as part of the ExportRequest. Rangekeys are not fingerprinted during
command evaluation, but instead returned to the client in a
pebble SST. This is because range keys do not have a stable,
discrete identity and so it is up to the caller to define a deterministic
ingerprinting scheme across all returned range keys.
The ExportRequest sent as part of this builtin does not set any DistSender
limit, thereby allowing concurrent execution across ranges. We are not
concerned about the ExportResponses growing too large since the SSTs
will only contain rangekeys that should be few in number. If this assumption
is proved incorrect in the future, we can revisit setting a
TargetBytes
to the header of the BatchRequest.
Fixes: #89336
Release note: None