I am assuming that you already looked at varnish and did not find it suitable for your case. There are two ways you can achieve what you want.
With nginx
Nginx has a default caching mechanism that you can configure for your use.
If that does not help, you should give Nginx compiled with the 3rd party Ngx_Lua module a try. This is also conveniently packaged along with other useful modules and the required Lua environment as Openresty.
With Ngx_Lua, you can use the shared dictionary to cache your couchdb responses. As the name suggests shared dictionary uses a shared memory zone in Ngx_Lua's execution environment. This is similar to the way the proxy_cache works in Nginx(which also defines a shared memory zone in Nginx's execution environment) but comes with the added advantage that you can program it.
The steps required to build a couchdb cache are pretty simple (with this approach you don't need to send etags to the client)
- You make a request to couchdb
- You save the
{Url-Etag:response}
pair
- Next time the request comes to the same url query for etags using a HEAD request.
- If response etag matches the
{Url-Etag:response}
pair then send the cached response otherwise query couchdb again using (get/post) methods and update the {Url-Etag:response}
pair before sending the response to the client.
Of course if you program a cache by hand you will have to define max cache size and a mechanism to remove old items from the cache. The lua_shared_dict directive can help you define a memory size for which the responses will be cached. When saving the values in the shared dictionary you can specify the time for which the value will remain the memory zone after which it will automatically expire. Combining the max cache size parameter and cache time parameter of the shared dictionary you should be able to program fairly complex caching mechanism for your users.
With erlang
Since couchdb is written in erlang you already have an erlang env on your machine. So if you can program in it you can create a very robust distributed cache with mnesia. The steps are the same. Erlang timers can be combined with gen_*
behaviours to give you the automatic expiry of items and mnesia has functions to monitor it's memory usage and notify you about it. The two approaches are almost equivalent the only difference being that mnesia can be distributed.
Update
As @abyz suggested redis is also good choice when it comes to caching.