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 issue with default value in workspaceState/globalState #8424

Merged
merged 1 commit into from
Aug 28, 2020

Conversation

daimor
Copy link
Contributor

@daimor daimor commented Aug 24, 2020

What it does

Fixes issues described in #8435
The Issue with parsing JSON happens just after writeJSON with correct and valid data, but the file is very empty. Replacement to writeJSONSync solves the issue.

Another issue with getting value from workspaceState/globalState, if previously updated with undefined. Original Memento suppose to return defaultValue if no previous value or undefined.

How to test

Examples provided in the linked issue
Test code

			// Just clear start, completely nothing in globalState, yet
			val = globalState.get('test', "default") || "WTF";

			globalState.update('test1', undefined);
			val1 = globalState.get('test1', "default") || "WTF";

			globalState.update('test2', "somevalue");
			val2 = globalState.get('test2', "default") || "WTF";	

Expected values,

val="default"
val1="default"
val2="somevalue"

Review checklist

Reminder for reviewers

@@ -101,7 +101,7 @@ export class PluginsKeyValueStorage {
return {};
}
try {
return await fs.readJSON(pathToFile);
return fs.readJSONSync(pathToFile);
Copy link
Member

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.

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

@akosyakov akosyakov added the vscode issues related to VSCode compatibility label Aug 25, 2020
@@ -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];
Copy link
Member

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);
Copy link
Member

@paul-marechal paul-marechal Aug 26, 2020

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) => {
Copy link
Member

@akosyakov akosyakov Aug 27, 2020

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?

Copy link
Contributor Author

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.

@daimor daimor force-pushed the key-value-storage branch from 74e6495 to 156ae53 Compare August 27, 2020 13:52
@daimor
Copy link
Contributor Author

daimor commented Aug 27, 2020

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.

@daimor daimor changed the title Fix couple of issues with worspaceState/globalState Fix issue with default value in workspaceState/globalState Aug 27, 2020
Copy link
Member

@akosyakov akosyakov left a comment

Choose a reason for hiding this comment

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

thank you!

@akosyakov akosyakov merged commit e36195e into eclipse-theia:master Aug 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
vscode issues related to VSCode compatibility
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants