Compare commits

..

No commits in common. "master" and "v4.0.0" have entirely different histories.

18 changed files with 343 additions and 159 deletions

View File

@ -1,29 +0,0 @@
name: lint
on:
pull_request:
types: [opened, reopened, synchronize]
branches:
- master
- v3
- v4
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
filter: 'blob:none'
- name: setup go
uses: actions/setup-go@v5
with:
cache-dependency-path: "**/*.sum"
go-version: 'stable'
- name: setup deps
run: go get -v ./...
- name: run lint
uses: https://github.com/golangci/golangci-lint-action@v6
with:
version: 'latest'

View File

@ -1,34 +0,0 @@
name: test
on:
pull_request:
types: [opened, reopened, synchronize]
branches:
- master
- v3
- v4
push:
branches:
- master
- v3
- v4
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
filter: 'blob:none'
- name: setup go
uses: actions/setup-go@v5
with:
cache-dependency-path: "**/*.sum"
go-version: 'stable'
- name: setup deps
run: go get -v ./...
- name: run test
env:
INTEGRATION_TESTS: yes
run: go test -mod readonly -v ./...

View File

@ -1,53 +0,0 @@
name: test
on:
pull_request:
types: [opened, reopened, synchronize]
branches:
- master
- v3
- v4
push:
branches:
- master
- v3
- v4
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
filter: 'blob:none'
- name: checkout tests
uses: actions/checkout@v4
with:
ref: master
filter: 'blob:none'
repository: unistack-org/micro-tests
path: micro-tests
- name: setup go
uses: actions/setup-go@v5
with:
cache-dependency-path: "**/*.sum"
go-version: 'stable'
- name: setup go work
env:
GOWORK: /workspace/${{ github.repository_owner }}/go.work
run: |
go work init
go work use .
go work use micro-tests
- name: setup deps
env:
GOWORK: /workspace/${{ github.repository_owner }}/go.work
run: go get -v ./...
- name: run tests
env:
INTEGRATION_TESTS: yes
GOWORK: /workspace/${{ github.repository_owner }}/go.work
run: |
cd micro-tests
go test -mod readonly -v ./... || true

19
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,19 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
# Maintain dependencies for Golang
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"

20
.github/workflows/autoapprove.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: "autoapprove"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
permissions:
pull-requests: write
contents: write
jobs:
autoapprove:
runs-on: ubuntu-latest
steps:
- name: approve
uses: hmarr/auto-approve-action@v3
if: github.actor == 'vtolstov' || github.actor == 'dependabot[bot]'
id: approve
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

21
.github/workflows/automerge.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: "automerge"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
permissions:
pull-requests: write
contents: write
jobs:
automerge:
runs-on: ubuntu-latest
if: github.actor == 'vtolstov'
steps:
- name: merge
id: merge
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.TOKEN}}

47
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: build
on:
push:
branches:
- master
- v3
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: setup
uses: actions/setup-go@v3
with:
go-version: 1.17
- name: checkout
uses: actions/checkout@v3
- name: cache
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: deps
run: go get -v -t -d ./...
- name: test
env:
INTEGRATION_TESTS: yes
run: go test -mod readonly -v ./...
lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: lint
uses: golangci/golangci-lint-action@v3.4.0
continue-on-error: true
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.30
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true

78
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,78 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "codeql"
on:
workflow_run:
workflows: ["prbuild"]
types:
- completed
push:
branches: [ master, v3 ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master, v3 ]
schedule:
- cron: '34 1 * * 0'
jobs:
analyze:
name: analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup
uses: actions/setup-go@v3
with:
go-version: 1.17
# Initializes the CodeQL tools for scanning.
- name: init
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: analyze
uses: github/codeql-action/analyze@v2

View File

