-
Notifications
You must be signed in to change notification settings - Fork 442
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
Provide a setting to use a different REST url during SSR execution #3358
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything works as expected, but still "leaks" the SSR URL and does double work since the transfer state is ignored
map(() => true), | ||
), | ||
); | ||
// The app state can be transferred only when SSR and CSR are using the same base url for the REST API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A separate REST URL could be a performance benefit during SSR, but if we discard all cached requests we'll always do double work and CSR performance will suffer.
Could we consider just running a search/replace on the transfer state to replace the SSR URL with the CSR URL?
- This shouldn't be too heavy of an operation so I think the net impact on performance could still be positive
- I did a very quick check and replacing all URLs right after the full HTML has been generated takes at most 0.25% of the total time needed for SSR
- Only tested for
/home
, not authenticated - SSR took ~400ms, HTML search replace took between 0 and 1ms
- Only tested for
Otherwise we should at least exclude the transfer state entirely; right now it's sent but never read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand your suggestion, but I believe implementing a search/replace operation for URLs might not be a good idea due to performance concerns. Even though the initial tests show a minimal impact, this might not be the case for all routes or under different conditions and it could become a bottleneck.
Moreover adding an extra step in the Server-side rendering process can increase the overall latency, negatively affecting the user experience, particularly for pages with larger amount of request.
Honestly I don't see a real issue to fetch again the request during the CSR.
I'll try to exclude the transfer state entirely
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I don't see a real issue to fetch again the request during the CSR
My main concern is that it has a significant impact on perceived performance, especially on slower connections.
This counteracts our push towards decreasing the amount of REST requests we send ~ #3110 (comment).
I've made a quick branch where you can toggle both things: ybnd/dspace-angular → task/main/DURACOM-288
ui.transferState
→ whether NGRX state is transferred from SSR to CSRreplaceRestUrl
→ whether the SSR URL is replaced after SSR is done
As far as I can tell, a simple RegEx to replace SSR URLs scales a lot better than DSpace itself
- A
/search
page with 1000 results (!) takes ~50000ms to build with SSR on my machine - Replacing all URLs (~16000) takes ~20ms
On the other hand, for a /search
page with 100 results, Lighthouse reports about double the blocking time if we don't transfer state on CSR.
@@ -100,6 +101,14 @@ export class ServerInitService extends InitService { | |||
} | |||
|
|||
private saveAppConfigForCSR(): void { | |||
this.transferState.set<AppConfig>(APP_CONFIG_STATE, environment as AppConfig); | |||
if (isNotEmpty(environment.rest.ssrBaseUrl) && environment.rest.baseUrl !== environment.rest.ssrBaseUrl) { | |||
// Avoid to transfer ssrBaseUrl in order to prevent security issues |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree about the potential security concern, good idea.
The state transfer JSON will still list the SSR URL. It's a bit more "hidden" but not secure either.
Hi @atarix83, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @atarix83 ! I gave this a code review today, and have some minor requests for improvement. I haven't had a chance to fully test this yet, but I plan to do so next week.
@@ -6,4 +6,6 @@ export class ServerConfig implements Config { | |||
public port: number; | |||
public nameSpace: string; | |||
public baseUrl?: string; | |||
public ssrBaseUrl?: string; | |||
public hasSsrBaseUrl?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add a comment here to describe how/where this boolean value is set? This doesn't appear to be something we want people to specify in their config.yml
, so it would be good to document that this is set automatically based on the value of ssrBaseUrl
in your config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I'd recommend we add a comment above this setting to clarify that this should NEVER be copied to config.*.yml
. Something like:
This boolean will be automatically set on server startup based on whether "baseUrl" and "ssrBaseUrl" have different values.
I've improved my implementation trying to address your feedback. Here the main changes:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@atarix83 : I was able to test this locally today by using temporary public URLs for the frontend & backend. I ran the frontend & backend on different domains, and ran the frontend in production mode (npm run build:prod; npm run serve:ssr
).
Overall, the basics of this PR (and the backend PR) work well. However, I've found a few bugs in my testing:
- Authentication doesn't work. This might be a side effect of an error I'm seeing on
main
, but here's what I'm trying.- If I use these PRs with the
dspace.server.ssr.url
andrest.ssrBaseUrl
settings disabled, then authentication works fine. However, whenever I authenticate, in the SSR logs I see an "doesn't contain the link profiles" error. This seems harmless, but it appears on every login - If I then enable the
dspace.server.ssr.url
andrest.ssrBaseUrl
settings and point them at http://localhost:8080/, authentication no longer works. After attempting to authenticate, the page hangs and I just see the loading image (on a blank white page). No obvious errors are seen in the logs.
- If I use these PRs with the
- Whenever SSR is triggered, I see a 404 request to an
/undefined
path. This appears in my browser's DevTools "Network" tab.- Enable the
dspace.server.ssr.url
andrest.ssrBaseUrl
settings and point them at http://localhost:8080/ - Go to any Item page.
- Click reload in your browser (to trigger SSR). The
404 /undefined
path error will appear in your DevTools
- Enable the
- Thumbnail images appear to be accessed initially via the private URL.
- Enable the
dspace.server.ssr.url
andrest.ssrBaseUrl
settings and point them at http://localhost:8080/ - Go to an Item page, for an Item that has a thumbnail.
- Click reload in your browser (to trigger SSR)
- Look closely at the
/content
request in your browser's DevTools. It will initially be against the private URL ("http://localhost:8080/"). However, once the page redraws, it will send again to the correct public URL. - If I add the
replaceRestUrl: true
setting to myconfig.prod.yml
and try again, then this behavior will no longer occur. It only occurs ifreplaceRestUrl: false
is set.
- Enable the
Overall, this appears to be working. When this feature is enabled, I do visually notice the flash/redraw of the page when transitioning from SSR to CSR. It's much more severe than when this feature is disabled. However, if there's no way to minimize that flash, then we may just need to warn people that the page flash is more severe when this separate REST URL feature is enabled.
I also have a few minor comments inline below on the code. Mostly it's looking good though.
@@ -6,4 +6,6 @@ export class ServerConfig implements Config { | |||
public port: number; | |||
public nameSpace: string; | |||
public baseUrl?: string; | |||
public ssrBaseUrl?: string; | |||
public hasSsrBaseUrl?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I'd recommend we add a comment above this setting to clarify that this should NEVER be copied to config.*.yml
. Something like:
This boolean will be automatically set on server startup based on whether "baseUrl" and "ssrBaseUrl" have different values.
Hi @atarix83, |
68531a7
to
6e7c0da
Compare
I had difficult to find the problem, but i think i found it. Could you please try again running this branch https://github.com/4Science/DSpace/tree/task/main/DURACOM-288_fix_hal_map on the REST side. If it works I'll update the REST PR with IT
It should be resolved with my last commit, could you check?
I can't reproduce this issue. I encountered the problem during the implementation which led me to change the ServerHardRedirectService. Could you try again with latest changes? I'm still working on addressing the other feedback |
Hi @atarix83, thank you for this important pr! I tried to set the rest.ssrBaseUrl via environment variables and it failed. I added the following two lines to
Regarding authentication: I was able to authenticate and it worked until I let my browser reload the page. Once I reloaded the page I was logged out. I have to test the branch you mentioned above, but did not had the time yet to do that. So far, my test installation is an empty repository. I will add items and thumbnails and check the commit you mentioned above for the authentication. I will test more asap. Best, |
Hi @atarix83, |
# Conflicts: # config/config.example.yml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@atarix83 : I've retested this today, using your separate REST branch (https://github.com/4Science/DSpace/tree/task/main/DURACOM-288_fix_hal_map) and everything seems to be working now!
Login now succeeds! I can no longer reproduce either of the other bugs that I had seen previously. I've also verified that, when I turn off Javascript, it appears all request are going to http://localhost:8080/ instead of the public URL. I've not noticed any obvious bugs after trying out a new submission, search, browse, etc.
So, I think the fixes you've applied to your branch must have solved the authentication errors. If you wanted to move those over to the REST API PR (DSpace/DSpace#9856) then I can give them a code review there. Thanks!
Overall, I think I'm +1 this PR now. But, I want to give it all one last code review & test once all the code is in these PRs.
@tdonohue I pushed changes to REST PR, could you please take a look? thanks |
References
Add references/links to any related issues or PRs. These may include:
Description
This pull request provide the possibility to use a different DSpace REST url during the SSR
Instructions for Reviewers
List of changes in this PR:
ssrBaseUrl
where to specify a different DSpace REST url to use during SSRssrBaseUrl
property. If this URL was not publicly accessible, it would result in an error. By rendering the thumbnail only in the browser, we avoid this problem.ServerHardRedirectService
. In case the url to redirect contains thessrBaseUrl
it's reinstated with the public base url. This is necessary otherwise download bitstream page doesn't work.Include guidance for how to test or review your PR.
Unfortunately it is difficult to test this PR. The best would be to deploy it in a production like environment and configure the internal base url property.
This might be done on both Angular and REST side, like in the example below where public base url is
https://dspace-rest.org/server
and internal base url ishttp://localhost:8080/server
.ssrBaseUrl
property, e.g :dspace.server.ssr.url
property, e.g. :In order to test it locally you could add an alias hostname for localhost ip to be used as public url
Checklist
main
branch of code (unless it is a backport or is fixing an issue specific to an older branch).npm run lint
npm run check-circ-deps
)package.json
), I've made sure their licenses align with the DSpace BSD License based on the Licensing of Contributions documentation.