fix: extract tarball to temp directory on Windows - #2846

Merged
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix
May 25, 2023
Merged

fix: extract tarball to temp directory on Windows#2846
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix

Conversation

@dsanders11

@dsanders11dsanders11 commented May 11, 2023

Copy link
Copy Markdown
Contributor
Checklist
  • npm install && npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines
Description of change

Fixes#2482.
Fixes#2484.
Fixes#2584.
Fixes#2683.
Fixes#2751.
Fixes#2832.

NOTE: This PR contains a temporary initial commit which reverts a PR which causes CI workflow failures on main, so that the tests for this PR can run clean. It should be removed before merging this PR. #2837 is currently trying to fix that issue.

node-tar unlinks file paths before writing to them on Windows, which causes errors when there are parallel installs going on which are extracting the headers tarball to devDir. This currently leads to random corruption of the cache at devDir with missing files and hanging files with <filename>.DELETE.<random> names. See more in-depth discussion about the underlying issue here.

This PR fixes the situation by:

  • Fixing usage of node-tar to watch for errors during extraction
  • Extracting the headers tarball on Windows to a temp directory and then copies the files over
  • Using the exponential-backoff package in case of collision (EBUSY) when copying the files to devDir
  • Bumps installVersion to force clearing of existing cached versions which might be corrupt on Windows

This is set up in commits which were pushed one at a time to this PR so that the CI workflows for each commit can be seen, confirming that the added tests fail until the final fix is applied.

Unfortunately the new tests, despite being simple, are a bit flaky, due to network requests to https://nodejs.org/download/release/. The tests also take significant longer (greater than 2x) on Windows with Node.js 18.x and 20.x - it's not clear why, but since it repros consistently with those versions, it seems likely to be some upstream regression in Node.js, possibly something to do with the fs module.

