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

WIP caching #166

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 7 additions & 7 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,7 @@

[[constraint]]
name = "gopkg.in/yaml.v2"

[[constraint]]
branch = "master"
name = "github.com/gregjones/httpcache"
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ COPY bin/my-wag-service /usr/bin/my-wag-service
* Must be a simple or array type. If an array must be an array of strings
* If the type is 'simple' and the parameter is not required, the type will be a pointer
* If the type is 'array' it won't be a pointer. Query parameters can't distinguish between an empty array and an nil array so it converts both these cases to a nil array. If you need to distinguish between the two use a body parameter
* In other cases the parameter is not a pointer
* In other cases the parameter is not a pointer
* Header parameters
* Must be simple types
* If marked required will ensure that the input isn't the nil value. Headers cannot have pointer types since HTTP doesn't distinguish between empty and missing headers.
* If marked required will ensure that the input isn't the nil value. Headers cannot have pointer types since HTTP doesn't distinguish between empty and missing headers.
* If it doesn't have a default value specified, the default value will be the nil value for the type

### Paging
Expand All @@ -191,6 +191,20 @@ COPY bin/my-wag-service /usr/bin/my-wag-service
that exposes `map`, `forEach`, and `toArray` functions to iterate over the
results, again requesting new pages as needed.

### Caching
* Wag can add `max-age` cache-control headers on respones if you use the `x-caching`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: mention that max-age is in seconds

configuration:
```
/bookscached/{id}:
get:
operationId: getBookByIDCached
x-caching:
max-age: 3600
```
* Wag clients have a `SetCache` method to enable caching of responses. `SetCache`
takes a single argument, which is an interface that defines a mechanism for storing
cached response data.

