Skip to content
Merged
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
13 changes: 12 additions & 1 deletion .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ steps:
ATHENS_MONGO_STORAGE_URL: mongodb://mongo:27017
ATHENS_MINIO_ENDPOINT: minio:9000
REDIS_TEST_ENDPOINT: redis:6379
REDIS_SENTINEL_TEST_ENDPOINT: redis-sentinel:26379
REDIS_SENTINEL_TEST_MASTER_NAME: redis-1
REDIS_SENTINEL_TEST_PASSWORD: sekret
PROTECTED_REDIS_TEST_ENDPOINT: protectedredis:6380
ATHENS_PROTECTED_REDIS_PASSWORD: AthensPass1
GCS_SERVICE_ACCOUNT:
Expand Down Expand Up @@ -156,13 +159,21 @@ services:
image: redis
ports:
- 6379
- name: redis-sentinel
image: bitnami/redis-sentinel
environment:
REDIS_MASTER_HOST: redis
REDIS_MASTER_SET: redis-1
REDIS_SENTINEL_PASSWORD: sekret
REDIS_SENTINEL_QUORUM: "1"
ports:
- 26379
- name: protectedredis
image: redis
ports:
- 6380
commands:
- "redis-server ./test/redis.conf"

- name: athens-proxy
image: gomods/athens:canary
pull: always
Expand Down
10 changes: 10 additions & 0 deletions cmd/proxy/actions/app_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ func getSingleFlight(c *config.Config, checker storage.Checker) (stash.Wrapper,
return nil, fmt.Errorf("Redis config must be present")
}
return stash.WithRedisLock(c.SingleFlight.Redis.Endpoint, c.SingleFlight.Redis.Password, checker)
case "redis-sentinel":
if c.SingleFlight == nil || c.SingleFlight.RedisSentinel == nil {
return nil, fmt.Errorf("Redis config must be present")
}
return stash.WithRedisSentinelLock(
c.SingleFlight.RedisSentinel.Endpoints,
c.SingleFlight.RedisSentinel.MasterName,
c.SingleFlight.RedisSentinel.SentinelPassword,
checker,
)
case "gcp":
if c.StorageType != "gcp" {
return nil, fmt.Errorf("gcp SingleFlight only works with a gcp storage type and not: %v", c.StorageType)
Expand Down
17 changes: 16 additions & 1 deletion config.dev.toml
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ DownloadURL = ""
# and the second request will wait for the first one to finish so that
# it doesn't override the storage.

# Options are ["memory", "etcd", "redis", "gcp", "azureblob"]
# Options are ["memory", "etcd", "redis", "redis-sentinel", "gcp", "azureblob"]

# The default option is "memory" which means that only one instance of Athens
# should be used.
Expand All @@ -275,6 +275,10 @@ DownloadURL = ""
# and therefore it will use its strong-consistency features to ensure
# that only one module is ever written even when concurrent saves happen
# at the same time.
# The "redis" single flight will use a single redis instance as a locking mechanism
# for updating the underlying storage
# The "redis-sentinel" single flight works similarly to "redis" but obtains a redis connection
# via a redis-sentinel
# Env override: ATHENS_SINGLE_FLIGHT_TYPE
SingleFlightType = "memory"

Expand All @@ -290,6 +294,17 @@ SingleFlightType = "memory"
# TODO(marwan): enable multiple endpoints for redis clusters.
# Env override: ATHENS_REDIS_ENDPOINT
Endpoint = "127.0.0.1:6379"
[SingleFlight.RedisSentinel]
# Endpoints is the redis sentinel endpoints to discover a redis
# master for a SingleFlight lock.
# Env override: ATHENS_REDIS_SENTINEL_ENDPOINTS
Endpoints = ["127.0.0.1:26379"]
# MasterName is the redis sentinel master name to use to discover
# the master for a SingleFlight lock
MasterName = "redis-1"
# SentinelPassword is an optional password for authenticating with
# redis sentinel
SentinelPassword = "sekret"

# Password is the password for a redis SingleFlight lock.
# Env override: ATHENS_REDIS_PASSWORD
Expand Down
9 changes: 9 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ services:
image: redis
ports:
- 6379:6379
redis-sentinel:
image: bitnami/redis-sentinel
environment:
- REDIS_MASTER_HOST=redis
- REDIS_MASTER_SET=redis-1
- REDIS_SENTINEL_PASSWORD=sekret
- REDIS_SENTINEL_QUORUM=1
ports:
- 26379:26379
protectedredis:
image: redis
ports:
Expand Down
92 changes: 92 additions & 0 deletions docs/content/configuration/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,95 @@ It assumes that you already have the following:
# Name of container in the blob storage
# Env override: ATHENS_AZURE_CONTAINER_NAME
ContainerName = "MY_AZURE_BLOB_CONTAINER_NAME"

## Running multiple Athens pointed at the same storage

Athens has the ability to run concurrently pointed at the same storage medium, using
a distributed locking mechanism called "single flight".

By default, Athens is configured to use the `memory` single flight, which
stores locks in local memory. This works when running a single Athens instance, given
the process has access to it's own memory. However, when running multiple Athens instances
pointed at the same storage, a distributed locking mechansism is required.

Athens supports several distributed locking mechanisms:

- `etcd`
- `redis`
- `redis-sentinel`
- `gcp` (available when using the `gcp` storage type)
- `azureblob` (available when using the `azureblob` storage type)

Setting the `SingleFlightType` (or `ATHENS_SINGLE_FLIGHT TYPE` in the environment) configuration
value will enable usage of one of the above mechanisms. The `azureblob` and `gcp` types require
no extra configuration.

### Using etcd as the single flight mechanism

Using the `etcd` mechanism is very simple, just a comma separated list of etcd endpoints.
The recommend configuration is 3 endpoints, however, more can be used.

SingleFlightType = "etcd"

[SingleFlight]
[SingleFlight.Etcd]
# Env override: ATHENS_ETCD_ENDPOINTS
Endpoints = "localhost:2379,localhost:22379,localhost:32379"

### Using redis as the single flight mechanism

Athens supports two mechanisms of communicating with redis: direct connection, and
connecting via redis sentinels.

#### Direct connection to redis

Using a direct connection to redis is simple, and only requires a single `redis-server`.
You can also optionally specify a password to connect to the redis server with

SingleFlightType = "redis"

[SingleFlight]
[SingleFlight.Redis]
# Endpoint is the redis endpoint for the single flight mechanism
# Env override: ATHENS_REDIS_ENDPOINT
Endpoint = "127.0.0.1:6379"

# Password is the password for the redis instance
# Env override: ATHENS_REDIS_PASSWORD
Password = ""

#### Connecting to redis via redis sentinel

**NOTE**: redis-sentinel requires a working knowledge of redis and is not recommended for
everyone.

redis sentinel is a high-availability set up for redis, it provides automated monitoring, replication,
failover and configuration of multiple redis servers in a leader-follower setup. It is more
complex than running a single redis server and requires multiple disperate instances of redis
running distributed across nodes.

For more details on redis-sentinel, check out the [documentation](https://redis.io/topics/sentinel)

As redis-sentinel is a more complex set up of redis, it requires more configuration than standard redis.

Required configuration:

- `Endpoints` is a list of redis-sentinel endpoints to connect to, typically 3, but more can be used
- `MasterName` is the named master instance, as configured in the `redis-sentinel` [configuration](https://redis.io/topics/sentinel#configuring-sentinel)

Optionally, like `redis`, you can also specify a password to connect to the `redis-sentinel` endpoints with

SingleFlightType = "redis-sentinel"

[SingleFlight]
[SingleFlight.RedisSentinel]
# Endpoints is the redis sentinel endpoints to discover a redis
# master for a SingleFlight lock.
# Env override: ATHENS_REDIS_SENTINEL_ENDPOINTS
Endpoints = ["127.0.0.1:26379"]
# MasterName is the redis sentinel master name to use to discover
# the master for a SingleFlight lock
MasterName = "redis-1"
# SentinelPassword is an optional password for authenticating with
# redis sentinel
SentinelPassword = "sekret"
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ require (
github.com/DataDog/opencensus-go-exporter-datadog v0.0.0-20180917103902-e6c7f767dc57
github.com/aws/aws-sdk-go v1.15.24
github.com/bsm/redis-lock v8.0.0+incompatible
github.com/bsm/redislock v0.4.2
github.com/codegangsta/negroni v1.0.0 // indirect
github.com/fatih/color v1.7.0
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-redis/redis v6.15.2+incompatible
github.com/go-redis/redis/v7 v7.2.0
github.com/gobuffalo/envy v1.6.7
github.com/gobuffalo/httptest v1.0.4
github.com/golang/snappy v0.0.1 // indirect
Expand Down
21 changes: 21 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k=
github.com/bsm/redis-lock v8.0.0+incompatible h1:QgB0J2pNG8hUfndTIvpPh38F5XsUTTvO7x8Sls++9Mk=
github.com/bsm/redis-lock v8.0.0+incompatible/go.mod h1:8dGkQ5GimBCahwF2R67tqGCJbyDZSp0gzO7wq3pDrik=
github.com/bsm/redislock v0.4.2 h1:+7WydoauDwf5Qw0ajaI/g3t26dQ/ovGU0Dv59sVvQzc=
github.com/bsm/redislock v0.4.2/go.mod h1:zeuSDdDFtEDtbAgKsw7NDucfSVR0zLWBv8tMpro/6UM=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY=
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
Expand Down Expand Up @@ -90,6 +92,10 @@ github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rm
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
github.com/go-redis/redis/v7 v7.0.0-beta.4/go.mod h1:xhhSbUMTsleRPur+Vgx9sUHtyN33bdjxY+9/0n9Ig8s=
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
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/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
Expand All @@ -111,6 +117,8 @@ github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
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/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
Expand Down Expand Up @@ -209,9 +217,15 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
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/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
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/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
Expand Down Expand Up @@ -333,6 +347,8 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand All @@ -353,6 +369,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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=
Expand Down Expand Up @@ -388,6 +406,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
Expand All @@ -403,6 +422,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
5 changes: 5 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ func defaultConfig() *Config {
SingleFlight: &SingleFlight{
Etcd: &Etcd{"localhost:2379,localhost:22379,localhost:32379"},
Redis: &Redis{"127.0.0.1:6379", ""},
RedisSentinel: &RedisSentinel{
Endpoints: []string{"127.0.0.1:26379"},
MasterName: "redis-1",
SentinelPassword: "sekret",
},
},
}
}
Expand Down
13 changes: 11 additions & 2 deletions pkg/config/singleflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ package config
// backend configurations for a distributed
// lock or single flight mechanism.
type SingleFlight struct {
Etcd *Etcd
Redis *Redis
Etcd *Etcd
Redis *Redis
RedisSentinel *RedisSentinel
}

// Etcd holds client side configuration
Expand All @@ -21,3 +22,11 @@ type Redis struct {
Endpoint string `envconfig:"ATHENS_REDIS_ENDPOINT"`
Password string `envconfig:"ATHENS_REDIS_PASSWORD"`
}

// RedisSentinel is the configuration for using redis with sentinel
// for SingleFlight
type RedisSentinel struct {
Endpoints []string `envconfig:"ATHENS_REDIS_SENTINEL_ENDPOINTS"`
MasterName string `envconfig:"ATHENS_REDIS_SENTINEL_MASTER_NAME"`
SentinelPassword string `envconfig:"ATHENS_REDIS_SENTINEL_PASSWORD"`
}
14 changes: 6 additions & 8 deletions pkg/stash/with_redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"context"
"time"

lock "github.com/bsm/redis-lock"
"github.com/go-redis/redis"
lock "github.com/bsm/redislock"
"github.com/go-redis/redis/v7"
"github.com/gomods/athens/pkg/config"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/observ"
Expand Down Expand Up @@ -44,17 +44,15 @@ func (s *redisLock) Stash(ctx context.Context, mod, ver string) (newVer string,
mv := config.FmtModVer(mod, ver)

// Obtain a new lock with default settings
lock, err := lock.Obtain(s.client, mv, &lock.Options{
LockTimeout: time.Minute * 5,
RetryCount: 60 * 5,
RetryDelay: time.Second,
lock, err := lock.Obtain(s.client, mv, time.Minute*5, &lock.Options{
RetryStrategy: lock.LimitRetry(lock.LinearBackoff(time.Second), 60*5),
})
if err != nil {
return ver, errors.E(op, err)
}
defer func() {
const op errors.Op = "redis.Unlock"
lockErr := lock.Unlock()
const op errors.Op = "redis.Release"
lockErr := lock.Release()
if err == nil && lockErr != nil {
err = errors.E(op, lockErr)
}
Expand Down
30 changes: 30 additions & 0 deletions pkg/stash/with_redis_sentinel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package stash

import (
"github.com/go-redis/redis/v7"
"github.com/gomods/athens/pkg/errors"
"github.com/gomods/athens/pkg/storage"
)

// WithRedisSentinelLock returns a distributed singleflight
// with a redis cluster that utilizes sentinel for quorum and failover
func WithRedisSentinelLock(endpoints []string, master, password string, checker storage.Checker) (Wrapper, error) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This could alternatively be compacted into WithRedisLock, but I didn't want to add too much complexity. Thoughts?

Copy link
Member

Choose a reason for hiding this comment

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

I like that you've separated sentinel from redis itself. sentinel is treated differently enough in the redis docs that I think we should treat it differently here from a docs and code perspective.

const op errors.Op = "stash.WithRedisSentinelLock"
// The redis client constructor does not return an error when no endpoints
// are provided, so we check for ourselves.
if len(endpoints) == 0 {
return nil, errors.E(op, "no endpoints specified")
}
client := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: master,
SentinelAddrs: endpoints,
SentinelPassword: password,
})
_, err := client.Ping().Result()
if err != nil {
return nil, errors.E(op, err)
}
return func(s Stasher) Stasher {
return &redisLock{client, s, checker}
}, nil
}
Loading