@ -0,0 +1,27 @@
name: "dependabot-automerge"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
permissions:
pull-requests: write
contents: write
jobs:
automerge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: metadata
id: metadata
uses: dependabot/fetch-metadata@v1.3.6
with:
github-token: "${{ secrets.TOKEN }}"
- name: merge
id: merge
if: ${{contains(steps.metadata.outputs.dependency-names, 'go.unistack.org')}}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.TOKEN}}

47
.github/workflows/pr.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: prbuild
on:
pull_request:
branches:
- master
- v3
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: setup
uses: actions/setup-go@v3
with:
go-version: 1.17
- name: checkout
uses: actions/checkout@v3
- name: cache
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: deps
run: go get -v -t -d ./...
- name: test
env:
INTEGRATION_TESTS: yes
run: go test -mod readonly -v ./...
lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: lint
uses: golangci/golangci-lint-action@v3.4.0
continue-on-error: true
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.30
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true

View File

@ -1,5 +0,0 @@
run:
concurrency: 8
deadline: 5m
issues-exit-code: 1
tests: true

View File

@ -5,8 +5,14 @@ import (
"go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/options"
) )
type universalConfigKey struct{} type configKey struct{}
func UniversalConfig(c *redis.UniversalOptions) options.Option { func Config(c *redis.Options) options.Option {
return options.ContextOption(universalConfigKey{}, c) return options.ContextOption(configKey{}, c)
}
type clusterConfigKey struct{}
func ClusterConfig(c *redis.ClusterOptions) options.Option {
return options.ContextOption(clusterConfigKey{}, c)
} }

106
redis.go
View File

