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

Avoid multiple refresh #136

Merged
merged 7 commits into from
Dec 2, 2024
Merged

Conversation

zhiyuanliang-ms
Copy link
Contributor

@zhiyuanliang-ms zhiyuanliang-ms commented Nov 28, 2024

Why this PR?

class Timer {
    #backoffEnd; // timestamp
    #interval;

    constructor(interval) {
        this.#interval = interval;
        this.#backoffEnd = Date.now();
    }

    canRefresh() {
        return Date.now() >= this.#backoffEnd;
    }

    reset() {
        this.#backoffEnd = Date.now() + this.#interval;
    }
}

async function refresh() {
    await f();
}

async function f() {
    if (!timer.canRefresh()) {
        console.log('timer rejects')
        return Promise.resolve(false);
    }
    console.log('f starts')
    await new Promise(resolve => setTimeout(resolve, 1000))
    console.log('f ends')
    timer.reset()
}

for (let i = 0; i < 3; i++) {
    refresh();
}

The above code snippet simulate the current implementation of refresh and it will produce the following output

f starts
f starts
f starts
f ends
f ends
f ends

The timer fails to prevent refresh operation from being executed multiple times during refresh interval.

Solution

Except for the web worker scenario, javascript is in general single thread. For an async function, it will be synchronously executed until an await occurs, then it will give the control to the event loop. So this should be atomic. So the solution is to maintain a boolean variable refreshInProgress to indicate whether there is any ongoing refresh call. The check/setting of the flag will be done synchronously so it should be concurrent/"thread" safe.

src/common/lock.ts Outdated Show resolved Hide resolved
@RichardChen820
Copy link

Do we really need to address concurrency issue in this method? The refresh method's job is very simple, attempt to refresh config once, the concurrency issue really need to be considered by the caller depends on how the caller use it.

@RichardChen820
Copy link

If we think it‘s of great value resolving it in the provider, I would rather like to have a new method something like ExclusiveRefresh to address it.

@Eskibear
Copy link
Member

the concurrency issue really need to be considered by the caller depends on how the caller use it.

That makes sense. Then this task can simply become to add some details to the API mentioning RefreshTimer, to make sure customers don't have the gap about the behavior.

export type AzureAppConfiguration = {
/**
* API to trigger refresh operation.
*/
refresh(): Promise<void>;

src/common/lock.ts Outdated Show resolved Hide resolved
@zhiyuanliang-ms zhiyuanliang-ms changed the title Add refresh lock Avoid multiple refresh Dec 2, 2024
@juniwang
Copy link
Contributor

juniwang commented Dec 2, 2024

The latest code is much simpler, I like it!

@zhiyuanliang-ms zhiyuanliang-ms merged commit 3f2ffeb into main Dec 2, 2024
6 checks passed
@zhiyuanliang-ms zhiyuanliang-ms deleted the zhiyuanliang/add-refresh-lock branch December 2, 2024 08:52
@zhiyuanliang-ms zhiyuanliang-ms mentioned this pull request Dec 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants