count_project_size.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import readline from 'readline'
  2. import { waitForDb, ObjectId, db } from '../app/src/infrastructure/mongodb.js'
  3. import ProjectEntityHandler from '../app/src/Features/Project/ProjectEntityHandler.js'
  4. import ProjectGetter from '../app/src/Features/Project/ProjectGetter.js'
  5. import Errors from '../app/src/Features/Errors/Errors.js'
  6. import FileStoreHandler from '../app/src/Features/FileStore/FileStoreHandler.js'
  7. // Handles a list of project IDs from stdin, one per line, and outputs the count of files and docs
  8. // in the project, along with the aggregated size in bytes for all files and docs.
  9. //
  10. // It outputs to stderr, so that the logging junk can be piped elsewhere - e.g., running like:
  11. // node scripts/count_project_size.mjs < /tmp/project_ids.txt /dev/null 2> /tmp/output.txt
  12. //
  13. // The output format is line-per-project with data separated by a single space, containing:
  14. // - projectId
  15. // - file count
  16. // - deleted files count
  17. // - doc count
  18. // - deleted docs count
  19. // - total size in bytes of (non deleted) files
  20. // - total size in bytes of (non deleted) docs
  21. async function countProjectFiles() {
  22. const rl = readline.createInterface({
  23. input: process.stdin,
  24. })
  25. for await (const projectId of rl) {
  26. try {
  27. const project = await ProjectGetter.promises.getProject(projectId)
  28. if (!project) {
  29. throw new Errors.NotFoundError('project not found')
  30. }
  31. const { files, docs } =
  32. ProjectEntityHandler.getAllEntitiesFromProject(project)
  33. const [fileSize, docSize] = await Promise.all([
  34. countFilesSize(files, projectId),
  35. countDocsSizes(docs),
  36. ])
  37. console.error(
  38. projectId,
  39. files.length,
  40. (project.deletedFiles && project.deletedFiles.length) || 0,
  41. docs.length,
  42. (project.deletedDocs && project.deletedDocs.length) || 0,
  43. fileSize,
  44. docSize
  45. )
  46. } catch (err) {
  47. if (err instanceof Errors.NotFoundError) {
  48. console.error(projectId, 'NOTFOUND')
  49. } else {
  50. console.log(projectId, 'ERROR', err.name, err.message)
  51. }
  52. }
  53. }
  54. }
  55. async function countFilesSize(files, projectId) {
  56. if (!files?.length > 0) {
  57. return 0
  58. }
  59. const ids = files.map(fileObject => fileObject.file._id)
  60. let totalFileSize = 0
  61. for (const fileId of ids) {
  62. const contentLength = await FileStoreHandler.promises.getFileSize(
  63. projectId,
  64. fileId
  65. )
  66. const size = parseInt(contentLength, 10)
  67. if (isNaN(size)) {
  68. throw new Error(
  69. `Unable to fetch file size for fileId=${fileId} and projectId=${projectId}`
  70. )
  71. }
  72. totalFileSize += size
  73. }
  74. return totalFileSize
  75. }
  76. async function countDocsSizes(docs) {
  77. if (!docs?.length > 0) {
  78. return 0
  79. }
  80. const ids = docs.map(docObject => docObject.doc._id)
  81. let totalDocSize = 0
  82. for (const docId of ids) {
  83. const result = await db.docs.aggregate([
  84. {
  85. $match: { _id: new ObjectId(docId) },
  86. },
  87. {
  88. $project: {
  89. lineSizeInBytes: {
  90. $reduce: {
  91. input: { $ifNull: ['$lines', []] },
  92. initialValue: 0,
  93. in: {
  94. $add: ['$$value', { $strLenBytes: '$$this' }],
  95. },
  96. },
  97. },
  98. },
  99. },
  100. ])
  101. const { lineSizeInBytes } = await result.next()
  102. if (isNaN(lineSizeInBytes)) {
  103. throw new Error(`Unable to fetch 'lineSizeInBytes' for docId=${docId}`)
  104. }
  105. totalDocSize += lineSizeInBytes
  106. }
  107. return totalDocSize
  108. }
  109. try {
  110. await waitForDb()
  111. await countProjectFiles()
  112. process.exit(0)
  113. } catch (error) {
  114. console.log('Aiee, something went wrong!', error)
  115. process.exit(1)
  116. }