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

Server side caching #193

Merged
merged 68 commits into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
d5a429c
#128 cache wip implementation
drakos74 Mar 14, 2020
af77c43
#128 adjust to patron new version
drakos74 Mar 14, 2020
9115ffb
#128 refine requirements
drakos74 Mar 14, 2020
cb572cf
#128 first iteration of server cache functionality
drakos74 Mar 22, 2020
784cf71
#128 server route cache implementation
drakos74 Mar 30, 2020
8bf60d7
Add validation for brokers in kafka.NewBuilder (#191)
superstas Mar 24, 2020
4c3cbc0
Add ActiveBrokers() method to Kafka AsyncProducer (#192)
gm42 Mar 27, 2020
42c23b5
#128 revert sixth example main
drakos74 Mar 30, 2020
a3a1784
#128 update readme
drakos74 Mar 30, 2020
88cf1a2
#128 fix linting
drakos74 Apr 1, 2020
2daa01b
#128 fix more linting
drakos74 Apr 1, 2020
96319d8
#128 fix tests
drakos74 Apr 1, 2020
dc451e2
#128 fix tests
drakos74 Apr 2, 2020
da8addc
#128 add vendor file
drakos74 Apr 3, 2020
ba17250
Add Route struct getters (#195)
Oberonus Mar 31, 2020
ee3470e
Introduce dockertest to integration tests (#182)
drakos74 Apr 1, 2020
1e2f936
#128 wrap up implementation
drakos74 Apr 4, 2020
4de2bc0
#128 fix tests
drakos74 Apr 4, 2020
9e987f2
#128 add readme details on cache metrics
drakos74 Apr 4, 2020
f2b9841
#128 readme adjustments
drakos74 Apr 4, 2020
654b7fc
#128 finalise implementation
drakos74 Apr 4, 2020
1f0b1fd
#128 increase test timeout
drakos74 Apr 4, 2020
e7de5c7
#128 decouple tests
drakos74 Apr 4, 2020
f3fbc2e
#128 fix tests
drakos74 Apr 6, 2020
4b84518
#193 fix tests
drakos74 Apr 6, 2020
1e85af8
Update tracing and metrics dependencies (#190)
tpaschalis Apr 7, 2020
758808b
#128 implement review comments
drakos74 Apr 9, 2020
b699ea1
#128 implement review comments
drakos74 Apr 9, 2020
9b826cc
#128 fix linting
drakos74 Apr 9, 2020
0dbf79d
#128 update vendors
drakos74 Apr 9, 2020
446496d
#128 implement review comments
drakos74 Apr 15, 2020
8674de6
#128 implement review comments
drakos74 Apr 15, 2020
273dbb5
#128 implement review comments
drakos74 Apr 15, 2020
fb5a40d
#128 implement review comments
drakos74 Apr 17, 2020
36fe2c5
Upgraded github actions v2 (#198)
Apr 17, 2020
636547f
#128 make abstraction simpler and easier to use
drakos74 Apr 28, 2020
42dea7b
#128 add comment
drakos74 Apr 28, 2020
5cb52c3
#128 fix linting
drakos74 Apr 28, 2020
7499a8b
#128 trigger the build
drakos74 Apr 29, 2020
5e6f3b9
#128 unexport response read writer
drakos74 May 2, 2020
4feccb3
#128 check for min < max
drakos74 May 2, 2020
55e9cb0
#128 update vendor
drakos74 May 2, 2020
bd38a5e
#128 remove timeinstant and make use of ttl cache only for route caching
drakos74 May 2, 2020
13f8709
Merge branch 'master' into server-side-caching
drakos74 May 2, 2020
88d8d2b
#128 update readme after latest changes
drakos74 May 2, 2020
a39ffaa
#128 fix redis cache interaction and create cached route example
drakos74 May 2, 2020
1aebd67
#128 remove the processor specific logic for the cache
drakos74 May 2, 2020
82313cd
#128 use the patron header alias instead of a raw map
drakos74 May 2, 2020
039a3b4
#128 fix linting
drakos74 May 2, 2020
e518a9b
#128 expose cache functionality as a middleware
drakos74 May 2, 2020
ea9d562
#128 fix linting
drakos74 May 2, 2020
6b04ff6
Merge branch 'master' into server-side-caching
drakos74 May 4, 2020
e68d2a3
#128 refactor cache logic to separate package
drakos74 May 9, 2020
45c2316
#128 remove timeinstant abstraction
drakos74 May 9, 2020
f17d471
#128 avoid defining the prometheus registerer
drakos74 May 9, 2020
8de7e51
#128 fix linting and exported objects
drakos74 May 9, 2020
6527324
#128 make middleware cache abstraction more concrete
drakos74 May 9, 2020
64edaa4
#128 make the naming conventions and structure more go like
drakos74 May 9, 2020
48211a7
#128 fix imports
drakos74 May 9, 2020
18c447d
#128 move timing request example to user service
drakos74 May 9, 2020
c8f4994
Merge branch 'master' into server-side-caching
drakos74 May 15, 2020
ded88e6
Merge branch 'master' into server-side-caching
drakos74 May 25, 2020
a6e1b61
Merge branch 'master' into server-side-caching
May 25, 2020
83d9a92
Merge branch 'master' into server-side-caching
drakos74 Jun 2, 2020
e3dcaa2
Merge branch 'master' into server-side-caching
drakos74 Jul 22, 2020
da3698c
#128 fix typo
drakos74 Jul 22, 2020
f2470c8
Merge branch 'master' into server-side-caching
Jul 23, 2020
6b3f075
#128 add package doc
drakos74 Jul 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 126 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,131 @@ route := NewRoute("/index", "GET" ProcessorFunc, true, ...MiddlewareFunc)
routeWithAuth := NewAuthRoute("/index", "GET" ProcessorFunc, true, Authendicator, ...MiddlewareFunc)
```

### HTTP Caching

The caching layer for HTTP routes is specified per Route.

```go
// RouteCache is the builder needed to build a cache for the corresponding route
type RouteCache struct {
// cache is the ttl cache implementation to be used
cache cache.TTLCache
// age specifies the minimum and maximum amount for max-age and min-fresh header values respectively
// regarding the client cache-control requests in seconds
age age
}

func NewRouteCache(ttlCache cache.TTLCache, age Age) *RouteCache
```

#### server cache
- The **cache key** is based on the route path and the url request parameters.
- The server caches only **GET requests**.
- The server implementation must specify an **Age** parameters upon construction.
- Age with **Min=0** and **Max=0** effectively disables caching
- The route should return always the most fresh object instance.
- An **ETag header** must be always in responses that are part of the cache, representing the hash of the response.
- Requests within the time-to-live threshold, will be served from the cache.
Otherwise the request will be handled as usual by the route processor function.
The resulting response will be cached for future requests.
- Requests where the client control header requirements cannot be met i.e. **very low max-age** or **very high min-fresh** parameters,
will be returned to the client with a `Warning` header present in the response.

```
Note : When a cache is used, the handler execution might be skipped.
That implies that all generic handler functionalities MUST be delegated to a custom middleware.
i.e. counting number of server client requests etc ...
```

### Usage

- provide the cache in the route builder
```go
NewRouteBuilder("/", handler).
WithRouteCache(cache, http.Age{
Min: 30 * time.Minute,
Max: 1 * time.Hour,
}).
MethodGet()
```

- use the cache as a middleware
```go
NewRouteBuilder("/", handler).
WithMiddlewares(NewCachingMiddleware(NewRouteCache(cc, Age{Max: 10 * time.Second}))).
MethodGet()
```

#### client cache-control
The client can control the cache with the appropriate Headers
- `max-age=?`

returns the cached instance only if the age of the instance is lower than the max-age parameter.
This parameter is bounded from below by the server option `minAge`.
This is to avoid chatty clients with no cache control policy (or very aggressive max-age policy) to effectively disable the cache
- `min-fresh=?`

returns the cached instance if the time left for expiration is lower than the provided parameter.
This parameter is bounded from above by the server option `maxFresh`.
This is to avoid chatty clients with no cache control policy (or very aggressive min-fresh policy) to effectively disable the cache

- `no-cache` / `no-store`

returns a new response to the client by executing the route processing function.
NOTE : Except for cases where a `minAge` or `maxFresh` parameter has been specified in the server.
This is again a safety mechanism to avoid 'aggressive' clients put unexpected load on the server.
The server is responsible to cap the refresh time, BUT must respond with a `Warning` header in such a case.
- `only-if-cached`

expects any response that is found in the cache, otherwise returns an empty response

#### metrics
drakos74 marked this conversation as resolved.
Show resolved Hide resolved

The http cache exposes several metrics, used to
- assess the state of the cache
- help trim the optimal time-to-live policy
- identify client control interference

By default we are using prometheus as the the pre-defined metrics framework.

- `additions = misses + evictions`

Always , the cache addition operations (objects added to the cache),
must be equal to the misses (requests that were not cached) plus the evictions (expired objects).
Otherwise we would expect to notice also an increased amount of errors or having the cache misbehaving in a different manner.

- `additions ~ misses`

If the additions and misses are comparable e.g. misses are almost as many as the additions,
it would point to some cleanup of the cache itself. In that case the cache seems to not be able to support
the request patterns and control headers.

- `hits ~ additions`

The cache hit count represents how well the cache performs for the access patterns of client requests.
If this number is rather low e.g. comparable to the additions,
this would signify that probably a cache is not a good option for the access patterns at hand.

- `eviction age`

The age at which the objects are evicted from the cache is a very useful indicator.
If the vast amount of evictions are close to the time to live setting, it would indicate a nicely working cache.
If we find that many evictions happen before the time to live threshold, clients would be making use cache-control headers.


#### cache design reference
- https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
- https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

#### improvement considerations
- we can could the storing of the cached objects and their age counter. That way we would avoid loading the whole object in memory,
if the object is already expired. This approach might provide considerable performance (in terms of memory utilisation)
improvement for big response objects.
- we could extend the metrics to use the key of the object as a label as well for more fine-grained tuning.
But this has been left out for now, due to the potentially huge number of metric objects.
We can review according to usage or make this optional in the future.
- improve the serialization performance for the cache response objects

### Asynchronous

The implementation of the async processor follows exactly the same principle as the sync processor.
Expand Down Expand Up @@ -370,4 +495,4 @@ GET /ready

Both can return either a `200 OK` or a `503 Service Unavailable` status code (default: `200 OK`).

It is possible to customize their behaviour by injecting an `http.AliveCheck` and/or an `http.ReadyCheck` `OptionFunc` to the HTTP component constructor.
It is possible to customize their behaviour by injecting an `http.AliveCheck` and/or an `http.ReadyCheck` `OptionFunc` to the HTTP component constructor.
Loading