Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Fix" repl race condition #1677

Closed
wants to merge 9 commits into from
Closed

"Fix" repl race condition #1677

wants to merge 9 commits into from

Conversation

chrisdickinson
Copy link
Contributor

For some value of "fix."

Per #1634, having multiple CLI repls open could lead to inconsistencies in the JSON data being stored. This PR changes the internal repl so that it commits to a temporary file first, then renames it to the target file. @bnoordhuis noted that this will only work if the tmpdir and target dir are on the same mount, but I imagine the worst case is not any worse than conflicting, racing writes, and this should solve the 80% use case.

This PR also re-addresses the problem of non-terminal readline instances corrupting history. Now the internal repl always defaults the written history to [] (vs. trying to set the options in internal/repl as before.)

Additionally, the PR makes it so that repls that fail to load history (for whatever reason) don't crash out – they simply continue without persistent history support.

Finally, this adds the internal modules to the list of files checked by eslint.

Will fix up the commit log once review is over, as I suspect reviewers will have thoughts about the strategy I've taken here!

R=@indutny, @bnoordhuis

@chrisdickinson chrisdickinson added the repl Issues and PRs related to the REPL subsystem. label May 11, 2015
throw err;
console.log('Encountered error with persistent history support.');
return cliRepl.createInternalRepl(
process.env, false, function(err, repl) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seems like an odd style choice, but make jslint didn't complain!

Copy link
Contributor

Choose a reason for hiding this comment

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

The linter is set up pretty conservative right now. Not sure it has a rule for odd linebreaks, but it seems fine to me there.

if (!tmpPath) {
return ready(new Error('no tmpdir available'));
}
tmpPath = path.join(tmpPath, process.pid + '-' + 'repl.tmp');
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe 'node-repl-' + process.pid to make it clear which process created that file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@silverwind
Copy link
Contributor

Recently had the issue that the REPL failed to spawn because some error when reading the history file, so I'd appreciate this being landed. The linter changes should go, they're not needed anymore.

@ghost
Copy link

ghost commented Jun 5, 2015

Another thing that would make the REPL history much more reliable is if it didn't save json, only lines of text. A few lines of garbled history is much better than the REPL refusing to open because of history corruption.
I just ran into this bug again and had to go in and fix the history manually.

@silverwind
Copy link
Contributor

Totally agree, lines should be stored as lines.

useGlobal: true
};

if (parseInt(env.NODE_NO_READLINE)) {
opts.terminal = false;
}
if (parseInt(env.NODE_FORCE_READLINE)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we use the radix here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It shouldn't be a problem in this case – we're just detecting whether the number is "0" or not.

Copy link
Contributor

Choose a reason for hiding this comment

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

Then we can explicitly do that right? As it is, the intention of this code is not clear, IMO.

@chrisdickinson
Copy link
Contributor Author

@seishun (edit: wow, my mind isn't working this morning!) @silverwind @substack Yep, if I could turn back time I would have stored lines instead of JSON. Switching to lines is a bit hairier than it seems now, though. We'd have to include JSON loading as well as line-based loading, include line-based storage, and even then folks who use nvm/nave to switch between versions might get bitten by switching to a JSON-only version. Additionally we have to differentiate the line-based version from the JSON version to avoid false positives. If we do this we should tackle it as a separate issue, and most likely in a major version. Sorry I made it JSON, folks! Hindsight is 20/20.

@silverwind
Copy link
Contributor

How about parsing the file as lines, but save them as JSON? Might get us at least some fault tolerance.

Edit: i think this will get too messy, see below.

@silverwind
Copy link
Contributor

Or, you know, just drop JSON support in a major, with an automatic conversion to line-based history in place. People who are using persistent history are early adopters that read changelogs, and I don't think many users will downgrade to previous versions of iojs. nvm is mostly used to switch between 0.10, 0.12 and iojs. If they really hit a iojs version that doesn't support line-based history, they'll know what to do.

Let's do it right, while the feature is still young, imho.

@ivan
Copy link
Contributor

ivan commented Jun 9, 2015

Here's one reason you might want to keep storing lines as JSON: users can enter multi-line input in the REPL (e.g. type a { and hit enter), and it would be better to store an entire multi-line input as one entry in the history file. That's easy when you're storing JSON, but harder with plaintext lines (unless you decide to encode newlines as some other control character?). Unfortunately, right now each line of the multi-line input gets its own history entry, but keeping the JSON leaves it easier to fix.

@chrisdickinson
Copy link
Contributor Author

@bnoordhuis Could I get an LGTM on this (or a "this approach will not work?")

@brendanashworth
Copy link
Contributor

I don't think this PR is still applicable - https://github.com/chrisdickinson/io.js/commit/46e2d6848ef33461757a4c43939c8b3adbf10784 was replaced by #1686, and the race conditions were fixed in d200932...ed6c249. I'll close this for now, it can be reopened if I'm mistaken.

@Fishrock123
Copy link
Contributor

I didn't actually fix the race conditions. Doing that requires considerably more work. I did make them have minimal impact though.

@jasnell
Copy link
Member

jasnell commented Oct 22, 2015

@chrisdickinson @Fishrock123 ... any progress on this?

@Fishrock123
Copy link
Contributor

@jasnell After some changes that I did it's much less prone to catastrophic error, but it still could work properly. It's pretty complex though.

(It's unlikely that I understand enough about atomic file operations to do this properly.)

@jasnell
Copy link
Member

jasnell commented Nov 16, 2015

@chrisdickinson ... ping :-)

@jasnell jasnell added the stalled Issues and PRs that are stalled. label Nov 16, 2015
@chrisdickinson
Copy link
Contributor Author

I haven't really seen any complaints about history race conditions since (it might have faded into "general weirdness that folks expect out of history-backed REPLs".) Closing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
repl Issues and PRs related to the REPL subsystem. stalled Issues and PRs that are stalled.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants