projectStatsToSampleRates.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import round from 'lodash/round';
  2. import {SeriesApi} from 'sentry/types';
  3. import {findClosestNumber} from 'sentry/utils/findClosestNumber';
  4. import {Outcome} from 'sentry/views/organizationStats/types';
  5. import {quantityField} from '.';
  6. const MAX_PER_HOUR = 100 * 60 * 60;
  7. const COMMON_SAMPLE_RATES = [
  8. 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,
  9. 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,
  10. 0.95, 1,
  11. ].sort((a, z) => a - z);
  12. export function projectStatsToSampleRates(stats: SeriesApi | undefined): {
  13. hoursOverLimit?: number;
  14. maxSafeSampleRate?: number;
  15. trueSampleRate?: number;
  16. } {
  17. if (!stats) {
  18. return {
  19. trueSampleRate: undefined,
  20. maxSafeSampleRate: undefined,
  21. hoursOverLimit: undefined,
  22. };
  23. }
  24. const {groups, intervals} = stats;
  25. const hours: number[] = [];
  26. const trueSampleRates: number[] = [];
  27. const safeSampleRates: number[] = [];
  28. let hoursOverLimit = 0;
  29. // We do not take filtered and invalid into account
  30. const accepted = groups.find(g => g.by.outcome === Outcome.ACCEPTED)?.series[
  31. quantityField
  32. ];
  33. const clientDiscard = groups.find(g => g.by.outcome === Outcome.CLIENT_DISCARD)?.series[
  34. quantityField
  35. ];
  36. const rateLimited = groups.find(g => g.by.outcome === Outcome.RATE_LIMITED)?.series[
  37. quantityField
  38. ];
  39. intervals.forEach((_interval, index) => {
  40. const hourAccepted = accepted?.[index] ?? 0;
  41. const hourClientDiscard = clientDiscard?.[index] ?? 0;
  42. const hourRateLimited = rateLimited?.[index] ?? 0;
  43. const hourRejected = hourClientDiscard + hourRateLimited;
  44. const hourTotal = hourAccepted + hourRejected;
  45. const hourTotalCapped = Math.min(hourTotal, MAX_PER_HOUR);
  46. const trueSampleRate = hourTotal === 0 ? 1 : 1 - hourRejected / hourTotal;
  47. const safeSampleRate = hourTotal === 0 ? 1 : hourTotalCapped / hourTotal;
  48. hours.push(hourTotal);
  49. trueSampleRates.push(trueSampleRate);
  50. safeSampleRates.push(safeSampleRate);
  51. if (hourTotal > MAX_PER_HOUR) {
  52. hoursOverLimit += 1;
  53. }
  54. });
  55. hours.sort((a, z) => a - z);
  56. trueSampleRates.sort((a, z) => a - z);
  57. safeSampleRates.sort((a, z) => a - z);
  58. let trueSampleRate = trueSampleRates[Math.floor(trueSampleRates.length / 2)];
  59. if (trueSampleRate > COMMON_SAMPLE_RATES[0]) {
  60. trueSampleRate = findClosestNumber(trueSampleRate, COMMON_SAMPLE_RATES);
  61. }
  62. let maxSafeSampleRate = safeSampleRates[0];
  63. if (maxSafeSampleRate > COMMON_SAMPLE_RATES[0]) {
  64. maxSafeSampleRate = findClosestNumber(maxSafeSampleRate, COMMON_SAMPLE_RATES);
  65. }
  66. return {
  67. trueSampleRate: round(trueSampleRate, 4),
  68. maxSafeSampleRate: round(maxSafeSampleRate, 4),
  69. hoursOverLimit,
  70. };
  71. }