-
Notifications
You must be signed in to change notification settings - Fork 568
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
[ENH] Browser Caching for Static Assets #5005
Comments
@mdmontesinos It should be noted that this enhancement will impact a variety of scenarios and could be considered a breaking behavioral change (as the framework will behave differently than it has traditionally). For local software development on modules/themes, it will result in static assets being cached which means developers will need to introduce versioning mechanisms into their code (which was never required previously) or they will need to hard-refresh their browser during development. For production environments, when upgrading the application/modules/themes there may be new versions of static assets which are included - however older versions will be cached, and as a result users may experience broken functionality after an upgrade (ie. they will need to hard-refresh their browser - which is a challenging scenario for many non-technical users). When uploading content files which replace existing files, if the direct path to the file is used (rather than the Files server) the old version will be cached and will be served rather than the new version. Based on these breaking changes I am hesitant to include this enhancement as it has the potential to create a lot of support problems. Some of these challenges can be mitigated through configuration ie. disable caching in development environments. Other challenges would require developers to change the way they develop (e. the Oqtane framework itself as well as third party modules and themes would need to use versioned assets). In order to avoid unexpected behavior, perhaps this feature would need to be opt-in ie. an option set per site. However this still would not solve the broader "upgrade" problem - as it would require asset versioning. |
@sbwalker Thanks for pointing out additional considerations about the topic. You're right that it has some complications, but I really believe this change is vital to enhance the capabilities of Oqtane. Almost all modern frameworks include mechanisms for assets caching, which greatly improves performance for the initial load. Now that we've confirmed that the "/files" server does not go through the static assets middleware, perhaps it would be possible to use the new MapStaticAssets capability to solve some of the issues presented. This would handle all the cache invalidation with fingerprinting so developers don't have to manually version assets and provide other improvements. Obviously, this also has some considerations:
I believe this takes care of most of the problems you described previously, at the expense of only affecting truly static assets directly bundled from modules/themes and not from the dynamic file management system in Oqtane. Still, a big performance boost. Also, it could also be made opt-in per site just in case. Please let me know your opinion about this. |
@mdmontesinos by default when you build a .NET 9 project in it will produce a static asset manifest in the /bin folder named: {AssemblyName}.staticwebassets.endpoints.json This file could easily be included by the debug.cmd and *.nuspec files in the Module/Theme Package project so that it is deployed in the same way as standard .NET assemblies MapStaticAssets has the ability to point to multiple manifest files, so they do not need to be combined into a single file on startup - instead all individual manifest files could be registered during app startup. I really wish Microsoft had implemented MapStaticAssets using a different approach. Rather than optimizing during build, it would have been possible to lazily optimize assets at run-time ie. when a static asset is first requested it would be dynamically optimized. This approach would have avoided the need for static manifests and would support static assets deployed in ANY manner to a server, including user uploaded files. It would also be much more efficient, as instead of loading a collection of every possible static asset into memory during startup, it would progressively build the collection in memory based on the static assets which are actually used in the application. MapStaticAssets is a good idea but it feels like it was not implemented for a real world scenario. |
@sbwalker Taking into account what you said, it should be technically possible to implement it in a "relatively" easy and non-disruptive way, right? Although perhaps you are still not convinced that it's the correct approach from your last sentence.
|
@mdmontesinos what I am struggling with is the dilemma between the Microsoft Alignment principle in the Oqtane Philosophy (https://www.oqtane.org/blog/!/20/oqtane-philosophy) and the fact that a lot of the capabilities provided by Microsoft are unfortunately designed for static application scenarios and therefore are not a great fit for Oqtane (due to its dynamic nature). |
Some additional items to note... Because MapStaticAssets relies on pre-optimization during the build stage, Nuget packages would need to include all of these optimized assets (ie. *.br, *.gzip) which will make the packages much larger. There is also no easy way to manage which files are included or excluded in the staticwebassets.endpoints.json files. Basically everything in wwwroot is included - which could include a variety of supporting files which are never expected to be served as content. And for those people who currently deploy granular assets manually using FTP, etc... it would no longer be possible without also deploying the optimized assets and the updated staticwebassets.endpoints.json. |
@sbwalker I totally understand your dilemma, but the alternatives are using 3rd party software (which is totally not aligned with Oqtane's philosophy), implementing a whole optimization/caching system for Oqtane from scratch to support dynamic assets, or perhaps settling in the Microsoft's approach as a starting point to cover at least the static assets. None of the options are ideal, but the Microsoft's approach requires the least amount of work (I hope). As for the additional notes
Yes, that's true, but that will be the case for every nuget package in .NET 9 that includes assets, so Oqtane wouldn't be an exception. Also, this should be opt-out (or even opt-in) for developers in case they prefer not optimizing the assets. Oqtane should use the regular static assets middleware as a fallback.
IMHO, if they are never expected to be served as content, they shouldn't even be included in wwwroot in the first place, as it's publicly available. For example, I believe module and theme templates shouldn't be there, as custom templates might contain details about code infrastructure which can be used to gather information and discover vulnerabilities.
And as for this, again, the assets optimization should be optional for developers in case they don't want to deal with the drawbacks you mentioned (but also not obtain the performance enhancements). |
It turns out that my earlier comments related to "This file could easily be included by the debug.cmd and *.nuspec files in the Module/Theme Package project so that it is deployed in the same way as standard .NET assemblies" is not correct... In .NET 9 the actual optimized static assets will only be created if you perform a Publish on a project (ie. you need to select the Server project in your solution, select Build... Publish... create a Publish Profile including a folder to publish to.... select Publish). This will create a wwwroot folder within the Publish folder which includes the optimized assets. This is a fairly significant change for module/theme developers in terms of how to develop/deploy their artifacts to Oqtane. In addition, it creates challenges for the debug.cmd and *.nuspec files in terms of where to locate the static assets. This would likely require a new set of module/theme templates in order to use this approach. |
At this point it is clear that MapStaticAssets has MANY disadvantages... so I believe it makes sense to explore alternative approaches. |
That's really unfortunate. Have you got any suggestions for alternatives? |
The UseStaticFiles middleware has always supported eTags since .NET Core was first introduced. What it does not support is client caching or fingerprinting. #5007 provides a simple way to include client caching - but this has problems outlined earlier in this thread if assets change. Oqtane already supports fingerprinting by manually adding a version querystring to Resource Urls (note that in .NET 9 and MapStaticAssets you do not get fingerprinting for free either - you need to modify your code to use the new Assets[] concept). Oqtane could easily be enhanced to add the module or theme version number to the associated Resource Urls at run-time (ignoring Resource Urls which already have a querystring specified) which would provide an automatic fingerprinting mechanism. The core framework assets can use the Framework version. This approach would then support client caching and cache busting. Note that this is just an idea - it has not been tested. |
That's nice! Are there any use cases in which assets are updated but not the module version? For example, you mentioned manually deploying with FTP. |
Absolutely... if you deploy individual static assets (ie. using FTP, etc...) the old version will be still be served.... this is the big problem with client caching. A related problem is that the .NET Core middleware was not designed to support a multi-tenant environment - so you have to enable it for your entire installation (even if some sites in the installation would like to opt out of caching). |
Oqtane Info
Version - 6.0.1
Render Mode - Static
Interactivity - Server
Database - SQL Server
Describe the enhancement
As discussed in #4755 (comment), Oqtane won't be able to take advantage of the new MapStaticAssets capability to improve performance for static assets loading.
Still, I believe Oqtane should try to include some of its functionalities, like setting caching headers so that browsers can cache them. Basically, adding "Cache-Control" header in the UseStaticFiles response, as stated in https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-9.0#set-http-response-headers.
This would be the easiest of the techniques to implement, it wouldn't affect framework performance and it improves Lighthouse performance metrics. For example, this was taken from the Oqtane.org site:
For busting the cache, in #4745 it was discussed that a framework level hashing/fingerprinting was not necessary, and developers should add querystrings with versioning for this purpose.
I assume this caching should only affect static assets and exclude the ones uploaded via the framework File Manager, but it's still an improvement.
Anything else?
The text was updated successfully, but these errors were encountered: