-
Notifications
You must be signed in to change notification settings - Fork 3
Conversation
Implement two samplers, PerKey and Avg, with the same interface. Check README for how to create new samplers and other docs. Public API is to instantiate a new sampler and then use `getSampleRate(key)`. ```javascript const sampler = new SomeSampler() sampler.getSampleRate("some-key") ```
@@ -0,0 +1,52 @@ | |||
jest.mock("nanotimer"); |
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 line isn't required but makes it nice for the next person who might be unfamiliar with mocks and why nanotimer now has a .tick() function but only in tests.
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.
+1 this line in general. better to be explicit for future readers.
… and we're publishing multiple formats
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.
} | ||
} | ||
} | ||
``` |
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.
Love all the code examples in the README. 😍
Not a blocker for shipping this at all, but at some point pulling in more of the verbiage from the dynsampler-go readme — esp the "what is sampling" bits from the top — could be handy. Want me to open issues for readme nice-to-haves like that? (The other one I'm thinking of is the answer to "can I use this in a browser?" 😳)
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.
yes please! Open issues and I'll knock em down.
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.
Yay, will do.
README.md
Outdated
}); | ||
this.savedSampleRates = newRates; | ||
} | ||
} |
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 thought the example invocation in the source comment was helpful — maybe worth having that in this example too?
const sampler = new PerKey({clearFrequencySec: 100, perKeyThroughputSec: 2})
Helps document the config options too.
index.test.js
Outdated
|
||
test("gets a sample rate", () => { | ||
const sampler = new PerKey(); | ||
new Array(1500).fill(1).forEach(() => sampler.getSampleRate("my-key")); |
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.
Any reason behind 1500
here? Just curious.
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.
just needed a value that I can predict the sample rate of
package.json
Outdated
"repository": "https://github.com/honeycombio/dynamic-sampler.js", | ||
"author": | ||
"Christopher Biscardi <[email protected]> (@chrisbiscardi)", | ||
"license": "MIT", |
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.
Sorry to resurrect our chat bikeshed on this topic, but thoughts on using Apache 2.0 for consistency with our other repos, for 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.
sure.
For later discussion: This is the default boilerplate for yarn init
and npm init
though so we should write down the reasoning for "why apache 2.0" somewhere since this is the community default for JS.
// No traffic means this is the goal sample rate | ||
expect(a).toEqual(10); | ||
sampler.timer.tick(); | ||
expect(sampler.getSampleRate("my-key")).toEqual(1); |
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.
Not a blocker, but it takes me a fair amount of mental context threading to see how the log math above leads to this set of expectations... maybe a few more comments or something a little more verbose would be helpful? (The dynsampler-ruby equivalent feels more parseable somehow.)
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'm curious if it's the table-based approach in the ruby tests that makes it more parseable
Cross that, the ruby test looks like we're reaching into the internals to set the rate and it's not actually triggering an updateMap
so wouldn't have the additional tick
s. For comparison, this test is only using the public API with a mock for timers so we can manually trigger the updateMap()
through the timer callback.
Curious as to your opinion on the difference in the tests. This test feels like it approximates more real-world usage
- create a sampler
- throw traffic at it
- the timer callback triggers
updateMaps
- get the sample rate
Whereas the ruby test seems like:
- Reach into internals to set internal properties
- Check that those internal properties match what we'd expect
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.
ugh, when did Shift+Enter become "commit comment". Too much time in Slack.
Do you think adding the "stage" comments above is enough? or should I try to do something else.
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.
Ah yeah, fair point, agree that it's a bit nicer to focus on testing the public API. 👍for adding the stage comments for now and I'll noodle more on what my dream tests would look like.
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.
general bikeshed comment:
- dynsampler-js
- AvgSampleRate + PerKeyThroughput
- ideally
dynsampler
for the package name, but a lot more leeway for package idioms there.
@@ -0,0 +1,52 @@ | |||
jest.mock("nanotimer"); |
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.
+1 this line in general. better to be explicit for future readers.
index.js
Outdated
export class PerKey extends Sampler { | ||
constructor(opts = {}) { | ||
super(opts); | ||
this.perKeyThroughputSec = opts.perKeyThroughputSec || 5; |
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.
both go and ruby default to 10 here.
index.js
Outdated
updateMaps() { | ||
debug("PerKey.updateMaps()", this.id && this.id); | ||
if (this.currentCounts.size == 0) { | ||
//no traffic in the last 30s. clear the result Map |
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.
suuuuuper minor nit. s/30s/clearFrequencySec/
README.md
Outdated
|
||
const newRates = new Map(); | ||
this.currentCounts.forEach((val, key) => { | ||
newRates.set(key, Math.max(1, val / actualPerKeyRate)); |
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.
both ruby and golang coerce the value to an int. :shakes fist: at JS. any reason not to do that here?
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.
Nope, going to go with Math.floor
to match golang.
index.js
Outdated
|
||
const newRates = new Map(); | ||
this.currentCounts.forEach((val, key) => { | ||
newRates.set(key, Math.max(1, val / actualPerKeyRate)); |
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.
same here wrt coercing to an int
}); | ||
const goalCount = sumEvents / this.goalSampleRate; | ||
const goalRatio = goalCount / logSum; | ||
|
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.
both the go/ruby implementations sort the keys here and iterate in sorted order. my head hurts looking the ruby implementation. I expect it's a problem to drop the sort, but ... /shrug?
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.
Since we're using Map
s I'm pretty sure we're good here, unless we need a specific kind of sort?
The forEach() method executes a provided function once per each key/value pair in the Map object, in insertion order.
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.
yeah that's the question I had - if we give the dynsamplers the same input, do we expect the same results? both other implementations go beyond the "sort them in the same order for this implementation to fix rounding errors" and use a sort that should be the same across implementations.
eh, we can deal with that when it's an issue for someone (e.g. "I'm using two dynsampler implementations and things are different between them"). And the answer even then might be WONTFIX.
} else { | ||
// TODO: how well supported is this syntax | ||
// (basically no IE native support. what about babel/etc?) | ||
return super.getSampleRate(key); |
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 hope babel supports it. I've been writing code for a diff that uses it 😬
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 believe it does. My concern was centered more at what buble does and whether we should care about that before microbundle gets it's babel support: developit/microbundle#39.
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.
Ah, +1 "dynsampler-js" as the project name, can't believe I didn't look at that 🤦♂️. There have been recent efforts to make our herd of open source projects a little better at following naming conventions, so seems like a good chance to establish a convention here too.
} | ||
} | ||
} | ||
``` |
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.
Yay, will do.
// No traffic means this is the goal sample rate | ||
expect(a).toEqual(10); | ||
sampler.timer.tick(); | ||
expect(sampler.getSampleRate("my-key")).toEqual(1); |
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.
Ah yeah, fair point, agree that it's a bit nicer to focus on testing the public API. 👍for adding the stage comments for now and I'll noodle more on what my dream tests would look like.
@toshok Are there still changes here you want to see? I'd like to merge it and move on to more work like setting up Travis, etc. |
thanks for the ping. other than repo renaming, this LGTM |
Implement two samplers, PerKey and Avg, with the same interface. Check
README for how to create new samplers and other docs.
Public API is to instantiate a new sampler and then use
getSampleRate(key)
.