rebalancing.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. interface BalancingItem {
  2. count: number;
  3. id: string;
  4. sampleRate: number;
  5. }
  6. interface Params<T extends BalancingItem> {
  7. items: T[];
  8. targetSampleRate: number;
  9. intensity?: number;
  10. minBudget?: number;
  11. }
  12. /**
  13. * Balances the sample rate of items to match the target sample rate.
  14. * Mirrors the behavior of the dynamic sampling backend.
  15. *
  16. * See `src/sentry/dynamic_sampling/models/projects_rebalancing.py`
  17. * and `src/sentry/dynamic_sampling/models/full_rebalancing.py`
  18. *
  19. * @param targetSampleRate The target sample rate to balance the items to.
  20. * @param items The items to balance.
  21. * @param intensity The intensity of the balancing. How close to the ideal should we go from our current position (0=do not change, 1 go to ideal)
  22. * @param minBudget Ensure that we use at least min_budget (in order to keep the overall rate)
  23. * @returns The balanced items and the used budget.
  24. */
  25. export function balanceSampleRate<T extends BalancingItem>({
  26. targetSampleRate,
  27. items,
  28. intensity = 1,
  29. minBudget: minBudgetParam,
  30. }: Params<T>): {
  31. balancedItems: T[];
  32. usedBudget: number;
  33. } {
  34. // Sort the items ascending by count, so the available budget is distributed to the items with the lowest count first
  35. const sortedItems = items.toSorted((a, b) => a.count - b.count);
  36. const total = items.reduce((acc, item) => acc + item.count, 0);
  37. let numItems = items.length;
  38. let ideal = (total * targetSampleRate) / numItems;
  39. let minBudget = Math.min(total, minBudgetParam ?? total * targetSampleRate);
  40. let usedBudget = 0;
  41. const balancedItems: T[] = [];
  42. for (const item of sortedItems) {
  43. const count = item.count;
  44. let newSampleRate = 0;
  45. let used = 0;
  46. if (ideal * numItems < minBudget) {
  47. // If we keep to our ideal we will not be able to use the minimum budget (readjust our target)
  48. ideal = minBudget / numItems;
  49. }
  50. const sampled = count * targetSampleRate;
  51. const delta = ideal - sampled;
  52. const correction = delta * intensity;
  53. const desiredCount = sampled + correction;
  54. if (desiredCount > count) {
  55. // We desire more than we have, so we give it all
  56. newSampleRate = 1;
  57. used = count;
  58. } else {
  59. newSampleRate = desiredCount / count;
  60. used = desiredCount;
  61. }
  62. usedBudget += used;
  63. minBudget -= used;
  64. numItems -= 1;
  65. balancedItems.push({
  66. ...item,
  67. sampleRate: newSampleRate,
  68. });
  69. }
  70. return {balancedItems, usedBudget};
  71. }