-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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 issue with default value in workspaceState/globalState #8424
Conversation
@@ -101,7 +101,7 @@ export class PluginsKeyValueStorage { | |||
return {}; | |||
} | |||
try { | |||
return await fs.readJSON(pathToFile); | |||
return fs.readJSONSync(pathToFile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should avoid to use sync method, since it blocks the backend.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we can replace it with fs.read
and fs.write
to have it async but creating proper not empty content?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, just looking now, how to avoid sync but still keep right file content.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found the core of the issue
https://stackoverflow.com/questions/30886217/node-js-fs-writefile-empties-the-file
so, I've changed it to save asynchronously to a temporary file (just suffix .tmp), and rename to the original file. And it solved the issue. Wanted to add a test, but did not manage to reproduce the issue there.
@@ -51,7 +51,11 @@ export class Memento implements theia.Memento { | |||
|
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |||
update(key: string, value: any): Promise<void> { | |||
this.cache[key] = value; | |||
if (value === undefined) { | |||
delete this.cache[key]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
if (error) { | ||
reject(error); | ||
} else { | ||
fs.rename(tmpFile, pathToFile, resolve); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Callback shouldn't be resolve
alone but something like
error => {
if (error) {
reject(error);
} else {
resolve();
}
}
Alternatively you could use Node's promises
fs apis if you find it simplier.
@@ -110,7 +110,16 @@ export class PluginsKeyValueStorage { | |||
|
|||
private async writeToFile(pathToFile: string, data: KeysToKeysToAnyValue): Promise<void> { | |||
await fs.ensureDir(path.dirname(pathToFile)); | |||
await fs.writeJSON(pathToFile, data); | |||
const tmpFile = pathToFile + '.tmp'; | |||
await new Promise((resolve, reject) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the solution looks strange to me. There should be a way to call only one fs method. Maybe don't use fs-extra
but fs
module directly and use JSON.stringify
to serialise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried already a few options, and this way in fact does not also. It fails with the error, .tmp files does not exist.
fs.promises
, does not help as well.
The main issue actually not here, but in subsequent read
, and it happens when OS did not finish an actual write, and the file is completely empty. I think the best way would be to use write to disk only as a way to keep file up to date, but read it only if have not changed since our last read. And even do not write if we have not changed anything.
Signed-off-by: Dmitry Maslennikov <[email protected]>
74e6495
to
156ae53
Compare
Decided to keep only one change here, about getting default value from globalState/workspaceState. The issue with an empty json file needs some re-work, and have to be solved separately. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you!
What it does
Fixes issues described in #8435
The Issue with parsing JSON happens just afterwriteJSON
with correct and valid data, but the file is very empty. Replacement towriteJSONSync
solves the issue.Another issue with getting value from
workspaceState
/globalState
, if previously updated withundefined
. Original Memento suppose to returndefaultValue
if no previous value or undefined.How to test
Examples provided in the linked issue
Test code
Expected values,
Review checklist
Reminder for reviewers