update deps
This commit is contained in:
+1
-1
@@ -69,4 +69,4 @@ issues:
|
||||
exclude-use-default: false
|
||||
|
||||
service:
|
||||
golangci-lint-version: 1.15.x
|
||||
golangci-lint-version: 1.19.x
|
||||
|
||||
+5
-5
@@ -4,21 +4,21 @@ services:
|
||||
- mongodb
|
||||
|
||||
go:
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
|
||||
install: true
|
||||
|
||||
before_install:
|
||||
- export TZ=America/Chicago
|
||||
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.17.1
|
||||
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.19.1
|
||||
- golangci-lint --version
|
||||
- go get github.com/mattn/goveralls
|
||||
- export MONGO_TEST=mongodb://127.0.0.1:27017
|
||||
- export PATH=$(pwd)/bin:$PATH
|
||||
|
||||
script:
|
||||
- GO111MODULE=on go get ./...
|
||||
- GO111MODULE=on go mod vendor
|
||||
- GO111MODULE=on go test -v -mod=vendor -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
|
||||
- go get ./...
|
||||
- go mod vendor
|
||||
- go test -v -mod=vendor -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
|
||||
- golangci-lint run --tests=false || travis_terminate 1;
|
||||
- $GOPATH/bin/goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Umputun
|
||||
Copyright (c) 2019 Umputun
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-12
@@ -2,8 +2,6 @@ package avatar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -58,7 +56,7 @@ func (b *BoltDB) Put(userID string, reader io.Reader) (avatar string, err error)
|
||||
return errors.Wrapf(err, "can't put to bucket with %s", avatarID)
|
||||
}
|
||||
// store sha1 of the image
|
||||
return tx.Bucket([]byte(metasBktName)).Put([]byte(avatarID), []byte(b.sha1(buf.Bytes(), avatarID)))
|
||||
return tx.Bucket([]byte(metasBktName)).Put([]byte(avatarID), []byte(hash(buf.Bytes(), avatarID)))
|
||||
})
|
||||
return avatarID, err
|
||||
}
|
||||
@@ -130,12 +128,3 @@ func (b *BoltDB) Close() error {
|
||||
func (b *BoltDB) String() string {
|
||||
return fmt.Sprintf("boltdb, path=%s", b.fileName)
|
||||
}
|
||||
|
||||
func (b *BoltDB) sha1(data []byte, avatarID string) (id string) {
|
||||
h := sha1.New()
|
||||
if _, err := h.Write(data); err != nil {
|
||||
log.Printf("[DEBUG] can't apply sha1 for content of '%s', %s", avatarID, err)
|
||||
return encodeID(avatarID)
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
+97
-66
@@ -2,122 +2,153 @@ package avatar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/mgo"
|
||||
"github.com/go-pkgz/mongo"
|
||||
"github.com/pkg/errors"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/gridfs"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// NewGridFS makes gridfs (mongo) avatar store
|
||||
func NewGridFS(conn *mongo.Connection) *GridFS {
|
||||
return &GridFS{Connection: conn}
|
||||
func NewGridFS(client *mongo.Client, dbName, bucketName string, timeout time.Duration) *GridFS {
|
||||
return &GridFS{client: client, db: client.Database(dbName), bucketName: bucketName, timeout: timeout}
|
||||
}
|
||||
|
||||
// GridFS implements Store for GridFS
|
||||
type GridFS struct {
|
||||
Connection *mongo.Connection
|
||||
client *mongo.Client
|
||||
db *mongo.Database
|
||||
bucketName string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// Put avatar to gridfs object, try to resize
|
||||
func (gf *GridFS) Put(userID string, reader io.Reader) (avatar string, err error) {
|
||||
id := encodeID(userID)
|
||||
err = gf.Connection.WithDB(func(dbase *mgo.Database) error {
|
||||
fh, e := dbase.GridFS("fs").Create(id + imgSfx)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer func() {
|
||||
if err = fh.Close(); err != nil {
|
||||
log.Printf("[WARN] can't close avatar file %v, %s", fh, err)
|
||||
}
|
||||
}()
|
||||
bucket, err := gridfs.NewBucket(gf.db, &options.BucketOptions{Name: &gf.bucketName})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, e = io.Copy(fh, reader)
|
||||
return e
|
||||
})
|
||||
buf := &bytes.Buffer{}
|
||||
if _, err = io.Copy(buf, reader); err != nil {
|
||||
return "", errors.Wrapf(err, "can't read avatar for %s", userID)
|
||||
}
|
||||
|
||||
avaHash := hash(buf.Bytes(), id)
|
||||
_, err = bucket.UploadFromStream(id+imgSfx, buf, &options.UploadOptions{Metadata: bson.M{"hash": avaHash}})
|
||||
return id + imgSfx, err
|
||||
}
|
||||
|
||||
// Get avatar reader for avatar id.image
|
||||
func (gf *GridFS) Get(avatar string) (reader io.ReadCloser, size int, err error) {
|
||||
bucket, err := gridfs.NewBucket(gf.db, &options.BucketOptions{Name: &gf.bucketName})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
err = gf.Connection.WithDB(func(dbase *mgo.Database) error {
|
||||
fh, e := dbase.GridFS("fs").Open(avatar)
|
||||
if e != nil {
|
||||
return errors.Wrapf(e, "can't load avatar %s", avatar)
|
||||
}
|
||||
if _, e = io.Copy(buf, fh); e != nil {
|
||||
return errors.Wrapf(e, "can't copy avatar %s", avatar)
|
||||
}
|
||||
size = int(fh.Size())
|
||||
return fh.Close()
|
||||
})
|
||||
return ioutil.NopCloser(buf), size, err
|
||||
sz, e := bucket.DownloadToStreamByName(avatar, buf)
|
||||
return ioutil.NopCloser(buf), int(sz), errors.Wrapf(e, "can't read avatar %s", avatar)
|
||||
}
|
||||
|
||||
//
|
||||
// ID returns a fingerprint of the avatar content. Uses MD5 because gridfs provides it directly
|
||||
func (gf *GridFS) ID(avatar string) (id string) {
|
||||
err := gf.Connection.WithDB(func(dbase *mgo.Database) error {
|
||||
fh, e := dbase.GridFS("fs").Open(avatar)
|
||||
if e != nil {
|
||||
return errors.Wrapf(e, "can't open avatar %s", avatar)
|
||||
}
|
||||
id = fh.MD5()
|
||||
return errors.Wrapf(fh.Close(), "can't close avatar")
|
||||
})
|
||||
|
||||
finfo := struct {
|
||||
ID primitive.ObjectID `bson:"_id"`
|
||||
Len int `bson:"length"`
|
||||
FileName string `bson:"filename"`
|
||||
MetaData struct {
|
||||
Hash string `bson:"hash"`
|
||||
} `bson:"metadata"`
|
||||
}{}
|
||||
|
||||
bucket, err := gridfs.NewBucket(gf.db, &options.BucketOptions{Name: &gf.bucketName})
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] can't get file info '%s', %s", avatar, err)
|
||||
return encodeID(avatar)
|
||||
}
|
||||
return id
|
||||
cursor, err := bucket.Find(bson.M{"filename": avatar})
|
||||
if err != nil {
|
||||
return encodeID(avatar)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), gf.timeout)
|
||||
defer cancel()
|
||||
if found := cursor.Next(ctx); found {
|
||||
if err = cursor.Decode(&finfo); err != nil {
|
||||
return encodeID(avatar)
|
||||
}
|
||||
return finfo.MetaData.Hash
|
||||
}
|
||||
return encodeID(avatar)
|
||||
}
|
||||
|
||||
// Remove avatar from gridfs
|
||||
func (gf *GridFS) Remove(avatar string) error {
|
||||
return gf.Connection.WithDB(func(dbase *mgo.Database) error {
|
||||
fh, e := dbase.GridFS("fs").Open(avatar)
|
||||
if e != nil {
|
||||
return errors.Wrapf(e, "can't get avatar %s", avatar)
|
||||
bucket, err := gridfs.NewBucket(gf.db, &options.BucketOptions{Name: &gf.bucketName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cursor, err := bucket.Find(bson.M{"filename": avatar})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := struct {
|
||||
ID primitive.ObjectID `bson:"_id"`
|
||||
}{}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), gf.timeout)
|
||||
defer cancel()
|
||||
if found := cursor.Next(ctx); found {
|
||||
if err := cursor.Decode(&r); err != nil {
|
||||
return err
|
||||
}
|
||||
if e = fh.Close(); e != nil {
|
||||
log.Printf("[WARN] can't close avatar %s, %s", avatar, e)
|
||||
}
|
||||
return dbase.GridFS("fs").Remove(avatar)
|
||||
})
|
||||
return bucket.Delete(r.ID)
|
||||
}
|
||||
return errors.Errorf("avatar %s not found", avatar)
|
||||
}
|
||||
|
||||
// List all avatars (ids) on gfs
|
||||
// note: id includes .image suffix
|
||||
func (gf *GridFS) List() (ids []string, err error) {
|
||||
|
||||
type gfsFile struct {
|
||||
UploadDate time.Time `bson:"uploadDate"`
|
||||
Length int64 `bson:",minsize"`
|
||||
MD5 string
|
||||
Filename string `bson:",omitempty"`
|
||||
bucket, err := gridfs.NewBucket(gf.db, &options.BucketOptions{Name: &gf.bucketName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files := []gfsFile{}
|
||||
err = gf.Connection.WithDB(func(dbase *mgo.Database) error {
|
||||
return dbase.GridFS("fs").Find(nil).All(&files)
|
||||
})
|
||||
|
||||
for _, f := range files {
|
||||
ids = append(ids, f.Filename)
|
||||
gfsFile := struct {
|
||||
Filename string `bson:"filename,omitempty"`
|
||||
}{}
|
||||
cursor, err := bucket.Find(bson.M{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ids, errors.Wrap(err, "can't list avatars")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), gf.timeout)
|
||||
defer cancel()
|
||||
for cursor.Next(ctx) {
|
||||
if err := cursor.Decode(&gfsFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids = append(ids, gfsFile.Filename)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// Close gridfs does nothing but satisfies interface
|
||||
func (gf *GridFS) Close() error {
|
||||
return nil
|
||||
ctx, cancel := context.WithTimeout(context.Background(), gf.timeout)
|
||||
defer cancel()
|
||||
return gf.client.Disconnect(ctx)
|
||||
}
|
||||
|
||||
func (gf *GridFS) String() string {
|
||||
return fmt.Sprintf("mongo (grid fs), conn=%s", gf.Connection)
|
||||
return fmt.Sprintf("mongo (grid fs), db=%s, bucket=%s", gf.db.Name(), gf.bucketName)
|
||||
}
|
||||
|
||||
+25
-5
@@ -3,7 +3,9 @@ package avatar
|
||||
//go:generate sh -c "mockery -inpkg -name Store -print > /tmp/mock.tmp && mv /tmp/mock.tmp store_mock.go"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
_ "image/gif" // initializing packages for supporting GIF
|
||||
_ "image/jpeg" // initializing packages for supporting JPEG.
|
||||
@@ -17,8 +19,9 @@ import (
|
||||
|
||||
bolt "github.com/coreos/bbolt"
|
||||
"github.com/go-pkgz/auth/token"
|
||||
"github.com/go-pkgz/mongo"
|
||||
"github.com/pkg/errors"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// imgSfx for avatars
|
||||
@@ -46,15 +49,23 @@ func NewStore(uri string) (Store, error) {
|
||||
case !strings.Contains(uri, "://"):
|
||||
return NewLocalFS(uri), nil
|
||||
case strings.HasPrefix(uri, "mongodb://"):
|
||||
db, coll, u, err := parseExtMongoURI(uri)
|
||||
|
||||
db, bucketName, u, err := parseExtMongoURI(uri)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't parse mongo store uri %s", uri)
|
||||
}
|
||||
mg, err := mongo.NewServerWithURL(u, time.Second)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(u))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to make mongo server")
|
||||
return nil, errors.Wrap(err, "failed to connect to mongo server")
|
||||
}
|
||||
return NewGridFS(mongo.NewConnection(mg, db, coll)), nil
|
||||
if err = client.Ping(ctx, nil); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to connect to mongo server")
|
||||
}
|
||||
return NewGridFS(client, db, bucketName, time.Second*5), nil
|
||||
case strings.HasPrefix(uri, "bolt://"):
|
||||
return NewBoltDB(strings.TrimPrefix(uri, "bolt://"), bolt.Options{})
|
||||
}
|
||||
@@ -113,3 +124,12 @@ func parseExtMongoURI(uri string) (db, collection, cleanURI string, err error) {
|
||||
u.RawQuery = q.Encode()
|
||||
return db, collection, u.String(), nil
|
||||
}
|
||||
|
||||
func hash(data []byte, avatarID string) (id string) {
|
||||
h := sha1.New()
|
||||
if _, err := h.Write(data); err != nil {
|
||||
log.Printf("[DEBUG] can't apply sha1 for content of '%s', %s", avatarID, err)
|
||||
return encodeID(avatarID)
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
+8
-4
@@ -5,21 +5,25 @@ require (
|
||||
github.com/coreos/bbolt v1.3.3
|
||||
github.com/dghubble/oauth1 v0.6.0
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
||||
github.com/go-chi/chi v4.0.2+incompatible // indirect
|
||||
github.com/go-pkgz/auth/_example v0.0.0-20190722170031-705d3f732438 // indirect
|
||||
github.com/go-pkgz/mongo v1.1.2
|
||||
github.com/go-pkgz/rest v1.4.1
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.2
|
||||
github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/tidwall/pretty v1.0.0 // indirect
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
|
||||
github.com/xdg/stringprep v1.0.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.3 // indirect
|
||||
go.mongodb.org/mongo-driver v1.1.1
|
||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff
|
||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae // indirect
|
||||
google.golang.org/appengine v1.6.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/oauth2.v3 v3.10.1
|
||||
)
|
||||
|
||||
|
||||
+28
-23
@@ -7,9 +7,7 @@ cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlr
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f h1:zvClvFQwU++UpIUBGC8YmDlfhUrweEy1R1Fj1gu5iIM=
|
||||
github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -21,30 +19,17 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gavv/httpexpect v0.0.0-20180803094507-bdde30871313 h1:GSPjYG49Uqn3S1oeFgJtlGI3ykTavl/yvYgZlz6wsoI=
|
||||
github.com/gavv/httpexpect v0.0.0-20180803094507-bdde30871313/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||
github.com/gavv/monotime v0.0.0-20171021193802-6f8212e8d10d h1:oYXrtNhqNKL1dVtKdv8XUq5zqdGVFNQ0/4tvccXZOLM=
|
||||
github.com/gavv/monotime v0.0.0-20171021193802-6f8212e8d10d/go.mod h1:vmp8DIyckQMXOPl0AQVHt+7n5h7Gb7hS6CUydiV8QeA=
|
||||
github.com/globalsign/mgo v0.0.0-20180615134936-113d3961e731/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-chi/chi v4.0.1+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
|
||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-pkgz/auth v0.4.1/go.mod h1:CWtB8dHmOv+TfF3MUzKwk/YwTLepC2TaDL05A+pFVBM=
|
||||
github.com/go-pkgz/auth/_example v0.0.0-20190722170031-705d3f732438 h1:A9QL16LR1e+WzK85qUX1tIVe+9dhuOZrnp/5Lsyl0Po=
|
||||
github.com/go-pkgz/auth/_example v0.0.0-20190722170031-705d3f732438/go.mod h1:rvZtFFkmm3p+E0CHmfUqTGKweCVg2ddsRUrEEPB1iaE=
|
||||
github.com/go-pkgz/lgr v0.2.2/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
|
||||
github.com/go-pkgz/lgr v0.6.2 h1:Twf2YIe2J5tg7mKs+IkDDxrDF7GWlTCl/LzqELWjT5o=
|
||||
github.com/go-pkgz/lgr v0.6.2/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
|
||||
github.com/go-pkgz/mongo v1.0.0/go.mod h1:R9si/F2aJsjz4MUxhzuppIHY8yLV3YCeuCpgcI50cu4=
|
||||
github.com/go-pkgz/mongo v1.1.2 h1:2Vqn3CWQJkkx4gxxDiQUitAW2FN/CH26lKHkipmpKcc=
|
||||
github.com/go-pkgz/mongo v1.1.2/go.mod h1:0NkWnzpiUxoL5fYZuttCtJrpC67oNDidfYxcdPqHTf0=
|
||||
github.com/go-pkgz/rest v1.2.0/go.mod h1:COazNj35u3RXAgQNBr6neR599tYP3URiOpsu9p0rOtk=
|
||||
github.com/go-pkgz/rest v1.4.1 h1:DmaVLPH2O7yLehrWOW0uz01d2mVHz9fBR/iuTiPRzaw=
|
||||
github.com/go-pkgz/rest v1.4.1/go.mod h1:COazNj35u3RXAgQNBr6neR599tYP3URiOpsu9p0rOtk=
|
||||
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
@@ -52,24 +37,30 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||
github.com/klauspost/compress v1.4.0 h1:8nsMz3tWa9SWWPL60G1V6CUsf4lLjWLTNEtibhe8gh8=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
@@ -80,7 +71,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
|
||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
||||
@@ -89,7 +82,9 @@ github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOA
|
||||
github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022 h1:Ys0rDzh8s4UMlGaDa1UTA0sfKgvF0hQZzTYX8ktjiDc=
|
||||
github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022/go.mod h1:x4NsS+uc7ecH/Cbm9xKQ6XzmJM57rWTkjywjfB2yQ18=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -104,7 +99,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -118,6 +112,8 @@ github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb h1:5NSYaAdrnblKByzd7
|
||||
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M=
|
||||
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2KoJQD9cTQ6dyP2co9q4yzmT9FZo=
|
||||
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao=
|
||||
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ3KKDyCkxapfufrqDqwmGjcHfAyXRrE=
|
||||
@@ -127,6 +123,10 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
|
||||
github.com/valyala/fasthttp v1.0.0 h1:BwIoZQbBsTo3v2F5lz5Oy3TlTq4wLKTLV260EVTEWco=
|
||||
github.com/valyala/fasthttp v1.0.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
@@ -139,14 +139,17 @@ github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCO
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.mongodb.org/mongo-driver v1.1.1 h1:Sq1fR+0c58RME5EoqKdjkiQAmPjmfHlZOoRI6fTUOcs=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff h1:+2zgJKVDVAz/BWSsuniCmU1kLCjL88Z8/kv39xCI9NQ=
|
||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -160,7 +163,6 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -170,7 +172,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a h1:+KkCgOMgnKSgenxTBoiwkMqTiouMIy/3o8RLdmSbGoY=
|
||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -179,10 +180,10 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -191,6 +192,7 @@ golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae h1:xiXzMMEQdQcric9hXtr1QU98M
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -218,10 +220,13 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/oauth2.v3 v3.10.1 h1:/abis3O6tZFizY1/FgKGoDOmmu2ddvscBSSPdHVa6OI=
|
||||
gopkg.in/oauth2.v3 v3.10.1/go.mod h1:nTG+m2PRcHR9jzGNrGdxSsUKz7vvwkqSlhFrstgZcRU=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// Package provider implements all oauth2, oauth1 as well as custom and direct providers
|
||||
package provider
|
||||
|
||||
import (
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// Package sender provides email sender
|
||||
package sender
|
||||
|
||||
import (
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// Package token wraps jwt-go library and provides higher level abstraction to work with JWT.
|
||||
package token
|
||||
|
||||
import (
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 15
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 2
|
||||
misspell:
|
||||
locale: US
|
||||
lll:
|
||||
line-length: 140
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- performance
|
||||
- style
|
||||
- experimental
|
||||
disabled-checks:
|
||||
- wrapperFunc
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- megacheck
|
||||
- govet
|
||||
- unconvert
|
||||
- megacheck
|
||||
- structcheck
|
||||
- gas
|
||||
- gocyclo
|
||||
- dupl
|
||||
- misspell
|
||||
- unparam
|
||||
- varcheck
|
||||
- deadcode
|
||||
- typecheck
|
||||
- ineffassign
|
||||
- varcheck
|
||||
fast: false
|
||||
|
||||
|
||||
run:
|
||||
# modules-download-mode: vendor
|
||||
skip-dirs:
|
||||
- vendor
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- text: "weak cryptographic primitive"
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
service:
|
||||
golangci-lint-version: 1.19.x
|
||||
+1
-1
@@ -9,7 +9,7 @@ go_import_path: github.com/go-pkgz/lcw
|
||||
|
||||
before_install:
|
||||
- export TZ=America/Chicago
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.13.2
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.19.1
|
||||
- go get github.com/mattn/goveralls
|
||||
- export PATH=$(pwd)/bin:$PATH
|
||||
|
||||
|
||||
+19
-10
@@ -3,26 +3,26 @@
|
||||
|
||||
The library adds a thin layer on top of [lru cache](https://github.com/hashicorp/golang-lru) and [patrickmn/go-cache](https://github.com/patrickmn/go-cache).
|
||||
|
||||
| Cache name | Constructor | Defaults | Description |
|
||||
| -------------- | --------------------- | ----------------- | --------------------- |
|
||||
| LruCache | lcw.NewLruCache | keys=1000 | LRU cache with limits |
|
||||
| ExpirableCache | lcw.NewExpirableCache | keys=1000, ttl=5m | TTL cache with limits |
|
||||
| Nop | lcw.NewNopCache | | Do-nothing cache |
|
||||
| Cache name | Constructor | Defaults | Description |
|
||||
| -------------- | --------------------- | ----------------- | ----------------------- |
|
||||
| LruCache | lcw.NewLruCache | keys=1000 | LRU cache with limits |
|
||||
| ExpirableCache | lcw.NewExpirableCache | keys=1000, ttl=5m | TTL cache with limits |
|
||||
| RedisCache | lcw.NewRedisCache | ttl=5m | Redis cache with limits |
|
||||
| Nop | lcw.NewNopCache | | Do-nothing cache |
|
||||
|
||||
|
||||
Main features:
|
||||
|
||||
- LoadingCache (guava style)
|
||||
- Limit maximum cache size (in bytes)
|
||||
- Limit maximum key size
|
||||
- Limit maximum size of a value
|
||||
- Limit maximum size of a value
|
||||
- Limit number of keys
|
||||
- TTL support (`ExpirableCache` only)
|
||||
- Callback on eviction event
|
||||
- TTL support (`ExpirableCache` and `RedisCache`)
|
||||
- Callback on eviction event (not supported in `RedisCache`)
|
||||
- Functional style invalidation
|
||||
- Functional options
|
||||
- Sane defaults
|
||||
|
||||
|
||||
## Install and update
|
||||
|
||||
`go get -u github.com/go-pkgz/lcw`
|
||||
@@ -45,6 +45,15 @@ s := val.(string) // cached value
|
||||
|
||||
```
|
||||
|
||||
### Cache with URI
|
||||
|
||||
Cache can be created with URIs:
|
||||
|
||||
- `mem://lru?max_key_size=10&max_val_size=1024&max_keys=50&max_cache_size=64000` - creates LRU cache with given limits
|
||||
- `mem://expirable?ttl=30s&max_key_size=10&max_val_size=1024&max_keys=50&max_cache_size=64000` - create expirable cache
|
||||
- `redis://10.0.0.1:1234?db=16&password=qwerty&network=tcp4&dial_timeout=1s&read_timeout=5s&write_timeout=3s` - create redis cache
|
||||
- `nop://` - create Nop cache
|
||||
|
||||
## Details
|
||||
|
||||
- All byte-size limits (MaxCacheSize and MaxValSize) only work for values implementing `lcw.Sizer` interface.
|
||||
|
||||
+6
-4
@@ -1,12 +1,14 @@
|
||||
// Package lcw adds a thin layer on top of lru cache and go-cache providing more limits and common interface.
|
||||
// The primary method to get (and set) data to/from the cache is LoadingCache.Get retruning stored data for a given key or
|
||||
// call provided func to retrive and store, similar to Guava loading cache.
|
||||
// The primary method to get (and set) data to/from the cache is LoadingCache.Get returning stored data for a given key or
|
||||
// call provided func to retrieve and store, similar to Guava loading cache.
|
||||
// Limits allow max values for key size, number of keys, value size and total size of values in the cache.
|
||||
// CacheStat gives general stats on cache performance.
|
||||
// 3 flavours of cache provided - NoP (do-nothing cache), ExpirableCache (TTL based), and LruCache
|
||||
// 3 flavors of cache provided - NoP (do-nothing cache), ExpirableCache (TTL based), and LruCache
|
||||
package lcw
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Value type wraps interface{}
|
||||
type Value interface{}
|
||||
|
||||
+15
-1
@@ -1,9 +1,23 @@
|
||||
module github.com/go-pkgz/lcw
|
||||
|
||||
require (
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.0
|
||||
github.com/go-redis/redis/v7 v7.0.0-beta.4
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||
github.com/hashicorp/go-multierror v1.0.0
|
||||
github.com/hashicorp/golang-lru v0.5.3
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036 // indirect
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
||||
go 1.13
|
||||
|
||||
+66
-2
@@ -1,9 +1,42 @@
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-redis/redis/v7 v7.0.0-beta.4 h1:p6z7Pde69EGRWvlC++y8aFcaWegyrKHzOBGo0zUACTQ=
|
||||
github.com/go-redis/redis/v7 v7.0.0-beta.4/go.mod h1:xhhSbUMTsleRPur+Vgx9sUHtyN33bdjxY+9/0n9Ig8s=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
@@ -13,3 +46,34 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036 h1:1b6PAtenNyhsmo/NKXVe34h7JEZKva1YB/ne7K7mqKM=
|
||||
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952 h1:FDfvYgoVsA7TTZSbgiqjAbfPbK47CNHdWl3h/PJtii0=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// LruCache wraps lru.LruCache with laoding cache Get and size limits
|
||||
// LruCache wraps lru.LruCache with loading cache Get and size limits
|
||||
type LruCache struct {
|
||||
options
|
||||
CacheStat
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package lcw
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
redis "github.com/go-redis/redis/v7"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// RedisValueSizeLimit is maximum allowed value size in Redis
|
||||
const RedisValueSizeLimit = 512 * 1024 * 1024
|
||||
|
||||
// RedisCache implements LoadingCache for Redis.
|
||||
type RedisCache struct {
|
||||
options
|
||||
CacheStat
|
||||
backend *redis.Client
|
||||
}
|
||||
|
||||
// NewRedisCache makes Redis LoadingCache implementation.
|
||||
func NewRedisCache(backend *redis.Client, opts ...Option) (*RedisCache, error) {
|
||||
|
||||
res := RedisCache{
|
||||
options: options{
|
||||
ttl: 5 * time.Minute,
|
||||
},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
if err := opt(&res.options); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set cache option")
|
||||
}
|
||||
}
|
||||
|
||||
if res.maxValueSize <= 0 || res.maxValueSize > RedisValueSizeLimit {
|
||||
res.maxValueSize = RedisValueSizeLimit
|
||||
}
|
||||
|
||||
res.backend = backend
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// Get gets value by key or load with fn if not found in cache
|
||||
func (c *RedisCache) Get(key string, fn func() (Value, error)) (data Value, err error) {
|
||||
|
||||
v, getErr := c.backend.Get(key).Result()
|
||||
switch getErr {
|
||||
// RedisClient returns nil when find a key in DB
|
||||
case nil:
|
||||
atomic.AddInt64(&c.Hits, 1)
|
||||
return v, nil
|
||||
// RedisClient returns redis.Nil when doesn't find a key in DB
|
||||
case redis.Nil:
|
||||
if data, err = fn(); err != nil {
|
||||
atomic.AddInt64(&c.Errors, 1)
|
||||
return data, err
|
||||
}
|
||||
// RedisClient returns !nil when something goes wrong while get data
|
||||
default:
|
||||
atomic.AddInt64(&c.Errors, 1)
|
||||
return v, getErr
|
||||
}
|
||||
atomic.AddInt64(&c.Misses, 1)
|
||||
|
||||
if c.allowed(key, data) {
|
||||
_, setErr := c.backend.Set(key, data, c.ttl).Result()
|
||||
if setErr != nil {
|
||||
atomic.AddInt64(&c.Errors, 1)
|
||||
return data, setErr
|
||||
}
|
||||
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Invalidate removes keys with passed predicate fn, i.e. fn(key) should be true to get evicted
|
||||
func (c *RedisCache) Invalidate(fn func(key string) bool) {
|
||||
for _, key := range c.backend.Keys("*").Val() { // Keys() returns copy of cache's key, safe to remove directly
|
||||
if fn(key) {
|
||||
c.backend.Del(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
|
||||
func (c *RedisCache) Peek(key string) (Value, bool) {
|
||||
ret, err := c.backend.Get(key).Result()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return ret, true
|
||||
}
|
||||
|
||||
// Purge clears the cache completely.
|
||||
func (c *RedisCache) Purge() {
|
||||
c.backend.FlushDB()
|
||||
|
||||
}
|
||||
|
||||
// Delete cache item by key
|
||||
func (c *RedisCache) Delete(key string) {
|
||||
c.backend.Del(key)
|
||||
}
|
||||
|
||||
// Stat returns cache statistics
|
||||
func (c *RedisCache) Stat() CacheStat {
|
||||
return CacheStat{
|
||||
Hits: c.Hits,
|
||||
Misses: c.Misses,
|
||||
Size: c.size(),
|
||||
Keys: c.keys(),
|
||||
Errors: c.Errors,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RedisCache) size() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *RedisCache) keys() int {
|
||||
return int(c.backend.DBSize().Val())
|
||||
}
|
||||
|
||||
func (c *RedisCache) allowed(key string, data Value) bool {
|
||||
if c.maxKeys > 0 && c.backend.DBSize().Val() >= int64(c.maxKeys) {
|
||||
return false
|
||||
}
|
||||
if c.maxKeySize > 0 && len(key) > c.maxKeySize {
|
||||
return false
|
||||
}
|
||||
if s, ok := data.(Sizer); ok {
|
||||
if c.maxValueSize > 0 && (s.Size() >= c.maxValueSize) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package lcw
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v7"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// New parses uri and makes any of supported caches
|
||||
// supported URIs:
|
||||
// - redis://<ip>:<port>?db=123&max_keys=10
|
||||
// - mem://lru?max_keys=10&max_cache_size=1024
|
||||
// - mem://expirable?ttl=30s&max_val_size=100
|
||||
// - nop://
|
||||
func New(uri string) (LoadingCache, error) {
|
||||
u, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "parse cache uri %s", uri)
|
||||
}
|
||||
|
||||
query := u.Query()
|
||||
opts, err := optionsFromQuery(query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "parse uri options %s", uri)
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "redis":
|
||||
redisOpts, err := redisOptionsFromURL(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := NewRedisCache(redis.NewClient(redisOpts), opts...)
|
||||
return res, errors.Wrapf(err, "make redis for %s", uri)
|
||||
case "mem":
|
||||
switch u.Hostname() {
|
||||
case "lru":
|
||||
return NewLruCache(opts...)
|
||||
case "expirable":
|
||||
return NewExpirableCache(opts...)
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported mem cache type %s", u.Hostname())
|
||||
}
|
||||
case "nop":
|
||||
return NewNopCache(), nil
|
||||
}
|
||||
return nil, errors.Errorf("unsupported cache type %s", u.Scheme)
|
||||
}
|
||||
|
||||
func optionsFromQuery(q url.Values) (opts []Option, err error) {
|
||||
|
||||
errs := new(multierror.Error)
|
||||
|
||||
if v := q.Get("max_val_size"); v != "" {
|
||||
vv, e := strconv.Atoi(v)
|
||||
if e != nil {
|
||||
errs = multierror.Append(errs, errors.Wrapf(e, "max_val_size query param %s", v))
|
||||
} else {
|
||||
opts = append(opts, MaxValSize(vv))
|
||||
}
|
||||
}
|
||||
|
||||
if v := q.Get("max_key_size"); v != "" {
|
||||
vv, e := strconv.Atoi(v)
|
||||
if e != nil {
|
||||
errs = multierror.Append(errs, errors.Wrapf(e, "max_key_size query param %s", v))
|
||||
} else {
|
||||
opts = append(opts, MaxKeySize(vv))
|
||||
}
|
||||
}
|
||||
|
||||
if v := q.Get("max_keys"); v != "" {
|
||||
vv, e := strconv.Atoi(v)
|
||||
if e != nil {
|
||||
errs = multierror.Append(errs, errors.Wrapf(e, "max_keys query param %s", v))
|
||||
} else {
|
||||
opts = append(opts, MaxKeys(vv))
|
||||
}
|
||||
}
|
||||
|
||||
if v := q.Get("max_cache_size"); v != "" {
|
||||
vv, e := strconv.ParseInt(v, 10, 64)
|
||||
if e != nil {
|
||||
errs = multierror.Append(errs, errors.Wrapf(e, "max_cache_size query param %s", v))
|
||||
} else {
|
||||
opts = append(opts, MaxCacheSize(vv))
|
||||
}
|
||||
}
|
||||
|
||||
if v := q.Get("ttl"); v != "" {
|
||||
vv, e := time.ParseDuration(v)
|
||||
if e != nil {
|
||||
errs = multierror.Append(errs, errors.Wrapf(e, "ttl query param %s", v))
|
||||
} else {
|
||||
opts = append(opts, TTL(vv))
|
||||
}
|
||||
}
|
||||
|
||||
return opts, errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
func redisOptionsFromURL(u *url.URL) (*redis.Options, error) {
|
||||
query := u.Query()
|
||||
|
||||
db, err := strconv.Atoi(query.Get("db"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "db from %s", u)
|
||||
}
|
||||
|
||||
res := &redis.Options{
|
||||
Addr: u.Hostname() + ":" + u.Port(),
|
||||
DB: db,
|
||||
Password: query.Get("password"),
|
||||
Network: query.Get("network"),
|
||||
}
|
||||
|
||||
if dialTimeout, err := time.ParseDuration(query.Get("dial_timeout")); err == nil {
|
||||
res.DialTimeout = dialTimeout
|
||||
}
|
||||
|
||||
if readTimeout, err := time.ParseDuration(query.Get("read_timeout")); err == nil {
|
||||
res.ReadTimeout = readTimeout
|
||||
}
|
||||
|
||||
if writeTimeout, err := time.ParseDuration(query.Get("write_timeout")); err == nil {
|
||||
res.WriteTimeout = writeTimeout
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
|
||||
install: true
|
||||
|
||||
go_import_path: github.com/go-pkgz/mongo
|
||||
|
||||
services: mongodb
|
||||
|
||||
before_install:
|
||||
- export TZ=America/Chicago
|
||||
- go get gopkg.in/alecthomas/gometalinter.v2
|
||||
- $GOPATH/bin/gometalinter.v2 --install
|
||||
- go get github.com/mattn/goveralls
|
||||
- export PATH=$(pwd)/bin:$PATH
|
||||
- export MONGO_TEST=mongodb://127.0.0.1:27017
|
||||
|
||||
|
||||
script:
|
||||
- GO111MODULE=on go get ./...
|
||||
- GO111MODULE=on go mod vendor
|
||||
- GO111MODULE=on go test -v -mod=vendor -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
|
||||
- $GOPATH/bin/gometalinter.v2 --deadline=120s --exclude=test --exclude=mock --exclude=vendor --exclude=_example --disable-all --enable=errcheck --enable=vet --enable=vetshadow --enable=megacheck --enable=ineffassign --enable=varcheck --enable=unconvert --enable=deadcode --enable=interfacer --enable=gotype ./... || travis_terminate 1;
|
||||
- $GOPATH/bin/goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Umputun
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
# Mongo [](https://travis-ci.org/go-pkgz/mongo) [](https://goreportcard.com/report/github.com/go-pkgz/mongo) [](https://coveralls.io/github/go-pkgz/mongo?branch=master)
|
||||
|
||||
Provides helpers on top of [mgo](https://github.com/globalsign/mgo)
|
||||
|
||||
## Install and update
|
||||
|
||||
`go get -u github.com/go-pkgz/mongo`
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
- `Server` represents mongo instance and provides session accessor. Application usually creates one server object and uses it for anything needed with this particular mongo host or replica set.
|
||||
|
||||
- `Connection` encapsulates session and provides auto-closable wrapper. Each requests runs inside one of With* function makes new mongo session and closes on completion.
|
||||
|
||||
- `BufferedWriter` implements buffered writer to mongo. Write method caching internally till it reached buffer size. Flush methods can be called manually at any time.
|
||||
|
||||
|
||||
```golang
|
||||
m, err := NewServerWithURL("mongodb://127.0.0.1:27017/test?debug=true", 3*time.Second)
|
||||
if err != nil {
|
||||
panic("can't make mongo server")
|
||||
}
|
||||
|
||||
type testRecord struct {
|
||||
Key1 string
|
||||
Kay2 int
|
||||
}
|
||||
|
||||
err = c.WithCollection(func(coll *mgo.Collection) error { // create session
|
||||
// insert 100 records
|
||||
for i := 0; i < 100; i++ {
|
||||
r := testRecord{
|
||||
Key1: fmt.Sprintf("key-%02d", i%5),
|
||||
Key2: i,
|
||||
}
|
||||
if e := coll.Insert(r); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [globalsign/mgo](https://github.com/globalsign/mgo) - mgo mongo driver
|
||||
- [stretchr/testify/](https://github.com/stretchr/testify) - testing library (test-only dependency)
|
||||
|
||||
## Testing
|
||||
|
||||
`testing.go` helps to create test for real mongo (not mocks)
|
||||
|
||||
- `mongo.MakeTestConnection` creates `mongo.Connection` for url defined in env `MONGO_TEST`. If not defined `mongodb://mongo:27017` used. By default it will use random connection with prefix `test_` in `test` DB.
|
||||
- `mongo.RemoveTestCollection` - drops collection used by `MakeTestConnection`
|
||||
- `mongo.RemoveTestCollections` - drops user-defined collections from `test` DB
|
||||
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
package mongo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/globalsign/mgo"
|
||||
)
|
||||
|
||||
// sessionFn is a function for all With*Collection calls
|
||||
type sessionFn func(coll *mgo.Collection) error
|
||||
|
||||
// Connection allows to run request in separate session, closing automatically
|
||||
type Connection struct {
|
||||
server *Server
|
||||
db, collection string
|
||||
}
|
||||
|
||||
// NewConnection makes a connection for server
|
||||
func NewConnection(server *Server, db string, collection string) *Connection {
|
||||
return &Connection{server: server, db: db, collection: collection}
|
||||
}
|
||||
|
||||
// WithCollection passes fun with mgo.Collection from session copy, closes it after done,
|
||||
// uses Connection.DB and Connection.Collection
|
||||
func (c *Connection) WithCollection(fun sessionFn) (err error) {
|
||||
return c.WithCustomCollection(c.collection, fun)
|
||||
}
|
||||
|
||||
// WithCustomCollection passes fun with mgo.Collection from session copy, closes it after done
|
||||
// uses Connection.DB or (if not defined) dial.Database, and user-defined collection
|
||||
func (c *Connection) WithCustomCollection(collection string, fun sessionFn) (err error) {
|
||||
db := c.server.dial.Database
|
||||
if c.db != "" {
|
||||
db = c.db
|
||||
}
|
||||
return c.WithCustomDbCollection(db, collection, fun)
|
||||
}
|
||||
|
||||
// WithCustomDbCollection passed fun with mgo.Collection from session copy, closes it after done
|
||||
// uses passed db and collection directly.
|
||||
func (c *Connection) WithCustomDbCollection(db string, collection string, fun sessionFn) (err error) {
|
||||
session := c.server.SessionCopy()
|
||||
defer session.Close()
|
||||
return fun(session.DB(db).C(collection))
|
||||
}
|
||||
|
||||
// WithDB passes fun with mgo.Database from session copy, closes it after done
|
||||
// uses Connection.DB or (if not defined) dial.Database
|
||||
func (c *Connection) WithDB(fun func(dbase *mgo.Database) error) (err error) {
|
||||
db := c.server.dial.Database
|
||||
if c.db != "" {
|
||||
db = c.db
|
||||
}
|
||||
return c.WithCustomDB(db, fun)
|
||||
}
|
||||
|
||||
// WithCustomDB passes fun with mgo.Database from session copy, closes it after done
|
||||
// uses passed db directly
|
||||
func (c *Connection) WithCustomDB(db string, fun func(dbase *mgo.Database) error) (err error) {
|
||||
session := c.server.SessionCopy()
|
||||
defer session.Close()
|
||||
return fun(session.DB(db))
|
||||
}
|
||||
|
||||
func (c *Connection) String() string {
|
||||
return fmt.Sprintf("mongo:%s, db:%s, collection:%s", c.server, c.db, c.collection)
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
module github.com/go-pkgz/mongo
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/globalsign/mgo v0.0.0-20180615134936-113d3961e731
|
||||
github.com/go-pkgz/lgr v0.2.2
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
github.com/stretchr/testify v1.3.0
|
||||
)
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/globalsign/mgo v0.0.0-20180615134936-113d3961e731 h1:y7wyeiA6T+TT+HGC9DYypvLkUeg99N4rqHMzn2MmjYk=
|
||||
github.com/globalsign/mgo v0.0.0-20180615134936-113d3961e731/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-pkgz/lgr v0.1.5 h1:oWj3VNlyYL2uUpdL6Gbi21BAH619Xb/8E78ozpyI2xo=
|
||||
github.com/go-pkgz/lgr v0.1.5/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
|
||||
github.com/go-pkgz/lgr v0.2.2 h1:HSOqMVoetAfvA40Gpy/X/HGyV0UUafIMPgp+SdZends=
|
||||
github.com/go-pkgz/lgr v0.2.2/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
// Package mongo wraps mgo to provide easier way to construct mongo server (with auth).
|
||||
// Connection provides With* func wrappers to run query with session copy
|
||||
package mongo
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/mgo"
|
||||
log "github.com/go-pkgz/lgr"
|
||||
)
|
||||
|
||||
// Server represents mongo instance and provides session accessor
|
||||
type Server struct {
|
||||
dial mgo.DialInfo
|
||||
params ServerParams
|
||||
sess *mgo.Session
|
||||
}
|
||||
|
||||
// ServerParams optional set of parameters
|
||||
type ServerParams struct {
|
||||
ConsistencyMode mgo.Mode
|
||||
Delay int // initial delay to give mongo server some time to start, in case if mongo part of the same compose
|
||||
Debug bool // turn on mgo debug mode
|
||||
SSL bool // enforce SSL connection
|
||||
}
|
||||
|
||||
// NewServerWithURL makes mongo server from url like
|
||||
// mongodb://remark42:password@127.0.0.1:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin
|
||||
func NewServerWithURL(url string, timeout time.Duration) (res *Server, err error) {
|
||||
dial, params, err := parseURL(url, timeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create mongo server from url %s, %s", url, err)
|
||||
}
|
||||
return NewServer(dial, params)
|
||||
}
|
||||
|
||||
// NewServer doing auth if passwd != "" and can delay to make sure local mongo is up
|
||||
func NewServer(dial mgo.DialInfo, params ServerParams) (res *Server, err error) {
|
||||
result := Server{dial: dial, params: params}
|
||||
|
||||
if params.Debug {
|
||||
mgo.SetDebug(true)
|
||||
mgo.SetLogger(&mgdLogger{})
|
||||
}
|
||||
|
||||
if len(dial.Addrs) == 0 {
|
||||
return nil, errors.New("missing mongo address")
|
||||
}
|
||||
|
||||
if params.Delay > 0 {
|
||||
log.Printf("[DEBUG] initial mongo delay=%d", params.Delay)
|
||||
time.Sleep(time.Duration(params.Delay) * time.Second)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] dial mongo %s, ssl=%v", dial.Addrs, params.SSL)
|
||||
|
||||
if params.SSL {
|
||||
tlsConfig := &tls.Config{}
|
||||
dial.DialServer = func(addr *mgo.ServerAddr) (net.Conn, error) {
|
||||
conn, e := tls.Dial("tcp", addr.String(), tlsConfig)
|
||||
return conn, e
|
||||
}
|
||||
}
|
||||
|
||||
session, err := mgo.DialWithInfo(&dial)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't connect to mongo, %v", err)
|
||||
}
|
||||
session.SetMode(params.ConsistencyMode, true)
|
||||
session.SetSyncTimeout(30 * time.Second)
|
||||
session.SetSocketTimeout(dial.Timeout)
|
||||
|
||||
if dial.Username != "" && dial.Password != "" {
|
||||
creds := &mgo.Credential{Username: dial.Username, Password: dial.Password, Source: dial.Source}
|
||||
log.Printf("[DEBUG] login to mongo, user=%s, db=%s", creds.Username, creds.Source)
|
||||
if err = session.Login(creds); err != nil {
|
||||
return nil, fmt.Errorf("can't login to mongo, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
result.sess = session
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// SessionCopy returns copy of main session. Client should close it
|
||||
func (m *Server) SessionCopy() *mgo.Session {
|
||||
return m.sess.Copy()
|
||||
}
|
||||
|
||||
func (m *Server) String() string {
|
||||
return fmt.Sprintf("%v%s", m.dial.Addrs, m.dial.Database)
|
||||
}
|
||||
|
||||
// parseURL extends mgo with debug option and extracts ssl flag to make ServerParams
|
||||
func parseURL(mongoURL string, connectTimeout time.Duration) (mgo.DialInfo, ServerParams, error) {
|
||||
params := ServerParams{
|
||||
ConsistencyMode: mgo.Monotonic,
|
||||
SSL: strings.Contains(mongoURL, "ssl=true"),
|
||||
Debug: strings.Contains(mongoURL, "debug=true"),
|
||||
}
|
||||
|
||||
mongoURL = strings.Replace(mongoURL, "&debug=true", "", 1)
|
||||
mongoURL = strings.Replace(mongoURL, "?debug=true", "", 1)
|
||||
|
||||
dial, err := mgo.ParseURL(mongoURL)
|
||||
if err != nil {
|
||||
return mgo.DialInfo{}, ServerParams{}, fmt.Errorf("failed to parse mongo url %s, %s", mongoURL, err)
|
||||
}
|
||||
dial.Timeout = connectTimeout
|
||||
return *dial, params, nil
|
||||
}
|
||||
|
||||
type mgdLogger struct{}
|
||||
|
||||
func (l *mgdLogger) Output(calldepth int, s string) error {
|
||||
log.Printf("[DEBUG] MGO %s", s)
|
||||
return nil
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
package mongo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/mgo"
|
||||
log "github.com/go-pkgz/lgr"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var conn *Connection
|
||||
var once sync.Once
|
||||
|
||||
// MakeTestConnection connects to MONGO_TEST url or "mongo" host (in no env) and returns new connection.
|
||||
// collection name randomized on each call
|
||||
func MakeTestConnection(t *testing.T) (*Connection, error) {
|
||||
mongoURL := getMongoURL(t)
|
||||
once.Do(func() {
|
||||
log.Print("[DEBUG] connect to mongo test instance")
|
||||
srv, err := NewServerWithURL(mongoURL, 10*time.Second)
|
||||
assert.Nil(t, err, "failed to dial")
|
||||
collName := fmt.Sprintf("test_%d", time.Now().Nanosecond())
|
||||
conn = NewConnection(srv, "test", collName)
|
||||
})
|
||||
RemoveTestCollection(t, conn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// RemoveTestCollection removes all records and drop collection from connection
|
||||
func RemoveTestCollection(t *testing.T, c *Connection) {
|
||||
log.Printf("[DEBUG] clean test collection %+v", c.collection)
|
||||
_ = c.WithCollection(func(coll *mgo.Collection) error {
|
||||
_, e := coll.RemoveAll(nil)
|
||||
require.Nil(t, e, "failed to remove records, %s", e)
|
||||
e = coll.DropCollection()
|
||||
if e != nil && e.Error() != "ns not found" {
|
||||
require.Nil(t, e, "failed to drop collection, %s", e)
|
||||
}
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveTestCollections clears passed collections
|
||||
func RemoveTestCollections(t *testing.T, c *Connection, collections ...string) {
|
||||
log.Printf("[DEBUG] clean test collections %+v", collections)
|
||||
for _, collection := range collections {
|
||||
_ = c.WithCustomCollection(collection, func(coll *mgo.Collection) error {
|
||||
_, e := coll.RemoveAll(nil)
|
||||
require.Nil(t, e, "failed to remove records, %s", e)
|
||||
e = coll.DropCollection()
|
||||
if e != nil && e.Error() != "ns not found" {
|
||||
require.Nil(t, e, "failed to drop collection, %s", e)
|
||||
}
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getMongoURL(t *testing.T) string {
|
||||
mongoURL := os.Getenv("MONGO_TEST")
|
||||
if mongoURL == "" {
|
||||
mongoURL = "mongodb://mongo:27017"
|
||||
t.Logf("no MONGO_TEST in env, defaulted to %s", mongoURL)
|
||||
}
|
||||
if mongoURL == "skip" {
|
||||
t.Skip("skip mongo test")
|
||||
}
|
||||
return mongoURL
|
||||
}
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
package mongo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/mgo"
|
||||
log "github.com/go-pkgz/lgr"
|
||||
)
|
||||
|
||||
// BufferedWriter defines interface for writes and flush
|
||||
type BufferedWriter interface {
|
||||
Write(rec interface{}) error
|
||||
Flush() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// BufferedWriterMgo collects records in local buffer and flushes them as filled. Thread safe
|
||||
// by default using both DB and collection from provided connection.
|
||||
// Collection can be customized by WithCollection method. Optional flush duration to save on interval
|
||||
type BufferedWriterMgo struct {
|
||||
connection *Connection
|
||||
bufferSize int
|
||||
collection string
|
||||
flushDuration time.Duration
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
buffer []interface{}
|
||||
lock sync.Mutex
|
||||
lastWriteTime time.Time
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// NewBufferedWriter makes batch writer for given size and connection
|
||||
func NewBufferedWriter(size int, connection *Connection) *BufferedWriterMgo {
|
||||
if size == 0 {
|
||||
size = 1
|
||||
}
|
||||
return &BufferedWriterMgo{
|
||||
bufferSize: size,
|
||||
buffer: make([]interface{}, 0, size+1),
|
||||
connection: connection,
|
||||
}
|
||||
}
|
||||
|
||||
// WithCollection sets custom collection to use with writer
|
||||
func (bw *BufferedWriterMgo) WithCollection(collection string) *BufferedWriterMgo {
|
||||
bw.collection = collection
|
||||
return bw
|
||||
}
|
||||
|
||||
// WithAutoFlush sets auto flush duration
|
||||
func (bw *BufferedWriterMgo) WithAutoFlush(duration time.Duration) *BufferedWriterMgo {
|
||||
bw.flushDuration = duration
|
||||
if duration > 0 { // activate background auto-flush
|
||||
bw.once.Do(func() {
|
||||
bw.ctx, bw.cancel = context.WithCancel(context.Background())
|
||||
ticker := time.NewTicker(duration)
|
||||
go func() {
|
||||
defer bw.cancel()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
var shouldFlush bool
|
||||
_ = bw.synced(func() error {
|
||||
shouldFlush = time.Now().After(bw.lastWriteTime.Add(bw.flushDuration)) && len(bw.buffer) > 0
|
||||
return nil
|
||||
})
|
||||
if shouldFlush {
|
||||
if err := bw.Flush(); err != nil {
|
||||
log.Printf("[WARN] flush failed, %s", err)
|
||||
}
|
||||
}
|
||||
case <-bw.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
return bw
|
||||
}
|
||||
|
||||
// Write to buffer and, as filled, to mongo. If flushDuration defined check for automatic flush
|
||||
func (bw *BufferedWriterMgo) Write(rec interface{}) error {
|
||||
return bw.synced(func() error {
|
||||
bw.lastWriteTime = time.Now()
|
||||
bw.buffer = append(bw.buffer, rec)
|
||||
if len(bw.buffer) >= bw.bufferSize {
|
||||
if err := bw.writeBuffer(); err != nil {
|
||||
return fmt.Errorf("failed to write to %s, %s", bw.connection, err)
|
||||
}
|
||||
bw.buffer = bw.buffer[0:0]
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Flush writes everything left in buffer to mongo
|
||||
func (bw *BufferedWriterMgo) Flush() error {
|
||||
err := bw.synced(func() error {
|
||||
err := bw.writeBuffer()
|
||||
bw.buffer = bw.buffer[0:0]
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to flush to %s, %s", bw.connection, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close flushes all in-fly records and terminates background auto-flusher
|
||||
func (bw *BufferedWriterMgo) Close() (err error) {
|
||||
return bw.synced(func() error {
|
||||
err = bw.writeBuffer()
|
||||
if bw.flushDuration > 0 {
|
||||
bw.cancel()
|
||||
<-bw.ctx.Done()
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// writeBuffer sends all collected records to mongo
|
||||
func (bw *BufferedWriterMgo) writeBuffer() (err error) {
|
||||
|
||||
if len(bw.buffer) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if bw.collection == "" { // no custom collection
|
||||
err = bw.connection.WithCollection(func(coll *mgo.Collection) error {
|
||||
return coll.Insert(bw.buffer...)
|
||||
})
|
||||
}
|
||||
|
||||
if bw.collection != "" { // with custom collection
|
||||
err = bw.connection.WithCustomCollection(bw.collection, func(coll *mgo.Collection) error {
|
||||
return coll.Insert(bw.buffer...)
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (bw *BufferedWriterMgo) synced(fn func() error) error {
|
||||
bw.lock.Lock()
|
||||
defer bw.lock.Unlock()
|
||||
return fn()
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 15
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 2
|
||||
misspell:
|
||||
locale: US
|
||||
lll:
|
||||
line-length: 140
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- performance
|
||||
- style
|
||||
- experimental
|
||||
disabled-checks:
|
||||
- wrapperFunc
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- megacheck
|
||||
- govet
|
||||
- unconvert
|
||||
- megacheck
|
||||
- structcheck
|
||||
- gas
|
||||
- gocyclo
|
||||
- dupl
|
||||
- misspell
|
||||
- unparam
|
||||
- varcheck
|
||||
- deadcode
|
||||
- typecheck
|
||||
- ineffassign
|
||||
- varcheck
|
||||
fast: false
|
||||
|
||||
|
||||
run:
|
||||
# modules-download-mode: vendor
|
||||
skip-dirs:
|
||||
- vendor
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- text: "weak cryptographic primitive"
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
service:
|
||||
golangci-lint-version: 1.16.x
|
||||
+11
-7
@@ -1,16 +1,20 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
|
||||
go_import_path: github.com/go-pkgz/repeater
|
||||
install: true
|
||||
|
||||
before_install:
|
||||
- export TZ=America/Chicago
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.13.2
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get gopkg.in/alecthomas/gometalinter.v2
|
||||
- $GOPATH/bin/gometalinter.v2 --install
|
||||
- export PATH=$(pwd)/bin:$PATH
|
||||
|
||||
script:
|
||||
- go test ./...
|
||||
- $GOPATH/bin/gometalinter.v2 --exclude=test --exclude=mock --exclude=vendor ./...
|
||||
- $GOPATH/bin/goveralls -service=travis-ci
|
||||
- GO111MODULE=on go get ./...
|
||||
- GO111MODULE=on go mod vendor
|
||||
- GO111MODULE=on go test -v -mod=vendor -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
|
||||
- GO111MODULE=on go test -v -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
|
||||
- golangci-lint run || travis_terminate 1;
|
||||
- $GOPATH/bin/goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Umputun
|
||||
Copyright (c) 2019 Umputun
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+4
@@ -1 +1,5 @@
|
||||
module github.com/go-pkgz/repeater
|
||||
|
||||
go 1.12
|
||||
|
||||
require github.com/stretchr/testify v1.3.0
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
+1
-2
@@ -45,7 +45,6 @@ func (r Repeater) Do(ctx context.Context, fun func() error, errors ...error) (er
|
||||
}
|
||||
|
||||
ch := r.Start(ctx) // channel of ticks-like events provided by strategy
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -57,7 +56,7 @@ func (r Repeater) Do(ctx context.Context, fun func() error, errors ...error) (er
|
||||
if err = fun(); err == nil {
|
||||
return nil
|
||||
}
|
||||
if err != nil && inErrors(err) { //terminate on critical error from provided list
|
||||
if err != nil && inErrors(err) { // terminate on critical error from provided list
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
// Backoff implements strategy.Interface for exponential-backoff
|
||||
// it starts from 100ms and goes in steps with last * math.Pow(factor, attempt)
|
||||
// it starts from 100ms (by default, if no Duration set) and goes in steps with last * math.Pow(factor, attempt)
|
||||
// optional jitter randomize intervals a little bit.
|
||||
type Backoff struct {
|
||||
Duration time.Duration
|
||||
@@ -23,7 +23,7 @@ type Backoff struct {
|
||||
// Start returns channel, similar to time.Timer
|
||||
// then publishing signals to channel ch for retries attempt. Closed ch indicates "done" event
|
||||
// consumer (repeater) should stop it explicitly after completion
|
||||
func (b *Backoff) Start(ctx context.Context) (ch chan struct{}) {
|
||||
func (b *Backoff) Start(ctx context.Context) <-chan struct{} {
|
||||
|
||||
b.once.Do(func() {
|
||||
if b.Duration == 0 {
|
||||
@@ -37,7 +37,7 @@ func (b *Backoff) Start(ctx context.Context) (ch chan struct{}) {
|
||||
}
|
||||
})
|
||||
|
||||
ch = make(chan struct{})
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
rnd := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
||||
@@ -45,14 +45,14 @@ func (b *Backoff) Start(ctx context.Context) (ch chan struct{}) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
ch <- struct{}{}
|
||||
delay := float64(b.Duration) * math.Pow(b.Factor, float64(i))
|
||||
if b.Jitter {
|
||||
delay = rnd.Float64()*(float64(2*b.Duration)) + (delay - float64(b.Duration))
|
||||
}
|
||||
sleep(ctx, time.Duration(delay))
|
||||
case ch <- struct{}{}:
|
||||
}
|
||||
|
||||
delay := float64(b.Duration) * math.Pow(b.Factor, float64(i))
|
||||
if b.Jitter {
|
||||
delay = rnd.Float64()*(float64(2*b.Duration)) + (delay - float64(b.Duration))
|
||||
}
|
||||
sleep(ctx, time.Duration(delay))
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
|
||||
+7
-6
@@ -14,21 +14,22 @@ type FixedDelay struct {
|
||||
// Start returns channel, similar to time.Timer
|
||||
// then publishing signals to channel ch for retries attempt.
|
||||
// can be terminated (canceled) via context.
|
||||
func (s *FixedDelay) Start(ctx context.Context) (ch chan struct{}) {
|
||||
func (s *FixedDelay) Start(ctx context.Context) <-chan struct{} {
|
||||
if s.Repeats == 0 {
|
||||
s.Repeats = 1
|
||||
}
|
||||
ch = make(chan struct{})
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
defer func() {
|
||||
close(ch)
|
||||
}()
|
||||
for i := 0; i < s.Repeats; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
ch <- struct{}{}
|
||||
sleep(ctx, s.Delay)
|
||||
case ch <- struct{}{}:
|
||||
}
|
||||
sleep(ctx, s.Delay)
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
|
||||
+3
-3
@@ -9,15 +9,15 @@ import (
|
||||
|
||||
// Interface for repeater strategy. Returns channel with ticks
|
||||
type Interface interface {
|
||||
Start(ctx context.Context) chan struct{}
|
||||
Start(ctx context.Context) <-chan struct{}
|
||||
}
|
||||
|
||||
// Once strategy eliminate repeats and makes a single try only
|
||||
type Once struct{}
|
||||
|
||||
// Start returns closed channel with a single element to prevent any repeats
|
||||
func (s *Once) Start(ctx context.Context) (ch chan struct{}) {
|
||||
ch = make(chan struct{})
|
||||
func (s *Once) Start(ctx context.Context) <-chan struct{} {
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
ch <- struct{}{}
|
||||
close(ch)
|
||||
|
||||
Reference in New Issue
Block a user