### Contexts
* The first argument to every Wag function is a `context.Context` (https://blog.golang.org/context). Contexts play a few important roles in Wag.
* They can be used to set request specific behavior like a retry policy in client libraries. This includes timeouts and cancellation.
Expand All @@ -203,7 +217,7 @@ COPY bin/my-wag-service /usr/bin/my-wag-service

* If you don't have a context to pass to a Wag function you have two options
* context.Background() - use this when this is the creator of the request chain, like a test or a top-level service.
* context.TODO() - use this when you haven't been passed a context from a caller yet, but you expect the caller to send you one at some point.
* context.TODO() - use this when you haven't been passed a context from a caller yet, but you expect the caller to send you one at some point.


### Tracing
Expand Down
45 changes: 44 additions & 1 deletion clients/go/gengo.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ import (
"strconv"
"encoding/json"
"strconv"
"sync/atomic"
"time"
"fmt"
"crypto/md5"

"{{.PackageName}}/models"
discovery "github.com/Clever/discovery-go"
"github.com/afex/hystrix-go/hystrix"
"github.com/gregjones/httpcache"
logger "gopkg.in/Clever/kayvee-go.v6/logger"
)

Expand All @@ -57,7 +59,7 @@ var _ = bytes.Compare
type WagClient struct {
basePath string
requestDoer doer
transport *http.Transport
transport http.RoundTripper
timeout time.Duration
// Keep the retry doer around so that we can set the number of retries
retryDoer *retryDoer
Expand Down Expand Up @@ -111,6 +113,47 @@ func NewFromDiscovery() (*WagClient, error) {
return New(url), nil
}

func newCacheHitCounter(cache httpcache.Cache, basePath string, l logger.KayveeLogger) *cacheHitCounter {
chc := &cacheHitCounter{Cache: cache, basePath: basePath}
go chc.log(l)
return chc
}

type cacheHitCounter struct {
httpcache.Cache
hits int64
misses int64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably not worth it yet, but i could see us wanting to know this on a per-endpoint basis

basePath string
}

func (c *cacheHitCounter) log(l logger.KayveeLogger) {
ticker := time.NewTicker(time.Second * 30)
for _ = range ticker.C {
hits := atomic.LoadInt64(&c.hits)
misses := atomic.LoadInt64(&c.misses)
l.InfoD("wag-cache-stats", map[string]interface{}{
"hits": hits,
"misses": misses,
"url": c.basePath,
})
}
}

func (c *cacheHitCounter) Get(key string) ([]byte, bool) {
resp, ok := c.Cache.Get(key)
if ok {
atomic.AddInt64(&c.hits, 1)
} else {
atomic.AddInt64(&c.misses, 1)
}
return resp, ok
}

// SetCache enables caching.
func (c *WagClient) SetCache(cache httpcache.Cache) {
c.transport = httpcache.NewTransport(newCacheHitCounter(cache, c.basePath, c.logger))
}

// SetRetryPolicy sets a the given retry policy for all requests.
func (c *WagClient) SetRetryPolicy(retryPolicy RetryPolicy) {
c.retryDoer.retryPolicy = retryPolicy
Expand Down
8 changes: 4 additions & 4 deletions hardcoded/hardcoded.go

Large diffs are not rendered by default.

45 changes: 44 additions & 1 deletion samples/gen-go-deprecated/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import (
"net/http"
"strconv"
"strings"
"sync/atomic"
"time"

discovery "github.com/Clever/discovery-go"
"github.com/Clever/wag/samples/gen-go-deprecated/models"
"github.com/afex/hystrix-go/hystrix"
"github.com/gregjones/httpcache"
logger "gopkg.in/Clever/kayvee-go.v6/logger"
)

Expand All @@ -26,7 +28,7 @@ var _ = bytes.Compare
type WagClient struct {
basePath string
requestDoer doer
transport *http.Transport
transport http.RoundTripper
timeout time.Duration
// Keep the retry doer around so that we can set the number of retries
retryDoer *retryDoer
Expand Down Expand Up @@ -80,6 +82,47 @@ func NewFromDiscovery() (*WagClient, error) {
return New(url), nil
}

func newCacheHitCounter(cache httpcache.Cache, basePath string, l logger.KayveeLogger) *cacheHitCounter {
chc := &cacheHitCounter{Cache: cache, basePath: basePath}
go chc.log(l)
return chc
}

type cacheHitCounter struct {
httpcache.Cache
hits int64
misses int64
basePath string
}

func (c *cacheHitCounter) log(l logger.KayveeLogger) {
ticker := time.NewTicker(time.Second * 30)
for _ = range ticker.C {
hits := atomic.LoadInt64(&c.hits)
misses := atomic.LoadInt64(&c.misses)
l.InfoD("wag-cache-stats", map[string]interface{}{
"hits": hits,
"misses": misses,
"url": c.basePath,
})
}
}

func (c *cacheHitCounter) Get(key string) ([]byte, bool) {
resp, ok := c.Cache.Get(key)
if ok {
atomic.AddInt64(&c.hits, 1)
} else {
atomic.AddInt64(&c.misses, 1)
}
return resp, ok
}

// SetCache enables caching.
func (c *WagClient) SetCache(cache httpcache.Cache) {
c.transport = httpcache.NewTransport(newCacheHitCounter(cache, c.basePath, c.logger))
}

// SetRetryPolicy sets a the given retry policy for all requests.
func (c *WagClient) SetRetryPolicy(retryPolicy RetryPolicy) {
c.retryDoer.retryPolicy = retryPolicy
Expand Down
24 changes: 0 additions & 24 deletions samples/gen-go-deprecated/models/bad_request.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// Code generated by go-swagger; DO NOT EDIT.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i assume this is from a go-swagger update?


package models

// This file was generated by the swagger tool.
Expand All @@ -9,20 +7,16 @@ import (
strfmt "github.com/go-openapi/strfmt"

"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
)

// BadRequest bad request
// swagger:model BadRequest

type BadRequest struct {

// message
Message string `json:"message,omitempty"`
}

/* polymorph BadRequest message false */

// Validate validates this bad request
func (m *BadRequest) Validate(formats strfmt.Registry) error {
var res []error
Expand All @@ -32,21 +26,3 @@ func (m *BadRequest) Validate(formats strfmt.Registry) error {
}
return nil
}

// MarshalBinary interface implementation
func (m *BadRequest) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}

// UnmarshalBinary interface implementation
func (m *BadRequest) UnmarshalBinary(b []byte) error {
var res BadRequest
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
24 changes: 0 additions & 24 deletions samples/gen-go-deprecated/models/internal_error.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// Code generated by go-swagger; DO NOT EDIT.

package models

// This file was generated by the swagger tool.
Expand All @@ -9,20 +7,16 @@ import (
strfmt "github.com/go-openapi/strfmt"

"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
)

// InternalError internal error
// swagger:model InternalError

type InternalError struct {

// message
Message string `json:"message,omitempty"`
}

/* polymorph InternalError message false */

// Validate validates this internal error
func (m *InternalError) Validate(formats strfmt.Registry) error {
var res []error
Expand All @@ -32,21 +26,3 @@ func (m *InternalError) Validate(formats strfmt.Registry) error {
}
return nil
}

// MarshalBinary interface implementation
func (m *InternalError) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}

// UnmarshalBinary interface implementation
func (m *InternalError) UnmarshalBinary(b []byte) error {
var res InternalError
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
24 changes: 0 additions & 24 deletions samples/gen-go-deprecated/models/not_found.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// Code generated by go-swagger; DO NOT EDIT.

package models

// This file was generated by the swagger tool.
Expand All @@ -9,20 +7,16 @@ import (
strfmt "github.com/go-openapi/strfmt"

"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
)

// NotFound not found
// swagger:model NotFound

type NotFound struct {

// message
Message string `json:"message,omitempty"`
}

/* polymorph NotFound message false */

// Validate validates this not found
func (m *NotFound) Validate(formats strfmt.Registry) error {
var res []error
Expand All @@ -32,21 +26,3 @@ func (m *NotFound) Validate(formats strfmt.Registry) error {
}
return nil
}

// MarshalBinary interface implementation
func (m *NotFound) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}

// UnmarshalBinary interface implementation
func (m *NotFound) UnmarshalBinary(b []byte) error {
var res NotFound
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
Loading