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

Add unit tests for BaseStore #4404

Merged
merged 3 commits into from
Nov 23, 2021
Merged

Conversation

jakolehm
Copy link
Contributor

@jakolehm jakolehm commented Nov 23, 2021

  • adds unit tests for BaseStore
  • removes misleading async from onModelChange & saveToFile

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
@jakolehm jakolehm added the bug Something isn't working label Nov 23, 2021
@jakolehm jakolehm added this to the 5.3.0 milestone Nov 23, 2021
@jakolehm jakolehm requested a review from a team as a code owner November 23, 2021 09:16
@jakolehm jakolehm requested review from nevalla and removed request for a team November 23, 2021 09:16
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
msa0311
msa0311 previously approved these changes Nov 23, 2021
@ixrock
Copy link
Member

ixrock commented Nov 23, 2021

Might be the issue related to sindresorhus/conf#114 ?

Currently BaseStore.saveToFile() looks like this:

  protected async saveToFile(model: T) {
    logger.info(`[STORE]: SAVING ${this.path}`);

    // todo: update when fixed https://github.com/sindresorhus/conf/issues/114
    if (this.storeConfig) {
      for (const [key, value] of Object.entries(model)) {
        this.storeConfig.set(key, value);
      }
    }
  }

I don't know how internally conf work, but here we have to create multiple savings to file instead of one model update.

@@ -168,7 +168,7 @@ export abstract class BaseStore<T> extends Singleton {

protected async onModelChange(model: T) {
if (ipcMain) {
this.saveToFile(model); // save config file
await this.saveToFile(model); // save config file
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like this isn't the solution here. For me, it looks that the real issue is that onModelChange is called multiple times. Not sure though.

Copy link
Member

Choose a reason for hiding this comment

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

If writing to the storage file is sync why it could be a an issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If writing to the storage file is sync why it could be a an issue?

onModelChange can be triggered multiple times, if we don't await then the order of execution of sync writes might happen out of order.

Copy link
Member

@ixrock ixrock Nov 23, 2021

Choose a reason for hiding this comment

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

Ok. What if remove async from onModelChange? And probably from saveToFile.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. What if remove async from onModelChange? And probably from saveToFile.

It works as well but it modifies api behaviour (this class is exposed via extension api).

Copy link
Member

Choose a reason for hiding this comment

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

@Nokel81 yes, probably you're right.
Initially I thought there could be some timing issues, but maybe not..
This code produces expected result:

class Foo {
  constructor(){
    this.bar();
  }

  bar(): void {
    console.log("in Foo.bar");
  }
}

class Bat extends Foo {
  async bar(): Promise<void> {
    await new Promise(resolve => setTimeout(resolve, 500));
    super.bar();
    console.log("in Bat.bar");
  }
}

new Bat();

OUTPUT:

[LOG]: "in Foo.bar" 
[LOG]: "in Bat.bar" 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Outcome of this discussion: we need to remove async from these functions so that reaction will be sync.

@ixrock @Nokel81 right?

Copy link
Collaborator

Choose a reason for hiding this comment

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

That is what I believe yes.

Copy link
Member

Choose a reason for hiding this comment

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

@jakolehm yes, it could be best solution I guess.

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 have been testing different things and I'm quite certain now that this PR mainly just adds tests for BaseStore, nothing actually changed in the execution order. I still removed async because it's quite misleading in this context.

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
@jakolehm jakolehm changed the title Fix possible race condition in BaseStore persistence Add unit tests for BaseStore Nov 23, 2021
@jakolehm jakolehm added area/extension Something to related to the extension api chore and removed bug Something isn't working labels Nov 23, 2021
@jakolehm jakolehm merged commit 32dfc74 into master Nov 23, 2021
@jakolehm jakolehm deleted the fix-possible-race-cond-in-base-store branch November 23, 2021 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/extension Something to related to the extension api chore
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants