Skip to content

getAssignableUsers and getMentionableUsers bypass globalState cache when fetch promise already exists #8669

@smit-p

Description

@smit-p

Bug Description

In src/github/folderRepositoryManager.ts, both getAssignableUsers() and getMentionableUsers() have an identical cache-bypass bug. On the first call, they correctly return globalStateCache ?? fetchPromise, allowing the UI to display cached data immediately while a background fetch updates the data. However, on subsequent calls (when the fetch promise already exists from a prior invocation), they skip the globalState cache entirely and return only the fetch promise, blocking the UI on the network request.

Affected Methods

getAssignableUsers()

async getAssignableUsers(clearCache?: boolean): Promise<{ [key: string]: IAccount[] }> {
    if (clearCache) {
        delete this._assignableUsers;
    }

    if (this._assignableUsers) {
        return this._assignableUsers;
    }

    const globalStateAssignableUsers = await this.getCachedFromGlobalState('assignableUsers');

    if (!this._fetchAssignableUsersPromise) {
        // First call: creates fetch promise
        this._fetchAssignableUsersPromise = new Promise(resolve => { /* ... */ });
        return globalStateAssignableUsers ?? this._fetchAssignableUsersPromise;  // ✅ Uses cache
    }

    return this._fetchAssignableUsersPromise;  // ❌ Ignores globalStateAssignableUsers
}

getMentionableUsers()

async getMentionableUsers(clearCache?: boolean): Promise<{ [key: string]: IAccount[] }> {
    if (clearCache) {
        delete this._mentionableUsers;
    }

    if (this._mentionableUsers) {
        return this._mentionableUsers;
    }

    const globalStateMentionableUsers = await this.getCachedFromGlobalState('mentionableUsers');

    if (!this._fetchMentionableUsersPromise) {
        this._fetchMentionableUsersPromise = this.createFetchMentionableUsersPromise();
        return globalStateMentionableUsers ?? this._fetchMentionableUsersPromise;  // ✅ Uses cache
    }

    return this._fetchMentionableUsersPromise;  // ❌ Ignores globalStateMentionableUsers
}

The Bug

Logic flow on first call (when _fetchPromise is undefined):

  1. Check in-memory cache → miss
  2. Load globalState cache from disk
  3. Enter if (!this._fetchPromise) block → create fetch promise
  4. Return globalStateCache ?? fetchPromise ✅ (cached data returned immediately)

Logic flow on subsequent calls (when _fetchPromise already exists):

  1. Check in-memory cache → miss (fetch hasn't completed yet, or in-memory was cleared)
  2. Load globalState cache from disk ← this succeeds, valid data is available
  3. Check if (!this._fetchPromise) → false, skip the block
  4. Return this._fetchPromise ❌ (blocks on network, ignores loaded cache)

Impact

On the first call after extension activation, cached user data is returned immediately while a background fetch refreshes it — correct behavior. On any subsequent call where the in-memory cache was cleared but the fetch promise still exists (e.g., after updateRepositories triggers getAssignableUsers(true)), the UI blocks on the network request even though valid cached data is sitting right there in globalStateAssignableUsers / globalStateMentionableUsers.

This causes noticeable delays when:

  • Assigning reviewers to a PR
  • Using @-mention autocomplete in PR comments/descriptions
  • Any UI interaction that triggers these methods after the initial load

This is especially impactful in enterprise environments with large organizations (10K+ members) behind corporate proxies or VPNs, where the assignable/mentionable user fetch can take significantly longer. During that window, every concurrent caller is forced to wait on the network rather than getting the instant disk-cached response.

Suggested Fix

For both methods, the final return should also check the globalState cache:

getAssignableUsers():

-    return this._fetchAssignableUsersPromise;
+    return globalStateAssignableUsers ?? this._fetchAssignableUsersPromise;

getMentionableUsers():

-    return this._fetchMentionableUsersPromise;
+    return globalStateMentionableUsers ?? this._fetchMentionableUsersPromise;

Version

Observed in v0.132.2.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions