History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: CACHE-140
Type: Improvement Improvement
Status: Open Open
Priority: Major Major
Assignee: Lars Torunski
Reporter: Simone Avogadro
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
OSCache

Option to avoid bypassing the Cache if browser has resource already

Created: 11/Feb/05 09:17 AM   Updated: 08/Jan/06 05:02 AM
Component/s: Filters
Affects Version/s: 2.1
Fix Version/s: 3.0


 Description  « Hide
if:
- filter gets a request from a browser
- doesn't have cached the response
- the request contains "If-Modified-Since"
- the application replies 304

then:
- oscache will always be bypassed, whitout being able to cache the response

fix:
CacheFilter.java, line91:
- chain.doFilter(request, cacheResponse);
+ CacheHttpServletRequestWrapper wrappedRequest= new CacheHttpServletRequestWrapper((HttpServletRequest) request);
+ chain.doFilter(wrappedRequest, cacheResponse);
new file: CacheHttpServletRequestWrapper.java:
/*
 */
package com.opensymphony.oscache.web.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * Simply tell underlaying application that we want to ignore
 * the if-modified-since header
 *
 * @author Simone Avogadro
 */
public class CacheHttpServletRequestWrapper extends HttpServletRequestWrapper {

static final String IF_MODIFIED_HEADER = "If-Modified-Since";
    
/**
     * @param arg0
     */
    public CacheHttpServletRequestWrapper(HttpServletRequest arg0) {
        super(arg0);
    }
    
public String getHeader(String name) {
        if (IF_MODIFIED_HEADER.equals(name)) {
            return null;
        } else {
            return super.getHeader(name);
        }
    }
    
public long getDateHeader(String name) {
        if (IF_MODIFIED_HEADER.equals(name)) {
            return -1;
        } else {
            return super.getDateHeader(name);
        }
    }

}


 All   Comments   Change History      Sort Order:
Lars Torunski - [11/Feb/05 05:23 PM ]
Doesn't the code throws a NeedsRefreshException if the key/response don't exists? Hence the CacheFilter will never return a SC_NOT_MODIFIED. Maybe you run into the CACHE-116 problem?

Simone Avogadro - [12/Feb/05 11:04 AM ]
I'll check again with the debugger, however to my understanding the problem is as follows:

without oscache:
- borwser: get /a.fig\n if-modified-after: 12-feb-2005
- fileServlet: 304 (because the image has not been modified)

with oscache:
- borwser: get /a.fig\n if-modified-after: 12-feb-2005
- oscache filter: throw NeedRefresh
- oscache filter: call next filter
- fileServlet: 304 (because the image has not been modified)
- oscache filter: don't cache the response because code!=200

as a result /a.fig will continue do to useless hits to the app-server

to solve this the proposed improvement will simply pass-over to the file-servlet a new request which does not contain the if-modified-after header


Lars Torunski - [13/Feb/05 03:27 AM ]
Now I fully understand the situation. So a design decission has to be make, if OSCache should remove the HEADER_IF_MODIFIED_SINCE in the request. My opinion is that the current implementation is correct. Just put the content into OSCache when a request without a HEADER_IF_MODIFIED_SINCE comes in.

Simone Avogadro - [13/Feb/05 01:44 PM ]
 I agree that it is 'correct', however to some extent a cache should change the beaviour of the requests to improve performance.
 The modification I propose is aimed at dramatically lowering the numer of requests to the application server, by anticipating the first request without the IF_EXPRIED header I try to attain that result.
 Whichever your choice for the standard behaviour I'd suggest that you add a parameter to enable the proposed behaviour.

Lars Torunski - [11/Dec/05 12:29 PM ]
Simone, I don't think that the "useless hits" to the app-server will be avoided if the "bypass" parameter is implemented.

with oscache with "bypass" parameter enabled:
- browser: get a if-modified-since: 12-feb-2005
- oscache filter: throw NeedsRefresh
- oscache filter: call next filter without the if-modified-since header
- fileServlet : returns a 200 code with the content and a last-modified header
- oscache filter: cache the response with the last-modified information

all next requests will result in a 304 answer until the refresh time expired:
- browser: get a if-modified-since: 12-feb-2005
- oscache filter: the content is in the cache
- oscache filter: 304 (because the refresh time isn't expired)

You will still get hits to the app server and the negativ aspect is that maybe huge files will be cached without any reason.

If you enable the "expires" header, the browser maybe won't request the app server. But this behaviour could be implemented in the fileServlet also.

What do you think?

Simone Avogadro - [11/Dec/05 04:55 PM ]
mh: the point you raise is interesting. In our own application app-server requests are almost always equally fast, so 1 full page request to the appserver is surely much better then 100 requests which respond 304
also: fileServlet will almost always reply with 304, but JSP and more complex composite page models will amost never be able to to this be themselved: they must relay on a cache to do this
the problem with huge files is a real issue and it's more general then CACHE-140: currenty we had to disable caching of such files entirely to avoid blasting the whole cache to contain static files (which the backend appserver can easyly handle)
Again this is almost only a filter-servelet specific problem, but I belive that we should be able to:
- limit the max size of files to be cached
- impose a global limit to cache's memory usage: currently we can limit the numer of items but not the total cache size, with too many big files this already results in OutOfMemory exceptions, which the customer doesnt like at all (currentle we use a custom-modified filter that will flush all the cache if less the 10Mb are available in the VM)

all in all I belive that:
- bypass parameter would still be useful
- it would probably go well with a response-size-limit parameter
- users should be warned against using this parameter is theis application has a very high time rate for HTTP200 vs.HTTP 304 response

p.s.: thanks for the good work, sorry we didn't give feedback but lately we've been really in a rush

Simone Avogadro - [11/Dec/05 04:57 PM ]
one more little note: we'd surely prefer to use the bypass option also because this allows us to hide small app-server downtimes (e.g.: webapp hot deploy)

Lars Torunski - [12/Dec/05 12:41 AM ]
You should inherit the CacheFilter and implement the method

protected boolean isCacheable(CacheHttpServletResponseWrapper cacheResponse) {
        return super.isCacheable(cacheResponse) && (cacheResponse.getSize() < 64000);
    }

and add a getSize() method to CacheHttpServletResponseWrapper

public int getSize() {
        return result.getSize();
    }

or open a new improvment request. ;-)