HealthCheckManager.js 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import metrics from '@overleaf/metrics'
  2. import logger from '@overleaf/logger'
  3. import os from 'node:os'
  4. const HOST = os.hostname()
  5. const PID = process.pid
  6. let COUNT = 0
  7. const CHANNEL_MANAGER = {} // hash of event checkers by channel name
  8. const CHANNEL_ERROR = {} // error status by channel name
  9. export default class HealthCheckManager {
  10. // create an instance of this class which checks that an event with a unique
  11. // id is received only once within a timeout
  12. constructor(channel, timeout) {
  13. // unique event string
  14. this.channel = channel
  15. this.id = `host=${HOST}:pid=${PID}:count=${COUNT++}`
  16. // count of number of times the event is received
  17. this.count = 0
  18. // after a timeout check the status of the count
  19. this.handler = setTimeout(() => {
  20. this.setStatus()
  21. }, timeout || 1000)
  22. // use a timer to record the latency of the channel
  23. this.timer = new metrics.Timer(`event.${this.channel}.latency`)
  24. // keep a record of these objects to dispatch on
  25. CHANNEL_MANAGER[this.channel] = this
  26. }
  27. processEvent(id) {
  28. // if this is our event record it
  29. if (id === this.id) {
  30. this.count++
  31. if (this.timer) {
  32. this.timer.done()
  33. }
  34. this.timer = undefined // only time the latency of the first event
  35. }
  36. }
  37. setStatus() {
  38. // if we saw the event anything other than a single time that is an error
  39. const isFailing = this.count !== 1
  40. if (isFailing) {
  41. logger.err(
  42. { channel: this.channel, count: this.count, id: this.id },
  43. 'redis channel health check error'
  44. )
  45. }
  46. CHANNEL_ERROR[this.channel] = isFailing
  47. }
  48. // class methods
  49. static check(channel, id) {
  50. // dispatch event to manager for channel
  51. if (CHANNEL_MANAGER[channel]) {
  52. CHANNEL_MANAGER[channel].processEvent(id)
  53. }
  54. }
  55. static status() {
  56. // return status of all channels for logging
  57. return CHANNEL_ERROR
  58. }
  59. static isFailing() {
  60. // check if any channel status is bad
  61. for (const channel in CHANNEL_ERROR) {
  62. const error = CHANNEL_ERROR[channel]
  63. if (error === true) {
  64. return true
  65. }
  66. }
  67. return false
  68. }
  69. }