FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

modules: prevent race condition while combining import and require by BridgeAR · Pull Request #27674 · nodejs/node · GitHub

/ node Public

modules: prevent race condition while combining import and require - #27674

Closed
BridgeAR wants to merge 1 commit into
nodejs:masterfrom
BridgeAR:fix-potential-race-condition
Closed

modules: prevent race condition while combining import and require#27674
BridgeAR wants to merge 1 commit into
nodejs:masterfrom
BridgeAR:fix-potential-race-condition

Conversation

Copy link
Copy Markdown
Member

This switches the async file read to the sync version to prevent
the file potentially being read multiple times during the actual
loading phase (that could happen if the translator is called multiple
times on the same file while the file read is not complete).

This is not critical (reading the files multiple times should be fine)
but it came up by eslint using the require-atomic-updates rule.

An alternative would be to use a map with all currently loading
files and each entry contains a promise that is resolved as soon as
the module is fully loaded. That way it could be awaited and the async
file read would still be fine. I have no strong opinion about either
solution @nodejs/modules-active-members.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

nodejs-github-bot commented May 13, 2019
edited by BridgeAR
Loading

Copy link
Copy Markdown
Collaborator

Copy link
Copy Markdown
Member

An alternative would be to use a map with all currently loading
files and each entry contains a promise that is resolved as soon as
the module is fully loaded. That way it could be awaited and the async
file read would still be fine.

Yes, I think that’s a better solution – it’s great that we now have a module system that can read asynchronously from the disk.

Copy link
Copy Markdown
Contributor

I'm not sure I understand why such a map is needed though? The loader registry exactly is that map as far as I'm concerned. Yes other parallel JSON loading mechanisms don't get to share it, but that doesn't seem like a problem to me?

BridgeAR commented May 13, 2019
edited
Loading

Copy link
Copy Markdown
Member Author

@guybedford it should impact things like (without testing - I do not know the esm code well and I just had a very brief look at this):

import foo from './foo.json'
import foo from './foo.json'

Here both imports would start reading the file from disk because the second import call is happening while the first is still loading. Instead, it should resolve to the same file read.

Copy link
Copy Markdown
Contributor

@BridgeAR these would resolve to the same module record, where the translator is only applied once per module record, so it wouldn't impact this case - there would only be one read.

Copy link
Copy Markdown
Member Author

@guybedford yes, I see. moduleMap catches this early on. That's something that the eslint rule is not able to know about. Thanks for pointing that out.

It would AFAIK still be a race condition for the following case though (both files are in the same directory and are loaded directly after each other):

// file1.js
require('./foo.json')

// file2.mjs
import foo from './foo.json'

I guess it's unlikely that this happens but it's at least a possibility. We could add a ignore statement in case we want to activate the eslint rule instead of adding the extra check. Shall I close the PR or do you think this should be addressed?

BridgeAR requested a review from guybedford May 16, 2019 11:05

Copy link
Copy Markdown
Member Author

@nodejs/modules-active-members PTAL at #27674 (comment)

Copy link
Copy Markdown
Contributor

@BridgeAR note that require() will inject its loads immediately into the ESM loader to ensure well-defined snapshotting, So even that example you provide is not race condition and will only do one JSON load.

That said, the converse is not currently true - import('./file.json'), require('./file.json') will result in a double load.

While I believe this is rare enough not to merit the need to make JSON loading sync (two stats for a very rare edge case is not a concern!), the fact that there is a different instance is a problem though, and this could possibly be resolved by having the require('./json') case "beat" the in-progress ESM load to ensure this.

I don't think the above should affect the discussion around whether JSON module parsing should be sync or async in Node.js, as that discussion should be based on performance arguments predominantly.

This comment has been minimized.

This checks if any require calls have happened to the same file
during the file read. If that was the case, it'll return the same
module instead of creating a new instance.
BridgeAR force-pushed the fix-potential-race-condition branch from 3a26941 to 3eb7036 Compare May 29, 2019 11:22

Copy link
Copy Markdown
Member Author

I just updated the code with your suggestion @guybedford PTAL.

BridgeAR added esm Issues and PRs related to the ECMAScript Modules implementation. module Issues and PRs related to the module subsystem. review wanted PRs that need reviews. labels May 29, 2019
BridgeAR changed the title modules: use sync file read to prevent race condition modules: prevent race condition while combining import and require May 29, 2019

guybedford 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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Seems the best approach to me.

BridgeAR added the author ready PRs that have at least one approval, no outstanding review comments, and a CI started. label Jun 4, 2019

This comment has been minimized.

This comment has been minimized.

This comment has been minimized.

Trott commented Jun 13, 2019

Copy link
Copy Markdown
Member

I imagine there's no reliable way to test this, is there?

BridgeAR added a commit to BridgeAR/node that referenced this pull request Jun 15, 2019
This checks if any require calls have happened to the same file
during the file read. If that was the case, it'll return the same
module instead of creating a new instance.

PR-URL: nodejs#27674
Reviewed-By: Guy Bedford <guybedford@gmail.com>

Copy link
Copy Markdown
Member Author

Landed in 9939762 🎉

BridgeAR closed this Jun 15, 2019

Copy link
Copy Markdown
Member Author

@Trott sorry, I just saw your comment after landing this. It is probably not easy to write a reliable test but it should be possible. Especially by using import.meta.url. I'll open a follow-up PR.

BridgeAR commented Jun 15, 2019
edited
Loading

Copy link
Copy Markdown
Member Author

@guybedford by our former discussion I would have expected the following to fail reliably before this patch but it always passes:

// Flags: --experimental-modules
import '../common/index.mjs';
import assert from 'assert';
import { createRequire } from 'module';
import json1 from '../fixtures/es-modules/json-cache/test.json';

const require = createRequire(import.meta.url);

const json2 = require('../fixtures/es-modules/json-cache/test.json');

assert.strictEqual(json1, json2);

Could you have a look at it / suggest an alternate test?


Update: I can't get it to fail with dynamic import on Node.js v12.4.0 either.

guybedford commented Jun 16, 2019
edited
Loading

Copy link
Copy Markdown
Contributor

@BridgeAR it seems the ESM loader was injecting into the CJS loader cache for JSON specifically already, while this fix was particularly about the race condition of a CJS require while the ESM load was "in progress" (eg the while JSON is still being fetched through an async read).

BridgeAR added a commit that referenced this pull request Jun 17, 2019
This checks if any require calls have happened to the same file
during the file read. If that was the case, it'll return the same
module instead of creating a new instance.

PR-URL: #27674
Reviewed-By: Guy Bedford <guybedford@gmail.com>
BridgeAR mentioned this pull request Jun 17, 2019
targos pushed a commit that referenced this pull request Jun 18, 2019
This checks if any require calls have happened to the same file
during the file read. If that was the case, it'll return the same
module instead of creating a new instance.

PR-URL: #27674
Reviewed-By: Guy Bedford <guybedford@gmail.com>
BridgeAR deleted the fix-potential-race-condition branch January 20, 2020 12:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs that have at least one approval, no outstanding review comments, and a CI started. esm Issues and PRs related to the ECMAScript Modules implementation. module Issues and PRs related to the module subsystem. review wanted PRs that need reviews.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants


Back | FazBrowse Home | New Git URL