@ -14,7 +14,21 @@ import (
type Store struct { type Store struct {
opts store.Options opts store.Options
cli redis.UniversalClient cli redisClient
}
type redisClient interface {
Get(ctx context.Context, key string) *redis.StringCmd
Del(ctx context.Context, keys ...string) *redis.IntCmd
Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.StatusCmd
Keys(ctx context.Context, pattern string) *redis.StringSliceCmd
MGet(ctx context.Context, keys ...string) *redis.SliceCmd
MSet(ctx context.Context, kv ...interface{}) *redis.StatusCmd
Exists(ctx context.Context, keys ...string) *redis.IntCmd
Ping(ctx context.Context) *redis.StatusCmd
Pipeline() redis.Pipeliner
Pipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error)
Close() error
} }
func (r *Store) Connect(ctx context.Context) error { func (r *Store) Connect(ctx context.Context) error {
@ -22,11 +36,11 @@ func (r *Store) Connect(ctx context.Context) error {
} }
func (r *Store) Init(opts ...options.Option) error { func (r *Store) Init(opts ...options.Option) error {
return r.configure(opts...) for _, o := range opts {
} o(&r.opts)
}
func (r *Store) Redis() redis.UniversalClient { return r.configure()
return r.cli
} }
func (r *Store) Disconnect(ctx context.Context) error { func (r *Store) Disconnect(ctx context.Context) error {
@ -233,11 +247,8 @@ func (r *Store) MWrite(ctx context.Context, keys []string, vals []interface{}, o
} }
cmds, err := r.cli.Pipelined(ctx, func(pipe redis.Pipeliner) error { cmds, err := r.cli.Pipelined(ctx, func(pipe redis.Pipeliner) error {
var err error
for idx := 0; idx < len(kvs); idx += 2 { for idx := 0; idx < len(kvs); idx += 2 {
if _, err = pipe.Set(ctx, kvs[idx], kvs[idx+1], options.TTL).Result(); err != nil { pipe.Set(ctx, kvs[idx], kvs[idx+1], options.TTL).Result()
return err
}
} }
return nil return nil
}) })
@ -334,38 +345,69 @@ func NewStore(opts ...options.Option) *Store {
return &Store{opts: store.NewOptions(opts...)} return &Store{opts: store.NewOptions(opts...)}
} }
func (r *Store) configure(opts ...options.Option) error { func (r *Store) configure() error {
if r.cli != nil && len(opts) == 0 { var redisOptions *redis.Options
var redisClusterOptions *redis.ClusterOptions
var err error
nodes := r.opts.Address
if len(nodes) == 0 {
nodes = []string{"redis://127.0.0.1:6379"}
}
if r.cli != nil && r.opts.Context == nil {
return nil return nil
} }
var redisUniversalOptions *redis.UniversalOptions
var err error
for _, o := range opts {
if err = o(r.opts); err != nil {
return err
}
}
if r.opts.Context != nil { if r.opts.Context != nil {
if c, ok := r.opts.Context.Value(universalConfigKey{}).(*redis.UniversalOptions); ok { if c, ok := r.opts.Context.Value(configKey{}).(*redis.Options); ok {
redisUniversalOptions = c redisOptions = c
if r.opts.TLSConfig != nil { if r.opts.TLSConfig != nil {
redisUniversalOptions.TLSConfig = r.opts.TLSConfig redisOptions.TLSConfig = r.opts.TLSConfig
}
}
if c, ok := r.opts.Context.Value(clusterConfigKey{}).(*redis.ClusterOptions); ok {
redisClusterOptions = c
if r.opts.TLSConfig != nil {
redisClusterOptions.TLSConfig = r.opts.TLSConfig
} }
} }
} }
if redisUniversalOptions == nil && r.cli != nil { if redisOptions != nil && redisClusterOptions != nil {
return fmt.Errorf("must specify only one option Config or ClusterConfig")
}
if redisOptions == nil && redisClusterOptions == nil && r.cli != nil {
return nil return nil
} }
if redisUniversalOptions == nil { if redisOptions == nil && redisClusterOptions == nil && len(nodes) == 1 {
redisUniversalOptions = &redis.UniversalOptions{ redisOptions, err = redis.ParseURL(nodes[0])
if err != nil {
// Backwards compatibility
redisOptions = &redis.Options{
Addr: nodes[0],
Username: "",
Password: "", // no password set
DB: 0, // use default DB
MaxRetries: 2,
MaxRetryBackoff: 256 * time.Millisecond,
DialTimeout: 1 * time.Second,
ReadTimeout: 1 * time.Second,
WriteTimeout: 1 * time.Second,
PoolTimeout: 1 * time.Second,
MinIdleConns: 10,
TLSConfig: r.opts.TLSConfig,
}
}
} else if redisOptions == nil && redisClusterOptions == nil && len(nodes) > 1 {
redisClusterOptions = &redis.ClusterOptions{
Addrs: nodes,
Username: "", Username: "",
Password: "", // no password set Password: "", // no password set
DB: 0, // use default DB
MaxRetries: 2, MaxRetries: 2,
MaxRetryBackoff: 256 * time.Millisecond, MaxRetryBackoff: 256 * time.Millisecond,
DialTimeout: 1 * time.Second, DialTimeout: 1 * time.Second,
@ -377,13 +419,11 @@ func (r *Store) configure(opts ...options.Option) error {
} }
} }
if len(r.opts.Address) > 0 { if redisOptions != nil {
redisUniversalOptions.Addrs = r.opts.Address r.cli = redis.NewClient(redisOptions)
} else if len(redisUniversalOptions.Addrs) == 0 { } else if redisClusterOptions != nil {
redisUniversalOptions.Addrs = []string{"redis://127.0.0.1:6379"} r.cli = redis.NewClusterClient(redisClusterOptions)
} }
r.cli = redis.NewUniversalClient(redisUniversalOptions)
return nil return nil
} }

View File

@ -72,11 +72,11 @@ func Test_rkv_configure(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
rc := &Store{ r := &Store{
opts: tt.fields.options, opts: tt.fields.options,
cli: tt.fields.Client, cli: tt.fields.Client,
} }
err := rc.configure() err := r.configure()
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("configure() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("configure() error = %v, wantErr %v", err, tt.wantErr)
return return