Update to 5.0.2 - Cache(only) improvements

Cache - only improvements
This commit is contained in:
Gerasimos Maropoulos 2016-11-15 20:20:29 +02:00
parent 948eb2ecc1
commit 8b88aabc05
6 changed files with 106 additions and 45 deletions

View File

@ -2,6 +2,17 @@
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
## 5.0.1 -> 5.0.2
- [geekypanda/httpcache](https://github.com/geekypanda/httpcache) has been re-written,
by me, got rid of the mutex locks and use individual statcks instead,
gain even more performance boost
- `InvalidateCache` has been removed,
it wasn't working well for big apps, let cache work with
its automation, is better.
- Add tests for the `iris.Cache`
## v3 -> [v4](https://github.com/kataras/iris/tree/4.0.0) (fasthttp-based) long term support
@ -371,7 +382,7 @@ type WebsocketConfiguration struct {
```
- **REMOVE**: `github.com/kataras/iris/context/context.go` , this is no needed anymore. Its only usage was inside `sessions` and `websockets`, a month ago I did improvements to the sessions as a standalone package, the IContext interface is not being used there. With the today's changes, the iris-contrib/websocket doesn't needs the IContext interface too, so the whole folder `./context` is useless and removed now. Users developers don't have any side-affects from this change.
- **REMOVE**: `github.com/kataras/iris/context/context.go` , this is no needed anymore. Its only usage was inside `sessions` and `websockets`, a month ago I did improvements to the sessions as a standalone package, the IContext interface is not being used there. With the today's changes, the iris-contrib/websocket doesn't needs the IContext interface too, so the whole folder `./context` is useless and removed now. Users developers don't have any side-affects from this change.
[Examples](https://github.com/iris-contrib/examples), [Book](https://github.com/iris-contrib/gitbook) are up-to-date, just new configuration fields.
@ -835,7 +846,7 @@ OptionServerVScheme(val string)
// You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used
OptionServerName(val string)
```
```
View all configuration fields and options by navigating to the [kataras/iris/configuration.go source file](https://github.com/kataras/iris/blob/master/configuration.go)

View File

@ -20,7 +20,7 @@
<br/>
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%205.0.1%20-blue.svg?style=flat-square" alt="Releases"></a>
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%205.0.2%20-blue.svg?style=flat-square" alt="Releases"></a>
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
@ -825,7 +825,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
Versioning
------------
Current: **v5.0.1**
Current: **v5.0.2**
Stable: **[v4 LTS](https://github.com/kataras/iris/tree/4.0.0#versioning)**

View File

@ -561,7 +561,6 @@ func (ctx *Context) renderSerialized(contentType string, obj interface{}, option
if err != nil {
return err
}
gzipEnabled := ctx.framework.Config.Gzip
charset := ctx.framework.Config.Charset
if len(options) > 0 {
@ -1131,11 +1130,6 @@ func (ctx *Context) MaxAge() int64 {
return -1
}
// InvalidateCache clears the cache manually for this request uri context's handler's route
func (ctx *Context) InvalidateCache() {
ctx.framework.InvalidateCache(ctx)
}
// Log logs to the iris defined logger
func (ctx *Context) Log(format string, a ...interface{}) {
ctx.framework.Logger.Printf(format, a...)

27
http.go
View File

@ -13,6 +13,7 @@ import (
"sync"
"time"
"github.com/geekypanda/httpcache"
"github.com/iris-contrib/letsencrypt"
"github.com/kataras/go-errors"
"github.com/valyala/fasthttp"
@ -696,9 +697,29 @@ func (e *muxEntry) precedenceTo(index int) int {
return newindex
}
//
//
//
// cachedMuxEntry is just a wrapper for the Cache functionality
// it seems useless but I prefer to keep the cached handler on its own memory stack,
// reason: no clojures hell in the Cache function
type cachedMuxEntry struct {
cachedHandler fasthttp.RequestHandler
}
func newCachedMuxEntry(f *Framework, bodyHandler HandlerFunc, expiration time.Duration) *cachedMuxEntry {
fhandler := func(reqCtx *fasthttp.RequestCtx) {
ctx := f.AcquireCtx(reqCtx)
bodyHandler.Serve(ctx)
f.ReleaseCtx(ctx)
}
cachedHandler := httpcache.CacheFasthttp(fhandler, expiration)
return &cachedMuxEntry{
cachedHandler: cachedHandler,
}
}
func (c *cachedMuxEntry) Serve(ctx *Context) {
c.cachedHandler(ctx.RequestCtx)
}
type (
// Route contains some useful information about a route

View File

@ -11,10 +11,12 @@ import (
"fmt"
"io/ioutil"
"os"
"sync/atomic"
"testing"
"time"
"github.com/gavv/httpexpect"
"github.com/kataras/go-errors"
"github.com/kataras/iris"
"github.com/kataras/iris/httptest"
"github.com/valyala/fasthttp"
@ -689,3 +691,63 @@ func TestMuxFireMethodNotAllowed(t *testing.T) {
e.POST("/mypath").Expect().Status(iris.StatusMethodNotAllowed).Body().Equal("Hello from my custom 405 page")
iris.Close()
}
var (
cacheDuration = 5 * time.Second
errCacheTestFailed = errors.New("Expected the main handler to be executed %d times instead of %d.")
)
// ~14secs
func runCacheTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBodyStr, expectedContentType string) error {
e.GET(path).Expect().Status(iris.StatusOK).Body().Equal(expectedBodyStr)
time.Sleep(cacheDuration / 5) // lets wait for a while, cache should be saved and ready
e.GET(path).Expect().Status(iris.StatusOK).Body().Equal(expectedBodyStr)
counter := atomic.LoadUint32(counterPtr)
if counter > 1 {
// n should be 1 because it doesn't changed after the first call
return errCacheTestFailed.Format(1, counter)
}
time.Sleep(cacheDuration)
// cache should be cleared now
e.GET(path).Expect().Status(iris.StatusOK).ContentType(expectedContentType, "utf-8").Body().Equal(expectedBodyStr)
time.Sleep(cacheDuration / 5)
// let's call again , the cache should be saved
e.GET(path).Expect().Status(iris.StatusOK).ContentType(expectedContentType, "utf-8").Body().Equal(expectedBodyStr)
counter = atomic.LoadUint32(counterPtr)
if counter != 2 {
return errCacheTestFailed.Format(2, counter)
}
return nil
}
func TestCache(t *testing.T) {
iris.ResetDefault()
expectedBodyStr := "Imagine it as a big message to achieve x20 response performance!"
var textCounter, htmlCounter uint32
iris.Get("/text", iris.Cache(func(ctx *iris.Context) {
atomic.AddUint32(&textCounter, 1)
ctx.Text(iris.StatusOK, expectedBodyStr)
}, cacheDuration))
iris.Get("/html", iris.Cache(func(ctx *iris.Context) {
atomic.AddUint32(&htmlCounter, 1)
ctx.HTML(iris.StatusOK, expectedBodyStr)
}, cacheDuration))
e := httptest.New(iris.Default, t)
// test cache on text/plain
if err := runCacheTest(e, "/text", &textCounter, expectedBodyStr, "text/plain"); err != nil {
t.Fatal(err)
}
// text cache on text/html
if err := runCacheTest(e, "/html", &htmlCounter, expectedBodyStr, "text/html"); err != nil {
t.Fatal(err)
}
}

37
iris.go
View File

@ -52,6 +52,7 @@ visit https://www.gitbook.com/book/kataras/iris/details
package iris // import "github.com/kataras/iris"
import (
"bytes"
"fmt"
"log"
"net"
@ -65,8 +66,6 @@ import (
"sync"
"time"
"bytes"
"github.com/geekypanda/httpcache"
"github.com/kataras/go-errors"
"github.com/kataras/go-fs"
"github.com/kataras/go-serializer"
@ -81,7 +80,7 @@ const (
// IsLongTermSupport flag is true when the below version number is a long-term-support version
IsLongTermSupport = false
// Version is the current version number of the Iris web framework
Version = "5.0.1"
Version = "5.0.2"
banner = ` _____ _
|_ _| (_)
@ -167,7 +166,6 @@ type (
TemplateSourceString(string, interface{}) string
SerializeToString(string, interface{}, ...map[string]interface{}) string
Cache(HandlerFunc, time.Duration) HandlerFunc
InvalidateCache(*Context)
}
// Framework is our God |\| Google.Search('Greek mythology Iris')
@ -1127,7 +1125,7 @@ func (s *Framework) SerializeToString(keyOrContentType string, obj interface{},
// Usage: iris.Get("/", iris.Cache(func(ctx *iris.Context){
// ctx.WriteString("Hello, world!") // or a template or anything else
// }, time.Duration(10*time.Second))) // duration of expiration
// if <=time.Second then it tries to find it though request header's "cache-control" maxage value
// if <=2 seconds then it tries to find it though request header's "cache-control" maxage value
//
// Note that it depends on a station instance's cache service.
// Do not try to call it from default' station if you use the form of app := iris.New(),
@ -1146,33 +1144,8 @@ func Cache(bodyHandler HandlerFunc, expiration time.Duration) HandlerFunc {
// Do not try to call it from default' station if you use the form of app := iris.New(),
// use the app.Cache instead of iris.Cache
func (s *Framework) Cache(bodyHandler HandlerFunc, expiration time.Duration) HandlerFunc {
fh := httpcache.Fasthttp.Cache(func(reqCtx *fasthttp.RequestCtx) {
ctx := s.AcquireCtx(reqCtx)
bodyHandler.Serve(ctx)
s.ReleaseCtx(ctx)
}, expiration)
return func(ctx *Context) {
fh(ctx.RequestCtx)
}
}
// InvalidateCache clears the cache body for a specific context's url path(cache unique key)
//
// Note that it depends on a station instance's cache service.
// Do not try to call it from default' station if you use the form of app := iris.New(),
// use the app.InvalidateCache instead of iris.InvalidateCache
func InvalidateCache(ctx *Context) {
Default.InvalidateCache(ctx)
}
// InvalidateCache clears the cache body for a specific context's url path(cache unique key)
//
// Note that it depends on a station instance's cache service.
// Do not try to call it from default' station if you use the form of app := iris.New(),
// use the app.InvalidateCache instead of iris.InvalidateCache
func (s *Framework) InvalidateCache(ctx *Context) {
httpcache.Fasthttp.Invalidate(ctx.RequestCtx)
ce := newCachedMuxEntry(s, bodyHandler, expiration)
return ce.Serve
}
// -------------------------------------------------------------------------------------