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

Add SpectatorHistogram extension #15340

Merged
merged 7 commits into from
Jan 14, 2024
Merged

Conversation

bsyk
Copy link
Contributor

@bsyk bsyk commented Nov 8, 2023

Description

Adds the contribution extension providing support for SpectatorHistogram. A fast, small alternative to data-sketches or T-Digest for computing approximate percentiles.

See documentation included in the PR for more details.


This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added or updated version, license, or notice information in licenses.yaml
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

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

CodeQL found more than 10 potential problems in the proposed changes. Check the Files changed tab for more details.

@adarshsanjeev adarshsanjeev self-assigned this Nov 21, 2023
@adarshsanjeev
Copy link
Contributor

Thanks for the PR! This looks like a really cool addition to Druid. I'm going through the PR, and will add comments after it's done.

Copy link
Contributor

@suneet-s suneet-s left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution @bsyk !

I have not reviewed the code in depth, and have mostly read through the documentation.

From skimming the code, I think the extension is added in a safe way, and can be improved on in future patches.

Some things that could be done in the future:

  • Add vectorized implementations of the aggregator so that it allows queries to leverage that functionality.
  • Add sql bindings

| gce-extensions | GCE Extensions | [link](../development/extensions-contrib/gce-extensions.md) |
| prometheus-emitter | Exposes [Druid metrics](../operations/metrics.md) for Prometheus server collection (https://prometheus.io/) | [link](../development/extensions-contrib/prometheus.md) |
| kubernetes-overlord-extensions | Support for launching tasks in k8s without Middle Managers | [link](../development/extensions-contrib/k8s-jobs.md) |
| druid-spectator-histogram | Support for efficient approximate percentile queries | [link](../development/extensions-contrib/spectator-histogram.md) |
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you update your editor to undo the formatting changes to this table please.

https://github.com/apache/druid/blob/master/dev/druid_intellij_formatting.xml#L77-L80 - This was recently added to the druid_intellij_formatting.xml file- so if you re-import it, the formatter should no longer update the tables when you edit them.

data-sketches (depending on data-set, see limitations below).

## Limitations
* Supports positive numeric values within the range of [0, 2^53). Negatives are
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be good to call out that decimals are not supported - when I first read numeric values, I just assumed that decimals were supported, but the druid summit talk mentions those are not supported.

Comment on lines 78 to 79
* Fixed buckets with increasing bucket widths. Relative accuracy is maintained,
but absolute accuracy reduces with larger values.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain the accuracy tradeoff here vs other sketch implementations.

I don't understand what absolute accuracy reduces with larger values means. Maybe an example in the docs will help clear it up.

I think that sort of information will be helpful for users to decide which sketch implementation to use for their use case.

}
```

> Note: It's more efficient to request multiple percentiles in a single query
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Given this note, would it be a nicer UX if the extension did not provide a way to get a single percentile. If users want to get a single percentile, they could pass in an array with one element.

I don't have a strong opinion on this, so if you think having both functions is better - that's fine with me too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Where only a single percentile is wanted, often median or 95th, it's slightly nicer to get a single value back, rather than having to extract from an array in the results.
Also not a strong opinion.

Is the note misleading? It's trying to say, "if you want multiple percentiles from the same underlying metric, then ask for them all at once, rather than as separate metrics". 1 query being more efficient than 2.

// This will prevent class casting exceptions if trying to query with sum rather
// than explicitly as a SpectatorHistogram
//
// The SpectatorHistorgram is a Number. That number is of intValue(),
Copy link
Contributor

Choose a reason for hiding this comment

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

SpectatorHistogram

public void configure(Binder binder)
{
registerSerde();
//TODO: samarth this probably needs to be added for sql
Copy link
Contributor

Choose a reason for hiding this comment

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

This could either be removed to a comment till sql support is added

import java.util.BitSet;
import java.util.Objects;

public class NullableOffsetsHeader implements Serializer
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: A javadoc for this class as a small summary of its usages would help make the code more readable

}
final SpectatorHistogramAggregatorFactory that = (SpectatorHistogramAggregatorFactory) o;

//TODO: samarth should we check for equality of contents in count arrays?
Copy link
Contributor

Choose a reason for hiding this comment

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

Also to be resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was left over from an earlier implementation and no longer relevant.

@bsyk bsyk force-pushed the spectator-histogram branch 2 times, most recently from 89d8b15 to 8f604d1 Compare January 6, 2024 00:03
private void add(Object key, Number value)
{
if (key instanceof String) {
this.add(Integer.parseInt((String) key), value.longValue());

Check notice

Code scanning / CodeQL

Missing catch of NumberFormatException Note

Potential uncaught 'java.lang.NumberFormatException'.
}
// Treat as long number, if it looks like a number
if (Character.isDigit((objectString).charAt(0))) {
return Long.parseLong((String) object);

Check notice

Code scanning / CodeQL

Missing catch of NumberFormatException Note

Potential uncaught 'java.lang.NumberFormatException'.
Assert.assertEquals(1, results.size());
Map<String, ColumnAnalysis> columns = results.get(0).getColumns();
Assert.assertNotNull(columns.get("histogram"));
Assert.assertEquals("spectatorHistogramTimer", columns.get("histogram").getType());

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note test

Invoking
ColumnAnalysis.getType
should be avoided because it has been deprecated.
Assert.assertEquals(1, results.size());
Map<String, ColumnAnalysis> columns = results.get(0).getColumns();
Assert.assertNotNull(columns.get("histogram"));
Assert.assertEquals("spectatorHistogramDistribution", columns.get("histogram").getType());

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note test

Invoking
ColumnAnalysis.getType
should be avoided because it has been deprecated.
byte[] bytes = histogram.toBytes();
int keySize = Short.BYTES;
int valSize = 0;
Assert.assertEquals("Should compact small values within key bytes", 5 * (keySize + valSize), bytes.length);

Check warning

Code scanning / CodeQL

Result of multiplication cast to wider type Warning test

Potential overflow in
int multiplication
before it is converted to long by use in an invocation context.
byte[] bytes = histogram.toBytes();
int keySize = Short.BYTES;
int valSize = Short.BYTES;
Assert.assertEquals("Should compact medium values to short", 5 * (keySize + valSize), bytes.length);

Check warning

Code scanning / CodeQL

Result of multiplication cast to wider type Warning test

Potential overflow in
int multiplication
before it is converted to long by use in an invocation context.
byte[] bytes = histogram.toBytes();
int keySize = Short.BYTES;
int valSize = Integer.BYTES;
Assert.assertEquals("Should compact larger values to integer", 5 * (keySize + valSize), bytes.length);

Check warning

Code scanning / CodeQL

Result of multiplication cast to wider type Warning test

Potential overflow in
int multiplication
before it is converted to long by use in an invocation context.
byte[] bytes = histogram.toBytes();
int keySize = Short.BYTES;
int valSize = Long.BYTES;
Assert.assertEquals("Should not compact larger values", 5 * (keySize + valSize), bytes.length);

Check warning

Code scanning / CodeQL

Result of multiplication cast to wider type Warning test

Potential overflow in
int multiplication
before it is converted to long by use in an invocation context.

byte[] bytes = histogram.toBytes();
int keySize = Short.BYTES;
Assert.assertEquals("Should not compact larger values", (5 * keySize) + 0 + 2 + 4 + 8 + 8, bytes.length);

Check warning

Code scanning / CodeQL

Result of multiplication cast to wider type Warning test

Potential overflow in
int multiplication
before it is converted to long by use in an invocation context.

byte[] bytes = histogram.toBytes();
int keySize = Short.BYTES;
Assert.assertEquals("Should compact", (8 * keySize) + 0 + 1 + 1 + 2 + 2 + 4 + 4 + 8, bytes.length);

Check warning

Code scanning / CodeQL

Result of multiplication cast to wider type Warning test

Potential overflow in
int multiplication
before it is converted to long by use in an invocation context.
Copy link
Contributor

@adarshsanjeev adarshsanjeev left a comment

Choose a reason for hiding this comment

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

Thanks for addressing the comments! I've gone through the changes, and they seem to be fine. The build failures look unrelated.

docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
docs/development/extensions-contrib/spectator-histogram.md Outdated Show resolved Hide resolved
Copy link
Member

@vtlim vtlim left a comment

Choose a reason for hiding this comment

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

Left one more comment to try to explain the opinionated behavior. Otherwise LGTM for docs.

@bsyk
Copy link
Contributor Author

bsyk commented Jan 9, 2024

Left one more comment to try to explain the opinionated behavior. Otherwise LGTM for docs.

Fantastic. Thanks for all your suggestions, they've made the docs a lot more clear.

@maytasm
Copy link
Contributor

maytasm commented Jan 12, 2024

@suneet-s @vtlim @adarshsanjeev
Are we all good on merging this change in? I'll merge this change in at the end of the day if no one has any objection.
This change is contained in it's own extension (and is a contrib extension) so should be safe to merge.
Including my approval, we have 3 +1s from committers and total reviews from 4 committers.
I think this extension does serve a specific use case and does it well. This could be beneficial to some people and hence I do see a value in getting this merge in so that Druid users can easily use/try it out.
By getting this extension merge in, we can also keep this extension up-to-dated with any new Druid changes, encourage improvements to this extension from the community (such as adding vectorization, etc) and get feedback about the extension. Note that at Netflix, we have been running this extension at scale in production for a few years so it is also battle tested.

bsyk and others added 7 commits January 12, 2024 11:17
Cleanup comments
so that we support being queried as a Number using longSum or doubleSum aggregators as well as a histogram.
When queried as a Number, we're returning the count of entries in the histogram.
@suneet-s
Copy link
Contributor

@maytasm Good with me to merge once CI is green. Thanks @bsyk for the contribution and patience with the review :)

Copy link
Member

@clintropolis clintropolis left a comment

Choose a reason for hiding this comment

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

since this is already approved is probably fine to do the changes i suggested as follow-up PR

@Override
public Comparator<Double> getComparator()
{
return Doubles::compare;
Copy link
Member

Choose a reason for hiding this comment

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

this doesn't seem like the correct comparator if the output type is double array (ColumnType has a comparator available type.getNullableStrategy() if there might be nulls, or type.getStrategy() if not that should work)

Comment on lines +199 to +209
@Override
public ValueType getType()
{
return ValueType.COMPLEX;
}

@Override
public ValueType getFinalizedType()
{
return ValueType.COMPLEX;
}
Copy link
Member

Choose a reason for hiding this comment

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

these methods are deprecated and will be removed at some point, please implement getIntermediateType and getResultType instead.

@maytasm
Copy link
Contributor

maytasm commented Jan 14, 2024

@bsyk
Thank you for the change!
Merging this in as CI failure is unrelated to this PR (and master branch is failing on the same failure)

@maytasm maytasm merged commit e49a7bb into apache:master Jan 14, 2024
81 of 83 checks passed
@bsyk bsyk deleted the spectator-histogram branch January 25, 2024 17:51
@LakshSingla LakshSingla added this to the 29.0.0 milestone Jan 29, 2024
lloydchang added a commit to lloydchang/bsyk-druid-demo that referenced this pull request Feb 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants