HealthCheckController.mjs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import RedisWrapper from '../../infrastructure/RedisWrapper.js'
  2. import settings from '@overleaf/settings'
  3. import logger from '@overleaf/logger'
  4. import UserGetter from '../User/UserGetter.js'
  5. import {
  6. SmokeTestFailure,
  7. runSmokeTests,
  8. } from './../../../../test/smoke/src/SmokeTests.js'
  9. const rclient = RedisWrapper.client('health_check')
  10. export default {
  11. check(req, res, next) {
  12. if (!settings.siteIsOpen || !settings.editorIsOpen) {
  13. // always return successful health checks when site is closed
  14. res.sendStatus(200)
  15. } else {
  16. // detach from express for cleaner stack traces
  17. setTimeout(() => runSmokeTestsDetached(req, res).catch(next))
  18. }
  19. },
  20. checkActiveHandles(req, res, next) {
  21. if (!(settings.maxActiveHandles > 0) || !process._getActiveHandles) {
  22. return next()
  23. }
  24. const activeHandlesCount = (process._getActiveHandles() || []).length
  25. if (activeHandlesCount > settings.maxActiveHandles) {
  26. logger.err(
  27. { activeHandlesCount, maxActiveHandles: settings.maxActiveHandles },
  28. 'exceeded max active handles, failing health check'
  29. )
  30. return res.sendStatus(500)
  31. } else {
  32. logger.debug(
  33. { activeHandlesCount, maxActiveHandles: settings.maxActiveHandles },
  34. 'active handles are below maximum'
  35. )
  36. next()
  37. }
  38. },
  39. checkApi(req, res, next) {
  40. rclient.healthCheck(err => {
  41. if (err) {
  42. logger.err({ err }, 'failed api redis health check')
  43. return res.sendStatus(500)
  44. }
  45. if (!settings.smokeTest.userId) {
  46. logger.err({}, 'smokeTest.userId is undefined in health check')
  47. return res.sendStatus(404)
  48. }
  49. UserGetter.getUserEmail(settings.smokeTest.userId, (err, email) => {
  50. if (err) {
  51. logger.err({ err }, 'failed api mongo health check')
  52. return res.sendStatus(500)
  53. }
  54. if (email == null) {
  55. logger.err({ err }, 'failed api mongo health check (no email)')
  56. return res.sendStatus(500)
  57. }
  58. res.sendStatus(200)
  59. })
  60. })
  61. },
  62. checkRedis(req, res, next) {
  63. return rclient.healthCheck(function (error) {
  64. if (error != null) {
  65. logger.err({ err: error }, 'failed redis health check')
  66. return res.sendStatus(500)
  67. } else {
  68. return res.sendStatus(200)
  69. }
  70. })
  71. },
  72. checkMongo(req, res, next) {
  73. return UserGetter.getUserEmail(
  74. settings.smokeTest.userId,
  75. function (err, email) {
  76. if (err != null) {
  77. logger.err({ err }, 'mongo health check failed, error present')
  78. return res.sendStatus(500)
  79. } else if (email == null) {
  80. logger.err(
  81. { err },
  82. 'mongo health check failed, no emai present in find result'
  83. )
  84. return res.sendStatus(500)
  85. } else {
  86. return res.sendStatus(200)
  87. }
  88. }
  89. )
  90. },
  91. }
  92. async function runSmokeTestsDetached(req, res) {
  93. function isAborted() {
  94. return req.destroyed
  95. }
  96. const stats = { start: new Date(), steps: [] }
  97. let status, response
  98. try {
  99. try {
  100. await runSmokeTests({ isAborted, stats })
  101. } finally {
  102. stats.end = new Date()
  103. stats.duration = stats.end - stats.start
  104. }
  105. status = 200
  106. response = { stats }
  107. } catch (e) {
  108. let err = e
  109. if (!(e instanceof SmokeTestFailure)) {
  110. err = new SmokeTestFailure('low level error', {}, e)
  111. }
  112. logger.err({ err, stats }, 'health check failed')
  113. status = 500
  114. response = { stats, error: err.message }
  115. }
  116. if (isAborted()) return
  117. res.status(status).json(response)
  118. }