How does caching work in JAX-RS?

Chan picture Chan · Jul 5, 2013 · Viewed 8.7k times · Source

Suppose I have the following web service call using @GET method:

@GET
@Path(value = "/user/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getUserCache(@PathParam("id") String id, @Context HttpHeaders headers) throws Exception {
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("id", id);
    SqlSession session = ConnectionFactory.getSqlSessionFactory().openSession();
    Cre8Mapper mapper = session.getMapper(Cre8Mapper.class);

    // slow it down 5 seconds
    Thread.sleep(5000);

    // get data from database
    User user = mapper.getUser(map);

    if (user == null) {
        return Response.ok().status(Status.NOT_FOUND).build();
    } else {
        CacheControl cc = new CacheControl();
        // save data for 60 seconds
        cc.setMaxAge(60);
        cc.setPrivate(true);
        return Response.ok(gson.toJson(user)).cacheControl(cc).status(Status.OK).build();
    }
}   

To experiment, I slow down the current thread 5 seconds before fetching data from my database.
When I call my web service using Firefox Poster, within 60 seconds it seemed much faster on the 2nd, 3rd calls and so forth, until it passed 60 seconds.
However, when I paste the URI to a browser (Chrome), it seemed to slow down 5s everytime. And I'm really confused about how caching is actually done with this technique. Here are my questions:

  1. Does POSTER actually look at the header max-age and decide when to fetch the data?
  2. In client side (web, android....), when accessing my web service do I need to check the header and then perform caching manually or the browser already cached the data itself?
  3. Is there a way to avoid fetching data from the database every time? I guess I would have to store my data in memory somehow, but could it potentially run out of memory?
  4. In this tutorial JAX-RS caching tutorial: How does caching actually work? The first line always fetch the data from the database:

    Book myBook = getBookFromDB(id);

So how it is considered cached? Unless the code doesn't execute in top/down order.

    @Path("/book/{id}")
    @GET
    public Response getBook(@PathParam("id") long id, @Context Request request) {
        Book myBook = getBookFromDB(id);
        CacheControl cc = new CacheControl();
        cc.setMaxAge(86400);
        EntityTag etag = new EntityTag(Integer.toString(myBook.hashCode()));        
        ResponseBuilder builder = request.evaluatePreconditions(etag);
        // cached resource did change -> serve updated content
        if (builder == null){
            builder = Response.ok(myBook);
            builder.tag(etag);
        }
        builder.cacheControl(cc);
        return builder.build();
    } 

Answer

invariant picture invariant · Jun 9, 2014

From your questions i see that you're mixing client side caching(http) with server side caching(database). I think the root cause for this is the different behavior you observed in firefox and chrome first i will try to clear this

When I call my web service using Firefox Poster, within 60 seconds it seemed much faster on the 2nd, 3rd calls and so forth, until it passed 60 seconds. However, when I paste the URI to a browser (Chrome), it seemed to slow down 5s everytime.

Example :

 @Path("/book")
    public Response getBook() throws InterruptedException {
        String book = " Sample Text Book";
        TimeUnit.SECONDS.sleep(5); // thanks @fge
        final CacheControl cacheControl = new CacheControl();
        cacheControl.setMaxAge((int) TimeUnit.MINUTES.toSeconds(1)); 
        return Response.ok(book).cacheControl(cacheControl).build();
    }

I have a restful webservice up and running and url for this is

http://localhost:8780/caching-1.0/api/cache/book - GET

FireFox :

First time when i accessed url ,browser sent request to server and got response back with cache control headers.

fiefox initital req

Second Request with in 60 seconds (using Enter) : This time firefox didn't went to server to get response,instead its loaded data from cache

enter image description here

Third Request after 60 seconds (using Enter) :

this time firefox made request to server and got response.

Fourth Request using Refresh (F5 or ctrl F5) :

If i refresh page ( instead of hitting enter) with in 60 seconds of previous request firefox didn't load data from cache instead it made request to server with special header in request

enter image description here

Chrome :

Second Request with in 60 seconds (using Enter ) : This time chrome sent request again to server instead of loading data from cache ,and in request it added header cache-control = "max-age=0"

Aggregating Results :

As chrome responding differently to enter click you saw different behavior in firefox and chrome ,its nothing do with jax-rs or your http response . To summarize clients (firefox/chrome/safari/opera) will cache data for specified time period in cache control , client will not make new request to server unless time expires or until we do a force refresh .

I hope this clarifies your questions 1,2,3.

4.In this tutorial JAX-RS caching tutorial: How does caching actually work? The first line always fetch the data from the database:

Book myBook = getBookFromDB(id);

So how it is considered cached? Unless the code doesn't execute in top/down order.

The example you referring is not talking about minimizing data base calls instead its about saving band width over network ,Client already has data and its checking with server(revalidating) if data is updated or not if there is no update in data in response you're sending actual entity .