Skip to content

Commit 7925a51

Browse files
authored
feat: support concurrent installation of browsers (#3929)
A few details on locking registry to prohibit concurrent access: - locking is done by creating a `__dirlock` directory in the top-level of our registry. - since `__dirlock` directory does not match any of browser directories, old versions of the installer will ignore it - in case of concurrent access, installation will wait for a lock to be released for 10 minutes, periodically trying to grab the lock. If it fails to do so in 10 minutes, the installation will fail. Fixes #3912
1 parent 2fbe767 commit 7925a51

File tree

3 files changed

+55
-5
lines changed

3 files changed

+55
-5
lines changed

package-lock.json

Lines changed: 36 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"mime": "^2.4.6",
4444
"pngjs": "^5.0.0",
4545
"progress": "^2.0.3",
46+
"proper-lockfile": "^4.1.1",
4647
"proxy-from-env": "^1.1.0",
4748
"rimraf": "^3.0.2",
4849
"ws": "^7.3.1"
@@ -55,6 +56,7 @@
5556
"@types/node": "^10.17.28",
5657
"@types/pngjs": "^3.4.2",
5758
"@types/progress": "^2.0.3",
59+
"@types/proper-lockfile": "^4.1.1",
5860
"@types/proxy-from-env": "^1.0.1",
5961
"@types/rimraf": "^3.0.0",
6062
"@types/ws": "7.2.6",

src/install/installer.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import * as fs from 'fs';
1919
import * as path from 'path';
2020
import * as util from 'util';
2121
import * as removeFolder from 'rimraf';
22+
import * as lockfile from 'proper-lockfile';
2223
import * as browserPaths from '../utils/browserPaths';
2324
import * as browserFetcher from './browserFetcher';
2425
import { getFromENV } from '../utils/utils';
@@ -32,16 +33,29 @@ const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
3233
const removeFolderAsync = util.promisify(removeFolder);
3334

3435
export async function installBrowsersWithProgressBar(packagePath: string) {
35-
const browsersPath = browserPaths.browsersPath(packagePath);
36-
const linksDir = path.join(browsersPath, '.links');
37-
3836
if (getFromENV('PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD')) {
3937
browserFetcher.logPolitely('Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set');
4038
return false;
4139
}
40+
41+
const browsersPath = browserPaths.browsersPath(packagePath);
42+
await fsMkdirAsync(browsersPath, { recursive: true });
43+
const releaseLock = await lockfile.lock(browsersPath, {
44+
retries: {
45+
retries: 10,
46+
// Retry 20 times during 10 minutes with
47+
// exponential back-off.
48+
// See documentation at: https://www.npmjs.com/package/retry#retrytimeoutsoptions
49+
factor: 1.27579,
50+
},
51+
lockfilePath: path.join(browsersPath, '__dirlock'),
52+
});
53+
const linksDir = path.join(browsersPath, '.links');
54+
4255
await fsMkdirAsync(linksDir, { recursive: true });
4356
await fsWriteFileAsync(path.join(linksDir, sha1(packagePath)), packagePath);
4457
await validateCache(packagePath, browsersPath, linksDir);
58+
await releaseLock();
4559
}
4660

4761
async function validateCache(packagePath: string, browsersPath: string, linksDir: string) {

0 commit comments

Comments
 (0)