-
Notifications
You must be signed in to change notification settings - Fork 64
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
G11n java client - Fix for expiration-related issues (issues 604, 664, and 662) #699
G11n java client - Fix for expiration-related issues (issues 604, 664, and 662) #699
Conversation
https://sonarcloud.io/project/issues?id=jessiejuachon-java-client-g11n-java-client&issues=AXDG68NFL24bPpi-pS5s&open=AXDG68NFL24bPpi-pS5s https://sonarcloud.io/project/issues?id=jessiejuachon-java-client-g11n-java-client&issues=AXDG68IGL24bPpi-pS2r&open=AXDG68IGL24bPpi-pS2r Signed-off-by: Jessie <jessiejuachon@gmail.com>
Signed-off-by: Jessie <jessiejuachon@gmail.com>
…and return them upstream.
…t in order to get a 304 NOT MODIFIED http response
…ne if component locale's cache has expired
…ingleton into g11n-java-client
…using cacheExpiredTime config
…g file. This means max age from server will be used. Setting VIPCfg.cacheExpiredTime to 0 disables caching.
…t need to be maintained.
if (cachedData != null) | ||
private final Map<String, String> cachedData = new HashMap<>(); | ||
|
||
public synchronized void setCacheItem(String locale, Map<String, String> cachedData, String etag, long timestamp, Long maxAgeMillis) { |
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.
public synchronized void setCacheItem(String locale, Map<String, String> cachedData, String etag, long timestamp, Long maxAgeMillis) { | |
public synchronized void setCacheItem(String locale, Map<String, String> dataToCache, String etag, long timestamp, Long maxAgeMillis) { |
|
||
cacheItem.addCachedData(messages); | ||
cacheItem.setTimestamp(System.currentTimeMillis()); | ||
cacheItem.setCacheItem(dto.getLocale(), messages, null, System.currentTimeMillis(), null); |
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.
Don't need to store time to cache because data from local will never expire.
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.
Data from local must expire. Use case: If data from local was fetched as a fallback for when Singleton service is temporarily down. In this case, when the cached data from local expires, it should try to fetch from the service again.
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 should be set the time in other place because here is Local Opt, it doesn't need to consider the time.
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.
So where do you recommend to set the time? By the way, there is nothing about expire time here, only timestamp of when the data was fetched from local bundles. And it has been there before this PR, just combined them into 1 call, so I do not want to make any change about that in this PR.
public synchronized void setMaxAgeMillis(Long maxAgeMillis) { | ||
this.maxAgeMillis = maxAgeMillis; | ||
} | ||
public String getLocale() { return locale; } | ||
|
||
public boolean isExpired() { |
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.
public boolean isExpired() { | |
public synchronized boolean isExpired() { |
src/main/java/com/vmware/vipclient/i18n/messages/api/opt/server/ComponentBasedOpt.java
Show resolved
Hide resolved
@@ -96,7 +96,7 @@ public ComponentsService(final VIPCfg config) { | |||
final MessagesDTO dto = new MessagesDTO(); | |||
dto.setComponent(comp); | |||
dto.setLocale(locale); | |||
new CacheService(dto).addCacheOfComponent(new MessageCacheItem(messages, cacheItem.getEtag(), | |||
new CacheService(dto).addCacheOfComponent(new MessageCacheItem(locale, messages, cacheItem.getEtag(), |
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.
Here the etag is for all the components instead of a single component. so it's a wrong etag.
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.
Right! Good catch. I'll set the etag to null for this one for now.
#646
MessagesDTO fallbackLocaleDTO = new MessagesDTO(dto.getComponent(), cacheItemLocale, dto.getProductID(), dto.getVersion()); | ||
new ComponentService(fallbackLocaleDTO).fetchMessages(cacheItem, VIPCfg.getInstance().getMsgOriginsQueue().listIterator()); |
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.
For some reasons, the requested locale failed last time. We still need to try the requested locale first, then fallback to default locale if failure.
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 will update line 103 with this: if (cacheItem.isExpired() || !cacheItem.getLocale().equals(this.dto.getLocale()))
. It should resolve the issue.
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'm afraid it can't. If one locale gets the default locale's messages, it will always get the default locale's messages. It's wrong.
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.
Right! My mistake. See the changes please
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 mean that the requested locale fails not because locale isn't supported but other reasons such as network error. So next time to update cache, still need to get the requested locale first.
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.
Yes, that is what is happening as I explained to Huihui.
In line 110, if the cache contains the fallback locale and not the requested locale, it will always try to fetch the requested locale first (line 113) and try to assign that to the requested locales cache key (line 116). Only if it fails again will it point to the fallback locale’s cacheItem again (line 122).
populateCacheTask only refreshes the content of the object that is already in the cache (e.g. either the target or fallback locale cacheItem), but it will not change the mapping of the object to the cache key/s. So this line you commented on will not affect the existing mapping/s of the object to cache key/s. It will just update the contents (the insides) of cacheItem object itself.
If you still do not get it, let's connect over Zoom
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 have also updated the code to make it easier to understand, but same logic.
@@ -99,7 +99,8 @@ public MessageCacheItem getMessages(Iterator<Locale> fallbackLocalesIter) { | |||
MessageCacheItem cacheItem = null; | |||
if (cacheService.isContainComponent()) { // Item is in cache | |||
cacheItem = cacheService.getCacheOfComponent(); | |||
if (cacheItem.isExpired()) { // cacheItem has expired | |||
// If the cacheItem is either expired or for a fallback locale | |||
if (cacheItem.isExpired() || !cacheItem.getLocale().equals(this.dto.getLocale())) { |
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.
With this condition !cacheItem.getLocale().equals(this.dto.getLocale()),
cache default locale's content for requested locale may not take effect, because they never be equal.
// If the cacheItem is for a fallback locale, create and store cacheItem for the requested locale in a separate thread. | ||
if (!LocaleUtility.isSameLocale(cacheItem.getLocale(), this.dto.getLocale())) { | ||
this.createCacheItemTask(fallbackLocalesIter); | ||
} |
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.
How about moving this into above if block so it doesn't fetch every time.
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.
We actually want to always try to fetch for the requested locale every time in a separate thread, not just when the cacheItem has expired. This is because if the cacheItem is for a fallback locale, any other call for a non-supported locale can trigger a refresh of the fallback locale's cacheItem. If that happens, the requested locale may not satisfy the if(cacheItem.isExpired()
because the fallback locale has been refreshed by some other request. In this case, we still want to try and fetch the data for the requested locale.
if (!cacheItem.getCachedData().isEmpty()) { | ||
cacheService.addCacheOfComponent(cacheItem); | ||
} else if (!dto.getLocale().equals(ConstantsKeys.SOURCE) && fallbackLocalesIter!=null && fallbackLocalesIter.hasNext()) { | ||
// If failed to fetch message for the requetsed DTO, use MessageCacheItem of the next fallback locale. |
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.
Need to judge if current fallback locale is same as the locale in DTO. If same, should skip it.
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.
If you follow the recursive logic, the locale in the dto will be the previous locale in the fallback locale queue.
* @param fallbackLocalesIter The fallback locale queue to use in case of failure. | ||
* | ||
*/ | ||
private MessageCacheItem createCacheItem(Iterator<Locale> fallbackLocalesIter) { |
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 seems here has a recursive call: getMessages(Iterator fallbackLocalesIter) -> createCacheItem(Iterator fallbackLocalesIter) -> new ComponentService(fallbackLocaleDTO).getMessages(fallbackLocalesIter)
In my opinion, the hierarchical relation between methods are not clear.
} else if (!dto.getLocale().equals(ConstantsKeys.SOURCE) && fallbackLocalesIter!=null && fallbackLocalesIter.hasNext()) { | ||
// If failed to fetch message for the requetsed DTO, use MessageCacheItem of the next fallback locale. | ||
MessagesDTO fallbackLocaleDTO = new MessagesDTO(dto.getComponent(), fallbackLocalesIter.next().toLanguageTag(), dto.getProductID(), dto.getVersion()); | ||
cacheItem = new ComponentService(fallbackLocaleDTO).getMessages(fallbackLocalesIter); | ||
if (!cacheItem.getCachedData().isEmpty()) { | ||
cacheService.addCacheOfComponent(cacheItem); | ||
} |
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.
Here only use the first fallback locale in the fallback locale chain, doesn't go through the whole fallback locale chain.
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 actually does go through the whole cahin/queue. In line 138, you see fallbackLocalesIter.next()
. By calling .next(), the current pointer of fallbackLocalesIter moves to the next item in the queue. When you pass fallbackLocalesIter to ComponentService.getMessages() in line139, the iterator holds the correct place (current pointer) in the queue.
…, and 662) (vmware#699) Fix for expiration-related issues (issues 604, 664, and 662) (vmware#699)
No description provided.