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

HTTP cache and conditional requests support in RestTemplate [SPR-5821] #10491

Closed
spring-projects-issues opened this issue Jun 10, 2009 · 11 comments
Labels
status: declined A suggestion or change that we don't feel we should currently apply

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jun 10, 2009

Oliver Drotbohm opened SPR-5821 and commented

The main goal is to create a CachingClientHttpRequestInterceptor which provides the following:

  • Cache the received HTTP responses for further use, if those are marked as cacheable
  • Cache those response in a org.springframework.cache.Cache
  • if the Cache contains a valid response but its content is stale, then the Interceptor can issue conditional requests to revalidate the cached content
  • by default, a sane configuration should be provided and the Cache should be backed by a ConcurrentCacheMap

This could be used like this:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new CachingClientHttpRequestInterceptor()));

// this response is cacheable and has appropriate headers
ResponseEntity<Book> response = restTemplate.getForEntity("http://example.org/resource", Book.class);
// it is now cached
String etag = response.getHeaders().getEtag();

// if the response is still fresh, then no network call should happen and the response should be reused
Book book = restTemplate.getForObject("http://example.org/resource", Book.class);

Affects: 3.0 M3

Attachments:

Issue Links:

10 votes, 15 watchers

@spring-projects-issues
Copy link
Collaborator Author

Oliver Drotbohm commented

Would like to attach an implemented version but seems I don't have permission to do so.

@spring-projects-issues
Copy link
Collaborator Author

Arjen Poutsma commented

After reading through the Caching chapter in HTTP: The Definitive Guide (highly recommended, btw), this seems a bit more complex than I initially thought. If we want to do caching the right way, then we need to adhere to Cache-Control, Pragma, and Expiry headers, etc.

I'm rescheduling this feature for 3.1.

@spring-projects-issues
Copy link
Collaborator Author

Arjen Poutsma commented

Attaching Oliver's original EtagCachingRestTemplate and the alternative CachingHttpRequestFactory, sent to me via email.

@spring-projects-issues
Copy link
Collaborator Author

Marc Weinberger commented

Are there any plans to provide an extension point for cache providers like ehcache? In an distributed RESTful service environment a solid caching feature for RestTemplate would be great.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 26, 2011

Arjen Poutsma commented

@Marc, you can certainly use the high-level cache abstraction for that, see #11967.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 26, 2011

Marc Weinberger commented

Thanks Arjen,

#11967 looks very promising. I'll look into it, once the high level cache API is available.

@spring-projects-issues
Copy link
Collaborator Author

Arjen Poutsma commented

Postponing till after 3.1.

@spring-projects-issues
Copy link
Collaborator Author

Michel Zanini commented

This will be good to be included in Android Rest Template also because ETag support is very useful on a mobile environment.

@spring-projects-issues
Copy link
Collaborator Author

Oliver Drotbohm commented

After a recent team discussion we decided to slightly shift the scope of this ticket. Caching is not the right word for what we're trying to achieve here. The core idea is to use the headers sent by the server in response to an original request in subsequent requests to the very same resource. Thus it's rather using HTTP means to optimize the request (using If-None-Match, If-Modified-Since) depending on what the server indicates to understand.

@spring-projects-issues
Copy link
Collaborator Author

Brian Clozel commented

I've given this some thoughts after our discussion and found several problems with that approach.

Leaky design

So with that approach, we'd like to remain at the request/response level and let the framework generate conditional requests for us. Something (very imperfect) like this:

String requestUrl = "http://example.org/users/12"

ResponseEntity<User> cachedResponse = cache.get(requestUrl, User.class);
if(cachedResponse == null) {

  ResponseEntity<User> response = restTemplate.getForEntity(requestUrl, User.class);
  cache.put(requestUrl, response);
}
else {
  ConditionalRequest<User> cRequest = cachedResponse.conditional(restTemplate);
  User user;
  if(cRequest.checkNotModified()) { // issue a conditional GET request and returns true if 304
    user = cRequest.getCachedResponse().getBody();
  }
  else {
    user = cRequest.getResponse().getBody();
    cache.put("http://example.org/users/12", cRequest.getResponse());
  }
}        

This API can be improved in many ways, but I think that leaving the abstraction at that level means we'd need to:

  • ask the user to cache the full ResponseEntity
  • store the original request in the ResponseEntity
  • create conditional requests from responses
  • expose both the response to the conditional request, and the cached one

Not simplifying things

This approach generates conditional requests (with proper Etag or If- headers), but leave to the user the following problems:

  • should you cache or not a response (given its status, headers)?
  • is a response eligible for a conditional request? (does it say "no-cache"?)?
  • is the request eligible for a conditional request? (does it say "max-age=0")?
  • how to manage cache expiration given HTTP headers values?
  • and many other questions, which are far harder to solve than setting those conditional request headers

As soon as we're trying to tackle one of those issues, we're beginning to implement a client http cache.

Implementing a client http cache

Many frameworks/libraries went down that path with more or less complete or elegant implementations. And I'm wondering if there would be interest in this for Spring Framework given that:

  1. even with a great implementation, we'd probably miss some part of the spec or some specific browser/server quirks. It looks like this is the kind of feature that is driven by endless issues and feature requests that can contradict each others.
  2. Spring's HTTP client does not belong to a separate module which make it harder to reuse in other contexts. Such a feature is probably more useful if the http client is somehow standalone
  3. a lot of applications probably implement very simple, ad-hoc clients with @Cacheable annotations for their own use case (like this one

Jakub Jirutka implemented a RequestInterceptor for this - see his project. Some parts can be improved, like conditional requests support and cache keys generation (currently the cache key is the URL itself and does not take into account HTTP headers such as Vary, or encoding...). But it's still in a good shape IMO.

I've sketched something based on Jakub's work and I'm trying to find ways to come up with clean interfaces that would allow custom strategies for caching, conditional requests, etc. But I'm still struggling with the 3 points listed above.

Actually Jakub Jirutka, what do you think about those points?
Can you share your experience and tell us why you created this project in the first place and how it's being used?

@spring-projects-issues spring-projects-issues added type: enhancement A general enhancement has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: web Issues in web modules (web, webmvc, webflux, websocket) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.x Backlog milestone Jan 11, 2019
@bclozel bclozel removed this from the 5.x Backlog milestone Mar 31, 2020
@bclozel bclozel added status: declined A suggestion or change that we don't feel we should currently apply and removed has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement labels Mar 31, 2020
@bclozel
Copy link
Member

bclozel commented Mar 31, 2020

In the meantime, RestTemplate has been put in maintenance mode, so we don't intend to invest more there.

@bclozel bclozel closed this as completed Mar 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

2 participants