Comment threadlib/install.js
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
// copy over the files from the temp tarball extract directory to devDir
if (tarExtractDir !== devDir) {
await copyDirectory(tarExtractDir, devDir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy? This could still race condition if it's not atomic?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy?

Since there are parallel installs happening, there's no way to atomically rename to devDir. That directory is created (if it doesn't already exist) at the start of the install process to confirm permissions, so a direct rename of tarExtractDir to devDir would fail. Any rename process would need to first rename devDir, which opens a race condition where devDir disappears momentarily during an ongoing build, causing a failure. That was my original approach for fixing this issue, but I switched to the current implementation to close that race condition.

This could still race condition if it's not atomic?

The copyFile approach stays similar to the existing implementation, with node-tar overwriting the existing files when there are parallel installs going on. The main difference is that on Windows node-tar always unlinks the destination file path before writing to it to avoid certain conditions (same path multiple times in an archive) which don't exist for this usage.

I've stress tested this change (on CI and locally with a real package) with 35 parallel installs and didn't trip over any race conditions in practice.

Comment threadlib/install.js
Comment threadtest/test-install.js Outdated

@StefanStojanovicStefanStojanovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Since there are already 2 PRs addressing GitHub Action issues, we should make sure 6bb7cd6 is removed from here before landing it. Ideally, we'd first land a GitHub Actions fix, then rebase this PR, remove the mentioned commit and push changes.

Comment threadlib/install.js Outdated
@StefanStojanovic

Copy link
Copy Markdown
Contributor

@rvagg@cclauss I'm changing tests from tap to mocha in #2851. Since this PR adds some tests and it's already approved, what are your thoughts on landing it (without 6bb7cd6) so I can change those tests in my PR as well?

@cclauss

Copy link
Copy Markdown
Contributor

I have no objection to these changes but I am not a Windows user so I lack the skills to be a reviewer.

@StefanStojanovic

Copy link
Copy Markdown
Contributor

Hey, @dsanders11 can you prepare your branch for merging by removing 6bb7cd6 and potentially addressing #2846 (comment) and squashing some commits together (especially the ones addressing PR comments)?

@StefanStojanovicStefanStojanovic mentioned this pull request May 24, 2023
3 tasks
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
@dsanders11
dsanders11force-pushed the windows-parallel-installs-fix branch from 2bb914b to 9f9d7fbCompareMay 24, 2023 23:38
@dsanders11

Copy link
Copy Markdown
ContributorAuthor

@StefanStojanovic, done! Removed 6bb7cd6 and squashed it back down to the original three commits. For CI reference, 2bb914b was the last commit before removing 6bb7cd6, and it went green.

@StefanStojanovic
StefanStojanovic merged commit aaa117c into nodejs:mainMay 25, 2023
@dsanders11
dsanders11 deleted the windows-parallel-installs-fix branch August 3, 2023 23:00
lukekarrys pushed a commit that referenced this pull request Dec 2, 2024
* fix: check for errors while extracting downloaded tarball
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* test: parallel installs
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* fix: extract tarball to temp directory on Windows
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
---------
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants

@dsanders11@StefanStojanovic@cclauss@MarshallOfSound
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: extract tarball to temp directory on Windows - #2846

Merged
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix
May 25, 2023
Merged

fix: extract tarball to temp directory on Windows#2846
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix

Conversation

@dsanders11

@dsanders11dsanders11 commented May 11, 2023

Copy link
Copy Markdown
Contributor
Checklist
  • npm install && npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines
Description of change

Fixes#2482.
Fixes#2484.
Fixes#2584.
Fixes#2683.
Fixes#2751.
Fixes#2832.

NOTE: This PR contains a temporary initial commit which reverts a PR which causes CI workflow failures on main, so that the tests for this PR can run clean. It should be removed before merging this PR. #2837 is currently trying to fix that issue.

node-tar unlinks file paths before writing to them on Windows, which causes errors when there are parallel installs going on which are extracting the headers tarball to devDir. This currently leads to random corruption of the cache at devDir with missing files and hanging files with <filename>.DELETE.<random> names. See more in-depth discussion about the underlying issue here.

This PR fixes the situation by:

  • Fixing usage of node-tar to watch for errors during extraction
  • Extracting the headers tarball on Windows to a temp directory and then copies the files over
  • Using the exponential-backoff package in case of collision (EBUSY) when copying the files to devDir
  • Bumps installVersion to force clearing of existing cached versions which might be corrupt on Windows

This is set up in commits which were pushed one at a time to this PR so that the CI workflows for each commit can be seen, confirming that the added tests fail until the final fix is applied.

Unfortunately the new tests, despite being simple, are a bit flaky, due to network requests to https://nodejs.org/download/release/. The tests also take significant longer (greater than 2x) on Windows with Node.js 18.x and 20.x - it's not clear why, but since it repros consistently with those versions, it seems likely to be some upstream regression in Node.js, possibly something to do with the fs module.

Comment threadlib/install.js
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
// copy over the files from the temp tarball extract directory to devDir
if (tarExtractDir !== devDir) {
await copyDirectory(tarExtractDir, devDir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy? This could still race condition if it's not atomic?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy?

Since there are parallel installs happening, there's no way to atomically rename to devDir. That directory is created (if it doesn't already exist) at the start of the install process to confirm permissions, so a direct rename of tarExtractDir to devDir would fail. Any rename process would need to first rename devDir, which opens a race condition where devDir disappears momentarily during an ongoing build, causing a failure. That was my original approach for fixing this issue, but I switched to the current implementation to close that race condition.

This could still race condition if it's not atomic?

The copyFile approach stays similar to the existing implementation, with node-tar overwriting the existing files when there are parallel installs going on. The main difference is that on Windows node-tar always unlinks the destination file path before writing to it to avoid certain conditions (same path multiple times in an archive) which don't exist for this usage.

I've stress tested this change (on CI and locally with a real package) with 35 parallel installs and didn't trip over any race conditions in practice.

Comment threadlib/install.js
Comment threadtest/test-install.js Outdated

@StefanStojanovicStefanStojanovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Since there are already 2 PRs addressing GitHub Action issues, we should make sure 6bb7cd6 is removed from here before landing it. Ideally, we'd first land a GitHub Actions fix, then rebase this PR, remove the mentioned commit and push changes.

Comment threadlib/install.js Outdated
@StefanStojanovic

Copy link
Copy Markdown
Contributor

@rvagg@cclauss I'm changing tests from tap to mocha in #2851. Since this PR adds some tests and it's already approved, what are your thoughts on landing it (without 6bb7cd6) so I can change those tests in my PR as well?

@cclauss

Copy link
Copy Markdown
Contributor

I have no objection to these changes but I am not a Windows user so I lack the skills to be a reviewer.

@StefanStojanovic

Copy link
Copy Markdown
Contributor

Hey, @dsanders11 can you prepare your branch for merging by removing 6bb7cd6 and potentially addressing #2846 (comment) and squashing some commits together (especially the ones addressing PR comments)?

@StefanStojanovicStefanStojanovic mentioned this pull request May 24, 2023
3 tasks
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
@dsanders11
dsanders11force-pushed the windows-parallel-installs-fix branch from 2bb914b to 9f9d7fbCompareMay 24, 2023 23:38
@dsanders11

Copy link
Copy Markdown
ContributorAuthor

@StefanStojanovic, done! Removed 6bb7cd6 and squashed it back down to the original three commits. For CI reference, 2bb914b was the last commit before removing 6bb7cd6, and it went green.

@StefanStojanovic
StefanStojanovic merged commit aaa117c into nodejs:mainMay 25, 2023
@dsanders11
dsanders11 deleted the windows-parallel-installs-fix branch August 3, 2023 23:00
lukekarrys pushed a commit that referenced this pull request Dec 2, 2024
* fix: check for errors while extracting downloaded tarball
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* test: parallel installs
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* fix: extract tarball to temp directory on Windows
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
---------
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants

@dsanders11@StefanStojanovic@cclauss@MarshallOfSound
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: extract tarball to temp directory on Windows - #2846

Merged
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix
May 25, 2023
Merged

fix: extract tarball to temp directory on Windows#2846
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix

Conversation

@dsanders11

@dsanders11dsanders11 commented May 11, 2023

Copy link
Copy Markdown
Contributor
Checklist
  • npm install && npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines
Description of change

Fixes#2482.
Fixes#2484.
Fixes#2584.
Fixes#2683.
Fixes#2751.
Fixes#2832.

NOTE: This PR contains a temporary initial commit which reverts a PR which causes CI workflow failures on main, so that the tests for this PR can run clean. It should be removed before merging this PR. #2837 is currently trying to fix that issue.

node-tar unlinks file paths before writing to them on Windows, which causes errors when there are parallel installs going on which are extracting the headers tarball to devDir. This currently leads to random corruption of the cache at devDir with missing files and hanging files with <filename>.DELETE.<random> names. See more in-depth discussion about the underlying issue here.

This PR fixes the situation by:

  • Fixing usage of node-tar to watch for errors during extraction
  • Extracting the headers tarball on Windows to a temp directory and then copies the files over
  • Using the exponential-backoff package in case of collision (EBUSY) when copying the files to devDir
  • Bumps installVersion to force clearing of existing cached versions which might be corrupt on Windows

This is set up in commits which were pushed one at a time to this PR so that the CI workflows for each commit can be seen, confirming that the added tests fail until the final fix is applied.

Unfortunately the new tests, despite being simple, are a bit flaky, due to network requests to https://nodejs.org/download/release/. The tests also take significant longer (greater than 2x) on Windows with Node.js 18.x and 20.x - it's not clear why, but since it repros consistently with those versions, it seems likely to be some upstream regression in Node.js, possibly something to do with the fs module.

Comment threadlib/install.js
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
// copy over the files from the temp tarball extract directory to devDir
if (tarExtractDir !== devDir) {
await copyDirectory(tarExtractDir, devDir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy? This could still race condition if it's not atomic?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy?

Since there are parallel installs happening, there's no way to atomically rename to devDir. That directory is created (if it doesn't already exist) at the start of the install process to confirm permissions, so a direct rename of tarExtractDir to devDir would fail. Any rename process would need to first rename devDir, which opens a race condition where devDir disappears momentarily during an ongoing build, causing a failure. That was my original approach for fixing this issue, but I switched to the current implementation to close that race condition.

This could still race condition if it's not atomic?

The copyFile approach stays similar to the existing implementation, with node-tar overwriting the existing files when there are parallel installs going on. The main difference is that on Windows node-tar always unlinks the destination file path before writing to it to avoid certain conditions (same path multiple times in an archive) which don't exist for this usage.

I've stress tested this change (on CI and locally with a real package) with 35 parallel installs and didn't trip over any race conditions in practice.

Comment threadlib/install.js
Comment threadtest/test-install.js Outdated

@StefanStojanovicStefanStojanovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Since there are already 2 PRs addressing GitHub Action issues, we should make sure 6bb7cd6 is removed from here before landing it. Ideally, we'd first land a GitHub Actions fix, then rebase this PR, remove the mentioned commit and push changes.

Comment threadlib/install.js Outdated
@StefanStojanovic

Copy link
Copy Markdown
Contributor

@rvagg@cclauss I'm changing tests from tap to mocha in #2851. Since this PR adds some tests and it's already approved, what are your thoughts on landing it (without 6bb7cd6) so I can change those tests in my PR as well?

@cclauss

Copy link
Copy Markdown
Contributor

I have no objection to these changes but I am not a Windows user so I lack the skills to be a reviewer.

@StefanStojanovic

Copy link
Copy Markdown
Contributor

Hey, @dsanders11 can you prepare your branch for merging by removing 6bb7cd6 and potentially addressing #2846 (comment) and squashing some commits together (especially the ones addressing PR comments)?

@StefanStojanovicStefanStojanovic mentioned this pull request May 24, 2023
3 tasks
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
@dsanders11
dsanders11force-pushed the windows-parallel-installs-fix branch from 2bb914b to 9f9d7fbCompareMay 24, 2023 23:38
@dsanders11

Copy link
Copy Markdown
ContributorAuthor

@StefanStojanovic, done! Removed 6bb7cd6 and squashed it back down to the original three commits. For CI reference, 2bb914b was the last commit before removing 6bb7cd6, and it went green.

@StefanStojanovic
StefanStojanovic merged commit aaa117c into nodejs:mainMay 25, 2023
@dsanders11
dsanders11 deleted the windows-parallel-installs-fix branch August 3, 2023 23:00
lukekarrys pushed a commit that referenced this pull request Dec 2, 2024
* fix: check for errors while extracting downloaded tarball
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* test: parallel installs
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* fix: extract tarball to temp directory on Windows
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
---------
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants

@dsanders11@StefanStojanovic@cclauss@MarshallOfSound
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: extract tarball to temp directory on Windows - #2846

Merged
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix
May 25, 2023
Merged

fix: extract tarball to temp directory on Windows#2846
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix

Conversation

@dsanders11

@dsanders11dsanders11 commented May 11, 2023

Copy link
Copy Markdown
Contributor
Checklist
  • npm install && npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines
Description of change

Fixes#2482.
Fixes#2484.
Fixes#2584.
Fixes#2683.
Fixes#2751.
Fixes#2832.

NOTE: This PR contains a temporary initial commit which reverts a PR which causes CI workflow failures on main, so that the tests for this PR can run clean. It should be removed before merging this PR. #2837 is currently trying to fix that issue.

node-tar unlinks file paths before writing to them on Windows, which causes errors when there are parallel installs going on which are extracting the headers tarball to devDir. This currently leads to random corruption of the cache at devDir with missing files and hanging files with <filename>.DELETE.<random> names. See more in-depth discussion about the underlying issue here.

This PR fixes the situation by:

  • Fixing usage of node-tar to watch for errors during extraction
  • Extracting the headers tarball on Windows to a temp directory and then copies the files over
  • Using the exponential-backoff package in case of collision (EBUSY) when copying the files to devDir
  • Bumps installVersion to force clearing of existing cached versions which might be corrupt on Windows

This is set up in commits which were pushed one at a time to this PR so that the CI workflows for each commit can be seen, confirming that the added tests fail until the final fix is applied.

Unfortunately the new tests, despite being simple, are a bit flaky, due to network requests to https://nodejs.org/download/release/. The tests also take significant longer (greater than 2x) on Windows with Node.js 18.x and 20.x - it's not clear why, but since it repros consistently with those versions, it seems likely to be some upstream regression in Node.js, possibly something to do with the fs module.

Comment threadlib/install.js
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
// copy over the files from the temp tarball extract directory to devDir
if (tarExtractDir !== devDir) {
await copyDirectory(tarExtractDir, devDir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy? This could still race condition if it's not atomic?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy?

Since there are parallel installs happening, there's no way to atomically rename to devDir. That directory is created (if it doesn't already exist) at the start of the install process to confirm permissions, so a direct rename of tarExtractDir to devDir would fail. Any rename process would need to first rename devDir, which opens a race condition where devDir disappears momentarily during an ongoing build, causing a failure. That was my original approach for fixing this issue, but I switched to the current implementation to close that race condition.

This could still race condition if it's not atomic?

The copyFile approach stays similar to the existing implementation, with node-tar overwriting the existing files when there are parallel installs going on. The main difference is that on Windows node-tar always unlinks the destination file path before writing to it to avoid certain conditions (same path multiple times in an archive) which don't exist for this usage.

I've stress tested this change (on CI and locally with a real package) with 35 parallel installs and didn't trip over any race conditions in practice.

Comment threadlib/install.js
Comment threadtest/test-install.js Outdated

@StefanStojanovicStefanStojanovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Since there are already 2 PRs addressing GitHub Action issues, we should make sure 6bb7cd6 is removed from here before landing it. Ideally, we'd first land a GitHub Actions fix, then rebase this PR, remove the mentioned commit and push changes.

Comment threadlib/install.js Outdated
@StefanStojanovic

Copy link
Copy Markdown
Contributor

@rvagg@cclauss I'm changing tests from tap to mocha in #2851. Since this PR adds some tests and it's already approved, what are your thoughts on landing it (without 6bb7cd6) so I can change those tests in my PR as well?

@cclauss

Copy link
Copy Markdown
Contributor

I have no objection to these changes but I am not a Windows user so I lack the skills to be a reviewer.

@StefanStojanovic

Copy link
Copy Markdown
Contributor

Hey, @dsanders11 can you prepare your branch for merging by removing 6bb7cd6 and potentially addressing #2846 (comment) and squashing some commits together (especially the ones addressing PR comments)?

@StefanStojanovicStefanStojanovic mentioned this pull request May 24, 2023
3 tasks
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
@dsanders11
dsanders11force-pushed the windows-parallel-installs-fix branch from 2bb914b to 9f9d7fbCompareMay 24, 2023 23:38
@dsanders11

Copy link
Copy Markdown
ContributorAuthor

@StefanStojanovic, done! Removed 6bb7cd6 and squashed it back down to the original three commits. For CI reference, 2bb914b was the last commit before removing 6bb7cd6, and it went green.

@StefanStojanovic
StefanStojanovic merged commit aaa117c into nodejs:mainMay 25, 2023
@dsanders11
dsanders11 deleted the windows-parallel-installs-fix branch August 3, 2023 23:00
lukekarrys pushed a commit that referenced this pull request Dec 2, 2024
* fix: check for errors while extracting downloaded tarball
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* test: parallel installs
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* fix: extract tarball to temp directory on Windows
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
---------
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants

@dsanders11@StefanStojanovic@cclauss@MarshallOfSound
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: extract tarball to temp directory on Windows - #2846

Merged
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix
May 25, 2023
Merged

fix: extract tarball to temp directory on Windows#2846
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix

Conversation

@dsanders11

@dsanders11dsanders11 commented May 11, 2023

Copy link
Copy Markdown
Contributor
Checklist
  • npm install && npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines
Description of change

Fixes#2482.
Fixes#2484.
Fixes#2584.
Fixes#2683.
Fixes#2751.
Fixes#2832.

NOTE: This PR contains a temporary initial commit which reverts a PR which causes CI workflow failures on main, so that the tests for this PR can run clean. It should be removed before merging this PR. #2837 is currently trying to fix that issue.

node-tar unlinks file paths before writing to them on Windows, which causes errors when there are parallel installs going on which are extracting the headers tarball to devDir. This currently leads to random corruption of the cache at devDir with missing files and hanging files with <filename>.DELETE.<random> names. See more in-depth discussion about the underlying issue here.

This PR fixes the situation by:

  • Fixing usage of node-tar to watch for errors during extraction
  • Extracting the headers tarball on Windows to a temp directory and then copies the files over
  • Using the exponential-backoff package in case of collision (EBUSY) when copying the files to devDir
  • Bumps installVersion to force clearing of existing cached versions which might be corrupt on Windows

This is set up in commits which were pushed one at a time to this PR so that the CI workflows for each commit can be seen, confirming that the added tests fail until the final fix is applied.

Unfortunately the new tests, despite being simple, are a bit flaky, due to network requests to https://nodejs.org/download/release/. The tests also take significant longer (greater than 2x) on Windows with Node.js 18.x and 20.x - it's not clear why, but since it repros consistently with those versions, it seems likely to be some upstream regression in Node.js, possibly something to do with the fs module.

Comment threadlib/install.js
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
// copy over the files from the temp tarball extract directory to devDir
if (tarExtractDir !== devDir) {
await copyDirectory(tarExtractDir, devDir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy? This could still race condition if it's not atomic?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy?

Since there are parallel installs happening, there's no way to atomically rename to devDir. That directory is created (if it doesn't already exist) at the start of the install process to confirm permissions, so a direct rename of tarExtractDir to devDir would fail. Any rename process would need to first rename devDir, which opens a race condition where devDir disappears momentarily during an ongoing build, causing a failure. That was my original approach for fixing this issue, but I switched to the current implementation to close that race condition.

This could still race condition if it's not atomic?

The copyFile approach stays similar to the existing implementation, with node-tar overwriting the existing files when there are parallel installs going on. The main difference is that on Windows node-tar always unlinks the destination file path before writing to it to avoid certain conditions (same path multiple times in an archive) which don't exist for this usage.

I've stress tested this change (on CI and locally with a real package) with 35 parallel installs and didn't trip over any race conditions in practice.

Comment threadlib/install.js
Comment threadtest/test-install.js Outdated

@StefanStojanovicStefanStojanovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Since there are already 2 PRs addressing GitHub Action issues, we should make sure 6bb7cd6 is removed from here before landing it. Ideally, we'd first land a GitHub Actions fix, then rebase this PR, remove the mentioned commit and push changes.

Comment threadlib/install.js Outdated
@StefanStojanovic

Copy link
Copy Markdown
Contributor

@rvagg@cclauss I'm changing tests from tap to mocha in #2851. Since this PR adds some tests and it's already approved, what are your thoughts on landing it (without 6bb7cd6) so I can change those tests in my PR as well?

@cclauss

Copy link
Copy Markdown
Contributor

I have no objection to these changes but I am not a Windows user so I lack the skills to be a reviewer.

@StefanStojanovic

Copy link
Copy Markdown
Contributor

Hey, @dsanders11 can you prepare your branch for merging by removing 6bb7cd6 and potentially addressing #2846 (comment) and squashing some commits together (especially the ones addressing PR comments)?

@StefanStojanovicStefanStojanovic mentioned this pull request May 24, 2023
3 tasks
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
@dsanders11
dsanders11force-pushed the windows-parallel-installs-fix branch from 2bb914b to 9f9d7fbCompareMay 24, 2023 23:38
@dsanders11

Copy link
Copy Markdown
ContributorAuthor

@StefanStojanovic, done! Removed 6bb7cd6 and squashed it back down to the original three commits. For CI reference, 2bb914b was the last commit before removing 6bb7cd6, and it went green.

@StefanStojanovic
StefanStojanovic merged commit aaa117c into nodejs:mainMay 25, 2023
@dsanders11
dsanders11 deleted the windows-parallel-installs-fix branch August 3, 2023 23:00
lukekarrys pushed a commit that referenced this pull request Dec 2, 2024
* fix: check for errors while extracting downloaded tarball
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* test: parallel installs
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* fix: extract tarball to temp directory on Windows
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
---------
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants

@dsanders11@StefanStojanovic@cclauss@MarshallOfSound
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: extract tarball to temp directory on Windows - #2846

Merged
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix
May 25, 2023
Merged

fix: extract tarball to temp directory on Windows#2846
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix

Conversation

@dsanders11

@dsanders11dsanders11 commented May 11, 2023

Copy link
Copy Markdown
Contributor
Checklist
  • npm install && npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines
Description of change

Fixes#2482.
Fixes#2484.
Fixes#2584.
Fixes#2683.
Fixes#2751.
Fixes#2832.

NOTE: This PR contains a temporary initial commit which reverts a PR which causes CI workflow failures on main, so that the tests for this PR can run clean. It should be removed before merging this PR. #2837 is currently trying to fix that issue.

node-tar unlinks file paths before writing to them on Windows, which causes errors when there are parallel installs going on which are extracting the headers tarball to devDir. This currently leads to random corruption of the cache at devDir with missing files and hanging files with <filename>.DELETE.<random> names. See more in-depth discussion about the underlying issue here.

This PR fixes the situation by:

  • Fixing usage of node-tar to watch for errors during extraction
  • Extracting the headers tarball on Windows to a temp directory and then copies the files over
  • Using the exponential-backoff package in case of collision (EBUSY) when copying the files to devDir
  • Bumps installVersion to force clearing of existing cached versions which might be corrupt on Windows

This is set up in commits which were pushed one at a time to this PR so that the CI workflows for each commit can be seen, confirming that the added tests fail until the final fix is applied.

Unfortunately the new tests, despite being simple, are a bit flaky, due to network requests to https://nodejs.org/download/release/. The tests also take significant longer (greater than 2x) on Windows with Node.js 18.x and 20.x - it's not clear why, but since it repros consistently with those versions, it seems likely to be some upstream regression in Node.js, possibly something to do with the fs module.

Comment threadlib/install.js
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
// copy over the files from the temp tarball extract directory to devDir
if (tarExtractDir !== devDir) {
await copyDirectory(tarExtractDir, devDir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy? This could still race condition if it's not atomic?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy?

Since there are parallel installs happening, there's no way to atomically rename to devDir. That directory is created (if it doesn't already exist) at the start of the install process to confirm permissions, so a direct rename of tarExtractDir to devDir would fail. Any rename process would need to first rename devDir, which opens a race condition where devDir disappears momentarily during an ongoing build, causing a failure. That was my original approach for fixing this issue, but I switched to the current implementation to close that race condition.

This could still race condition if it's not atomic?

The copyFile approach stays similar to the existing implementation, with node-tar overwriting the existing files when there are parallel installs going on. The main difference is that on Windows node-tar always unlinks the destination file path before writing to it to avoid certain conditions (same path multiple times in an archive) which don't exist for this usage.

I've stress tested this change (on CI and locally with a real package) with 35 parallel installs and didn't trip over any race conditions in practice.

Comment threadlib/install.js
Comment threadtest/test-install.js Outdated

@StefanStojanovicStefanStojanovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Since there are already 2 PRs addressing GitHub Action issues, we should make sure 6bb7cd6 is removed from here before landing it. Ideally, we'd first land a GitHub Actions fix, then rebase this PR, remove the mentioned commit and push changes.

Comment threadlib/install.js Outdated
@StefanStojanovic

Copy link
Copy Markdown
Contributor

@rvagg@cclauss I'm changing tests from tap to mocha in #2851. Since this PR adds some tests and it's already approved, what are your thoughts on landing it (without 6bb7cd6) so I can change those tests in my PR as well?

@cclauss

Copy link
Copy Markdown
Contributor

I have no objection to these changes but I am not a Windows user so I lack the skills to be a reviewer.

@StefanStojanovic

Copy link
Copy Markdown
Contributor

Hey, @dsanders11 can you prepare your branch for merging by removing 6bb7cd6 and potentially addressing #2846 (comment) and squashing some commits together (especially the ones addressing PR comments)?

@StefanStojanovicStefanStojanovic mentioned this pull request May 24, 2023
3 tasks
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
@dsanders11
dsanders11force-pushed the windows-parallel-installs-fix branch from 2bb914b to 9f9d7fbCompareMay 24, 2023 23:38
@dsanders11

Copy link
Copy Markdown
ContributorAuthor

@StefanStojanovic, done! Removed 6bb7cd6 and squashed it back down to the original three commits. For CI reference, 2bb914b was the last commit before removing 6bb7cd6, and it went green.

@StefanStojanovic
StefanStojanovic merged commit aaa117c into nodejs:mainMay 25, 2023
@dsanders11
dsanders11 deleted the windows-parallel-installs-fix branch August 3, 2023 23:00
lukekarrys pushed a commit that referenced this pull request Dec 2, 2024
* fix: check for errors while extracting downloaded tarball
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* test: parallel installs
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* fix: extract tarball to temp directory on Windows
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
---------
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants

@dsanders11@StefanStojanovic@cclauss@MarshallOfSound
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: extract tarball to temp directory on Windows - #2846

Merged
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix
May 25, 2023
Merged

fix: extract tarball to temp directory on Windows#2846
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix

Conversation

@dsanders11

@dsanders11dsanders11 commented May 11, 2023

Copy link
Copy Markdown
Contributor
Checklist
  • npm install && npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines
Description of change

Fixes#2482.
Fixes#2484.
Fixes#2584.
Fixes#2683.
Fixes#2751.
Fixes#2832.

NOTE: This PR contains a temporary initial commit which reverts a PR which causes CI workflow failures on main, so that the tests for this PR can run clean. It should be removed before merging this PR. #2837 is currently trying to fix that issue.

node-tar unlinks file paths before writing to them on Windows, which causes errors when there are parallel installs going on which are extracting the headers tarball to devDir. This currently leads to random corruption of the cache at devDir with missing files and hanging files with <filename>.DELETE.<random> names. See more in-depth discussion about the underlying issue here.

This PR fixes the situation by:

  • Fixing usage of node-tar to watch for errors during extraction
  • Extracting the headers tarball on Windows to a temp directory and then copies the files over
  • Using the exponential-backoff package in case of collision (EBUSY) when copying the files to devDir
  • Bumps installVersion to force clearing of existing cached versions which might be corrupt on Windows

This is set up in commits which were pushed one at a time to this PR so that the CI workflows for each commit can be seen, confirming that the added tests fail until the final fix is applied.

Unfortunately the new tests, despite being simple, are a bit flaky, due to network requests to https://nodejs.org/download/release/. The tests also take significant longer (greater than 2x) on Windows with Node.js 18.x and 20.x - it's not clear why, but since it repros consistently with those versions, it seems likely to be some upstream regression in Node.js, possibly something to do with the fs module.

Comment threadlib/install.js
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
// copy over the files from the temp tarball extract directory to devDir
if (tarExtractDir !== devDir) {
await copyDirectory(tarExtractDir, devDir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy? This could still race condition if it's not atomic?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy?

Since there are parallel installs happening, there's no way to atomically rename to devDir. That directory is created (if it doesn't already exist) at the start of the install process to confirm permissions, so a direct rename of tarExtractDir to devDir would fail. Any rename process would need to first rename devDir, which opens a race condition where devDir disappears momentarily during an ongoing build, causing a failure. That was my original approach for fixing this issue, but I switched to the current implementation to close that race condition.

This could still race condition if it's not atomic?

The copyFile approach stays similar to the existing implementation, with node-tar overwriting the existing files when there are parallel installs going on. The main difference is that on Windows node-tar always unlinks the destination file path before writing to it to avoid certain conditions (same path multiple times in an archive) which don't exist for this usage.

I've stress tested this change (on CI and locally with a real package) with 35 parallel installs and didn't trip over any race conditions in practice.

Comment threadlib/install.js
Comment threadtest/test-install.js Outdated

@StefanStojanovicStefanStojanovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Since there are already 2 PRs addressing GitHub Action issues, we should make sure 6bb7cd6 is removed from here before landing it. Ideally, we'd first land a GitHub Actions fix, then rebase this PR, remove the mentioned commit and push changes.

Comment threadlib/install.js Outdated
@StefanStojanovic

Copy link
Copy Markdown
Contributor

@rvagg@cclauss I'm changing tests from tap to mocha in #2851. Since this PR adds some tests and it's already approved, what are your thoughts on landing it (without 6bb7cd6) so I can change those tests in my PR as well?

@cclauss

Copy link
Copy Markdown
Contributor

I have no objection to these changes but I am not a Windows user so I lack the skills to be a reviewer.

@StefanStojanovic

Copy link
Copy Markdown
Contributor

Hey, @dsanders11 can you prepare your branch for merging by removing 6bb7cd6 and potentially addressing #2846 (comment) and squashing some commits together (especially the ones addressing PR comments)?

@StefanStojanovicStefanStojanovic mentioned this pull request May 24, 2023
3 tasks
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
@dsanders11
dsanders11force-pushed the windows-parallel-installs-fix branch from 2bb914b to 9f9d7fbCompareMay 24, 2023 23:38
@dsanders11

Copy link
Copy Markdown
ContributorAuthor

@StefanStojanovic, done! Removed 6bb7cd6 and squashed it back down to the original three commits. For CI reference, 2bb914b was the last commit before removing 6bb7cd6, and it went green.

@StefanStojanovic
StefanStojanovic merged commit aaa117c into nodejs:mainMay 25, 2023
@dsanders11
dsanders11 deleted the windows-parallel-installs-fix branch August 3, 2023 23:00
lukekarrys pushed a commit that referenced this pull request Dec 2, 2024
* fix: check for errors while extracting downloaded tarball
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* test: parallel installs
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* fix: extract tarball to temp directory on Windows
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
---------
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants

@dsanders11@StefanStojanovic@cclauss@MarshallOfSound
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: extract tarball to temp directory on Windows - #2846

Merged
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix
May 25, 2023
Merged

fix: extract tarball to temp directory on Windows#2846
StefanStojanovic merged 3 commits into
nodejs:mainfrom
dsanders11:windows-parallel-installs-fix

Conversation

@dsanders11

@dsanders11dsanders11 commented May 11, 2023

Copy link
Copy Markdown
Contributor
Checklist
  • npm install && npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines
Description of change

Fixes#2482.
Fixes#2484.
Fixes#2584.
Fixes#2683.
Fixes#2751.
Fixes#2832.

NOTE: This PR contains a temporary initial commit which reverts a PR which causes CI workflow failures on main, so that the tests for this PR can run clean. It should be removed before merging this PR. #2837 is currently trying to fix that issue.

node-tar unlinks file paths before writing to them on Windows, which causes errors when there are parallel installs going on which are extracting the headers tarball to devDir. This currently leads to random corruption of the cache at devDir with missing files and hanging files with <filename>.DELETE.<random> names. See more in-depth discussion about the underlying issue here.

This PR fixes the situation by:

  • Fixing usage of node-tar to watch for errors during extraction
  • Extracting the headers tarball on Windows to a temp directory and then copies the files over
  • Using the exponential-backoff package in case of collision (EBUSY) when copying the files to devDir
  • Bumps installVersion to force clearing of existing cached versions which might be corrupt on Windows

This is set up in commits which were pushed one at a time to this PR so that the CI workflows for each commit can be seen, confirming that the added tests fail until the final fix is applied.

Unfortunately the new tests, despite being simple, are a bit flaky, due to network requests to https://nodejs.org/download/release/. The tests also take significant longer (greater than 2x) on Windows with Node.js 18.x and 20.x - it's not clear why, but since it repros consistently with those versions, it seems likely to be some upstream regression in Node.js, possibly something to do with the fs module.

Comment threadlib/install.js
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
// copy over the files from the temp tarball extract directory to devDir
if (tarExtractDir !== devDir) {
await copyDirectory(tarExtractDir, devDir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy? This could still race condition if it's not atomic?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we wouldn't want to rename / move here instead of copy?

Since there are parallel installs happening, there's no way to atomically rename to devDir. That directory is created (if it doesn't already exist) at the start of the install process to confirm permissions, so a direct rename of tarExtractDir to devDir would fail. Any rename process would need to first rename devDir, which opens a race condition where devDir disappears momentarily during an ongoing build, causing a failure. That was my original approach for fixing this issue, but I switched to the current implementation to close that race condition.

This could still race condition if it's not atomic?

The copyFile approach stays similar to the existing implementation, with node-tar overwriting the existing files when there are parallel installs going on. The main difference is that on Windows node-tar always unlinks the destination file path before writing to it to avoid certain conditions (same path multiple times in an archive) which don't exist for this usage.

I've stress tested this change (on CI and locally with a real package) with 35 parallel installs and didn't trip over any race conditions in practice.

Comment threadlib/install.js
Comment threadtest/test-install.js Outdated

@StefanStojanovicStefanStojanovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Since there are already 2 PRs addressing GitHub Action issues, we should make sure 6bb7cd6 is removed from here before landing it. Ideally, we'd first land a GitHub Actions fix, then rebase this PR, remove the mentioned commit and push changes.

Comment threadlib/install.js Outdated
@StefanStojanovic

Copy link
Copy Markdown
Contributor

@rvagg@cclauss I'm changing tests from tap to mocha in #2851. Since this PR adds some tests and it's already approved, what are your thoughts on landing it (without 6bb7cd6) so I can change those tests in my PR as well?

@cclauss

Copy link
Copy Markdown
Contributor

I have no objection to these changes but I am not a Windows user so I lack the skills to be a reviewer.

@StefanStojanovic

Copy link
Copy Markdown
Contributor

Hey, @dsanders11 can you prepare your branch for merging by removing 6bb7cd6 and potentially addressing #2846 (comment) and squashing some commits together (especially the ones addressing PR comments)?

@StefanStojanovicStefanStojanovic mentioned this pull request May 24, 2023
3 tasks
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
@dsanders11
dsanders11force-pushed the windows-parallel-installs-fix branch from 2bb914b to 9f9d7fbCompareMay 24, 2023 23:38
@dsanders11

Copy link
Copy Markdown
ContributorAuthor

@StefanStojanovic, done! Removed 6bb7cd6 and squashed it back down to the original three commits. For CI reference, 2bb914b was the last commit before removing 6bb7cd6, and it went green.

@StefanStojanovic
StefanStojanovic merged commit aaa117c into nodejs:mainMay 25, 2023
@dsanders11
dsanders11 deleted the windows-parallel-installs-fix branch August 3, 2023 23:00
lukekarrys pushed a commit that referenced this pull request Dec 2, 2024
* fix: check for errors while extracting downloaded tarball
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* test: parallel installs
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
* fix: extract tarball to temp directory on Windows
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
---------
Signed-off-by: David Sanders <dsanders11@ucsbalum.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants

@dsanders11@StefanStojanovic@cclauss@MarshallOfSound