-
Notifications
You must be signed in to change notification settings - Fork 26.2k
feat(service-worker): support multiple apps in one domain #27080
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
Conversation
@gkalpak Please review this PR and let me know any changes required. Also need help in test writing |
* in same domain with multiple apps | ||
*/ | ||
setBaseHref(baseHref: string) { | ||
if (baseHref && ['/'].indexOf(baseHref) == -1) { |
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.
It looks like you can just compare base href with '/' string:
if (baseHref && baseHref === '/') {
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.
@AndrewKushnir Hi,
ok now directly comparing with the '/'. Motivation is not to allow when base-href is not set that is '/'.
if (baseHref && baseHref !== '/') {
so that it work like current behavior
@sheikalthaf, I see you are still pushing changes to the PR. Is this ready for review? |
@gkalpak yes it is now ready for review |
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.
The general direction this is going seems reasonable. Keep in mind that we also need tests that verify the new behavior.
* Suffixing the baseHref with `ngsw` string to avoid clash of cache files | ||
* in same domain with multiple apps | ||
*/ | ||
setBaseHref(baseHref: string) { |
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.
IMO, this prefix should not be able to change at runtime. It should be determined during Adapter
instantiation (as it remains constant throughout the lifetime of a SW instance).
* in same domain with multiple apps | ||
*/ | ||
setBaseHref(baseHref: string) { | ||
if (baseHref && baseHref !== '/') { |
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.
AFAICT, baseHref
should never be empty (it would be at least /
).
But even so, why this check? Why not use /
? Is it to retain backwards compatibility with existing caches (thus avoiding re-fetching all assets from the server)?
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.
ok will remove this condition
*/ | ||
setBaseHref(baseHref: string) { | ||
if (baseHref && baseHref !== '/') { | ||
const str = baseHref.replace(/^\//, '').replace(/\/$/, '').replace(/\//, ':'); |
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.
Why not use it as-is?
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.
This is why because '/' is not allowed in file name right?
if the base is something like this /app/de/
then we have to replace them with ':' . so the result will be app:de
.
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.
The cache name is not necessarily a file name. I didn't look at the spec, but Chrome seems to handle /
s in cache names fine.
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.
ok thanks for the clarification. will remove the replace condition and use it as-is
@@ -23,16 +23,17 @@ export class CacheDatabase implements Database { | |||
if (this.tables.has(name)) { | |||
this.tables.delete(name); | |||
} | |||
return this.scope.caches.delete(`ngsw:db:${name}`); | |||
return this.scope.caches.delete(`${this.adapter.ngsw}:db:${name}`); |
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.
In most cases (with the sole exception of the control
DB), CacheDatabase
methods (such as delete
, list
, open
) are called with names that already contain ngsw:<base-href>:
, so prepending it again will get a little too verbose. But it is safer. I'm on the fence on this one.
Maybe we could strip the leading ngsw:<base-href>:
from the name (so that baseHref
is only included in the final cache name once).
* Extract baseHref from scope | ||
* send the baseHref to adapter to suffix with ngsw string | ||
*/ | ||
const baseHref = new URL(scope.registration.scope).pathname; |
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.
IMO, this should be moved to Adapter
's instantiation.
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.
Moved to adapter
const oldSwCacheNames = | ||
cacheNames.filter(name => /^ngsw:(?:active|staged|manifest:.+)$/.test(name)); | ||
const regex = new RegExp(`^${this.adapter.ngsw}:(?:active|staged|manifest:.+)$`); | ||
const oldSwCacheNames = cacheNames.filter(name => regex.test(name)); |
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.
This change should be reverted. The cleanupOldSwCaches()
method matches caches created by an old SW implementation.
In fact, it might be a good idea to also match (and delete) caches that don't have the pathname after ngsw:
. AFAICT, URL#pathname
will always start with a /
, so I think it is safe to delete all caches matching /^ngsw:(?!\/)/
(i.e. starting with ngsw:
but not followed by /
) - assuming we keep baseHref
's leading /
in cache names.
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.
now changed the regex to above mentioned matching pattern. also adding baseHref without any changes to cache
f60f85e
to
21d39bb
Compare
@gkalpak i have updated the code and spec but i don't know how to test this because the |
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.
Thx for the changes, @sheikalthaf. I left some more comments.
As discussed "offline", the development workflow on Windows is a little problematic atm. You should be able to run the tests with sh test.sh browserNoRouter
inside git bash (which comes bundled with git-for-windows).
@@ -54,6 +61,15 @@ export class Adapter { | |||
timeout(ms: number): Promise<void> { | |||
return new Promise<void>(resolve => { setTimeout(() => resolve(), ms); }); | |||
} | |||
|
|||
/** | |||
* Suffixing the baseHref with `ngsw` string to avoid clash of cache files |
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.
Actually, it is the other way around: Suffixing ngsw
(aka the cache name prefix) with the baseHref
.
* Suffixing the baseHref with `ngsw` string to avoid clash of cache files | ||
* in same domain with multiple apps | ||
*/ | ||
suffixBaseHref(hostUrl: string) { |
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.
This should not be a method, since the value better remain the same throughout the lifetime of the adapter. cacheNamePrefix
should be set once inside the constructor and be a readonly property.
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.
changes commited
@@ -254,6 +256,11 @@ export class SwTestHarness implements ServiceWorkerGlobalScope, Adapter, Context | |||
return promise; | |||
} | |||
|
|||
suffixBaseHref(hostUrl: string) { |
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, no need for a method here. We can interact with the property directly.
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.
@gkalpak removed this method
fef0acb
to
926178a
Compare
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
2931507
to
534632c
Compare
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've gone ahead and made some changes:
- Rebased on latest master.
- Squashed your commits into one.
- Added some more commits (including tests).
@@ -13,6 +13,12 @@ | |||
* from the global scope. | |||
*/ | |||
export class Adapter { | |||
cacheNamePrefix: string; |
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.
Can you also mark this property as readonly
(to better convey that it is not supposed or expected to change at runtime).
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.
Already it is marked as readonly
in my branch.
@@ -13,6 +13,12 @@ | |||
* from the global scope. | |||
*/ | |||
export class Adapter { | |||
cacheNamePrefix: string; | |||
constructor(scope: ServiceWorkerGlobalScope) { | |||
// Suffixing the baseHref with `ngsw` string to avoid clash of cache files |
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 would change to:
Suffixing `ngsw` with the baseHref to avoid clash of cache names for SWs with different scopes on the same domain.
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.
Meaningfull message 😊
@@ -77,6 +77,7 @@ export class SwTestHarness implements ServiceWorkerGlobalScope, Adapter, Context | |||
readonly clients = new MockClients(); | |||
private eventHandlers = new Map<string, Function>(); | |||
private skippedWaiting = true; | |||
cacheNamePrefix: string; |
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.
Mark as readonly
to better mimic the actual implementation.
(Note: We might still overwrite it during tests, but at least one has to be very intentional about it 😁)
534632c
to
3cc8cf7
Compare
…ame on browser and Node.js
… that do not support SW The tests will not be run anyway, so the artifacts are never used and there might be errors if creating the testing artifacts relies on APIs that are not available in that environment (e.g. `URL`).
…a domain Previously, it was not possible to have multiple apps (using `@angular/service-worker`) on different subpaths of the same domain, because each SW would overwrite the caches of the others (even though their scope was different). This commit fixes it by ensuring that the cache names created by the SW are different for each scope. Fixes angular#21388
…paths of a domain
This commit also ensures that the correct implementation is used on environments that do not support `URL` (e.g. Node.js).
010e61d
to
7be6483
Compare
Rebased. Thx again for working on this, @sheikalthaf 👍 merge-assistance: Needs CLA override. |
@IgorMinar @gkalpak Thanks for your awesome support in helping contributors. On this PR special thanks to @gkalpak making this possible with his great help. #loveAngular ❤ |
A Googler has manually verified that the CLAs look good. (Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.) ℹ️ Googlers: Go here for more info. |
…a domain (#27080) Previously, it was not possible to have multiple apps (using `@angular/service-worker`) on different subpaths of the same domain, because each SW would overwrite the caches of the others (even though their scope was different). This commit fixes it by ensuring that the cache names created by the SW are different for each scope. Fixes #21388 PR Close #27080
…)` optional (angular#27080) PR Close angular#27080
…ame on browser and Node.js (angular#27080) PR Close angular#27080
… that do not support SW (angular#27080) The tests will not be run anyway, so the artifacts are never used and there might be errors if creating the testing artifacts relies on APIs that are not available in that environment (e.g. `URL`). PR Close angular#27080
…a domain (angular#27080) Previously, it was not possible to have multiple apps (using `@angular/service-worker`) on different subpaths of the same domain, because each SW would overwrite the caches of the others (even though their scope was different). This commit fixes it by ensuring that the cache names created by the SW are different for each scope. Fixes angular#21388 PR Close angular#27080
…paths of a domain (angular#27080) PR Close angular#27080
angular#27080) This commit also ensures that the correct implementation is used on environments that do not support `URL` (e.g. Node.js). PR Close angular#27080
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Current behavior of the service-worker is caching data of multiple apps in cache DB file in a domain.
Due to this behavior service-worker makes the apps to break when switching from one app to other app in same domain. This PR will solve this issue by suffixing the base-href with
ngsw
string to separate caches files of an apps in a domainFixes issue #21388
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: #21388
Currently service-worker doen't seperate the cache files based on base-href. Due to this caches files get clashes with multiple apps hosted in one domain
What is the new behavior?
In this PR seperating the caches files by suffing the base-href with
ngsw
string like thisngsw:<base-href>
to seperate the caches files of an app. This will avoid the storing of cache data in same file for multiple apps in one domainDoes this PR introduce a breaking change?
Other information