pr_27397.patch 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. diff --git a/services/history-v1/storage/scripts/back_fill_file_hash.mjs b/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  2. index c0fdda35d8f..09212d426e3 100644
  3. --- a/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  4. +++ b/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  5. @@ -83,7 +83,7 @@ ObjectId.cacheHexString = true
  6. */
  7. function parseArgs() {
  8. const PUBLIC_LAUNCH_DATE = new Date('2012-01-01T00:00:00Z')
  9. - const DEFAULT_OUTPUT_FILE = `file-migration-${new Date()
  10. + const DEFAULT_OUTPUT_FILE = `/var/log/overleaf/file-migration-${new Date()
  11. .toISOString()
  12. .replace(/[:.]/g, '_')}.log`
  13. @@ -208,7 +208,7 @@ is equivalent to
  14. PROCESS_HASHED_FILES: !args['skip-hashed-files'],
  15. PROCESS_BLOBS: !args['skip-existing-blobs'],
  16. DRY_RUN: args['dry-run'],
  17. - OUTPUT_FILE: args.output,
  18. + OUTPUT_FILE: args.report ? '-' : args.output,
  19. BATCH_RANGE_START,
  20. BATCH_RANGE_END,
  21. LOGGING_IDENTIFIER: args['logging-id'] || BATCH_RANGE_START,
  22. @@ -256,6 +256,9 @@ const LOGGING_INTERVAL = parseInt(process.env.LOGGING_INTERVAL || '60000', 10)
  23. const SLEEP_BEFORE_EXIT = parseInt(process.env.SLEEP_BEFORE_EXIT || '1000', 10)
  24. // Log output to a file
  25. +if (OUTPUT_FILE !== '-') {
  26. + console.warn(`Writing logs into ${OUTPUT_FILE}`)
  27. +}
  28. logger.initialize('file-migration', {
  29. streams: [
  30. {
  31. diff --git a/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs b/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  32. index f6f4a6fb76d..c661ae9bc3f 100644
  33. --- a/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  34. +++ b/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  35. @@ -501,6 +501,7 @@ describe('back_fill_file_hash script', function () {
  36. timeout: TIMEOUT - 500,
  37. env: {
  38. ...process.env,
  39. + AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE: '1',
  40. USER_FILES_BUCKET_NAME,
  41. SLEEP_BEFORE_EXIT: '1',
  42. ...env,
  43. @@ -516,6 +517,7 @@ describe('back_fill_file_hash script', function () {
  44. }
  45. result = { stdout, stderr, status: code }
  46. }
  47. + // Ensure no tmp folder is left behind.
  48. expect((await fs.promises.readdir('/tmp')).join(';')).to.not.match(
  49. /back_fill_file_hash/
  50. )
  51. diff --git a/services/history-v1/storage/scripts/back_fill_file_hash.mjs b/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  52. index 09212d426e3..de4fca51db4 100644
  53. --- a/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  54. +++ b/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  55. @@ -1373,7 +1373,18 @@ async function main() {
  56. console.warn('Done.')
  57. }
  58. +async function cleanupBufferDir() {
  59. + try {
  60. + // Perform non-recursive removal of the BUFFER_DIR. Individual files
  61. + // should get removed in parallel as part of batch processing.
  62. + await fs.promises.rmdir(BUFFER_DIR)
  63. + } catch (err) {
  64. + console.error(`cleanup of BUFFER_DIR=${BUFFER_DIR} failed`, err)
  65. + }
  66. +}
  67. +
  68. if (DISPLAY_REPORT) {
  69. + await cleanupBufferDir()
  70. console.warn('Displaying report...')
  71. await displayReport()
  72. process.exit(0)
  73. @@ -1384,13 +1395,7 @@ try {
  74. await main()
  75. } finally {
  76. printStats(true)
  77. - try {
  78. - // Perform non-recursive removal of the BUFFER_DIR. Individual files
  79. - // should get removed in parallel as part of batch processing.
  80. - await fs.promises.rmdir(BUFFER_DIR)
  81. - } catch (err) {
  82. - console.error(`cleanup of BUFFER_DIR=${BUFFER_DIR} failed`, err)
  83. - }
  84. + await cleanupBufferDir()
  85. }
  86. let code = 0
  87. diff --git a/services/history-v1/storage/scripts/back_fill_file_hash.mjs b/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  88. index de4fca51db4..e9a7721944c 100644
  89. --- a/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  90. +++ b/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  91. @@ -316,6 +316,7 @@ async function getStatsForCollection(
  92. projectsWithAllHashes: 0,
  93. fileCount: 0,
  94. fileWithHashCount: 0,
  95. + fileMissingInHistoryCount: 0,
  96. }
  97. // Pick a random sample of projects and estimate the number of files without hashes
  98. const result = await collection
  99. @@ -342,25 +343,43 @@ async function getStatsForCollection(
  100. const filesWithoutHash = fileTree.match(/\{"_id":"[0-9a-f]{24}"\}/g) || []
  101. // count the number of files with a hash, these are uniquely identified
  102. // by the number of "hash" strings due to the filtering
  103. - const filesWithHash = fileTree.match(/"hash"/g) || []
  104. + const filesWithHash = fileTree.match(/"hash":"[0-9a-f]{40}"/g) || []
  105. stats.fileCount += filesWithoutHash.length + filesWithHash.length
  106. stats.fileWithHashCount += filesWithHash.length
  107. stats.projectCount++
  108. stats.projectsWithAllHashes += filesWithoutHash.length === 0 ? 1 : 0
  109. + const projectId = project._id.toString()
  110. + const { blobs: perProjectBlobs } = await getProjectBlobsBatch([projectId])
  111. + const blobs = new Set(
  112. + (perProjectBlobs.get(projectId) || []).map(b => b.getHash())
  113. + )
  114. + const uniqueHashes = new Set(filesWithHash.map(m => m.slice(8, 48)))
  115. + for (const hash of uniqueHashes) {
  116. + if (blobs.has(hash) || GLOBAL_BLOBS.has(hash)) continue
  117. + stats.fileMissingInHistoryCount++
  118. + }
  119. }
  120. console.log(`Sampled stats for ${name}:`)
  121. const fractionSampled = stats.projectCount / collectionCount
  122. - const percentageSampled = (fractionSampled * 100).toFixed(1)
  123. + const percentageSampled = (fractionSampled * 100).toFixed(0)
  124. const fractionConverted = stats.projectsWithAllHashes / stats.projectCount
  125. - const percentageConverted = (fractionConverted * 100).toFixed(1)
  126. + const percentageConverted = (fractionConverted * 100).toFixed(0)
  127. + const fractionMissing = stats.fileMissingInHistoryCount / stats.fileCount
  128. + const percentageMissing = (fractionMissing * 100).toFixed(0)
  129. console.log(
  130. - `- Sampled ${name}: ${stats.projectCount} (${percentageSampled}%)`
  131. + `- Sampled ${name}: ${stats.projectCount} (${percentageSampled}% of all ${name})`
  132. )
  133. console.log(
  134. `- Sampled ${name} with all hashes present: ${stats.projectsWithAllHashes}`
  135. )
  136. console.log(
  137. - `- Percentage of ${name} converted: ${percentageConverted}% (estimated)`
  138. + `- Percentage of ${name} that need back-filling hashes: ${percentageConverted}% (estimated)`
  139. + )
  140. + console.log(
  141. + `- Sampled ${name} have ${stats.fileCount} files that need to be checked against the full project history system.`
  142. + )
  143. + console.log(
  144. + `- Sampled ${name} have ${stats.fileMissingInHistoryCount} files that need to be uploaded to the full project history system (estimating ${percentageMissing}% of all files).`
  145. )
  146. }
  147. @@ -369,13 +388,15 @@ async function getStatsForCollection(
  148. * including counts and estimated progress based on a sample.
  149. */
  150. async function displayReport() {
  151. - const projectsCountResult = await projectsCollection.countDocuments()
  152. + const projectsCountResult = await projectsCollection.estimatedDocumentCount()
  153. const deletedProjectsCountResult =
  154. - await deletedProjectsCollection.countDocuments()
  155. + await deletedProjectsCollection.estimatedDocumentCount()
  156. const sampleSize = 1000
  157. console.log('Current status:')
  158. - console.log(`- Projects: ${projectsCountResult}`)
  159. - console.log(`- Deleted projects: ${deletedProjectsCountResult}`)
  160. + console.log(`- Total number of projects: ${projectsCountResult}`)
  161. + console.log(
  162. + `- Total number of deleted projects: ${deletedProjectsCountResult}`
  163. + )
  164. console.log(`Sampling ${sampleSize} projects to estimate progress...`)
  165. await getStatsForCollection(
  166. sampleSize,
  167. diff --git a/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs b/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  168. index c661ae9bc3f..7248e74cb3f 100644
  169. --- a/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  170. +++ b/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  171. @@ -481,21 +481,14 @@ describe('back_fill_file_hash script', function () {
  172. /**
  173. * @param {Array<string>} args
  174. * @param {Record<string, string>} env
  175. - * @param {boolean} shouldHaveWritten
  176. - * @return {Promise<{result, stats: any}>}
  177. + * @return {Promise<{result: { stdout: string, stderr: string, status: number }, stats: any}>}
  178. */
  179. - async function tryRunScript(args = [], env = {}, shouldHaveWritten) {
  180. + async function rawRunScript(args = [], env = {}) {
  181. let result
  182. try {
  183. result = await promisify(execFile)(
  184. process.argv0,
  185. - [
  186. - 'storage/scripts/back_fill_file_hash.mjs',
  187. - '--output=-',
  188. - '--projects',
  189. - '--deleted-projects',
  190. - ...args,
  191. - ],
  192. + ['storage/scripts/back_fill_file_hash.mjs', ...args],
  193. {
  194. encoding: 'utf-8',
  195. timeout: TIMEOUT - 500,
  196. @@ -521,6 +514,20 @@ describe('back_fill_file_hash script', function () {
  197. expect((await fs.promises.readdir('/tmp')).join(';')).to.not.match(
  198. /back_fill_file_hash/
  199. )
  200. + return result
  201. + }
  202. +
  203. + /**
  204. + * @param {Array<string>} args
  205. + * @param {Record<string, string>} env
  206. + * @param {boolean} shouldHaveWritten
  207. + * @return {Promise<{result, stats: any}>}
  208. + */
  209. + async function tryRunScript(args = [], env = {}, shouldHaveWritten) {
  210. + const result = await rawRunScript(
  211. + ['--output=-', '--projects', '--deleted-projects', ...args],
  212. + env
  213. + )
  214. const extraStatsKeys = ['eventLoop', 'readFromGCSThroughputMiBPerSecond']
  215. const stats = JSON.parse(
  216. result.stderr
  217. @@ -1078,6 +1085,35 @@ describe('back_fill_file_hash script', function () {
  218. })
  219. commonAssertions(true)
  220. })
  221. + describe('report mode', function () {
  222. + let output
  223. + before('prepare environment', prepareEnvironment)
  224. + before('run script', async function () {
  225. + output = await rawRunScript(['--report'], {})
  226. + })
  227. + it('should print the report', () => {
  228. + expect(output.status).to.equal(0)
  229. + console.log(output.stdout)
  230. + expect(output.stdout).to.equal(`\
  231. +Current status:
  232. +- Total number of projects: 10
  233. +- Total number of deleted projects: 5
  234. +Sampling 1000 projects to estimate progress...
  235. +Sampled stats for projects:
  236. +- Sampled projects: 9 (90% of all projects)
  237. +- Sampled projects with all hashes present: 5
  238. +- Percentage of projects that need back-filling hashes: 56% (estimated)
  239. +- Sampled projects have 11 files that need to be checked against the full project history system.
  240. +- Sampled projects have 3 files that need to be uploaded to the full project history system (estimating 27% of all files).
  241. +Sampled stats for deleted projects:
  242. +- Sampled deleted projects: 4 (80% of all deleted projects)
  243. +- Sampled deleted projects with all hashes present: 3
  244. +- Percentage of deleted projects that need back-filling hashes: 75% (estimated)
  245. +- Sampled deleted projects have 2 files that need to be checked against the full project history system.
  246. +- Sampled deleted projects have 1 files that need to be uploaded to the full project history system (estimating 50% of all files).
  247. +`)
  248. + })
  249. + })
  250. describe('full run in dry-run mode', function () {
  251. let output
  252. diff --git a/services/history-v1/storage/scripts/back_fill_file_hash.mjs b/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  253. index e9a7721944c..9c2a9818680 100644
  254. --- a/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  255. +++ b/services/history-v1/storage/scripts/back_fill_file_hash.mjs
  256. @@ -79,7 +79,7 @@ ObjectId.cacheHexString = true
  257. */
  258. /**
  259. - * @return {{PROJECT_IDS_FROM: string, PROCESS_HASHED_FILES: boolean, LOGGING_IDENTIFIER: string, BATCH_RANGE_START: string, BATCH_RANGE_END: string, PROCESS_NON_DELETED_PROJECTS: boolean, PROCESS_DELETED_PROJECTS: boolean, PROCESS_BLOBS: boolean, DRY_RUN: boolean, OUTPUT_FILE: string, DISPLAY_REPORT: boolean}}
  260. + * @return {{PROJECT_IDS_FROM: string, PROCESS_HASHED_FILES: boolean, LOGGING_IDENTIFIER: string, BATCH_RANGE_START: string, BATCH_RANGE_END: string, PROCESS_NON_DELETED_PROJECTS: boolean, PROCESS_DELETED_PROJECTS: boolean, PROCESS_BLOBS: boolean, DRY_RUN: boolean, OUTPUT_FILE: string, DISPLAY_REPORT: boolean, CONCURRENCY: number, CONCURRENT_BATCHES: number, RETRIES: number, RETRY_DELAY_MS: number, RETRY_FILESTORE_404: boolean, BUFFER_DIR_PREFIX: string, STREAM_HIGH_WATER_MARK: number, LOGGING_INTERVAL: number, SLEEP_BEFORE_EXIT: number }}
  261. */
  262. function parseArgs() {
  263. const PUBLIC_LAUNCH_DATE = new Date('2012-01-01T00:00:00Z')
  264. @@ -95,6 +95,12 @@ function parseArgs() {
  265. { name: 'skip-hashed-files', type: Boolean },
  266. { name: 'skip-existing-blobs', type: Boolean },
  267. { name: 'from-file', type: String, defaultValue: '' },
  268. + { name: 'concurrency', type: Number, defaultValue: 10 },
  269. + { name: 'concurrent-batches', type: Number, defaultValue: 1 },
  270. + { name: 'stream-high-water-mark', type: Number, defaultValue: 1024 * 1024 },
  271. + { name: 'retries', type: Number, defaultValue: 10 },
  272. + { name: 'retry-delay-ms', type: Number, defaultValue: 100 },
  273. + { name: 'retry-filestore-404', type: Boolean },
  274. { name: 'dry-run', alias: 'n', type: Boolean },
  275. {
  276. name: 'output',
  277. @@ -114,6 +120,13 @@ function parseArgs() {
  278. defaultValue: new Date().toISOString(),
  279. },
  280. { name: 'logging-id', type: String, defaultValue: '' },
  281. + { name: 'logging-interval-ms', type: Number, defaultValue: 60_000 },
  282. + {
  283. + name: 'buffer-dir-prefix',
  284. + type: String,
  285. + defaultValue: '/tmp/back_fill_file_hash-',
  286. + },
  287. + { name: 'sleep-before-exit-ms', type: Number, defaultValue: 1_000 },
  288. ])
  289. // If no arguments are provided, display a usage message
  290. @@ -143,6 +156,8 @@ Logging options:
  291. (default: file-migration-<timestamp>.log)
  292. --logging-id <id> Identifier for logging
  293. (default: BATCH_RANGE_START)
  294. + --logging-interval-ms <ms> Interval for logging progres stats
  295. + (default: 60000, 1min)
  296. Batch range options:
  297. --BATCH_RANGE_START <date> Start date for processing
  298. @@ -150,10 +165,30 @@ Batch range options:
  299. --BATCH_RANGE_END <date> End date for processing
  300. (default: ${args.BATCH_RANGE_END})
  301. +Concurrency:
  302. + --concurrency <n> Number of files to process concurrently
  303. + (default: 10)
  304. + --concurrent-batches <n> Number of project batches to process concurrently
  305. + (default: 1)
  306. + --stream-high-water-mark n In-Memory buffering threshold
  307. + (default: 1MiB)
  308. +
  309. +Retries:
  310. + --retries <n> Number of times to retry processing a file
  311. + (default: 10)
  312. + --retry-delay-ms <ms> How long to wait before processing a file again
  313. + (default: 100, 100ms)
  314. + --retry-filestore-404 Retry downloading a file when receiving a 404
  315. + (default: false)
  316. +
  317. Other options:
  318. --report Display a report of the current status
  319. --dry-run, -n Perform a dry run without making changes
  320. --help, -h Show this help message
  321. + --buffer-dir-prefix <p> Folder/prefix for buffering files on disk
  322. + (default: ${args['buffer-dir-prefix']})
  323. + --sleep-before-exit-ms <n> Defer exiting from the script
  324. + (default: 1000, 1s)
  325. Typical usage:
  326. @@ -212,8 +247,17 @@ is equivalent to
  327. BATCH_RANGE_START,
  328. BATCH_RANGE_END,
  329. LOGGING_IDENTIFIER: args['logging-id'] || BATCH_RANGE_START,
  330. + LOGGING_INTERVAL: args['logging-interval-ms'],
  331. PROJECT_IDS_FROM: args['from-file'],
  332. DISPLAY_REPORT: args.report,
  333. + CONCURRENCY: args.concurrency,
  334. + CONCURRENT_BATCHES: args['concurrent-batches'],
  335. + STREAM_HIGH_WATER_MARK: args['stream-high-water-mark'],
  336. + RETRIES: args.retries,
  337. + RETRY_DELAY_MS: args['retry-delay-ms'],
  338. + RETRY_FILESTORE_404: args['retry-filestore-404'],
  339. + BUFFER_DIR_PREFIX: args['buffer-dir-prefix'],
  340. + SLEEP_BEFORE_EXIT: args['sleep-before-exit-ms'],
  341. }
  342. }
  343. @@ -229,6 +273,15 @@ const {
  344. LOGGING_IDENTIFIER,
  345. PROJECT_IDS_FROM,
  346. DISPLAY_REPORT,
  347. + CONCURRENCY,
  348. + CONCURRENT_BATCHES,
  349. + RETRIES,
  350. + RETRY_DELAY_MS,
  351. + RETRY_FILESTORE_404,
  352. + BUFFER_DIR_PREFIX,
  353. + STREAM_HIGH_WATER_MARK,
  354. + LOGGING_INTERVAL,
  355. + SLEEP_BEFORE_EXIT,
  356. } = parseArgs()
  357. // We need to handle the start and end differently as ids of deleted projects are created at time of deletion.
  358. @@ -236,24 +289,7 @@ if (process.env.BATCH_RANGE_START || process.env.BATCH_RANGE_END) {
  359. throw new Error('use --BATCH_RANGE_START and --BATCH_RANGE_END')
  360. }
  361. -// Concurrency for downloading from GCS and updating hashes in mongo
  362. -const CONCURRENCY = parseInt(process.env.CONCURRENCY || '100', 10)
  363. -const CONCURRENT_BATCHES = parseInt(process.env.CONCURRENT_BATCHES || '2', 10)
  364. -// Retries for processing a given file
  365. -const RETRIES = parseInt(process.env.RETRIES || '10', 10)
  366. -const RETRY_DELAY_MS = parseInt(process.env.RETRY_DELAY_MS || '100', 10)
  367. -
  368. -const RETRY_FILESTORE_404 = process.env.RETRY_FILESTORE_404 === 'true'
  369. -const BUFFER_DIR = fs.mkdtempSync(
  370. - process.env.BUFFER_DIR_PREFIX || '/tmp/back_fill_file_hash-'
  371. -)
  372. -// https://nodejs.org/api/stream.html#streamgetdefaulthighwatermarkobjectmode
  373. -const STREAM_HIGH_WATER_MARK = parseInt(
  374. - process.env.STREAM_HIGH_WATER_MARK || (64 * 1024).toString(),
  375. - 10
  376. -)
  377. -const LOGGING_INTERVAL = parseInt(process.env.LOGGING_INTERVAL || '60000', 10)
  378. -const SLEEP_BEFORE_EXIT = parseInt(process.env.SLEEP_BEFORE_EXIT || '1000', 10)
  379. +const BUFFER_DIR = fs.mkdtempSync(BUFFER_DIR_PREFIX)
  380. // Log output to a file
  381. if (OUTPUT_FILE !== '-') {
  382. @@ -416,7 +452,7 @@ async function displayReport() {
  383. )
  384. }
  385. -// Filestore endpoint location
  386. +// Filestore endpoint location (configured by /etc/overleaf/env.sh)
  387. const FILESTORE_HOST = process.env.FILESTORE_HOST || '127.0.0.1'
  388. const FILESTORE_PORT = process.env.FILESTORE_PORT || '3009'
  389. diff --git a/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs b/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  390. index 7248e74cb3f..601cea13b6a 100644
  391. --- a/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  392. +++ b/services/history-v1/test/acceptance/js/storage/back_fill_file_hash.test.mjs
  393. @@ -61,9 +61,8 @@ function objectIdFromTime(timestamp) {
  394. const PRINT_IDS_AND_HASHES_FOR_DEBUGGING = false
  395. -describe('back_fill_file_hash script', function () {
  396. +describe.only('back_fill_file_hash script', function () {
  397. this.timeout(TIMEOUT)
  398. - const USER_FILES_BUCKET_NAME = 'fake-user-files-gcs'
  399. const projectId0 = objectIdFromTime('2017-01-01T00:00:00Z')
  400. const projectId1 = objectIdFromTime('2017-01-01T00:01:00Z')
  401. @@ -480,24 +479,24 @@ describe('back_fill_file_hash script', function () {
  402. /**
  403. * @param {Array<string>} args
  404. - * @param {Record<string, string>} env
  405. * @return {Promise<{result: { stdout: string, stderr: string, status: number }, stats: any}>}
  406. */
  407. - async function rawRunScript(args = [], env = {}) {
  408. + async function rawRunScript(args = []) {
  409. let result
  410. try {
  411. result = await promisify(execFile)(
  412. process.argv0,
  413. - ['storage/scripts/back_fill_file_hash.mjs', ...args],
  414. + [
  415. + 'storage/scripts/back_fill_file_hash.mjs',
  416. + '--sleep-before-exit-ms=1',
  417. + ...args,
  418. + ],
  419. {
  420. encoding: 'utf-8',
  421. timeout: TIMEOUT - 500,
  422. env: {
  423. ...process.env,
  424. AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE: '1',
  425. - USER_FILES_BUCKET_NAME,
  426. - SLEEP_BEFORE_EXIT: '1',
  427. - ...env,
  428. LOG_LEVEL: 'warn', // Override LOG_LEVEL of acceptance tests
  429. },
  430. }
  431. @@ -519,15 +518,16 @@ describe('back_fill_file_hash script', function () {
  432. /**
  433. * @param {Array<string>} args
  434. - * @param {Record<string, string>} env
  435. * @param {boolean} shouldHaveWritten
  436. * @return {Promise<{result, stats: any}>}
  437. */
  438. - async function tryRunScript(args = [], env = {}, shouldHaveWritten) {
  439. - const result = await rawRunScript(
  440. - ['--output=-', '--projects', '--deleted-projects', ...args],
  441. - env
  442. - )
  443. + async function tryRunScript(args = [], shouldHaveWritten) {
  444. + const result = await rawRunScript([
  445. + '--output=-',
  446. + '--projects',
  447. + '--deleted-projects',
  448. + ...args,
  449. + ])
  450. const extraStatsKeys = ['eventLoop', 'readFromGCSThroughputMiBPerSecond']
  451. const stats = JSON.parse(
  452. result.stderr
  453. @@ -558,12 +558,11 @@ describe('back_fill_file_hash script', function () {
  454. /**
  455. * @param {Array<string>} args
  456. - * @param {Record<string, string>} env
  457. * @param {boolean} shouldHaveWritten
  458. * @return {Promise<{result, stats: any}>}
  459. */
  460. - async function runScript(args = [], env = {}, shouldHaveWritten = true) {
  461. - const { stats, result } = await tryRunScript(args, env, shouldHaveWritten)
  462. + async function runScript(args = [], shouldHaveWritten = true) {
  463. + const { stats, result } = await tryRunScript(args, shouldHaveWritten)
  464. if (result.status !== 0) {
  465. console.log(result)
  466. expect(result).to.have.property('status', 0)
  467. @@ -812,7 +811,6 @@ describe('back_fill_file_hash script', function () {
  468. it('should process nothing on re-run', async function () {
  469. const rerun = await runScript(
  470. !processHashedFiles ? ['--skip-hashed-files'] : [],
  471. - {},
  472. false
  473. )
  474. let stats = {
  475. @@ -937,10 +935,11 @@ describe('back_fill_file_hash script', function () {
  476. it('should gracefully handle fatal errors', async function () {
  477. mockFilestore.deleteObject(projectId0, fileId0)
  478. const t0 = Date.now()
  479. - const { stats, result } = await tryRunScript(['--skip-hashed-files'], {
  480. - RETRIES: '10',
  481. - RETRY_DELAY_MS: '1000',
  482. - })
  483. + const { stats, result } = await tryRunScript([
  484. + '--skip-hashed-files',
  485. + '--retries=10',
  486. + '--retry-delay-ms=1000',
  487. + ])
  488. const t1 = Date.now()
  489. expectNotFoundError(result, 'failed to process file')
  490. expect(result.status).to.equal(1)
  491. @@ -972,11 +971,12 @@ describe('back_fill_file_hash script', function () {
  492. value: { stats, result },
  493. },
  494. ] = await Promise.allSettled([
  495. - tryRunScript(['--skip-hashed-files'], {
  496. - RETRY_DELAY_MS: '100',
  497. - RETRIES: '60',
  498. - RETRY_FILESTORE_404: 'true', // 404s are the easiest to simulate in tests
  499. - }),
  500. + tryRunScript([
  501. + '--skip-hashed-files',
  502. + '--retries=60',
  503. + '--retry-delay-ms=1000',
  504. + '--retry-filestore-404',
  505. + ]),
  506. restoreFileAfter5s(),
  507. ])
  508. expectNotFoundError(result, 'failed to process file, trying again')
  509. @@ -998,9 +998,7 @@ describe('back_fill_file_hash script', function () {
  510. let output
  511. before('prepare environment', prepareEnvironment)
  512. before('run script', async function () {
  513. - output = await runScript(['--skip-hashed-files'], {
  514. - CONCURRENCY: '1',
  515. - })
  516. + output = await runScript(['--skip-hashed-files', '--concurrency=1'])
  517. })
  518. /**
  519. @@ -1067,10 +1065,10 @@ describe('back_fill_file_hash script', function () {
  520. let output1, output2
  521. before('prepare environment', prepareEnvironment)
  522. before('run script without hashed files', async function () {
  523. - output1 = await runScript(['--skip-hashed-files'], {})
  524. + output1 = await runScript(['--skip-hashed-files'])
  525. })
  526. before('run script with hashed files', async function () {
  527. - output2 = await runScript([], {})
  528. + output2 = await runScript([])
  529. })
  530. it('should print stats for the first run without hashed files', function () {
  531. expect(output1.stats).deep.equal(STATS_ALL)
  532. @@ -1089,7 +1087,7 @@ describe('back_fill_file_hash script', function () {
  533. let output
  534. before('prepare environment', prepareEnvironment)
  535. before('run script', async function () {
  536. - output = await rawRunScript(['--report'], {})
  537. + output = await rawRunScript(['--report'])
  538. })
  539. it('should print the report', () => {
  540. expect(output.status).to.equal(0)
  541. @@ -1127,13 +1125,7 @@ Sampled stats for deleted projects:
  542. .toArray()
  543. })
  544. before('run script', async function () {
  545. - output = await runScript(
  546. - ['--dry-run'],
  547. - {
  548. - CONCURRENCY: '1',
  549. - },
  550. - false
  551. - )
  552. + output = await runScript(['--dry-run', '--concurrency=1'], false)
  553. })
  554. it('should print stats for dry-run mode', function () {
  555. @@ -1174,9 +1166,7 @@ Sampled stats for deleted projects:
  556. let output
  557. before('prepare environment', prepareEnvironment)
  558. before('run script', async function () {
  559. - output = await runScript(['--skip-hashed-files'], {
  560. - CONCURRENCY: '10',
  561. - })
  562. + output = await runScript(['--skip-hashed-files', '--concurrency=10'])
  563. })
  564. it('should print stats', function () {
  565. expect(output.stats).deep.equal(STATS_ALL)
  566. @@ -1184,13 +1174,14 @@ Sampled stats for deleted projects:
  567. commonAssertions()
  568. })
  569. - describe('full run STREAM_HIGH_WATER_MARK=1MB', function () {
  570. + describe('full run STREAM_HIGH_WATER_MARK=64kiB', function () {
  571. let output
  572. before('prepare environment', prepareEnvironment)
  573. before('run script', async function () {
  574. - output = await runScript(['--skip-hashed-files'], {
  575. - STREAM_HIGH_WATER_MARK: (1024 * 1024).toString(),
  576. - })
  577. + output = await runScript([
  578. + '--skip-hashed-files',
  579. + `--stream-high-water-mark=${64 * 1024}`,
  580. + ])
  581. })
  582. it('should print stats', function () {
  583. expect(output.stats).deep.equal(STATS_ALL)
  584. @@ -1202,7 +1193,7 @@ Sampled stats for deleted projects:
  585. let output
  586. before('prepare environment', prepareEnvironment)
  587. before('run script', async function () {
  588. - output = await runScript([], {})
  589. + output = await runScript([])
  590. })
  591. it('should print stats', function () {
  592. expect(output.stats).deep.equal(
  593. @@ -1231,9 +1222,7 @@ Sampled stats for deleted projects:
  594. })
  595. let output
  596. before('run script', async function () {
  597. - output = await runScript(['--skip-hashed-files'], {
  598. - CONCURRENCY: '1',
  599. - })
  600. + output = await runScript(['--skip-hashed-files', '--concurrency=1'])
  601. })
  602. it('should print stats', function () {
  603. @@ -1252,20 +1241,18 @@ Sampled stats for deleted projects:
  604. let outputPart0, outputPart1
  605. before('prepare environment', prepareEnvironment)
  606. before('run script on part 0', async function () {
  607. - outputPart0 = await runScript(
  608. - ['--skip-hashed-files', `--BATCH_RANGE_END=${edge}`],
  609. - {
  610. - CONCURRENCY: '1',
  611. - }
  612. - )
  613. + outputPart0 = await runScript([
  614. + '--skip-hashed-files',
  615. + `--BATCH_RANGE_END=${edge}`,
  616. + '--concurrency=1',
  617. + ])
  618. })
  619. before('run script on part 1', async function () {
  620. - outputPart1 = await runScript(
  621. - ['--skip-hashed-files', `--BATCH_RANGE_START=${edge}`],
  622. - {
  623. - CONCURRENCY: '1',
  624. - }
  625. - )
  626. + outputPart1 = await runScript([
  627. + '--skip-hashed-files',
  628. + `--BATCH_RANGE_START=${edge}`,
  629. + '--concurrency=1',
  630. + ])
  631. })
  632. it('should print stats for part 0', function () {