├── .github ├── dependabot.yml └── workflows │ ├── codeql.yaml │ ├── go.yml │ └── goreleaser.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── LICENSE ├── README.md ├── _example ├── producer-consumer │ ├── README.md │ ├── consumer │ │ └── main.go │ ├── go.mod │ ├── go.sum │ └── producer │ │ └── main.go └── worker │ ├── go.mod │ ├── go.sum │ └── main.go ├── conf └── redis.conf ├── go.mod ├── go.sum ├── images └── screen.png ├── options.go ├── redis.go └── redis_test.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | - package-ecosystem: gomod 8 | directory: / 9 | schedule: 10 | interval: weekly 11 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yaml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [main] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [main] 14 | schedule: 15 | - cron: "30 1 * * 0" 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | permissions: 23 | # required for all workflows 24 | security-events: write 25 | 26 | # only required for workflows in private repositories 27 | actions: read 28 | contents: read 29 | 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | # Override automatic language detection by changing the below list 34 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 35 | # TODO: Enable for javascript later 36 | language: ["go"] 37 | 38 | steps: 39 | - name: Checkout repository 40 | uses: actions/checkout@v4 41 | 42 | # Initializes the CodeQL tools for scanning. 43 | - name: Initialize CodeQL 44 | uses: github/codeql-action/init@v3 45 | with: 46 | languages: ${{ matrix.language }} 47 | # If you wish to specify custom queries, you can do so here or in a config file. 48 | # By default, queries listed here will override any specified in a config file. 49 | # Prefix the list here with "+" to use these queries and those in the config file. 50 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 51 | 52 | - name: Perform CodeQL Analysis 53 | uses: github/codeql-action/analyze@v3 54 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Run Testing 2 | on: push 3 | 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Setup go 9 | uses: actions/setup-go@v5 10 | with: 11 | go-version: "^1" 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Setup golangci-lint 16 | uses: golangci/golangci-lint-action@v6 17 | with: 18 | args: --verbose 19 | 20 | # Label of the container job 21 | test: 22 | strategy: 23 | matrix: 24 | os: [ubuntu-latest] 25 | go: [1.22, 1.23, 1.24] 26 | include: 27 | - os: ubuntu-latest 28 | go-build: ~/.cache/go-build 29 | name: ${{ matrix.os }} @ Go ${{ matrix.go }} 30 | runs-on: ${{ matrix.os }} 31 | 32 | steps: 33 | - name: Set up Go ${{ matrix.go }} 34 | uses: actions/setup-go@v5 35 | with: 36 | go-version: ${{ matrix.go }} 37 | 38 | - name: Checkout Code 39 | uses: actions/checkout@v4 40 | with: 41 | ref: ${{ github.ref }} 42 | 43 | - uses: actions/cache@v4 44 | with: 45 | path: | 46 | ${{ matrix.go-build }} 47 | ~/go/pkg/mod 48 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 49 | restore-keys: | 50 | ${{ runner.os }}-go- 51 | - name: Run Tests 52 | run: | 53 | go test -v -covermode=atomic -coverprofile=coverage.out 54 | 55 | - name: Upload coverage to Codecov 56 | uses: codecov/codecov-action@v5 57 | with: 58 | flags: ${{ matrix.os }},go-${{ matrix.go }} 59 | -------------------------------------------------------------------------------- /.github/workflows/goreleaser.yml: -------------------------------------------------------------------------------- 1 | name: Goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | goreleaser: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - name: Set up Go 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version: "^1" 23 | 24 | - name: Run GoReleaser 25 | uses: goreleaser/goreleaser-action@v6 26 | with: 27 | args: release --clean 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable-all: false 3 | disable-all: true 4 | fast: false 5 | enable: 6 | - dogsled 7 | - dupl 8 | - errcheck 9 | - exportloopref 10 | - exhaustive 11 | - gochecknoinits 12 | - goconst 13 | - gocritic 14 | - gocyclo 15 | - gofmt 16 | - goimports 17 | - goprintffuncname 18 | - gosec 19 | - gosimple 20 | - govet 21 | - ineffassign 22 | - lll 23 | - misspell 24 | - nakedret 25 | - nolintlint 26 | - staticcheck 27 | - stylecheck 28 | - typecheck 29 | - unconvert 30 | - unused 31 | - whitespace 32 | - gofumpt 33 | 34 | run: 35 | timeout: 3m 36 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | builds: 4 | - # If true, skip the build. 5 | # Useful for library projects. 6 | # Default is false 7 | skip: true 8 | 9 | changelog: 10 | # Set it to true if you wish to skip the changelog generation. 11 | # This may result in an empty release notes on GitHub/GitLab/Gitea. 12 | disable: false 13 | 14 | # Changelog generation implementation to use. 15 | # 16 | # Valid options are: 17 | # - `git`: uses `git log`; 18 | # - `github`: uses the compare GitHub API, appending the author login to the changelog. 19 | # - `gitlab`: uses the compare GitLab API, appending the author name and email to the changelog. 20 | # - `github-native`: uses the GitHub release notes generation API, disables the groups feature. 21 | # 22 | # Defaults to `git`. 23 | use: github 24 | 25 | # Sorts the changelog by the commit's messages. 26 | # Could either be asc, desc or empty 27 | # Default is empty 28 | sort: asc 29 | 30 | # Group commits messages by given regex and title. 31 | # Order value defines the order of the groups. 32 | # Proving no regex means all commits will be grouped under the default group. 33 | # Groups are disabled when using github-native, as it already groups things by itself. 34 | # 35 | # Default is no groups. 36 | groups: 37 | - title: Features 38 | regexp: "^.*feat[(\\w-)]*:+.*$" 39 | order: 0 40 | - title: "Bug fixes" 41 | regexp: "^.*fix[(\\w-)]*:+.*$" 42 | order: 1 43 | - title: "Enhancements" 44 | regexp: "^.*chore[(\\w-)]*:+.*$" 45 | order: 2 46 | - title: "Refactor" 47 | regexp: "^.*refactor[(\\w-)]*:+.*$" 48 | order: 3 49 | - title: "Build process updates" 50 | regexp: ^.*?(build|ci)(\(.+\))??!?:.+$ 51 | order: 4 52 | - title: "Documentation updates" 53 | regexp: ^.*?docs?(\(.+\))??!?:.+$ 54 | order: 4 55 | - title: Others 56 | order: 999 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 golang-queue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redis 2 | 3 | [![Run Testing](https://github.com/golang-queue/redisdb/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/golang-queue/redisdb/actions/workflows/go.yml) 4 | [![codecov](https://codecov.io/gh/golang-queue/redisdb/branch/main/graph/badge.svg?token=FFZN8E2ZZB)](https://codecov.io/gh/golang-queue/redisdb) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/golang-queue/redisdb)](https://goreportcard.com/report/github.com/golang-queue/redisdb) 6 | 7 | Redis [Pub/Sub](https://redis.io/docs/manual/pubsub/) as backend for [Queue package](https://github.com/golang-queue/queue) 8 | 9 | ## Setup 10 | 11 | start the redis server 12 | 13 | ```sh 14 | redis-server 15 | ``` 16 | 17 | ![screen](/images/screen.png) 18 | 19 | start the redis cluster, see the [config](./conf/redis.conf) 20 | 21 | ```sh 22 | # server 01 23 | mkdir server01 && cd server01 && redis-server redis.conf --port 6379 24 | # server 02 25 | mkdir server02 && cd server02 && redis-server redis.conf --port 6380 26 | ``` 27 | 28 | ## Example 29 | 30 | For single server 31 | 32 | ```go 33 | package main 34 | 35 | import ( 36 | "context" 37 | "encoding/json" 38 | "fmt" 39 | "log" 40 | "time" 41 | 42 | "github.com/golang-queue/queue" 43 | "github.com/golang-queue/redisdb" 44 | ) 45 | 46 | type job struct { 47 | Message string 48 | } 49 | 50 | func (j *job) Bytes() []byte { 51 | b, err := json.Marshal(j) 52 | if err != nil { 53 | panic(err) 54 | } 55 | return b 56 | } 57 | 58 | func main() { 59 | taskN := 100 60 | rets := make(chan string, taskN) 61 | 62 | // define the worker 63 | w := redisdb.NewWorker( 64 | redisdb.WithAddr("127.0.0.1:6379"), 65 | redisdb.WithChannel("foobar"), 66 | redisdb.WithRunFunc(func(ctx context.Context, m queue.TaskMessage) error { 67 | var v job 68 | if err := json.Unmarshal(m.Payload(), &v); err != nil { 69 | return err 70 | } 71 | 72 | rets <- v.Message 73 | return nil 74 | }), 75 | ) 76 | 77 | // define the queue 78 | q := queue.NewPool( 79 | 5, 80 | queue.WithWorker(w), 81 | ) 82 | 83 | // assign tasks in queue 84 | for i := 0; i < taskN; i++ { 85 | go func(i int) { 86 | if err := q.Queue(&job{ 87 | Message: fmt.Sprintf("handle the job: %d", i+1), 88 | }); err != nil { 89 | log.Fatal(err) 90 | } 91 | }(i) 92 | } 93 | 94 | // wait until all tasks done 95 | for i := 0; i < taskN; i++ { 96 | fmt.Println("message:", <-rets) 97 | time.Sleep(50 * time.Millisecond) 98 | } 99 | 100 | // shutdown the service and notify all the worker 101 | q.Release() 102 | } 103 | ``` 104 | 105 | ## Testing 106 | 107 | ```sh 108 | go test -v ./... 109 | ``` 110 | -------------------------------------------------------------------------------- /_example/producer-consumer/README.md: -------------------------------------------------------------------------------- 1 | # Example with server and client 2 | 3 | Please refer the following steps to build server and client. 4 | 5 | ## Build server 6 | 7 | ```sh 8 | go build -o app server/main.go 9 | ``` 10 | 11 | ## Build client 12 | 13 | ```sh 14 | go build -o agent client/main.go 15 | ``` 16 | 17 | ## Usage 18 | 19 | Run the multiple agent. (open two console in the same terminal) 20 | 21 | ```sh 22 | ./agent 23 | ``` 24 | 25 | Publish the message. 26 | 27 | ```sh 28 | ./app 29 | ``` 30 | -------------------------------------------------------------------------------- /_example/producer-consumer/consumer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/appleboy/graceful" 10 | "github.com/golang-queue/queue" 11 | "github.com/golang-queue/queue/core" 12 | "github.com/golang-queue/redisdb" 13 | ) 14 | 15 | type job struct { 16 | Message string 17 | } 18 | 19 | func (j *job) Bytes() []byte { 20 | b, err := json.Marshal(j) 21 | if err != nil { 22 | panic(err) 23 | } 24 | return b 25 | } 26 | 27 | func main() { 28 | taskN := 10000 29 | rets := make(chan string, taskN) 30 | 31 | m := graceful.NewManager() 32 | 33 | // define the worker 34 | w := redisdb.NewWorker( 35 | redisdb.WithAddr("127.0.0.1:6379"), 36 | redisdb.WithChannel("foobar"), 37 | redisdb.WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 38 | var v *job 39 | if err := json.Unmarshal(m.Payload(), &v); err != nil { 40 | return err 41 | } 42 | rets <- v.Message 43 | time.Sleep(2 * time.Second) 44 | return nil 45 | }), 46 | ) 47 | 48 | // define the queue 49 | q := queue.NewPool( 50 | 1, 51 | queue.WithWorker(w), 52 | ) 53 | 54 | m.AddRunningJob(func(ctx context.Context) error { 55 | for { 56 | select { 57 | case <-ctx.Done(): 58 | select { 59 | case m := <-rets: 60 | fmt.Println("message:", m) 61 | default: 62 | } 63 | return nil 64 | case m := <-rets: 65 | fmt.Println("message:", m) 66 | time.Sleep(50 * time.Millisecond) 67 | } 68 | } 69 | }) 70 | 71 | m.AddShutdownJob(func() error { 72 | // shutdown the service and notify all the worker 73 | q.Release() 74 | return nil 75 | }) 76 | 77 | <-m.Done() 78 | } 79 | -------------------------------------------------------------------------------- /_example/producer-consumer/go.mod: -------------------------------------------------------------------------------- 1 | module example 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/appleboy/graceful v0.0.4 7 | github.com/golang-queue/queue v0.3.0 8 | github.com/golang-queue/redisdb v0.0.4 9 | ) 10 | 11 | require ( 12 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 13 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 14 | github.com/jpillora/backoff v1.0.0 // indirect 15 | github.com/redis/go-redis/v9 v9.7.0 // indirect 16 | github.com/yassinebenaid/godump v0.11.1 // indirect 17 | ) 18 | 19 | replace github.com/golang-queue/redisdb => ../../ 20 | -------------------------------------------------------------------------------- /_example/producer-consumer/go.sum: -------------------------------------------------------------------------------- 1 | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 2 | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 3 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= 4 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 5 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 6 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 7 | github.com/appleboy/com v0.2.1 h1:dHAHauX3eYDuheAahI83HIGFxpi0SEb2ZAu9EZ9hbUM= 8 | github.com/appleboy/com v0.2.1/go.mod h1:kByEI3/vzI5GM1+O5QdBHLsXaOsmFsJcOpCSgASi4sg= 9 | github.com/appleboy/graceful v0.0.4 h1:Q4LCeq4DFy59qiACLtuH+mSqDERtUzwkQbCWpRaWwvQ= 10 | github.com/appleboy/graceful v0.0.4/go.mod h1:Q2mVx0t+N0lCDZc5MJudbcpTm6cgGM/J2gZCZIqD9dc= 11 | github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= 12 | github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 13 | github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= 14 | github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 15 | github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= 16 | github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 17 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 18 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 19 | github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= 20 | github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= 21 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 22 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 23 | github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= 24 | github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= 25 | github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= 26 | github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= 27 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 28 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 29 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 30 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 31 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 32 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 33 | github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= 34 | github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 35 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 36 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 37 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 38 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 39 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 40 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 41 | github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 42 | github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 43 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 44 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 45 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 46 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 47 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 48 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 49 | github.com/golang-queue/queue v0.3.0 h1:gyBLNT9EDOsChazYScp8iLiwLfG0SdnCDmNUybcHig4= 50 | github.com/golang-queue/queue v0.3.0/go.mod h1:SkjMwz1TjxZOrF7kABvbar1CagcMxwRtXt5Tx00wb4g= 51 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 52 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 53 | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= 54 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 55 | github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= 56 | github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 57 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 58 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 59 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 60 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 61 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 62 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 63 | github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= 64 | github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 65 | github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= 66 | github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= 67 | github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= 68 | github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= 69 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 70 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 71 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 72 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 73 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 74 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 75 | github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= 76 | github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 77 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 78 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 79 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 80 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 81 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 82 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 83 | github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= 84 | github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= 85 | github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= 86 | github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= 87 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 88 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 89 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 90 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 91 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 92 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 93 | github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= 94 | github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= 95 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 96 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 97 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 98 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 99 | github.com/yassinebenaid/godump v0.11.1 h1:SPujx/XaYqGDfmNh7JI3dOyCUVrG0bG2duhO3Eh2EhI= 100 | github.com/yassinebenaid/godump v0.11.1/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44= 101 | github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= 102 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 103 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 104 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= 105 | go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= 106 | go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= 107 | go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= 108 | go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= 109 | go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= 110 | go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= 111 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 112 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 113 | go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= 114 | go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 115 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 116 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 117 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 118 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 119 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 120 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 121 | -------------------------------------------------------------------------------- /_example/producer-consumer/producer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/golang-queue/queue" 10 | "github.com/golang-queue/redisdb" 11 | ) 12 | 13 | type job struct { 14 | Message string 15 | } 16 | 17 | func (j *job) Bytes() []byte { 18 | b, err := json.Marshal(j) 19 | if err != nil { 20 | panic(err) 21 | } 22 | return b 23 | } 24 | 25 | func main() { 26 | taskN := 100 27 | 28 | // define the worker 29 | w := redisdb.NewWorker( 30 | redisdb.WithAddr("127.0.0.1:6379"), 31 | redisdb.WithChannel("foobar"), 32 | ) 33 | 34 | // define the queue 35 | q := queue.NewPool( 36 | 0, 37 | queue.WithWorker(w), 38 | ) 39 | 40 | // assign tasks in queue 41 | for i := 0; i < taskN; i++ { 42 | go func(i int) { 43 | if err := q.Queue(&job{ 44 | Message: fmt.Sprintf("handle the job: %d", i+1), 45 | }); err != nil { 46 | log.Fatal(err) 47 | } 48 | }(i) 49 | } 50 | 51 | time.Sleep(1 * time.Second) 52 | // shutdown the service and notify all the worker 53 | q.Release() 54 | } 55 | -------------------------------------------------------------------------------- /_example/worker/go.mod: -------------------------------------------------------------------------------- 1 | module example 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/golang-queue/queue v0.3.0 7 | github.com/golang-queue/redisdb v0.0.0-00010101000000-000000000000 8 | ) 9 | 10 | require ( 11 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 12 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 13 | github.com/jpillora/backoff v1.0.0 // indirect 14 | github.com/redis/go-redis/v9 v9.7.0 // indirect 15 | github.com/yassinebenaid/godump v0.11.1 // indirect 16 | ) 17 | 18 | replace github.com/golang-queue/redisdb => ../../ 19 | -------------------------------------------------------------------------------- /_example/worker/go.sum: -------------------------------------------------------------------------------- 1 | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 2 | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 3 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= 4 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 5 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 6 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 7 | github.com/appleboy/com v0.2.1 h1:dHAHauX3eYDuheAahI83HIGFxpi0SEb2ZAu9EZ9hbUM= 8 | github.com/appleboy/com v0.2.1/go.mod h1:kByEI3/vzI5GM1+O5QdBHLsXaOsmFsJcOpCSgASi4sg= 9 | github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= 10 | github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 11 | github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= 12 | github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 13 | github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= 14 | github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 15 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 16 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 17 | github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= 18 | github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= 19 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 20 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 21 | github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= 22 | github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= 23 | github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= 24 | github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= 25 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 26 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 27 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 28 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 29 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 30 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 31 | github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= 32 | github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 33 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 34 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 35 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 36 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 37 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 38 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 39 | github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 40 | github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 41 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 42 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 43 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 44 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 45 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 46 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 47 | github.com/golang-queue/queue v0.3.0 h1:gyBLNT9EDOsChazYScp8iLiwLfG0SdnCDmNUybcHig4= 48 | github.com/golang-queue/queue v0.3.0/go.mod h1:SkjMwz1TjxZOrF7kABvbar1CagcMxwRtXt5Tx00wb4g= 49 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 50 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 51 | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= 52 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 53 | github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= 54 | github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 55 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 56 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 57 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 58 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 59 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 60 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 61 | github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= 62 | github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 63 | github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= 64 | github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= 65 | github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= 66 | github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= 67 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 68 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 69 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 70 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 71 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 72 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 73 | github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= 74 | github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 75 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 76 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 77 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 78 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 79 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 80 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 81 | github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= 82 | github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= 83 | github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= 84 | github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= 85 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 86 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 87 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 88 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 89 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 90 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 91 | github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= 92 | github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= 93 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 94 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 95 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 96 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 97 | github.com/yassinebenaid/godump v0.11.1 h1:SPujx/XaYqGDfmNh7JI3dOyCUVrG0bG2duhO3Eh2EhI= 98 | github.com/yassinebenaid/godump v0.11.1/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44= 99 | github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= 100 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 101 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 102 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= 103 | go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= 104 | go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= 105 | go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= 106 | go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= 107 | go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= 108 | go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= 109 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 110 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 111 | go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= 112 | go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 113 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 114 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 115 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 116 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 117 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 118 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 119 | -------------------------------------------------------------------------------- /_example/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "time" 9 | 10 | "github.com/golang-queue/queue" 11 | "github.com/golang-queue/queue/core" 12 | "github.com/golang-queue/redisdb" 13 | ) 14 | 15 | type job struct { 16 | Message string 17 | } 18 | 19 | func (j *job) Bytes() []byte { 20 | b, err := json.Marshal(j) 21 | if err != nil { 22 | panic(err) 23 | } 24 | return b 25 | } 26 | 27 | func main() { 28 | taskN := 50 29 | rets := make(chan string, taskN) 30 | 31 | // define the worker 32 | w := redisdb.NewWorker( 33 | redisdb.WithAddr("127.0.0.1:6380"), 34 | redisdb.WithChannel("foobar"), 35 | redisdb.WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 36 | var v job 37 | if err := json.Unmarshal(m.Payload(), &v); err != nil { 38 | return err 39 | } 40 | 41 | rets <- v.Message 42 | return nil 43 | }), 44 | ) 45 | 46 | // define the queue 47 | q := queue.NewPool( 48 | 5, 49 | queue.WithWorker(w), 50 | ) 51 | 52 | // assign tasks in queue 53 | for i := 0; i < taskN; i++ { 54 | go func(i int) { 55 | if err := q.Queue(&job{ 56 | Message: fmt.Sprintf("handle the job: %d", i+1), 57 | }); err != nil { 58 | log.Fatal(err) 59 | } 60 | }(i) 61 | } 62 | 63 | // wait until all tasks done 64 | for i := 0; i < taskN; i++ { 65 | fmt.Println("message:", <-rets) 66 | time.Sleep(50 * time.Millisecond) 67 | } 68 | 69 | // shutdown the service and notify all the worker 70 | q.Release() 71 | } 72 | -------------------------------------------------------------------------------- /conf/redis.conf: -------------------------------------------------------------------------------- 1 | protected-mode no 2 | cluster-enabled yes 3 | appendonly yes 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/golang-queue/redisdb 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/golang-queue/queue v0.3.0 7 | github.com/redis/go-redis/v9 v9.7.1 8 | github.com/stretchr/testify v1.10.0 9 | github.com/testcontainers/testcontainers-go v0.35.0 10 | github.com/yassinebenaid/godump v0.11.1 11 | go.uber.org/goleak v1.3.0 12 | ) 13 | 14 | require ( 15 | dario.cat/mergo v1.0.0 // indirect 16 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect 17 | github.com/Microsoft/go-winio v0.6.2 // indirect 18 | github.com/cenkalti/backoff/v4 v4.2.1 // indirect 19 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 20 | github.com/containerd/containerd v1.7.18 // indirect 21 | github.com/containerd/log v0.1.0 // indirect 22 | github.com/containerd/platforms v0.2.1 // indirect 23 | github.com/cpuguy83/dockercfg v0.3.2 // indirect 24 | github.com/davecgh/go-spew v1.1.1 // indirect 25 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 26 | github.com/distribution/reference v0.6.0 // indirect 27 | github.com/docker/docker v27.1.1+incompatible // indirect 28 | github.com/docker/go-connections v0.5.0 // indirect 29 | github.com/docker/go-units v0.5.0 // indirect 30 | github.com/felixge/httpsnoop v1.0.4 // indirect 31 | github.com/go-logr/logr v1.4.1 // indirect 32 | github.com/go-logr/stdr v1.2.2 // indirect 33 | github.com/go-ole/go-ole v1.2.6 // indirect 34 | github.com/gogo/protobuf v1.3.2 // indirect 35 | github.com/google/uuid v1.6.0 // indirect 36 | github.com/jpillora/backoff v1.0.0 // indirect 37 | github.com/klauspost/compress v1.17.4 // indirect 38 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect 39 | github.com/magiconair/properties v1.8.7 // indirect 40 | github.com/moby/docker-image-spec v1.3.1 // indirect 41 | github.com/moby/patternmatcher v0.6.0 // indirect 42 | github.com/moby/sys/sequential v0.5.0 // indirect 43 | github.com/moby/sys/user v0.1.0 // indirect 44 | github.com/moby/term v0.5.0 // indirect 45 | github.com/morikuni/aec v1.0.0 // indirect 46 | github.com/opencontainers/go-digest v1.0.0 // indirect 47 | github.com/opencontainers/image-spec v1.1.0 // indirect 48 | github.com/pkg/errors v0.9.1 // indirect 49 | github.com/pmezard/go-difflib v1.0.0 // indirect 50 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect 51 | github.com/shirou/gopsutil/v3 v3.23.12 // indirect 52 | github.com/shoenig/go-m1cpu v0.1.6 // indirect 53 | github.com/sirupsen/logrus v1.9.3 // indirect 54 | github.com/tklauser/go-sysconf v0.3.12 // indirect 55 | github.com/tklauser/numcpus v0.6.1 // indirect 56 | github.com/yusufpapurcu/wmi v1.2.3 // indirect 57 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect 58 | go.opentelemetry.io/otel v1.24.0 // indirect 59 | go.opentelemetry.io/otel/metric v1.24.0 // indirect 60 | go.opentelemetry.io/otel/trace v1.24.0 // indirect 61 | golang.org/x/crypto v0.31.0 // indirect 62 | golang.org/x/sys v0.29.0 // indirect 63 | gopkg.in/yaml.v3 v3.0.1 // indirect 64 | ) 65 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 2 | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 3 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= 4 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= 5 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= 6 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 7 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 8 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 9 | github.com/appleboy/com v0.2.1 h1:dHAHauX3eYDuheAahI83HIGFxpi0SEb2ZAu9EZ9hbUM= 10 | github.com/appleboy/com v0.2.1/go.mod h1:kByEI3/vzI5GM1+O5QdBHLsXaOsmFsJcOpCSgASi4sg= 11 | github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= 12 | github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 13 | github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= 14 | github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 15 | github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= 16 | github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 17 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 18 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 19 | github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= 20 | github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= 21 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 22 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 23 | github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= 24 | github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= 25 | github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= 26 | github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= 27 | github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 28 | github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 29 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 30 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 31 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 32 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 33 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 34 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 35 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 36 | github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= 37 | github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 38 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 39 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 40 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 41 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 42 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 43 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 44 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 45 | github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 46 | github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 47 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 48 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 49 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 50 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 51 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 52 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 53 | github.com/golang-queue/queue v0.3.0 h1:gyBLNT9EDOsChazYScp8iLiwLfG0SdnCDmNUybcHig4= 54 | github.com/golang-queue/queue v0.3.0/go.mod h1:SkjMwz1TjxZOrF7kABvbar1CagcMxwRtXt5Tx00wb4g= 55 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 56 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 57 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 58 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 59 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 60 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 61 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= 62 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= 63 | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= 64 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 65 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 66 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 67 | github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= 68 | github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 69 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 70 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 71 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 72 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 73 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 74 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 75 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 76 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 77 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 78 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 79 | github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= 80 | github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 81 | github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= 82 | github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= 83 | github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= 84 | github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= 85 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 86 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 87 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 88 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 89 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 90 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 91 | github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= 92 | github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 93 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 94 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 95 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 96 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 97 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 98 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 99 | github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc= 100 | github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= 101 | github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= 102 | github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= 103 | github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= 104 | github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= 105 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 106 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 107 | github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= 108 | github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 109 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 110 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 111 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 112 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 113 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 114 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 115 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 116 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 117 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 118 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 119 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 120 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 121 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 122 | github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= 123 | github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= 124 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 125 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 126 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 127 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 128 | github.com/yassinebenaid/godump v0.11.1 h1:SPujx/XaYqGDfmNh7JI3dOyCUVrG0bG2duhO3Eh2EhI= 129 | github.com/yassinebenaid/godump v0.11.1/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44= 130 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 131 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 132 | github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= 133 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 134 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 135 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= 136 | go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= 137 | go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= 138 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= 139 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= 140 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= 141 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= 142 | go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= 143 | go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= 144 | go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= 145 | go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= 146 | go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= 147 | go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= 148 | go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= 149 | go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= 150 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 151 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 152 | go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= 153 | go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 154 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 155 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 156 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 157 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 158 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 159 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 160 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 161 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 162 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 163 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 164 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 165 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 166 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 167 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 168 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 169 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 170 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 171 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 172 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 173 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 174 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 175 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 176 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 177 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 178 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 179 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 180 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 181 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 182 | golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= 183 | golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 184 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 185 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 186 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 187 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 188 | golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= 189 | golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 190 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 191 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 192 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 193 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 194 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 195 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 196 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 197 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 198 | google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0= 199 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI= 200 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 201 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= 202 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= 203 | google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= 204 | google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= 205 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 206 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 207 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 208 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 209 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 210 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 211 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 212 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 213 | gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= 214 | gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= 215 | -------------------------------------------------------------------------------- /images/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golang-queue/redisdb/939295e5feef0cff76837c4d231c4993424e6fbe/images/screen.png -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package redisdb 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | 7 | "github.com/golang-queue/queue" 8 | "github.com/golang-queue/queue/core" 9 | ) 10 | 11 | // Option for queue system 12 | type Option func(*options) 13 | 14 | type options struct { 15 | runFunc func(context.Context, core.TaskMessage) error 16 | logger queue.Logger 17 | addr string 18 | db int 19 | connectionString string 20 | username string 21 | password string 22 | channelName string 23 | channelSize int 24 | cluster bool 25 | sentinel bool 26 | masterName string 27 | tls *tls.Config 28 | debug bool 29 | } 30 | 31 | // WithAddr setup the addr of redis 32 | func WithAddr(addr string) Option { 33 | return func(w *options) { 34 | w.addr = addr 35 | } 36 | } 37 | 38 | // WithPassword redis password 39 | func WithDB(db int) Option { 40 | return func(w *options) { 41 | w.db = db 42 | } 43 | } 44 | 45 | // WithCluster redis cluster 46 | func WithCluster() Option { 47 | return func(w *options) { 48 | w.cluster = true 49 | } 50 | } 51 | 52 | // WithSentinel redis sentinel 53 | func WithSentinel() Option { 54 | return func(w *options) { 55 | w.sentinel = true 56 | } 57 | } 58 | 59 | // WithTLS returns an Option that configures the use of TLS for the connection. 60 | // It sets the minimum TLS version to TLS 1.2. 61 | func WithTLS() Option { 62 | return func(w *options) { 63 | w.tls = &tls.Config{ 64 | MinVersion: tls.VersionTLS12, 65 | } 66 | } 67 | } 68 | 69 | // WithSkipTLSVerify returns an Option that configures the TLS settings to skip 70 | // verification of the server's certificate. This is useful for connecting to 71 | // servers with self-signed certificates or when certificate verification is 72 | // not required. Use this option with caution as it makes the connection 73 | // susceptible to man-in-the-middle attacks. 74 | func WithSkipTLSVerify() Option { 75 | return func(w *options) { 76 | if w.tls == nil { 77 | w.tls = &tls.Config{ 78 | InsecureSkipVerify: true, //nolint: gosec 79 | 80 | } 81 | return 82 | } 83 | w.tls.InsecureSkipVerify = true 84 | } 85 | } 86 | 87 | // WithMasterName sentinel master name 88 | func WithMasterName(masterName string) Option { 89 | return func(w *options) { 90 | w.masterName = masterName 91 | } 92 | } 93 | 94 | // WithChannelSize redis channel size 95 | func WithChannelSize(size int) Option { 96 | return func(w *options) { 97 | w.channelSize = size 98 | } 99 | } 100 | 101 | // WithUsername redis username 102 | func WithUsername(username string) Option { 103 | return func(w *options) { 104 | w.username = username 105 | } 106 | } 107 | 108 | // WithPassword redis password 109 | func WithPassword(passwd string) Option { 110 | return func(w *options) { 111 | w.password = passwd 112 | } 113 | } 114 | 115 | // WithConnectionString redis connection string 116 | func WithConnectionString(connectionString string) Option { 117 | return func(w *options) { 118 | w.connectionString = connectionString 119 | } 120 | } 121 | 122 | // WithChannel setup the channel of redis 123 | func WithChannel(channel string) Option { 124 | return func(w *options) { 125 | w.channelName = channel 126 | } 127 | } 128 | 129 | // WithRunFunc setup the run func of queue 130 | func WithRunFunc(fn func(context.Context, core.TaskMessage) error) Option { 131 | return func(w *options) { 132 | w.runFunc = fn 133 | } 134 | } 135 | 136 | // WithLogger set custom logger 137 | func WithLogger(l queue.Logger) Option { 138 | return func(w *options) { 139 | w.logger = l 140 | } 141 | } 142 | 143 | // WithDebug set debug mode 144 | func WithDebug() Option { 145 | return func(w *options) { 146 | w.debug = true 147 | } 148 | } 149 | 150 | func newOptions(opts ...Option) options { 151 | defaultOpts := options{ 152 | channelName: "queue", 153 | // default channel size in go-redis package 154 | channelSize: 100, 155 | logger: queue.NewLogger(), 156 | runFunc: func(context.Context, core.TaskMessage) error { 157 | return nil 158 | }, 159 | } 160 | 161 | // Loop through each option 162 | for _, opt := range opts { 163 | // Call the option giving the instantiated 164 | opt(&defaultOpts) 165 | } 166 | 167 | return defaultOpts 168 | } 169 | -------------------------------------------------------------------------------- /redis.go: -------------------------------------------------------------------------------- 1 | package redisdb 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "strings" 7 | "sync" 8 | "sync/atomic" 9 | "time" 10 | 11 | "github.com/golang-queue/queue" 12 | "github.com/golang-queue/queue/core" 13 | "github.com/golang-queue/queue/job" 14 | 15 | "github.com/redis/go-redis/v9" 16 | "github.com/yassinebenaid/godump" 17 | ) 18 | 19 | var _ core.Worker = (*Worker)(nil) 20 | 21 | // Worker for Redis 22 | type Worker struct { 23 | // redis config 24 | rdb redis.Cmdable 25 | pubsub *redis.PubSub 26 | channel <-chan *redis.Message 27 | stopFlag int32 28 | stopOnce sync.Once 29 | stop chan struct{} 30 | opts options 31 | } 32 | 33 | // NewWorker creates a new Worker instance with the provided options. 34 | // It initializes a Redis client based on the options and establishes a connection to the Redis server. 35 | // The Worker is responsible for subscribing to a Redis channel and receiving messages from it. 36 | // It returns the created Worker instance. 37 | func NewWorker(opts ...Option) *Worker { 38 | var err error 39 | w := &Worker{ 40 | opts: newOptions(opts...), 41 | stop: make(chan struct{}), 42 | } 43 | 44 | if w.opts.debug { 45 | _ = godump.Dump(w.opts) 46 | } 47 | 48 | options := &redis.Options{ 49 | Addr: w.opts.addr, 50 | Username: w.opts.username, 51 | Password: w.opts.password, 52 | DB: w.opts.db, 53 | TLSConfig: w.opts.tls, 54 | } 55 | w.rdb = redis.NewClient(options) 56 | 57 | if w.opts.connectionString != "" { 58 | options, err := redis.ParseURL(w.opts.connectionString) 59 | if err != nil { 60 | w.opts.logger.Fatal(err) 61 | } 62 | w.rdb = redis.NewClient(options) 63 | } 64 | 65 | if w.opts.cluster { 66 | w.rdb = redis.NewClusterClient(&redis.ClusterOptions{ 67 | Addrs: strings.Split(w.opts.addr, ","), 68 | Username: w.opts.username, 69 | Password: w.opts.password, 70 | TLSConfig: w.opts.tls, 71 | }) 72 | } 73 | 74 | if w.opts.sentinel { 75 | w.rdb = redis.NewFailoverClient(&redis.FailoverOptions{ 76 | MasterName: w.opts.masterName, 77 | SentinelAddrs: strings.Split(w.opts.addr, ","), 78 | Username: w.opts.username, 79 | Password: w.opts.password, 80 | DB: w.opts.db, 81 | TLSConfig: w.opts.tls, 82 | }) 83 | } 84 | 85 | _, err = w.rdb.Ping(context.Background()).Result() 86 | if err != nil { 87 | w.opts.logger.Fatal(err) 88 | } 89 | 90 | ctx := context.Background() 91 | 92 | switch v := w.rdb.(type) { 93 | case *redis.Client: 94 | w.pubsub = v.Subscribe(ctx, w.opts.channelName) 95 | case *redis.ClusterClient: 96 | w.pubsub = v.Subscribe(ctx, w.opts.channelName) 97 | } 98 | 99 | var ropts []redis.ChannelOption 100 | 101 | if w.opts.channelSize > 1 { 102 | ropts = append(ropts, redis.WithChannelSize(w.opts.channelSize)) 103 | } 104 | 105 | w.channel = w.pubsub.Channel(ropts...) 106 | // make sure the connection is successful 107 | if err := w.pubsub.Ping(ctx); err != nil { 108 | w.opts.logger.Fatal(err) 109 | } 110 | 111 | return w 112 | } 113 | 114 | // Run to execute new task 115 | func (w *Worker) Run(ctx context.Context, task core.TaskMessage) error { 116 | return w.opts.runFunc(ctx, task) 117 | } 118 | 119 | // Shutdown worker 120 | func (w *Worker) Shutdown() error { 121 | if !atomic.CompareAndSwapInt32(&w.stopFlag, 0, 1) { 122 | return queue.ErrQueueShutdown 123 | } 124 | 125 | w.stopOnce.Do(func() { 126 | w.pubsub.Close() 127 | switch v := w.rdb.(type) { 128 | case *redis.Client: 129 | v.Close() 130 | case *redis.ClusterClient: 131 | v.Close() 132 | } 133 | close(w.stop) 134 | }) 135 | return nil 136 | } 137 | 138 | // Queue send notification to queue 139 | func (w *Worker) Queue(job core.TaskMessage) error { 140 | if atomic.LoadInt32(&w.stopFlag) == 1 { 141 | return queue.ErrQueueShutdown 142 | } 143 | 144 | ctx := context.Background() 145 | 146 | // Publish a message. 147 | err := w.rdb.Publish(ctx, w.opts.channelName, job.Bytes()).Err() 148 | if err != nil { 149 | return err 150 | } 151 | 152 | return nil 153 | } 154 | 155 | // Request a new task 156 | func (w *Worker) Request() (core.TaskMessage, error) { 157 | clock := 0 158 | loop: 159 | for { 160 | select { 161 | case task, ok := <-w.channel: 162 | if !ok { 163 | return nil, queue.ErrQueueHasBeenClosed 164 | } 165 | var data job.Message 166 | err := json.Unmarshal([]byte(task.Payload), &data) 167 | if err != nil { 168 | return nil, err 169 | } 170 | return &data, nil 171 | case <-time.After(1 * time.Second): 172 | if clock == 5 { 173 | break loop 174 | } 175 | clock += 1 176 | } 177 | } 178 | 179 | return nil, queue.ErrNoTaskInQueue 180 | } 181 | -------------------------------------------------------------------------------- /redis_test.go: -------------------------------------------------------------------------------- 1 | package redisdb 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "runtime" 9 | "strings" 10 | "testing" 11 | "time" 12 | 13 | "github.com/golang-queue/queue" 14 | "github.com/golang-queue/queue/core" 15 | "github.com/golang-queue/queue/job" 16 | 17 | "github.com/stretchr/testify/assert" 18 | "github.com/stretchr/testify/require" 19 | "github.com/testcontainers/testcontainers-go" 20 | "github.com/testcontainers/testcontainers-go/wait" 21 | "go.uber.org/goleak" 22 | ) 23 | 24 | func TestMain(m *testing.M) { 25 | goleak.VerifyTestMain(m) 26 | } 27 | 28 | type mockMessage struct { 29 | Message string 30 | } 31 | 32 | func (m mockMessage) Bytes() []byte { 33 | return []byte(m.Message) 34 | } 35 | 36 | func (m mockMessage) Payload() []byte { 37 | return []byte(m.Message) 38 | } 39 | 40 | func setupRedisSentinelContainer( 41 | ctx context.Context, 42 | t *testing.T, 43 | masterHost string, 44 | masterPort string, 45 | ) (testcontainers.Container, string) { 46 | req := testcontainers.ContainerRequest{ 47 | Image: "bitnami/redis-sentinel:7.4-debian-12", 48 | ExposedPorts: []string{ 49 | "26379/tcp", 50 | }, 51 | WaitingFor: wait.NewExecStrategy( 52 | []string{"redis-cli", "-h", "localhost", "-p", "26379", "ping"}, 53 | ), 54 | Env: map[string]string{ 55 | "REDIS_MASTER_HOST": masterHost, 56 | "REDIS_MASTER_PORT_NUMBER": masterPort, 57 | "REDIS_MASTER_SET": "mymaster", 58 | "REDIS_SENTINEL_QUORUM": "1", 59 | }, 60 | } 61 | redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ 62 | ContainerRequest: req, 63 | Started: true, 64 | }) 65 | require.NoError(t, err) 66 | 67 | endpoint, err := redisC.Endpoint(ctx, "") 68 | require.NoError(t, err) 69 | 70 | return redisC, endpoint 71 | } 72 | 73 | func setupRedisCluserContainer(ctx context.Context, t *testing.T) (testcontainers.Container, string) { 74 | req := testcontainers.ContainerRequest{ 75 | Image: "vishnunair/docker-redis-cluster:latest", 76 | ExposedPorts: []string{ 77 | "6379/tcp", 78 | "6380/tcp", 79 | "6381/tcp", 80 | "6382/tcp", 81 | "6383/tcp", 82 | "6384/tcp", 83 | }, 84 | WaitingFor: wait.NewExecStrategy( 85 | []string{"redis-cli", "-h", "localhost", "-p", "6379", "cluster", "info"}, 86 | ), 87 | } 88 | redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ 89 | ContainerRequest: req, 90 | Started: true, 91 | }) 92 | require.NoError(t, err) 93 | 94 | endpoint, err := redisC.Endpoint(ctx, "") 95 | require.NoError(t, err) 96 | 97 | return redisC, endpoint 98 | } 99 | 100 | func setupRedisContainer(ctx context.Context, t *testing.T) (testcontainers.Container, string) { 101 | req := testcontainers.ContainerRequest{ 102 | Image: "redis:6", 103 | ExposedPorts: []string{"6379/tcp"}, 104 | WaitingFor: wait.NewExecStrategy( 105 | []string{"redis-cli", "-h", "localhost", "-p", "6379", "ping"}, 106 | ), 107 | } 108 | redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ 109 | ContainerRequest: req, 110 | Started: true, 111 | }) 112 | require.NoError(t, err) 113 | 114 | endpoint, err := redisC.Endpoint(ctx, "") 115 | require.NoError(t, err) 116 | 117 | return redisC, endpoint 118 | } 119 | 120 | func TestWithRedis(t *testing.T) { 121 | ctx := context.Background() 122 | redisC, _ := setupRedisContainer(ctx, t) 123 | testcontainers.CleanupContainer(t, redisC) 124 | } 125 | 126 | func TestRedisDefaultFlow(t *testing.T) { 127 | ctx := context.Background() 128 | redisC, endpoint := setupRedisContainer(ctx, t) 129 | defer testcontainers.CleanupContainer(t, redisC) 130 | 131 | m := &mockMessage{ 132 | Message: "foo", 133 | } 134 | w := NewWorker( 135 | WithAddr(endpoint), 136 | WithChannel("test"), 137 | WithDebug(), 138 | ) 139 | q, err := queue.NewQueue( 140 | queue.WithWorker(w), 141 | queue.WithWorkerCount(2), 142 | ) 143 | assert.NoError(t, err) 144 | q.Start() 145 | time.Sleep(100 * time.Millisecond) 146 | assert.NoError(t, q.Queue(m)) 147 | m.Message = "bar" 148 | assert.NoError(t, q.Queue(m)) 149 | q.Shutdown() 150 | q.Wait() 151 | } 152 | 153 | func TestRedisShutdown(t *testing.T) { 154 | ctx := context.Background() 155 | redisC, endpoint := setupRedisContainer(ctx, t) 156 | defer testcontainers.CleanupContainer(t, redisC) 157 | 158 | w := NewWorker( 159 | WithAddr(endpoint), 160 | WithChannel("test2"), 161 | ) 162 | q, err := queue.NewQueue( 163 | queue.WithWorker(w), 164 | queue.WithWorkerCount(2), 165 | ) 166 | assert.NoError(t, err) 167 | q.Start() 168 | time.Sleep(1 * time.Second) 169 | q.Shutdown() 170 | // check shutdown once 171 | assert.Error(t, w.Shutdown()) 172 | assert.Equal(t, queue.ErrQueueShutdown, w.Shutdown()) 173 | q.Wait() 174 | } 175 | 176 | func TestCustomFuncAndWait(t *testing.T) { 177 | ctx := context.Background() 178 | redisC, endpoint := setupRedisContainer(ctx, t) 179 | defer testcontainers.CleanupContainer(t, redisC) 180 | m := &mockMessage{ 181 | Message: "foo", 182 | } 183 | w := NewWorker( 184 | WithAddr(endpoint), 185 | WithChannel("test3"), 186 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 187 | time.Sleep(500 * time.Millisecond) 188 | return nil 189 | }), 190 | ) 191 | q := queue.NewPool( 192 | 5, 193 | queue.WithWorker(w), 194 | ) 195 | time.Sleep(100 * time.Millisecond) 196 | assert.NoError(t, q.Queue(m)) 197 | assert.NoError(t, q.Queue(m)) 198 | assert.NoError(t, q.Queue(m)) 199 | assert.NoError(t, q.Queue(m)) 200 | time.Sleep(1000 * time.Millisecond) 201 | q.Release() 202 | // you will see the execute time > 1000ms 203 | } 204 | 205 | func TestRedisCluster(t *testing.T) { 206 | t.Helper() 207 | 208 | ctx := context.Background() 209 | redisC, _ := setupRedisCluserContainer(ctx, t) 210 | defer testcontainers.CleanupContainer(t, redisC) 211 | 212 | masterPort, err := redisC.MappedPort(ctx, "6379") 213 | assert.NoError(t, err) 214 | 215 | slavePort, err := redisC.MappedPort(ctx, "6382") 216 | assert.NoError(t, err) 217 | 218 | hostIP, err := redisC.Host(ctx) 219 | assert.NoError(t, err) 220 | 221 | m := &mockMessage{ 222 | Message: "foo", 223 | } 224 | 225 | masterName := fmt.Sprintf("%s:%s", hostIP, masterPort.Port()) 226 | slaveName := fmt.Sprintf("%s:%s", hostIP, slavePort.Port()) 227 | 228 | hosts := []string{masterName, slaveName} 229 | 230 | w := NewWorker( 231 | WithAddr(strings.Join(hosts, ",")), 232 | WithChannel("testCluster"), 233 | WithCluster(), 234 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 235 | time.Sleep(500 * time.Millisecond) 236 | return nil 237 | }), 238 | ) 239 | q := queue.NewPool( 240 | 5, 241 | queue.WithWorker(w), 242 | ) 243 | time.Sleep(100 * time.Millisecond) 244 | assert.NoError(t, q.Queue(m)) 245 | assert.NoError(t, q.Queue(m)) 246 | assert.NoError(t, q.Queue(m)) 247 | assert.NoError(t, q.Queue(m)) 248 | time.Sleep(1000 * time.Millisecond) 249 | q.Release() 250 | // you will see the execute time > 1000ms 251 | } 252 | 253 | func TestRedisSentinel(t *testing.T) { 254 | t.Helper() 255 | 256 | ctx := context.Background() 257 | 258 | // create redis master 259 | redisC, _ := setupRedisContainer(ctx, t) 260 | defer testcontainers.CleanupContainer(t, redisC) 261 | masterPort, err := redisC.MappedPort(ctx, "6379") 262 | assert.NoError(t, err) 263 | masterHost, err := redisC.Host(ctx) 264 | assert.NoError(t, err) 265 | 266 | sentinelC, _ := setupRedisSentinelContainer(ctx, t, masterHost, masterPort.Port()) 267 | defer testcontainers.CleanupContainer(t, sentinelC) 268 | 269 | sentinelPort, err := sentinelC.MappedPort(ctx, "26379") 270 | assert.NoError(t, err) 271 | 272 | sentinelHost, err := sentinelC.Host(ctx) 273 | assert.NoError(t, err) 274 | 275 | m := &mockMessage{ 276 | Message: "foo", 277 | } 278 | 279 | masterName := fmt.Sprintf("%s:%s", sentinelHost, sentinelPort.Port()) 280 | 281 | hosts := []string{masterName} 282 | 283 | w := NewWorker( 284 | WithAddr(strings.Join(hosts, ",")), 285 | WithMasterName("mymaster"), 286 | WithChannel("testSentinel"), 287 | WithSentinel(), 288 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 289 | time.Sleep(500 * time.Millisecond) 290 | return nil 291 | }), 292 | ) 293 | q := queue.NewPool( 294 | 5, 295 | queue.WithWorker(w), 296 | ) 297 | time.Sleep(100 * time.Millisecond) 298 | assert.NoError(t, q.Queue(m)) 299 | assert.NoError(t, q.Queue(m)) 300 | assert.NoError(t, q.Queue(m)) 301 | assert.NoError(t, q.Queue(m)) 302 | time.Sleep(1000 * time.Millisecond) 303 | q.Release() 304 | // you will see the execute time > 1000ms 305 | } 306 | 307 | func TestEnqueueJobAfterShutdown(t *testing.T) { 308 | ctx := context.Background() 309 | redisC, endpoint := setupRedisContainer(ctx, t) 310 | defer testcontainers.CleanupContainer(t, redisC) 311 | m := mockMessage{ 312 | Message: "foo", 313 | } 314 | w := NewWorker( 315 | WithAddr(endpoint), 316 | ) 317 | q, err := queue.NewQueue( 318 | queue.WithWorker(w), 319 | queue.WithWorkerCount(2), 320 | ) 321 | assert.NoError(t, err) 322 | q.Start() 323 | time.Sleep(50 * time.Millisecond) 324 | q.Shutdown() 325 | // can't queue task after shutdown 326 | err = q.Queue(m) 327 | assert.Error(t, err) 328 | assert.Equal(t, queue.ErrQueueShutdown, err) 329 | q.Wait() 330 | } 331 | 332 | func TestJobReachTimeout(t *testing.T) { 333 | ctx := context.Background() 334 | redisC, endpoint := setupRedisContainer(ctx, t) 335 | defer testcontainers.CleanupContainer(t, redisC) 336 | m := mockMessage{ 337 | Message: "foo", 338 | } 339 | w := NewWorker( 340 | WithAddr(endpoint), 341 | WithChannel("timeout"), 342 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 343 | for { 344 | select { 345 | case <-ctx.Done(): 346 | log.Println("get data:", string(m.Payload())) 347 | if errors.Is(ctx.Err(), context.Canceled) { 348 | log.Println("queue has been shutdown and cancel the job") 349 | } else if errors.Is(ctx.Err(), context.DeadlineExceeded) { 350 | log.Println("job deadline exceeded") 351 | } 352 | return nil 353 | default: 354 | } 355 | time.Sleep(50 * time.Millisecond) 356 | } 357 | }), 358 | ) 359 | q, err := queue.NewQueue( 360 | queue.WithWorker(w), 361 | queue.WithWorkerCount(2), 362 | ) 363 | assert.NoError(t, err) 364 | q.Start() 365 | time.Sleep(50 * time.Millisecond) 366 | assert.NoError(t, q.Queue(m, job.AllowOption{ 367 | Timeout: job.Time(20 * time.Millisecond), 368 | })) 369 | time.Sleep(2 * time.Second) 370 | q.Shutdown() 371 | q.Wait() 372 | } 373 | 374 | func TestCancelJobAfterShutdown(t *testing.T) { 375 | ctx := context.Background() 376 | redisC, endpoint := setupRedisContainer(ctx, t) 377 | defer testcontainers.CleanupContainer(t, redisC) 378 | m := mockMessage{ 379 | Message: "test", 380 | } 381 | w := NewWorker( 382 | WithAddr(endpoint), 383 | WithChannel("cancel"), 384 | WithLogger(queue.NewLogger()), 385 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 386 | for { 387 | select { 388 | case <-ctx.Done(): 389 | log.Println("get data:", string(m.Payload())) 390 | if errors.Is(ctx.Err(), context.Canceled) { 391 | log.Println("queue has been shutdown and cancel the job") 392 | } else if errors.Is(ctx.Err(), context.DeadlineExceeded) { 393 | log.Println("job deadline exceeded") 394 | } 395 | return nil 396 | default: 397 | } 398 | time.Sleep(50 * time.Millisecond) 399 | } 400 | }), 401 | ) 402 | q, err := queue.NewQueue( 403 | queue.WithWorker(w), 404 | queue.WithWorkerCount(2), 405 | ) 406 | assert.NoError(t, err) 407 | q.Start() 408 | time.Sleep(50 * time.Millisecond) 409 | assert.NoError(t, q.Queue(m, job.AllowOption{ 410 | Timeout: job.Time(3 * time.Second), 411 | })) 412 | time.Sleep(2 * time.Second) 413 | q.Shutdown() 414 | q.Wait() 415 | } 416 | 417 | func TestGoroutineLeak(t *testing.T) { 418 | ctx := context.Background() 419 | redisC, endpoint := setupRedisContainer(ctx, t) 420 | defer testcontainers.CleanupContainer(t, redisC) 421 | m := mockMessage{ 422 | Message: "foo", 423 | } 424 | w := NewWorker( 425 | WithAddr(endpoint), 426 | WithChannel("GoroutineLeak"), 427 | WithLogger(queue.NewEmptyLogger()), 428 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 429 | for { 430 | select { 431 | case <-ctx.Done(): 432 | log.Println("get data:", string(m.Payload())) 433 | if errors.Is(ctx.Err(), context.Canceled) { 434 | log.Println("queue has been shutdown and cancel the job") 435 | } else if errors.Is(ctx.Err(), context.DeadlineExceeded) { 436 | log.Println("job deadline exceeded") 437 | } 438 | return nil 439 | default: 440 | log.Println("get data:", string(m.Payload())) 441 | time.Sleep(50 * time.Millisecond) 442 | return nil 443 | } 444 | } 445 | }), 446 | ) 447 | q, err := queue.NewQueue( 448 | queue.WithLogger(queue.NewEmptyLogger()), 449 | queue.WithWorker(w), 450 | queue.WithWorkerCount(10), 451 | ) 452 | assert.NoError(t, err) 453 | q.Start() 454 | time.Sleep(50 * time.Millisecond) 455 | for i := 0; i < 50; i++ { 456 | m.Message = fmt.Sprintf("foobar: %d", i+1) 457 | assert.NoError(t, q.Queue(m)) 458 | } 459 | time.Sleep(1 * time.Second) 460 | q.Release() 461 | time.Sleep(1 * time.Second) 462 | fmt.Println("number of goroutines:", runtime.NumGoroutine()) 463 | } 464 | 465 | func TestGoroutinePanic(t *testing.T) { 466 | ctx := context.Background() 467 | redisC, endpoint := setupRedisContainer(ctx, t) 468 | defer testcontainers.CleanupContainer(t, redisC) 469 | m := mockMessage{ 470 | Message: "foo", 471 | } 472 | w := NewWorker( 473 | WithAddr(endpoint), 474 | WithChannel("GoroutinePanic"), 475 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 476 | panic("missing something") 477 | }), 478 | ) 479 | q, err := queue.NewQueue( 480 | queue.WithWorker(w), 481 | queue.WithWorkerCount(2), 482 | ) 483 | assert.NoError(t, err) 484 | q.Start() 485 | time.Sleep(50 * time.Millisecond) 486 | assert.NoError(t, q.Queue(m)) 487 | assert.NoError(t, q.Queue(m)) 488 | time.Sleep(200 * time.Millisecond) 489 | q.Shutdown() 490 | assert.Error(t, q.Queue(m)) 491 | q.Wait() 492 | } 493 | --------------------------------------------------------------------------------