Skip to content

Conversation

@gastonfournier
Copy link
Contributor

About the changes

This could backfire if our terraform integration might be expecting to see new data after creating a project... we'd need cache invalidation and that starts to feel like a lot of complexity...

@vercel
Copy link

vercel bot commented Nov 5, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Updated (UTC)
unleash-docs Ignored Ignored Preview Nov 6, 2025 8:04am

@github-actions
Copy link
Contributor

github-actions bot commented Nov 5, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

this.timer = (action) =>
metricsHelper.wrapTimer(eventBus, DB_TIME, {
store: 'project',
store: 'project-read-model',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to differentiate this from the project store that also has a similar query

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 236 to 265
const cacheTtl = secondsToMilliseconds(20);
this.getProjectsForAdminUiCached = memoizee(
(
projectsQuery?: IProjectQuery & IProjectsQuery,
projectsUserId?: number,
) =>
this.projectReadModel.getProjectsForAdminUi(
projectsQuery,
projectsUserId,
),
{
promise: true,
maxAge: cacheTtl,
normalizer: ([projectsQuery, projectsUserId]) =>
this.buildProjectsCacheKey(
projectsQuery as
| (IProjectQuery & IProjectsQuery)
| undefined,
projectsUserId as number | undefined,
),
},
);
}

async getProjects(
query?: IProjectQuery & IProjectsQuery,
userId?: number,
): Promise<ProjectForUi[]> {
const projects = await this.projectReadModel.getProjectsForAdminUi(
query,
userId,
);
const projects = await this.getProjectsForAdminUiCached(query, userId);

Choose a reason for hiding this comment

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

P1 Badge Invalidate cached project list after mutations

The memoized getProjectsForAdminUiCached now caches the project list per user for 20 s but the cache is never cleared when the underlying data changes. After a client loads the list once, creating or archiving a project or toggling a favorite within the same process will still return the cached result at the next getProjects() call, so the new project or updated favorite flag will not appear until the TTL expires. This is user-visible for the admin UI and automation that expects immediate consistency (the commit message already hints at Terraform usage). Consider clearing the memoization in the project mutation flows or otherwise invalidating the cache when projects change.

Useful? React with 👍 / 👎.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 should be fixed in a0c5b41

Copy link
Contributor

@kwasniew kwasniew left a comment

Choose a reason for hiding this comment

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

shouldn't we have CachedProjectReadModel that is a drop-in replacement for ProjectReadModel? IMO it would be much more clear than another method in the project service that is already overloaded with capabilities

return projects;
}

private buildProjectsCacheKey(
Copy link
Member

Choose a reason for hiding this comment

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

Pretty sure this will work but it feels a bit dirty to be using big ass JSON blobs as hash keys, we can't just do this?

private buildProjectsCacheKey(
    query?: IProjectQuery & IProjectsQuery,
    userId?: number,
): string {
    const hash = createHash('sha256');
    if (query?.ids) {
        const sorted = [...query.ids].sort();
        hash.update(sorted.join(','));
    }
    hash.update(String(userId ?? ''));
    hash.update(String(query?.id ?? ''));
    hash.update(String(query?.archived ?? ''));
    return hash.digest('base64url');
}

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, we could just concatenate strings... hsould be better, will do!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But I like your approach

@gastonfournier
Copy link
Contributor Author

shouldn't we have CachedProjectReadModel that is a drop-in replacement for ProjectReadModel? IMO it would be much more clear than another method in the project service that is already overloaded with capabilities

I think the idea is to always return the cached version. It should be smart enough to know when to query the cache or not, but it's still a workaround, to me the problem is having the API overloaded with features that can be different resources. So I prefer to keep it yanky for now.

I'm also tempted to just enable this to the customer having issues

@gastonfournier gastonfournier moved this from New to In Progress in Issues and PRs Nov 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

4 participants