url: Distributed hit counter with in memory cache (#114)

This commit is contained in:
Janos Dobronszki
2021-05-19 16:13:25 +01:00
committed by GitHub
parent 26ad8bc08d
commit 17cd2399c8
4 changed files with 95 additions and 33 deletions

View File

@@ -7,7 +7,9 @@ import (
"time"
"github.com/micro/micro/v3/service/config"
"github.com/micro/micro/v3/service/logger"
"github.com/micro/micro/v3/service/model"
cache "github.com/patrickmn/go-cache"
"github.com/teris-io/shortid"
"github.com/micro/services/pkg/tenant"
@@ -19,6 +21,7 @@ const hostPrefix = "https://m3o.one/u/"
type Url struct {
pairs model.Model
ownerIndex model.Index
cache *cache.Cache
hostPrefix string
}
@@ -44,6 +47,7 @@ func NewUrl() *Url {
pairs: m,
ownerIndex: ownerIndex,
hostPrefix: hp,
cache: cache.New(cache.NoExpiration, cache.NoExpiration),
}
}
@@ -85,6 +89,14 @@ func (e *Url) List(ctx context.Context, req *url.ListRequest, rsp *url.ListRespo
return err
}
for _, v := range rsp.UrlPairs {
// get the counter and add it to db value to improve
// accuracy
// A thing to keep in mind is that in memory cache hits
// from other nodes wont be added to this. Still, hopefully good enough
count, ok := e.cache.Get(v.ShortURL)
if ok {
v.HitCount += count.(int64)
}
v.ShortURL = e.hostPrefix + v.ShortURL
}
return nil
@@ -92,10 +104,31 @@ func (e *Url) List(ctx context.Context, req *url.ListRequest, rsp *url.ListRespo
func (e *Url) Proxy(ctx context.Context, req *url.ProxyRequest, rsp *url.ProxyResponse) error {
var pair url.URLPair
err := e.pairs.Read(model.QueryEquals("shortURL", strings.Replace(req.ShortURL, e.hostPrefix, "", -1)), &pair)
id := strings.Replace(req.ShortURL, e.hostPrefix, "", -1)
err := e.pairs.Read(model.QueryEquals("shortURL", id), &pair)
if err != nil {
return err
}
v, found := e.cache.Get(id)
// @todo there is an ABA problem with this solution
// when it comes to the hit counter
if !found {
e.cache.Set(id, int64(1), cache.NoExpiration)
} else {
// we null out the counter
e.cache.Set(id, 0, cache.NoExpiration)
if v.(int64)%10 == 0 {
go func() {
// We add instead of set in case the service runs in multiple
// instances
pair.HitCount += v.(int64) + int64(1)
err = e.pairs.Update(pair)
if err != nil {
logger.Error(err)
}
}()
}
}
rsp.DestinationURL = pair.DestinationURL
return nil