purge_non_logged_in_sessions.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. const RedisWrapper = require('@overleaf/redis-wrapper')
  2. const Settings = require('@overleaf/settings')
  3. const SessionManager = require('../app/src/Features/Authentication/SessionManager')
  4. const redis = RedisWrapper.createClient(Settings.redis.websessions)
  5. const argv = require('minimist')(process.argv.slice(2), {
  6. string: ['count'],
  7. boolean: ['dry-run', 'help'],
  8. alias: {
  9. count: 'c',
  10. 'dry-run': 'n',
  11. help: 'h',
  12. },
  13. })
  14. if (argv.help) {
  15. console.log(
  16. `Usage: node purge_non_logged_in_sessions.js [--count <count>] [--dry-run]
  17. --count <count> the number of keys to scan on each iteration (default 1000)
  18. --dry-run to not delete any keys
  19. --help to show this help
  20. Note: use --count=10000 to delete faster (this will impact redis performance,
  21. so use with caution)`
  22. )
  23. process.exit()
  24. }
  25. const scanCount = argv.count ? parseInt(argv.count, 10) : 1000
  26. const dryRun = argv['dry-run']
  27. console.log(`Scan count set to ${scanCount}`)
  28. if (dryRun) {
  29. console.log('Dry run, not deleting any keys')
  30. }
  31. // iterate over all redis keys matching sess:* and delete the ones
  32. // that are not logged in using async await and mget and mdel
  33. async function scanAndPurge() {
  34. let totalSessions = 0
  35. let totalDeletedSessions = 0
  36. const stream = redis.scanStream({
  37. match: 'sess:*',
  38. count: scanCount,
  39. })
  40. console.log('Starting scan...')
  41. for await (const resultKeys of stream) {
  42. if (resultKeys.length === 0) {
  43. continue // scan is allowed to return zero elements, the client should not consider the iteration complete
  44. }
  45. console.log(`Keys found, count: ${resultKeys.length}`)
  46. totalSessions += resultKeys.length
  47. const sessions = await redis.mget(resultKeys)
  48. const toDelete = []
  49. for (let i = 0; i < sessions.length; i++) {
  50. const resultKey = resultKeys[i]
  51. const session = sessions[i]
  52. if (!session) {
  53. continue
  54. }
  55. try {
  56. const sessionObject = JSON.parse(session)
  57. if (!SessionManager.isUserLoggedIn(sessionObject)) {
  58. totalDeletedSessions++
  59. toDelete.push(resultKey)
  60. }
  61. } catch (error) {
  62. console.error(`Error parsing session ${resultKeys[i]}: ${error}`)
  63. }
  64. }
  65. if (toDelete.length === 0) {
  66. continue
  67. }
  68. if (dryRun) {
  69. console.log(`Would delete ${toDelete.length} keys`)
  70. } else {
  71. await redis.del(toDelete)
  72. console.log(`Keys deleted so far: ${totalDeletedSessions}`)
  73. }
  74. }
  75. if (dryRun) {
  76. console.log(
  77. `Dry run: ${totalSessions} sessions checked, ${totalDeletedSessions} would have been deleted`
  78. )
  79. } else {
  80. console.log(
  81. `All ${totalSessions} sessions have been checked, ${totalDeletedSessions} deleted`
  82. )
  83. }
  84. redis.quit()
  85. }
  86. scanAndPurge().catch(err => {
  87. console.error(err)
  88. process.exit()
  89. })