-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(dynamic-sampling): Org-assist logic (#80588)
Add the logic for the org assist mode. Part of getsentry/projects#191
- Loading branch information
1 parent
c05703d
commit ed5f95a
Showing
3 changed files
with
244 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
static/app/views/settings/dynamicSampling/utils/scaleSampleRates.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
interface ScalingItem { | ||
count: number; | ||
sampleRate: number; | ||
} | ||
|
||
/** | ||
* Scales the sample rates of items proportionally to their current sample rate. | ||
*/ | ||
export function scaleSampleRates<T extends ScalingItem>({ | ||
items, | ||
sampleRate, | ||
}: { | ||
items: T[]; | ||
sampleRate: number; | ||
}): { | ||
scaledItems: T[]; | ||
} { | ||
const totalSpans = items.reduce((acc, item) => acc + item.count, 0); | ||
const oldSampleRate = items.reduce( | ||
(acc, item) => acc + item.sampleRate * (item.count / totalSpans), | ||
0 | ||
); | ||
|
||
if (sampleRate === oldSampleRate) { | ||
return {scaledItems: items}; | ||
} | ||
|
||
if ( | ||
oldSampleRate === 0 || | ||
oldSampleRate === 1 || | ||
sampleRate === 0 || | ||
sampleRate === 1 | ||
) { | ||
return { | ||
scaledItems: items.map(item => ({ | ||
...item, | ||
sampleRate, | ||
})), | ||
}; | ||
} | ||
|
||
const newSampled = totalSpans * sampleRate; | ||
|
||
let factor = sampleRate / oldSampleRate; | ||
let remainingTotal = totalSpans; | ||
let remainingSampleCount = newSampled; | ||
let remainingOldSampleCount = totalSpans * oldSampleRate; | ||
|
||
const sortedItems = items.toSorted((a, b) => a.count - b.count); | ||
|
||
const scaledItems: T[] = []; | ||
for (const item of sortedItems) { | ||
const newProjectRate = Math.min(1, Math.max(0, item.sampleRate * factor)); | ||
const newProjectSampleCount = item.count * newProjectRate; | ||
|
||
remainingTotal -= item.count; | ||
remainingSampleCount -= newProjectSampleCount; | ||
remainingOldSampleCount -= item.count * item.sampleRate; | ||
|
||
const newTargetRate = remainingSampleCount / remainingTotal; | ||
|
||
const remainingTotalRef = remainingTotal; | ||
const remainingOldSampleRate = remainingOldSampleCount / remainingTotalRef; | ||
|
||
factor = newTargetRate / remainingOldSampleRate; | ||
|
||
scaledItems.push({ | ||
...item, | ||
sampleRate: newProjectRate, | ||
}); | ||
} | ||
return {scaledItems}; | ||
} |
88 changes: 88 additions & 0 deletions
88
static/app/views/settings/dynamicSampling/utils/testScaleSapleRates.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import {scaleSampleRates} from 'sentry/views/settings/dynamicSampling/utils/scaleSampleRates'; | ||
|
||
function getAverageSampleRate(items: Array<{count: number; sampleRate: number}>) { | ||
const total = items.reduce((acc, item) => acc + item.count, 0); | ||
return items.reduce((acc, item) => acc + (item.count * item.sampleRate) / total, 0); | ||
} | ||
|
||
describe('scaleSampleRates', () => { | ||
it('scales the sample rate from 0', () => { | ||
const items = [ | ||
{count: 2, sampleRate: 0}, | ||
{count: 4, sampleRate: 0}, | ||
]; | ||
|
||
const sampleRate = 0.4; | ||
|
||
const {scaledItems} = scaleSampleRates({items, sampleRate}); | ||
expect(scaledItems[0].sampleRate).toEqual(0.4); | ||
expect(scaledItems[1].sampleRate).toEqual(0.4); | ||
}); | ||
|
||
it('scales the sample rate from 100', () => { | ||
const items = [ | ||
{count: 2, sampleRate: 0}, | ||
{count: 4, sampleRate: 0}, | ||
]; | ||
|
||
const sampleRate = 0.4; | ||
|
||
const {scaledItems} = scaleSampleRates({items, sampleRate}); | ||
expect(scaledItems[0].sampleRate).toEqual(0.4); | ||
expect(scaledItems[1].sampleRate).toEqual(0.4); | ||
}); | ||
|
||
it('scales the sample rate up', () => { | ||
const items = [ | ||
{count: 500, sampleRate: 0.6}, | ||
{count: 600, sampleRate: 0.2}, | ||
{count: 500, sampleRate: 0.2}, | ||
]; | ||
|
||
const sampleRate = 0.2; | ||
|
||
const {scaledItems} = scaleSampleRates({items, sampleRate}); | ||
expect(getAverageSampleRate(scaledItems)).toBeCloseTo(sampleRate, 15); | ||
}); | ||
|
||
it('handles reducing the sample rates', () => { | ||
const items = [ | ||
{count: 100, sampleRate: 0.3}, | ||
{count: 200, sampleRate: 0.6}, | ||
{count: 200, sampleRate: 0.9}, | ||
]; | ||
|
||
const sampleRate = 0.25; | ||
|
||
const {scaledItems} = scaleSampleRates({items, sampleRate}); | ||
expect(getAverageSampleRate(scaledItems)).toBeCloseTo(sampleRate, 15); | ||
}); | ||
|
||
it('does not decrease sample rates which are at 0', () => { | ||
const items = [ | ||
{count: 200, sampleRate: 0.6}, | ||
{count: 200, sampleRate: 0.9}, | ||
{count: 100, sampleRate: 0}, | ||
]; | ||
|
||
const sampleRate = 0.25; | ||
|
||
const {scaledItems} = scaleSampleRates({items, sampleRate}); | ||
expect(items.every(item => item.sampleRate >= 0)).toBe(true); | ||
expect(getAverageSampleRate(scaledItems)).toBeCloseTo(sampleRate, 15); | ||
}); | ||
|
||
it('does not increase sample rates which are at 1', () => { | ||
const items = [ | ||
{count: 100, sampleRate: 1}, | ||
{count: 200, sampleRate: 0.2}, | ||
{count: 200, sampleRate: 0.1}, | ||
]; | ||
|
||
const sampleRate = 0.8; | ||
|
||
const {scaledItems} = scaleSampleRates({items, sampleRate}); | ||
expect(items.every(item => item.sampleRate <= 1)).toBe(true); | ||
expect(getAverageSampleRate(scaledItems)).toBeCloseTo(sampleRate, 15); | ||
}); | ||
}); |