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

OSOE-413: Lock to prevent error. #40

Merged
merged 2 commits into from
Oct 24, 2022
Merged

Conversation

sarahelsaig
Copy link
Member

OSOE-413

An unhandled exception has occurred while executing the request. System.InvalidOperationException: Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.

"An unhandled exception has occurred while executing the request. System.InvalidOperationException: Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct."

public static void RemoveBootstrap5(this IOptions<ResourceManagementOptions> resourceManagementOptions)
{
lock (_lock)
Copy link
Member

Choose a reason for hiding this comment

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

This will serialize this part of all requests using the theme in the app. Can't this happen once during app start, for example?

Copy link
Member Author

@sarahelsaig sarahelsaig Oct 24, 2022

Choose a reason for hiding this comment

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

I was unsure so I did a little experiment:
image

The breakpoint has tripped on every page load, which tells me that the IOptions<ResourceManagementOptions> is populated every request. I have no idea how else I could prevent this, since i don't have access to the ResourceManagementOptionsConfiguration that adds the ˙bootstrap stylesheet.

I don't think this is necessarily a bottleneck, unless you have a lot of extremely coincident page loads .

Copy link
Member

Choose a reason for hiding this comment

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

I don't understand how this can cause a concurrency issue, then. That means that this code should only run once per request, and other requests don't bother each other.

If it's somehow a concurrency issue within a single request, then this should rather lock on not a static object but rather something that's unique to a request and available during the whole request.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's impossible to be a concurrency issue within the request, because this all happens synchronously.

I think it's because IOptions<T> from services.Configure<T>() is a singleton:
image

Different requests could totally bother each others' singletons, because requests live in independent scopes, but they share their singletons.

Copy link
Member

Choose a reason for hiding this comment

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

If it's a singleton, then it's really strange that it's populated on every request. Are you sure?

Taking a step back, why do we need this removal at all? Seems like a hackish solution. We could just name our script resource Site as well like the stylesheet, and include that instead of bootstrap.

Copy link
Member Author

Choose a reason for hiding this comment

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

If it's a singleton, then it's really strange that it's populated on every request. Are you sure?

Like I said, I've experimentally verified that it does. We could probably reverse engineer how it gets populated, but that doesn't help us in the short-term.

Taking a step back, why do we need this removal at all?

We use the bootstrap coming from npm and imported to the site stylesheet via the base theme's scss. We don't want to import a duplicate, sometimes older version via the bootstrap resource. But the admin theme (and probably certain built-in widgets) do just that.

Seems like a hackish solution.

Yes, because the resource manager was never designed for removing resources registered in a different module, so we have to hack it.

We could just name our script resource Site as well like the stylesheet, and include that instead of bootstrap.

We already do that, but this standalone "boostrap" resource is needlessly referenced by others. Also you can't overwrite it, because the resources are grouped by module so even if you register an empty resource for "bootstrap" it will be in the Lombiq.BaseTheme's resource dictionary, leaving the one in the admin theme's resource dictionary intact and rendered anyway. We have been over this stuff already when I first created the base theme.

Copy link
Member

Choose a reason for hiding this comment

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

So we're overriding a resource used on the admin from the theme. This is not something we should do. Especially that we shouldn't update that Bootstrap version independently of Orchard, since all of the built-in Orchard admin features depend on the Bootstrap version provided by Orchard.

So, I don't think overriding the Bootstrap resource for the admin theme is a valid use-case. If certain built-in widgets do that for the frontend, then a better approach is to override those specific shapes instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't want to be pushy about it, but is this really the time to consider fundamentally changing how the project works? I only wanted to add a simple lock around the existing code so all the tests in dev stop failing. I'll be leaving for today soon and don't want to be a blocker on basically every OSOCE issue in existence.

Copy link
Member

Choose a reason for hiding this comment

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

I'm merging this as a hotfix, but there's a fundamental problem with the whole approach, so we need to fix that, yes. Once that's addressed, we won't need this hack either. Please work on that next.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have created the issue #41.

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.

2 participants