projectStatsToSampleRates.tsx 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import round from 'lodash/round';
  2. import {Outcome, SeriesApi} from 'sentry/types';
  3. import {findClosestNumber} from 'sentry/utils/findClosestNumber';
  4. import {quantityField} from '.';
  5. const MAX_PER_HOUR = 300 * 60 * 60;
  6. const COMMON_SAMPLE_RATES = [
  7. 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1,
  8. 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9,
  9. 0.95, 1,
  10. ].sort((a, z) => a - z);
  11. export function projectStatsToSampleRates(stats: SeriesApi | undefined): {
  12. hoursOverLimit?: number;
  13. maxSafeSampleRate?: number;
  14. trueSampleRate?: number;
  15. } {
  16. if (!stats) {
  17. return {
  18. trueSampleRate: undefined,
  19. maxSafeSampleRate: undefined,
  20. hoursOverLimit: undefined,
  21. };
  22. }
  23. const {groups, intervals} = stats;
  24. const hours: number[] = [];
  25. const trueSampleRates: number[] = [];
  26. const safeSampleRates: number[] = [];
  27. let hoursOverLimit = 0;
  28. // We do not take filtered and invalid into account
  29. const accepted = groups.find(g => g.by.outcome === Outcome.ACCEPTED)?.series[
  30. quantityField
  31. ];
  32. const clientDiscard = groups.find(g => g.by.outcome === Outcome.CLIENT_DISCARD)?.series[
  33. quantityField
  34. ];
  35. const rateLimited = groups.find(g => g.by.outcome === Outcome.RATE_LIMITED)?.series[
  36. quantityField
  37. ];
  38. intervals.forEach((_interval, index) => {
  39. const hourAccepted = accepted?.[index] ?? 0;
  40. const hourClientDiscard = clientDiscard?.[index] ?? 0;
  41. const hourRateLimited = rateLimited?.[index] ?? 0;
  42. const hourRejected = hourClientDiscard + hourRateLimited;
  43. const hourTotal = hourAccepted + hourRejected;
  44. const hourTotalCapped = Math.min(hourTotal, MAX_PER_HOUR);
  45. const trueSampleRate = hourTotal === 0 ? 1 : 1 - hourRejected / hourTotal;
  46. const safeSampleRate = hourTotal === 0 ? 1 : hourTotalCapped / hourTotal;
  47. hours.push(hourTotal);
  48. trueSampleRates.push(trueSampleRate);
  49. safeSampleRates.push(safeSampleRate);
  50. if (hourTotal > MAX_PER_HOUR) {
  51. hoursOverLimit += 1;
  52. }
  53. });
  54. hours.sort((a, z) => a - z);
  55. trueSampleRates.sort((a, z) => a - z);
  56. safeSampleRates.sort((a, z) => a - z);
  57. let trueSampleRate = trueSampleRates[Math.floor(trueSampleRates.length / 2)];
  58. if (trueSampleRate > COMMON_SAMPLE_RATES[0]) {
  59. trueSampleRate = findClosestNumber(trueSampleRate, COMMON_SAMPLE_RATES);
  60. }
  61. let maxSafeSampleRate = safeSampleRates[0];
  62. if (maxSafeSampleRate > COMMON_SAMPLE_RATES[0]) {
  63. maxSafeSampleRate = findClosestNumber(maxSafeSampleRate, COMMON_SAMPLE_RATES);
  64. }
  65. return {
  66. trueSampleRate: round(trueSampleRate, 4),
  67. maxSafeSampleRate: round(maxSafeSampleRate, 4),
  68. hoursOverLimit,
  69. };
  70. }