mirror of
https://github.com/gofiber/storage.git
synced 2025-09-27 04:46:08 +08:00
Compare commits
24 Commits
dependabot
...
s3/v2.2.1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
40a6167b74 | ||
![]() |
e4d46ce66c | ||
![]() |
386b6f431f | ||
![]() |
d2af2eb76b | ||
![]() |
395f8a80da | ||
![]() |
76c52119bf | ||
![]() |
87c2b454f8 | ||
![]() |
12ea6b4978 | ||
![]() |
cce70b9e7f | ||
![]() |
f8776d0233 | ||
![]() |
740eed9579 | ||
![]() |
baaf5c76e0 | ||
![]() |
9fa1710604 | ||
![]() |
674175ded7 | ||
![]() |
fae399ccdc | ||
![]() |
381f0c134e | ||
![]() |
9013b623ea | ||
![]() |
512c66712a | ||
![]() |
692f78abb0 | ||
![]() |
1a7044aded | ||
![]() |
88906bb82b | ||
![]() |
5d095dc16b | ||
![]() |
a18385a6ba | ||
![]() |
1da3514e71 |
50
.github/release-drafter-valkey.yml
vendored
Normal file
50
.github/release-drafter-valkey.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name-template: 'Valkey - v$RESOLVED_VERSION'
|
||||
tag-template: 'valkey/v$RESOLVED_VERSION'
|
||||
tag-prefix: valkey/v
|
||||
include-paths:
|
||||
- valkey
|
||||
categories:
|
||||
- title: '❗ Breaking Changes'
|
||||
labels:
|
||||
- '❗ BreakingChange'
|
||||
- title: '🚀 New'
|
||||
labels:
|
||||
- '✏️ Feature'
|
||||
- title: '🧹 Updates'
|
||||
labels:
|
||||
- '🧹 Updates'
|
||||
- '🤖 Dependencies'
|
||||
- title: '🐛 Fixes'
|
||||
labels:
|
||||
- '☢️ Bug'
|
||||
- title: '📚 Documentation'
|
||||
labels:
|
||||
- '📒 Documentation'
|
||||
change-template: '- $TITLE (#$NUMBER)'
|
||||
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
|
||||
exclude-contributors:
|
||||
- dependabot
|
||||
- dependabot[bot]
|
||||
version-resolver:
|
||||
major:
|
||||
labels:
|
||||
- 'major'
|
||||
- '❗ BreakingChange'
|
||||
minor:
|
||||
labels:
|
||||
- 'minor'
|
||||
- '✏️ Feature'
|
||||
patch:
|
||||
labels:
|
||||
- 'patch'
|
||||
- '📒 Documentation'
|
||||
- '☢️ Bug'
|
||||
- '🤖 Dependencies'
|
||||
- '🧹 Updates'
|
||||
default: patch
|
||||
template: |
|
||||
$CHANGES
|
||||
|
||||
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...valkey/v$RESOLVED_VERSION
|
||||
|
||||
Thank you $CONTRIBUTORS for making this update possible.
|
3
.github/scripts/gen-test-certs.sh
vendored
3
.github/scripts/gen-test-certs.sh
vendored
@@ -56,7 +56,8 @@ _END_
|
||||
generate_cert server "Server-only" "-extfile ./tls/openssl.cnf -extensions server_cert"
|
||||
generate_cert client "Client-only" "-extfile ./tls/openssl.cnf -extensions client_cert"
|
||||
generate_cert redis "localhost" "-extfile ./tls/openssl.cnf -extensions server_cert"
|
||||
generate_cert valkey "localhost" "-extfile ./tls/openssl.cnf -extensions server_cert"
|
||||
|
||||
# List generated certs
|
||||
ls -la ./tls
|
||||
echo "$PWD"
|
||||
echo "$PWD"
|
||||
|
19
.github/workflows/release-drafter-valkey.yml
vendored
Normal file
19
.github/workflows/release-drafter-valkey.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Release Drafter Valkey
|
||||
on:
|
||||
push:
|
||||
# branches to consider in the event; optional, defaults to all
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
paths:
|
||||
- 'valkey/**'
|
||||
jobs:
|
||||
draft_release_valkey:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
with:
|
||||
config-name: release-drafter-valkey.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
2
.github/workflows/test-coherence.yml
vendored
2
.github/workflows/test-coherence.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
- name: Startup Coherence
|
||||
run: |
|
||||
docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:23.09.2
|
||||
docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:24.09
|
||||
sleep 30
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
|
63
.github/workflows/test-valkey.yml
vendored
Normal file
63
.github/workflows/test-valkey.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
paths:
|
||||
- 'valkey/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'valkey/**'
|
||||
- '.github/workflows/test-valkey.yml'
|
||||
name: "Tests Valkey"
|
||||
jobs:
|
||||
Tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version:
|
||||
- 1.23.x
|
||||
valkey:
|
||||
- '7.x'
|
||||
- '8.x'
|
||||
steps:
|
||||
- name: Fetch Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Generate TLS certs
|
||||
run: ./.github/scripts/gen-test-certs.sh
|
||||
|
||||
- name: Setup Valkey
|
||||
uses: shogo82148/actions-setup-redis@v1
|
||||
with:
|
||||
distribution: 'valkey'
|
||||
redis-version: ${{ matrix.valkey }}
|
||||
auto-start: 'false'
|
||||
redis-port: '6379'
|
||||
redis-tls-port: '6380'
|
||||
|
||||
- name: Run Valkey
|
||||
run: |
|
||||
valkey-server --tls-port 6380 --port 6379 \
|
||||
--tls-cert-file /home/runner/work/storage/storage/tls/valkey.crt \
|
||||
--tls-key-file /home/runner/work/storage/storage/tls/valkey.key \
|
||||
--tls-ca-cert-file /home/runner/work/storage/storage/tls/ca.crt &
|
||||
|
||||
- name: Setup Valkey Cluster
|
||||
uses: vishnudxb/redis-cluster@1.0.9
|
||||
with:
|
||||
master1-port: 7000
|
||||
master2-port: 7001
|
||||
master3-port: 7002
|
||||
slave1-port: 7003
|
||||
slave2-port: 7004
|
||||
slave3-port: 7005
|
||||
sleep-duration: 10
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '${{ matrix.go-version }}'
|
||||
|
||||
- name: Run Test
|
||||
run: cd ./valkey && go test ./... -v -race
|
@@ -75,3 +75,4 @@ type Storage interface {
|
||||
- [ScyllaDB](./scylladb/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+scylladb%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-scylladb.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||
- [SQLite3](./sqlite3/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Sqlite3%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-sqlite3.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||
- [ClickHouse](./clickhouse/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Clickhouse%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-clickhouse.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||
- [Valkey](./valkey/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+valkey%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-valkey.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# Coherence
|
||||
<!-- Copyright © 2023, Oracle and/or its affiliates. -->
|
||||
<!-- Copyright © 2023, 2025 Oracle and/or its affiliates. -->
|
||||
A Coherence storage driver using [https://github.com/oracle/coherence-go-client](https://github.com/oracle/coherence-go-client).
|
||||
|
||||
### Table of Contents
|
||||
@@ -35,10 +35,10 @@ necessary for the client to operate correctly.
|
||||
To start a Coherence cluster using Docker, issue the following:
|
||||
|
||||
```bash
|
||||
docker run -d -p 1408:1408 ghcr.io/oracle/coherence-ce:22.06.7
|
||||
docker run -d -p 1408:1408 ghcr.io/oracle/coherence-ce:24.09
|
||||
```
|
||||
|
||||
See the documentation [here](https://pkg.go.dev/github.com/oracle/coherence-go-client/coherence#hdr-Obtaining_a_Session) on connection options
|
||||
See the documentation [here](https://pkg.go.dev/github.com/oracle/coherence-go-client/v2@v2.0.0/coherence#hdr-Obtaining_a_Session) on connection options
|
||||
when creating a Coherence session.
|
||||
|
||||
### Examples
|
||||
|
@@ -1,13 +1,13 @@
|
||||
package coherence
|
||||
|
||||
/*
|
||||
* Copyright © 2023, 2024 Oracle and/or its affiliates.
|
||||
* Copyright © 2023, 2025 Oracle and/or its affiliates.
|
||||
*/
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
coh "github.com/oracle/coherence-go-client/coherence"
|
||||
coh "github.com/oracle/coherence-go-client/v2/coherence"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@@ -3,7 +3,7 @@ module github.com/gofiber/storage/coherence
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/oracle/coherence-go-client v1.2.2
|
||||
github.com/oracle/coherence-go-client/v2 v2.0.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
)
|
||||
|
||||
@@ -18,9 +18,11 @@ require (
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
golang.org/x/net v0.23.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/grpc v1.58.3 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
)
|
||||
|
@@ -9,21 +9,24 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/oracle/coherence-go-client v1.2.2 h1:TGK87WhV8MWeCiZKk0rC+aZbt40p2nRVvxDUbF+2gX8=
|
||||
github.com/oracle/coherence-go-client v1.2.2/go.mod h1:8wy6v4KvDGJv6iIiCD95aU0g8UL34DaKRHs3zqQN/Bg=
|
||||
github.com/oracle/coherence-go-client/v2 v2.0.0 h1:epRtq50pHgW0Wbl7piSPdDbwE3JVe+9XFyTS+j0YmEI=
|
||||
github.com/oracle/coherence-go-client/v2 v2.0.0/go.mod h1:tiCK6dVyBf/GVabOPY73Cl6+eKoLtStx/uWEEdJfpKg=
|
||||
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/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||
@@ -35,6 +38,7 @@ google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGm
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@@ -44,7 +44,9 @@ func New(config Config) *Storage {
|
||||
}
|
||||
|
||||
// Create db
|
||||
sess := awsdynamodb.NewFromConfig(awscfg)
|
||||
sess := awsdynamodb.NewFromConfig(awscfg, func(o *awsdynamodb.Options) {
|
||||
o.BaseEndpoint = aws.String(cfg.Endpoint)
|
||||
})
|
||||
|
||||
timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
@@ -232,23 +234,10 @@ func (s *Storage) requestContext() (context.Context, context.CancelFunc) {
|
||||
}
|
||||
|
||||
func returnAWSConfig(cfg Config) (aws.Config, error) {
|
||||
endpoint := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
|
||||
if cfg.Endpoint != "" {
|
||||
return aws.Endpoint{
|
||||
PartitionID: "aws",
|
||||
URL: cfg.Endpoint,
|
||||
SigningRegion: cfg.Region,
|
||||
HostnameImmutable: true,
|
||||
}, nil
|
||||
}
|
||||
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
|
||||
})
|
||||
|
||||
if cfg.Credentials != (Credentials{}) {
|
||||
credentials := credentials.NewStaticCredentialsProvider(cfg.Credentials.AccessKey, cfg.Credentials.SecretAccessKey, "")
|
||||
return awsconfig.LoadDefaultConfig(context.TODO(),
|
||||
awsconfig.WithRegion(cfg.Region),
|
||||
awsconfig.WithEndpointResolverWithOptions(endpoint),
|
||||
awsconfig.WithCredentialsProvider(credentials),
|
||||
awsconfig.WithRetryer(func() aws.Retryer {
|
||||
return retry.AddWithMaxAttempts(retry.NewStandard(), cfg.MaxAttempts)
|
||||
@@ -258,7 +247,6 @@ func returnAWSConfig(cfg Config) (aws.Config, error) {
|
||||
|
||||
return awsconfig.LoadDefaultConfig(context.TODO(),
|
||||
awsconfig.WithRegion(cfg.Region),
|
||||
awsconfig.WithEndpointResolverWithOptions(endpoint),
|
||||
awsconfig.WithRetryer(func() aws.Retryer {
|
||||
return retry.AddWithMaxAttempts(retry.NewStandard(), cfg.MaxAttempts)
|
||||
}),
|
||||
|
19
s3/s3.go
19
s3/s3.go
@@ -40,7 +40,10 @@ func New(config ...Config) *Storage {
|
||||
panic(fmt.Sprintf("unable to load SDK config, %v", err))
|
||||
}
|
||||
|
||||
sess := s3.NewFromConfig(awscfg)
|
||||
sess := s3.NewFromConfig(awscfg, func(o *s3.Options) {
|
||||
o.BaseEndpoint = aws.String(cfg.Endpoint)
|
||||
})
|
||||
|
||||
storage := &Storage{
|
||||
svc: sess,
|
||||
downloader: manager.NewDownloader(sess),
|
||||
@@ -173,23 +176,10 @@ func (s *Storage) requestContext() (context.Context, context.CancelFunc) {
|
||||
}
|
||||
|
||||
func returnAWSConfig(cfg Config) (aws.Config, error) {
|
||||
endpoint := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
|
||||
if cfg.Endpoint != "" {
|
||||
return aws.Endpoint{
|
||||
PartitionID: "aws",
|
||||
URL: cfg.Endpoint,
|
||||
SigningRegion: cfg.Region,
|
||||
HostnameImmutable: true,
|
||||
}, nil
|
||||
}
|
||||
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
|
||||
})
|
||||
|
||||
if cfg.Credentials != (Credentials{}) {
|
||||
creds := credentials.NewStaticCredentialsProvider(cfg.Credentials.AccessKey, cfg.Credentials.SecretAccessKey, "")
|
||||
return awsconfig.LoadDefaultConfig(context.TODO(),
|
||||
awsconfig.WithRegion(cfg.Region),
|
||||
awsconfig.WithEndpointResolverWithOptions(endpoint),
|
||||
awsconfig.WithCredentialsProvider(creds),
|
||||
awsconfig.WithRetryer(func() aws.Retryer {
|
||||
return retry.AddWithMaxAttempts(retry.NewStandard(), cfg.MaxAttempts)
|
||||
@@ -199,7 +189,6 @@ func returnAWSConfig(cfg Config) (aws.Config, error) {
|
||||
|
||||
return awsconfig.LoadDefaultConfig(context.TODO(),
|
||||
awsconfig.WithRegion(cfg.Region),
|
||||
awsconfig.WithEndpointResolverWithOptions(endpoint),
|
||||
awsconfig.WithRetryer(func() aws.Retryer {
|
||||
return retry.AddWithMaxAttempts(retry.NewStandard(), cfg.MaxAttempts)
|
||||
}),
|
||||
|
222
valkey/README.md
Normal file
222
valkey/README.md
Normal file
@@ -0,0 +1,222 @@
|
||||
---
|
||||
id: valkey
|
||||
title: Valkey
|
||||
---
|
||||
|
||||

|
||||
[](https://gofiber.io/discord)
|
||||

|
||||

|
||||

|
||||
|
||||
A fast Valkey Storage that does auto pipelining and supports client side caching. Implementation is based on [valkey-io/valkey](https://github.com/valkey-io/valkey-go).
|
||||
|
||||
### Table of Contents
|
||||
|
||||
- [Signatures](#signatures)
|
||||
- [Installation](#installation)
|
||||
- [Examples](#examples)
|
||||
- [Config](#config)
|
||||
- [Default Config](#default-config)
|
||||
|
||||
### Signatures
|
||||
|
||||
```go
|
||||
func New(config ...Config) Storage
|
||||
func (s *Storage) Get(key string) ([]byte, error)
|
||||
func (s *Storage) Set(key string, val []byte, exp time.Duration) error
|
||||
func (s *Storage) Delete(key string) error
|
||||
func (s *Storage) Reset() error
|
||||
func (s *Storage) Close() error
|
||||
func (s *Storage) Conn() valkey.Client
|
||||
```
|
||||
|
||||
### Installation
|
||||
|
||||
The valkey driver is tested on the latest two [Go version](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet:
|
||||
|
||||
```bash
|
||||
go mod init github.com/<user>/<repo>
|
||||
```
|
||||
|
||||
And then install the valkey implementation:
|
||||
|
||||
```bash
|
||||
go get github.com/gofiber/storage/valkey
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
Import the storage package.
|
||||
|
||||
```go
|
||||
import "github.com/gofiber/storage/valkey"
|
||||
```
|
||||
|
||||
You can use the one of the following options to create a Valkey Storage:
|
||||
|
||||
```go
|
||||
// Initialize default config (localhost:6379)
|
||||
store := valkey.New()
|
||||
|
||||
// Initialize custom config
|
||||
store := valkey.New(valkey.Config{
|
||||
InitAddress: []string{"localhost:6380"},
|
||||
Username: "",
|
||||
Password: "",
|
||||
Database: 0,
|
||||
Reset: false,
|
||||
TLSConfig: nil,
|
||||
})
|
||||
|
||||
// Initialize using Redis-style URL
|
||||
store := valkey.New(valkey.Config{
|
||||
URL: "redis://localhost:6379",
|
||||
})
|
||||
|
||||
// Initialize Valkey Cluster Client
|
||||
store := valkey.New(valkey.Config{
|
||||
InitAddress: []string{":6379", ":6380"},
|
||||
})
|
||||
|
||||
// Create a client with support for TLS
|
||||
cer, err := tls.LoadX509KeyPair("./client.crt", "./client.key")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
tlsCfg := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
InsecureSkipVerify: true,
|
||||
Certificates: []tls.Certificate{cer},
|
||||
}
|
||||
store = valkey.New(valkey.Config{
|
||||
InitAddress: []string{"localhost:6380"},
|
||||
Username: "<user>",
|
||||
Password: "<password>",
|
||||
SelectDB: 0,
|
||||
TLSConfig: tlsCfg,
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
// Server username
|
||||
//
|
||||
// Optional. Default is ""
|
||||
Username string
|
||||
|
||||
// Server password
|
||||
//
|
||||
// Optional. Default is ""
|
||||
Password string
|
||||
|
||||
// ClientName will execute the `CLIENT SETNAME ClientName` command for each conn.
|
||||
//
|
||||
// Optional. Default is ""
|
||||
ClientName string
|
||||
|
||||
// URL standard format Redis-style URL. If this is set all other config options, InitAddress, Username, Password, ClientName, and SelectDB have no effect.
|
||||
//
|
||||
// Example: redis://<user>:<pass>@localhost:6379/<db>
|
||||
// Optional. Default is ""
|
||||
URL string
|
||||
|
||||
// SelectDB to be selected after connecting to the server.
|
||||
//
|
||||
// Optional. Default is 0
|
||||
SelectDB int
|
||||
|
||||
// Either a single address or a seed list of host:port addresses, this enables FailoverClient and ClusterClient
|
||||
//
|
||||
// Optional. Default is []string{"127.0.0.1:6379"}
|
||||
InitAddress []string
|
||||
|
||||
// TLS Config to use. When set TLS will be negotiated.
|
||||
//
|
||||
// Optional. Default is nil
|
||||
TLSConfig *tls.Config
|
||||
|
||||
// CacheSizeEachConn is valkey client side cache size that bind to each TCP connection to a single valkey instance.
|
||||
//
|
||||
// Optional. The default is DefaultCacheBytes: 128 * (1 << 20)
|
||||
CacheSizeEachConn int
|
||||
|
||||
// RingScaleEachConn sets the size of the ring buffer in each connection to (2 ^ RingScaleEachConn).
|
||||
//
|
||||
// Optional. The default is RingScaleEachConn, which results into having a ring of size 2^10 for each connection.
|
||||
RingScaleEachConn int
|
||||
|
||||
// ReadBufferEachConn is the size of the bufio.NewReaderSize for each connection, default to DefaultReadBuffer (0.5 MiB).
|
||||
//
|
||||
// Optional. The default is DefaultReadBuffer: 1 << 19
|
||||
ReadBufferEachConn int
|
||||
|
||||
// WriteBufferEachConn is the size of the bufio.NewWriterSize for each connection, default to DefaultWriteBuffer (0.5 MiB).
|
||||
//
|
||||
// Optional. The default is DefaultWriteBuffer: 1 << 19
|
||||
WriteBufferEachConn int
|
||||
|
||||
// BlockingPoolSize is the size of the connection pool shared by blocking commands (ex BLPOP, XREAD with BLOCK).
|
||||
//
|
||||
// Optional. The default is DefaultPoolSize: 1000
|
||||
BlockingPoolSize int
|
||||
|
||||
// PipelineMultiplex determines how many tcp connections used to pipeline commands to one valkey instance.
|
||||
//
|
||||
// Optional. The default for single and sentinel clients is 2, which means 4 connections (2^2).
|
||||
PipelineMultiplex int
|
||||
|
||||
// DisableRetry disables retrying read-only commands under network errors
|
||||
//
|
||||
// Optional. The default is False
|
||||
DisableRetry bool
|
||||
|
||||
// DisableCache falls back Client.DoCache/Client.DoMultiCache to Client.Do/Client.DoMulti
|
||||
//
|
||||
// Optional. The default is false
|
||||
DisableCache bool
|
||||
|
||||
// AlwaysPipelining makes valkey.Client always pipeline valkey commands even if they are not issued concurrently.
|
||||
//
|
||||
// Optional. The default is true
|
||||
AlwaysPipelining bool
|
||||
|
||||
// Reset clears any existing keys in existing Collection
|
||||
//
|
||||
// Optional. Default is false
|
||||
Reset bool
|
||||
|
||||
// CacheTTL TTL
|
||||
//
|
||||
// Optional. Default is time.Minute
|
||||
CacheTTL time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
### Default Config
|
||||
|
||||
```go
|
||||
var ConfigDefault = Config{
|
||||
Username: "",
|
||||
Password: "",
|
||||
ClientName: "",
|
||||
SelectDB: 0,
|
||||
InitAddress: []string{"127.0.0.1:6379"},
|
||||
TLSConfig: nil,
|
||||
CacheSizeEachConn: valkey.DefaultCacheBytes,
|
||||
RingScaleEachConn: valkey.DefaultRingScale,
|
||||
ReadBufferEachConn: valkey.DefaultReadBuffer,
|
||||
WriteBufferEachConn: valkey.DefaultWriteBuffer,
|
||||
BlockingPoolSize: valkey.DefaultPoolSize,
|
||||
PipelineMultiplex: 2,
|
||||
DisableRetry: false,
|
||||
DisableCache: false,
|
||||
AlwaysPipelining: true,
|
||||
Reset: false,
|
||||
CacheTTL: time.Minute,
|
||||
}
|
||||
```
|
198
valkey/config.go
Normal file
198
valkey/config.go
Normal file
@@ -0,0 +1,198 @@
|
||||
package valkey
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"time"
|
||||
|
||||
"github.com/valkey-io/valkey-go"
|
||||
)
|
||||
|
||||
// Config defines the config for storage.
|
||||
type Config struct {
|
||||
// Server username
|
||||
//
|
||||
// Optional. Default is ""
|
||||
Username string
|
||||
|
||||
// Server password
|
||||
//
|
||||
// Optional. Default is ""
|
||||
Password string
|
||||
|
||||
// ClientName will execute the `CLIENT SETNAME ClientName` command for each conn.
|
||||
//
|
||||
// Optional. Default is ""
|
||||
ClientName string
|
||||
|
||||
// URL standard format Redis URL. If this is set all other config options, InitAddress, Username, Password, ClientName, and SelectDB have no effect.
|
||||
//
|
||||
// Example: redis://<user>:<pass>@localhost:6379/<db>
|
||||
// Optional. Default is ""
|
||||
URL string
|
||||
|
||||
// SelectDB to be selected after connecting to the server.
|
||||
//
|
||||
// Optional. Default is 0
|
||||
SelectDB int
|
||||
|
||||
// Either a single address or a seed list of host:port addresses, this enables FailoverClient and ClusterClient
|
||||
//
|
||||
// Optional. Default is []string{"127.0.0.1:6379"}
|
||||
InitAddress []string
|
||||
|
||||
// TLS Config to use. When set TLS will be negotiated.
|
||||
//
|
||||
// Optional. Default is nil
|
||||
TLSConfig *tls.Config
|
||||
|
||||
// CacheSizeEachConn is redis client side cache size that bind to each TCP connection to a single redis instance.
|
||||
//
|
||||
// Optional. The default is DefaultCacheBytes: 128 * (1 << 20)
|
||||
CacheSizeEachConn int
|
||||
|
||||
// RingScaleEachConn sets the size of the ring buffer in each connection to (2 ^ RingScaleEachConn).
|
||||
//
|
||||
// Optional. The default is RingScaleEachConn, which results into having a ring of size 2^10 for each connection.
|
||||
RingScaleEachConn int
|
||||
|
||||
// ReadBufferEachConn is the size of the bufio.NewReaderSize for each connection, default to DefaultReadBuffer (0.5 MiB).
|
||||
//
|
||||
// Optional. The default is DefaultReadBuffer: 1 << 19
|
||||
ReadBufferEachConn int
|
||||
|
||||
// WriteBufferEachConn is the size of the bufio.NewWriterSize for each connection, default to DefaultWriteBuffer (0.5 MiB).
|
||||
//
|
||||
// Optional. The default is DefaultWriteBuffer: 1 << 19
|
||||
WriteBufferEachConn int
|
||||
|
||||
// BlockingPoolSize is the size of the connection pool shared by blocking commands (ex BLPOP, XREAD with BLOCK).
|
||||
//
|
||||
// Optional. The default is DefaultPoolSize: 1000
|
||||
BlockingPoolSize int
|
||||
|
||||
// PipelineMultiplex determines how many tcp connections used to pipeline commands to one redis instance.
|
||||
//
|
||||
// Optional. The default for single and sentinel clients is 2, which means 4 connections (2^2).
|
||||
PipelineMultiplex int
|
||||
|
||||
// DisableRetry disables retrying read-only commands under network errors
|
||||
//
|
||||
// Optional. The default is False
|
||||
DisableRetry bool
|
||||
|
||||
// DisableCache falls back Client.DoCache/Client.DoMultiCache to Client.Do/Client.DoMulti
|
||||
//
|
||||
// Optional. The default is false
|
||||
DisableCache bool
|
||||
|
||||
// AlwaysPipelining makes valkey.Client always pipeline redis commands even if they are not issued concurrently.
|
||||
//
|
||||
// Optional. The default is true
|
||||
AlwaysPipelining bool
|
||||
|
||||
// Reset clears any existing keys in existing Collection
|
||||
//
|
||||
// Optional. Default is false
|
||||
Reset bool
|
||||
|
||||
// CacheTTL TTL
|
||||
//
|
||||
// Optional. Default is time.Minute
|
||||
CacheTTL time.Duration
|
||||
}
|
||||
|
||||
// ConfigDefault is the default config
|
||||
var ConfigDefault = Config{
|
||||
Username: "",
|
||||
Password: "",
|
||||
ClientName: "",
|
||||
URL: "",
|
||||
SelectDB: 0,
|
||||
InitAddress: []string{"127.0.0.1:6379"},
|
||||
TLSConfig: nil,
|
||||
CacheSizeEachConn: valkey.DefaultCacheBytes,
|
||||
RingScaleEachConn: valkey.DefaultRingScale,
|
||||
ReadBufferEachConn: valkey.DefaultReadBuffer,
|
||||
WriteBufferEachConn: valkey.DefaultWriteBuffer,
|
||||
BlockingPoolSize: valkey.DefaultPoolSize,
|
||||
PipelineMultiplex: 2,
|
||||
DisableRetry: false,
|
||||
DisableCache: false,
|
||||
AlwaysPipelining: true,
|
||||
Reset: false,
|
||||
CacheTTL: time.Minute,
|
||||
}
|
||||
|
||||
// Helper function to set default values
|
||||
func configDefault(config ...Config) Config {
|
||||
// Return default config if nothing provided
|
||||
if len(config) < 1 {
|
||||
return ConfigDefault
|
||||
}
|
||||
|
||||
// Start with the default configuration
|
||||
cfg := ConfigDefault
|
||||
|
||||
// Override default config with values from provided config
|
||||
userConfig := config[0]
|
||||
|
||||
if userConfig.Username != "" {
|
||||
cfg.Username = userConfig.Username
|
||||
}
|
||||
if userConfig.Password != "" {
|
||||
cfg.Password = userConfig.Password
|
||||
}
|
||||
if userConfig.ClientName != "" {
|
||||
cfg.ClientName = userConfig.ClientName
|
||||
}
|
||||
if userConfig.URL != "" {
|
||||
cfg.URL = userConfig.URL
|
||||
}
|
||||
if userConfig.SelectDB != 0 {
|
||||
cfg.SelectDB = userConfig.SelectDB
|
||||
}
|
||||
if len(userConfig.InitAddress) > 0 {
|
||||
cfg.InitAddress = userConfig.InitAddress
|
||||
}
|
||||
if userConfig.TLSConfig != nil {
|
||||
cfg.TLSConfig = userConfig.TLSConfig
|
||||
}
|
||||
if userConfig.CacheSizeEachConn != 0 {
|
||||
cfg.CacheSizeEachConn = userConfig.CacheSizeEachConn
|
||||
}
|
||||
if userConfig.RingScaleEachConn != 0 {
|
||||
cfg.RingScaleEachConn = userConfig.RingScaleEachConn
|
||||
}
|
||||
if userConfig.ReadBufferEachConn != 0 {
|
||||
cfg.ReadBufferEachConn = userConfig.ReadBufferEachConn
|
||||
}
|
||||
if userConfig.WriteBufferEachConn != 0 {
|
||||
cfg.WriteBufferEachConn = userConfig.WriteBufferEachConn
|
||||
}
|
||||
if userConfig.BlockingPoolSize != 0 {
|
||||
cfg.BlockingPoolSize = userConfig.BlockingPoolSize
|
||||
}
|
||||
if userConfig.PipelineMultiplex != 0 {
|
||||
cfg.PipelineMultiplex = userConfig.PipelineMultiplex
|
||||
}
|
||||
if userConfig.CacheTTL != time.Second {
|
||||
cfg.CacheTTL = userConfig.CacheTTL
|
||||
}
|
||||
if userConfig.DisableRetry {
|
||||
cfg.DisableRetry = true
|
||||
}
|
||||
|
||||
if userConfig.DisableCache {
|
||||
cfg.DisableCache = true
|
||||
}
|
||||
|
||||
if userConfig.AlwaysPipelining {
|
||||
cfg.AlwaysPipelining = true
|
||||
}
|
||||
|
||||
if userConfig.Reset {
|
||||
cfg.Reset = true
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
18
valkey/go.mod
Normal file
18
valkey/go.mod
Normal file
@@ -0,0 +1,18 @@
|
||||
module github.com/gofiber/storage/valkey
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/gofiber/utils/v2 v2.0.0-beta.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/valkey-io/valkey-go v1.0.52
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
33
valkey/go.sum
Normal file
33
valkey/go.sum
Normal file
@@ -0,0 +1,33 @@
|
||||
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/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co=
|
||||
github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
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/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/valkey-io/valkey-go v1.0.52 h1:ojrR736satGucqpllYzal8fUrNNROc11V10zokAyIYg=
|
||||
github.com/valkey-io/valkey-go v1.0.52/go.mod h1:BXlVAPIL9rFQinSFM+N32JfWzfCaUAqBpZkc4vPY6fM=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
135
valkey/valkey.go
Normal file
135
valkey/valkey.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package valkey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/utils/v2"
|
||||
"github.com/valkey-io/valkey-go"
|
||||
)
|
||||
|
||||
var cacheTTL = time.Second
|
||||
|
||||
// Storage interface that is implemented by storage providers
|
||||
type Storage struct {
|
||||
db valkey.Client
|
||||
}
|
||||
|
||||
// New creates a new valkey storage
|
||||
func New(config ...Config) *Storage {
|
||||
// Set default config
|
||||
cfg := configDefault(config...)
|
||||
|
||||
// Create new valkey client
|
||||
var db valkey.Client
|
||||
cacheTTL = cfg.CacheTTL
|
||||
|
||||
// Parse the URL and update config values accordingly
|
||||
if cfg.URL != "" {
|
||||
// This will panic if parsing URL fails
|
||||
options := valkey.MustParseURL(cfg.URL)
|
||||
|
||||
// Update the config values with the parsed URL values
|
||||
cfg.InitAddress = options.InitAddress
|
||||
cfg.Username = options.Username
|
||||
cfg.Password = options.Password
|
||||
cfg.SelectDB = options.SelectDB
|
||||
|
||||
// Update ClientName if returned
|
||||
if cfg.ClientName == "" && options.ClientName != "" {
|
||||
cfg.ClientName = options.ClientName
|
||||
}
|
||||
|
||||
// Update TLSConfig if returned
|
||||
if cfg.TLSConfig == nil && options.TLSConfig != nil {
|
||||
cfg.TLSConfig = options.TLSConfig
|
||||
}
|
||||
}
|
||||
|
||||
// Update config values accordingly and start new Client
|
||||
db, err := valkey.NewClient(valkey.ClientOption{
|
||||
Username: cfg.Username,
|
||||
Password: cfg.Password,
|
||||
ClientName: cfg.ClientName,
|
||||
SelectDB: cfg.SelectDB,
|
||||
InitAddress: cfg.InitAddress,
|
||||
TLSConfig: cfg.TLSConfig,
|
||||
CacheSizeEachConn: cfg.CacheSizeEachConn,
|
||||
RingScaleEachConn: cfg.RingScaleEachConn,
|
||||
ReadBufferEachConn: cfg.ReadBufferEachConn,
|
||||
WriteBufferEachConn: cfg.WriteBufferEachConn,
|
||||
BlockingPoolSize: cfg.BlockingPoolSize,
|
||||
PipelineMultiplex: cfg.PipelineMultiplex,
|
||||
DisableRetry: cfg.DisableRetry,
|
||||
DisableCache: cfg.DisableCache,
|
||||
AlwaysPipelining: cfg.AlwaysPipelining,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Test connection
|
||||
if err := db.Do(context.Background(), db.B().Ping().Build()).Error(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Empty collection if Clear is true
|
||||
if cfg.Reset {
|
||||
if err := db.Do(context.Background(), db.B().Flushdb().Build()).Error(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create new store
|
||||
return &Storage{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by key
|
||||
func (s *Storage) Get(key string) ([]byte, error) {
|
||||
if len(key) <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
val, err := s.db.DoCache(context.Background(), s.db.B().Get().Key(key).Cache(), cacheTTL).AsBytes()
|
||||
if err != nil && valkey.IsValkeyNil(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return val, err
|
||||
}
|
||||
|
||||
// Set key with value
|
||||
func (s *Storage) Set(key string, val []byte, exp time.Duration) error {
|
||||
if len(key) <= 0 || len(val) <= 0 {
|
||||
return nil
|
||||
}
|
||||
if exp > 0 {
|
||||
return s.db.Do(context.Background(), s.db.B().Set().Key(key).Value(utils.ToString(val)).Ex(exp).Build()).Error()
|
||||
} else {
|
||||
return s.db.Do(context.Background(), s.db.B().Set().Key(key).Value(utils.ToString(val)).Build()).Error()
|
||||
}
|
||||
}
|
||||
|
||||
// Delete key by key
|
||||
func (s *Storage) Delete(key string) error {
|
||||
if len(key) <= 0 {
|
||||
return nil
|
||||
}
|
||||
return s.db.Do(context.Background(), s.db.B().Del().Key(key).Build()).Error()
|
||||
}
|
||||
|
||||
// Reset all keys
|
||||
func (s *Storage) Reset() error {
|
||||
return s.db.Do(context.Background(), s.db.B().Flushdb().Build()).Error()
|
||||
}
|
||||
|
||||
// Close the database
|
||||
func (s *Storage) Close() error {
|
||||
s.db.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return database client
|
||||
func (s *Storage) Conn() valkey.Client {
|
||||
return s.db
|
||||
}
|
277
valkey/valkey_test.go
Normal file
277
valkey/valkey_test.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package valkey
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var testStore = New(Config{
|
||||
Reset: true,
|
||||
})
|
||||
|
||||
func Test_Valkey_Set(t *testing.T) {
|
||||
var (
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := testStore.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_Valkey_Set_Override(t *testing.T) {
|
||||
var (
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := testStore.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = testStore.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_Valkey_Get(t *testing.T) {
|
||||
var (
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := testStore.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := testStore.Get(key)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, val, result)
|
||||
}
|
||||
|
||||
func Test_Valkey_Set_Expiration(t *testing.T) {
|
||||
var (
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
exp = 1 * time.Second
|
||||
)
|
||||
|
||||
err := testStore.Set(key, val, exp)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(1100 * time.Millisecond)
|
||||
}
|
||||
|
||||
func Test_Valkey_Get_Expired(t *testing.T) {
|
||||
key := "john"
|
||||
|
||||
result, err := testStore.Get(key)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, len(result))
|
||||
}
|
||||
|
||||
func Test_Valkey_Get_NotExist(t *testing.T) {
|
||||
result, err := testStore.Get("notexist")
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, len(result))
|
||||
}
|
||||
|
||||
func Test_Valkey_Delete(t *testing.T) {
|
||||
var (
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := testStore.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = testStore.Delete(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := testStore.Get(key)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, len(result))
|
||||
}
|
||||
|
||||
func Test_Valkey_Reset(t *testing.T) {
|
||||
val := []byte("doe")
|
||||
|
||||
err := testStore.Set("john1", val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = testStore.Set("john2", val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = testStore.Reset()
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := testStore.Get("john1")
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, len(result))
|
||||
|
||||
result, err = testStore.Get("john2")
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, len(result))
|
||||
}
|
||||
|
||||
func Test_Valkey_Close(t *testing.T) {
|
||||
require.Nil(t, testStore.Close())
|
||||
}
|
||||
|
||||
func Test_Valkey_Conn(t *testing.T) {
|
||||
require.True(t, testStore.Conn() != nil)
|
||||
}
|
||||
|
||||
func Test_Valkey_WithTLS(t *testing.T) {
|
||||
cer, err := tls.LoadX509KeyPair("/home/runner/work/storage/storage/tls/client.crt", "/home/runner/work/storage/storage/tls/client.key")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
tlsCfg := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||
InsecureSkipVerify: true,
|
||||
Certificates: []tls.Certificate{cer},
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
}
|
||||
|
||||
storeTLS := New(Config{
|
||||
InitAddress: []string{"localhost:6380"},
|
||||
TLSConfig: tlsCfg,
|
||||
})
|
||||
|
||||
var (
|
||||
key = "clark"
|
||||
val = []byte("kent")
|
||||
)
|
||||
|
||||
err = storeTLS.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := storeTLS.Get(key)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, val, result)
|
||||
|
||||
err = storeTLS.Delete(key)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, storeTLS.Close())
|
||||
}
|
||||
|
||||
func Test_Valkey_With_HostPort(t *testing.T) {
|
||||
store := New(Config{
|
||||
InitAddress: []string{"localhost:6379"},
|
||||
})
|
||||
|
||||
var (
|
||||
key = "bruce"
|
||||
val = []byte("wayne")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, val, result)
|
||||
|
||||
err = store.Delete(key)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, store.Close())
|
||||
}
|
||||
|
||||
func Test_Valkey_With_URL(t *testing.T) {
|
||||
store := New(Config{
|
||||
URL: "redis://localhost:6379",
|
||||
})
|
||||
|
||||
var (
|
||||
key = "bruce"
|
||||
val = []byte("wayne")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, val, result)
|
||||
|
||||
err = store.Delete(key)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, store.Close())
|
||||
}
|
||||
|
||||
func Test_Valkey_Cluster(t *testing.T) {
|
||||
store := New(Config{
|
||||
InitAddress: []string{
|
||||
"localhost:7000",
|
||||
"localhost:7001",
|
||||
"localhost:7002",
|
||||
"localhost:7003",
|
||||
"localhost:7004",
|
||||
"localhost:7005",
|
||||
},
|
||||
})
|
||||
|
||||
var (
|
||||
key = "bruce"
|
||||
val = []byte("wayne")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, val, result)
|
||||
|
||||
err = store.Delete(key)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, store.Close())
|
||||
}
|
||||
|
||||
func Benchmark_Valkey_Set(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
err = testStore.Set("john", []byte("doe"), 0)
|
||||
}
|
||||
|
||||
require.NoError(b, err)
|
||||
}
|
||||
|
||||
func Benchmark_Valkey_Get(b *testing.B) {
|
||||
err := testStore.Set("john", []byte("doe"), 0)
|
||||
require.NoError(b, err)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = testStore.Get("john")
|
||||
}
|
||||
|
||||
require.NoError(b, err)
|
||||
}
|
||||
|
||||
func Benchmark_Valkey_SetAndDelete(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = testStore.Set("john", []byte("doe"), 0)
|
||||
err = testStore.Delete("john")
|
||||
}
|
||||
|
||||
require.NoError(b, err)
|
||||
}
|
Reference in New Issue
Block a user