StatsManager.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. const crypto = require('node:crypto')
  2. /**
  3. * Consistently sample a keyspace with a given sample percentage.
  4. * The same key will always produce a consistent percentile value that
  5. * can be compared against the sample percentage.
  6. * Example: if key is the userId and the samplePercentage is 10, then
  7. * we see all the activity for the 10% of users who are selected.
  8. *
  9. * @param {string} key - The unique identifier to be hashed and checked.
  10. * @param {number} samplePercentage - The percentage (0-100) of keys that should return true.
  11. * @returns {boolean} - True if the key is within the sample, false otherwise.
  12. */
  13. function sampleByHash(key, samplePercentage) {
  14. if (samplePercentage <= 0) {
  15. return false
  16. }
  17. const hash = crypto.createHash('md5').update(key).digest()
  18. const hashValue = hash.readUInt32BE(0)
  19. const percentile = Math.floor((hashValue / 0xffffffff) * 100)
  20. return percentile < samplePercentage
  21. }
  22. const EXCLUDED_METRICS_OPTS_PATHS = new Set(['health-check', 'clsi-perf'])
  23. /**
  24. * Determines whether a given request should be sampled based on user ID and sampling percentage.
  25. * The request will not be sampled if it lacks a user_id, if its metrics path is in the exclusion list,
  26. * or if the sampling percentage is zero or less.
  27. *
  28. * @param {object} request - The request object to check.
  29. * @param {string} [request.user_id] - The ID of the user making the request.
  30. * @param {object} [request.metricsOpts] - Metrics options for the request.
  31. * @param {string} [request.metricsOpts.path] - The path associated with the request metrics.
  32. * @param {number} samplingPercentage - The percentage of requests to sample (e.g., 10 for 10%).
  33. * @returns {boolean|undefined} The result from sampleByHash if the request is eligible for sampling, otherwise undefined.
  34. */
  35. function sampleRequest(request, samplingPercentage) {
  36. if (!request.user_id) {
  37. return
  38. }
  39. if (EXCLUDED_METRICS_OPTS_PATHS.has(request.metricsOpts?.path)) {
  40. return
  41. }
  42. if (samplingPercentage > 0) {
  43. return sampleByHash(request.user_id, samplingPercentage)
  44. }
  45. }
  46. module.exports = { sampleByHash, sampleRequest }