├── .github ├── dependabot.yml └── workflows │ ├── codeql.yaml │ ├── go.yml │ └── goreleaser.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── LICENSE ├── README.md ├── _example ├── producer-consumer │ ├── consumer │ │ └── main.go │ ├── go.mod │ ├── go.sum │ └── producer │ │ └── main.go └── worker │ ├── go.mod │ ├── go.sum │ └── main.go ├── go.mod ├── go.sum ├── nats.go ├── nats_test.go └── options.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: Checkout repository 9 | uses: actions/checkout@v4 10 | 11 | - name: Setup go 12 | uses: actions/setup-go@v5 13 | with: 14 | go-version-file: go.mod 15 | check-latest: true 16 | 17 | - name: Setup golangci-lint 18 | uses: golangci/golangci-lint-action@v6 19 | with: 20 | version: latest 21 | args: --verbose 22 | 23 | # Label of the container job 24 | test: 25 | strategy: 26 | matrix: 27 | os: [ubuntu-latest] 28 | go: [1.23, 1.24] 29 | include: 30 | - os: ubuntu-latest 31 | go-build: ~/.cache/go-build 32 | name: ${{ matrix.os }} @ Go ${{ matrix.go }} 33 | runs-on: ${{ matrix.os }} 34 | env: 35 | GO111MODULE: on 36 | GOPROXY: https://proxy.golang.org 37 | 38 | # Service containers to run with `container-job` 39 | services: 40 | nats01: 41 | image: nats 42 | ports: 43 | - 4222:4222 44 | nats02: 45 | image: nats 46 | ports: 47 | - 4223:4222 48 | 49 | steps: 50 | - name: Set up Go ${{ matrix.go }} 51 | uses: actions/setup-go@v5 52 | with: 53 | go-version: ${{ matrix.go }} 54 | 55 | - name: Checkout Code 56 | uses: actions/checkout@v4 57 | with: 58 | ref: ${{ github.ref }} 59 | 60 | - uses: actions/cache@v4 61 | with: 62 | path: | 63 | ${{ matrix.go-build }} 64 | ~/go/pkg/mod 65 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 66 | restore-keys: | 67 | ${{ runner.os }}-go- 68 | - name: Run Tests 69 | run: | 70 | go test -v -covermode=atomic -coverprofile=coverage.out 71 | 72 | - name: Upload coverage to Codecov 73 | uses: codecov/codecov-action@v5 74 | with: 75 | flags: ${{ matrix.os }},go-${{ matrix.go }} 76 | -------------------------------------------------------------------------------- /.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 repository 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Setup go 21 | uses: actions/setup-go@v5 22 | with: 23 | go-version-file: go.mod 24 | check-latest: true 25 | 26 | - name: Run GoReleaser 27 | uses: goreleaser/goreleaser-action@v6 28 | with: 29 | # either 'goreleaser' (default) or 'goreleaser-pro' 30 | distribution: goreleaser 31 | version: latest 32 | args: release --clean 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.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 | - bodyclose 7 | - dogsled 8 | - dupl 9 | - errcheck 10 | - exportloopref 11 | - exhaustive 12 | - gochecknoinits 13 | - goconst 14 | - gocritic 15 | - gocyclo 16 | - gofmt 17 | - goimports 18 | - goprintffuncname 19 | - gosec 20 | - gosimple 21 | - govet 22 | - ineffassign 23 | - lll 24 | - misspell 25 | - nakedret 26 | - noctx 27 | - nolintlint 28 | - staticcheck 29 | - stylecheck 30 | - typecheck 31 | - unconvert 32 | - unparam 33 | - unused 34 | - whitespace 35 | - gofumpt 36 | 37 | run: 38 | timeout: 3m 39 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - # If true, skip the build. 3 | # Useful for library projects. 4 | # Default is false 5 | skip: true 6 | 7 | changelog: 8 | # Set it to true if you wish to skip the changelog generation. 9 | # This may result in an empty release notes on GitHub/GitLab/Gitea. 10 | disable: false 11 | 12 | # Changelog generation implementation to use. 13 | # 14 | # Valid options are: 15 | # - `git`: uses `git log`; 16 | # - `github`: uses the compare GitHub API, appending the author login to the changelog. 17 | # - `gitlab`: uses the compare GitLab API, appending the author name and email to the changelog. 18 | # - `github-native`: uses the GitHub release notes generation API, disables the groups feature. 19 | # 20 | # Defaults to `git`. 21 | use: github 22 | 23 | # Sorts the changelog by the commit's messages. 24 | # Could either be asc, desc or empty 25 | # Default is empty 26 | sort: asc 27 | 28 | # Group commits messages by given regex and title. 29 | # Order value defines the order of the groups. 30 | # Proving no regex means all commits will be grouped under the default group. 31 | # Groups are disabled when using github-native, as it already groups things by itself. 32 | # 33 | # Default is no groups. 34 | groups: 35 | - title: Features 36 | regexp: "^.*feat[(\\w-)]*:+.*$" 37 | order: 0 38 | - title: "Bug fixes" 39 | regexp: "^.*fix[(\\w-)]*:+.*$" 40 | order: 1 41 | - title: "Enhancements" 42 | regexp: "^.*chore[(\\w-)]*:+.*$" 43 | order: 2 44 | - title: "Refactor" 45 | regexp: "^.*refactor[(\\w-)]*:+.*$" 46 | order: 3 47 | - title: "Build process updates" 48 | regexp: ^.*?(build|ci)(\(.+\))??!?:.+$ 49 | order: 4 50 | - title: "Documentation updates" 51 | regexp: ^.*?docs?(\(.+\))??!?:.+$ 52 | order: 4 53 | - title: Others 54 | order: 999 55 | -------------------------------------------------------------------------------- /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 | # nats 2 | 3 | [![CodeQL](https://github.com/golang-queue/nats/actions/workflows/codeql.yaml/badge.svg)](https://github.com/golang-queue/nats/actions/workflows/codeql.yaml) 4 | [![Run Testing](https://github.com/golang-queue/nats/actions/workflows/go.yml/badge.svg)](https://github.com/golang-queue/nats/actions/workflows/go.yml) 5 | [![codecov](https://codecov.io/gh/golang-queue/nats/branch/main/graph/badge.svg?token=FFZN8E2ZZB)](https://codecov.io/gh/golang-queue/nats) 6 | [![Go Report Card](https://goreportcard.com/badge/github.com/golang-queue/nats)](https://goreportcard.com/report/github.com/golang-queue/nats) 7 | 8 | NATS as backend with [Queue package](https://github.com/golang-queue/queue) (Connective Technology for Adaptive Edge & Distributed Systems) 9 | 10 | ## Testing 11 | 12 | setup the nats server 13 | 14 | ```sh 15 | docker run -d --name nats-main -p 4222:4222 -p 8222:8222 nats:latest 16 | docker run -d --name nats-slave -p 4223:4222 -p 8223:8222 nats:latest 17 | ``` 18 | 19 | run the test 20 | 21 | ```sh 22 | go test -v ./... 23 | ``` 24 | 25 | ## Example 26 | 27 | ```go 28 | package main 29 | 30 | import ( 31 | "context" 32 | "encoding/json" 33 | "fmt" 34 | "log" 35 | "time" 36 | 37 | "github.com/golang-queue/nats" 38 | "github.com/golang-queue/queue" 39 | "github.com/golang-queue/queue/core" 40 | ) 41 | 42 | type job struct { 43 | Message string 44 | } 45 | 46 | func (j *job) Bytes() []byte { 47 | b, err := json.Marshal(j) 48 | if err != nil { 49 | panic(err) 50 | } 51 | return b 52 | } 53 | 54 | func main() { 55 | taskN := 100 56 | rets := make(chan string, taskN) 57 | 58 | // define the worker 59 | w := nats.NewWorker( 60 | nats.WithAddr("127.0.0.1:4222"), 61 | nats.WithSubj("example"), 62 | nats.WithQueue("foobar"), 63 | nats.WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 64 | var v *job 65 | if err := json.Unmarshal(m.Payload(), &v); err != nil { 66 | return err 67 | } 68 | rets <- v.Message 69 | return nil 70 | }), 71 | ) 72 | 73 | // define the queue 74 | q, err := queue.NewQueue( 75 | queue.WithWorkerCount(10), 76 | queue.WithWorker(w), 77 | ) 78 | if err != nil { 79 | log.Fatal(err) 80 | } 81 | 82 | // start the five worker 83 | q.Start() 84 | 85 | // assign tasks in queue 86 | for i := 0; i < taskN; i++ { 87 | go func(i int) { 88 | if err := q.Queue(&job{ 89 | Message: fmt.Sprintf("handle the job: %d", i+1), 90 | }); err != nil { 91 | log.Fatal(err) 92 | } 93 | }(i) 94 | } 95 | 96 | // wait until all tasks done 97 | for i := 0; i < taskN; i++ { 98 | fmt.Println("message:", <-rets) 99 | time.Sleep(50 * time.Millisecond) 100 | } 101 | 102 | // shutdown the service and notify all the worker 103 | q.Release() 104 | } 105 | ``` 106 | -------------------------------------------------------------------------------- /_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/nats" 11 | "github.com/golang-queue/queue" 12 | "github.com/golang-queue/queue/core" 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 := nats.NewWorker( 35 | nats.WithAddr("127.0.0.1:4222"), 36 | nats.WithSubj("example"), 37 | nats.WithQueue("foobar"), 38 | nats.WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 39 | var v *job 40 | if err := json.Unmarshal(m.Payload(), &v); err != nil { 41 | return err 42 | } 43 | rets <- v.Message 44 | time.Sleep(2 * time.Second) 45 | return nil 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/nats v0.0.3-0.20210907015837-3e2e4b448b3d 8 | github.com/golang-queue/queue v0.3.0 9 | ) 10 | 11 | require ( 12 | github.com/jpillora/backoff v1.0.0 // indirect 13 | github.com/klauspost/compress v1.17.9 // indirect 14 | github.com/nats-io/nats.go v1.38.0 // indirect 15 | github.com/nats-io/nkeys v0.4.9 // indirect 16 | github.com/nats-io/nuid v1.0.1 // indirect 17 | golang.org/x/crypto v0.31.0 // indirect 18 | golang.org/x/sys v0.28.0 // indirect 19 | ) 20 | 21 | replace github.com/golang-queue/nats => ../../ 22 | -------------------------------------------------------------------------------- /_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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= 12 | github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 13 | github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= 14 | github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= 15 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 16 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 17 | github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= 18 | github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= 19 | github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= 20 | github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= 21 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 22 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 24 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 25 | github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= 26 | github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 27 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 28 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 29 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 30 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 31 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 32 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 33 | github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 34 | github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 35 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 36 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 37 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 38 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 39 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 40 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 41 | github.com/golang-queue/queue v0.3.0 h1:gyBLNT9EDOsChazYScp8iLiwLfG0SdnCDmNUybcHig4= 42 | github.com/golang-queue/queue v0.3.0/go.mod h1:SkjMwz1TjxZOrF7kABvbar1CagcMxwRtXt5Tx00wb4g= 43 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 44 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 45 | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= 46 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 47 | github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= 48 | github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 49 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 50 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 51 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 52 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 53 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 54 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 55 | github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= 56 | github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 57 | github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= 58 | github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= 59 | github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= 60 | github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= 61 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 62 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 63 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 64 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 65 | github.com/nats-io/nats.go v1.38.0 h1:A7P+g7Wjp4/NWqDOOP/K6hfhr54DvdDQUznt5JFg9XA= 66 | github.com/nats-io/nats.go v1.38.0/go.mod h1:IGUM++TwokGnXPs82/wCuiHS02/aKrdYUQkU8If6yjw= 67 | github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= 68 | github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= 69 | github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= 70 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= 82 | github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= 83 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 84 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 85 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 86 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 87 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 88 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 89 | github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= 90 | github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= 91 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 92 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 93 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 94 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 95 | github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= 96 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 97 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 98 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= 99 | go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= 100 | go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= 101 | go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= 102 | go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= 103 | go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= 104 | go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= 105 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 106 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 107 | go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= 108 | go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 109 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 110 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 111 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 112 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 113 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 114 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 115 | -------------------------------------------------------------------------------- /_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/nats" 10 | "github.com/golang-queue/queue" 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 := 10 27 | 28 | // define the worker 29 | w := nats.NewWorker( 30 | nats.WithAddr("127.0.0.1:4222"), 31 | nats.WithSubj("example"), 32 | nats.WithQueue("foobar"), 33 | ) 34 | 35 | // define the queue 36 | q := queue.NewPool( 37 | 0, 38 | queue.WithWorker(w), 39 | ) 40 | 41 | // assign tasks in queue 42 | for i := 0; i < taskN; i++ { 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 | } 49 | 50 | time.Sleep(1 * time.Second) 51 | // shutdown the service and notify all the worker 52 | q.Release() 53 | } 54 | -------------------------------------------------------------------------------- /_example/worker/go.mod: -------------------------------------------------------------------------------- 1 | module example 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/golang-queue/nats v0.0.2-0.20210822122542-200fdcf19ebf 7 | github.com/golang-queue/queue v0.3.0 8 | ) 9 | 10 | require ( 11 | github.com/jpillora/backoff v1.0.0 // indirect 12 | github.com/klauspost/compress v1.17.9 // indirect 13 | github.com/nats-io/nats.go v1.38.0 // indirect 14 | github.com/nats-io/nkeys v0.4.9 // indirect 15 | github.com/nats-io/nuid v1.0.1 // indirect 16 | golang.org/x/crypto v0.31.0 // indirect 17 | golang.org/x/sys v0.28.0 // indirect 18 | ) 19 | 20 | replace github.com/golang-queue/nats => ../../ 21 | -------------------------------------------------------------------------------- /_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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= 10 | github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 11 | github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= 12 | github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= 13 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 14 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 15 | github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= 16 | github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= 17 | github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= 18 | github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= 19 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 20 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 21 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 22 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 23 | github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= 24 | github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 25 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 26 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 27 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 28 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 29 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 30 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 31 | github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 32 | github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 33 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 34 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 35 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 36 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 37 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 38 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 39 | github.com/golang-queue/queue v0.3.0 h1:gyBLNT9EDOsChazYScp8iLiwLfG0SdnCDmNUybcHig4= 40 | github.com/golang-queue/queue v0.3.0/go.mod h1:SkjMwz1TjxZOrF7kABvbar1CagcMxwRtXt5Tx00wb4g= 41 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 42 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 43 | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= 44 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 45 | github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= 46 | github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 47 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 48 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 49 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 50 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 51 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 52 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 53 | github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= 54 | github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 55 | github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= 56 | github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= 57 | github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= 58 | github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= 59 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 60 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 61 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 62 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 63 | github.com/nats-io/nats.go v1.38.0 h1:A7P+g7Wjp4/NWqDOOP/K6hfhr54DvdDQUznt5JFg9XA= 64 | github.com/nats-io/nats.go v1.38.0/go.mod h1:IGUM++TwokGnXPs82/wCuiHS02/aKrdYUQkU8If6yjw= 65 | github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= 66 | github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= 67 | github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= 68 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 69 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 70 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 71 | github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= 72 | github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 73 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 74 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 75 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 76 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 77 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 78 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 79 | github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= 80 | github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= 81 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 82 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 83 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 84 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 85 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 86 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 87 | github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= 88 | github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= 89 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 90 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 91 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 92 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 93 | github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= 94 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 95 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 96 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= 97 | go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= 98 | go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= 99 | go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= 100 | go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= 101 | go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= 102 | go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= 103 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 104 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 105 | go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= 106 | go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 107 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 108 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 109 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 110 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 111 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 112 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 113 | -------------------------------------------------------------------------------- /_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/nats" 11 | "github.com/golang-queue/queue" 12 | "github.com/golang-queue/queue/core" 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 := 100 29 | rets := make(chan string, taskN) 30 | 31 | // define the worker 32 | w := nats.NewWorker( 33 | nats.WithAddr("127.0.0.1:4222"), 34 | nats.WithSubj("example"), 35 | nats.WithQueue("foobar"), 36 | nats.WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 37 | var v *job 38 | if err := json.Unmarshal(m.Payload(), &v); err != nil { 39 | return err 40 | } 41 | rets <- v.Message 42 | return nil 43 | }), 44 | ) 45 | 46 | // define the queue 47 | q, err := queue.NewQueue( 48 | queue.WithWorkerCount(10), 49 | queue.WithWorker(w), 50 | ) 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | 55 | // start the five worker 56 | q.Start() 57 | 58 | // assign tasks in queue 59 | for i := 0; i < taskN; i++ { 60 | go func(i int) { 61 | if err := q.Queue(&job{ 62 | Message: fmt.Sprintf("handle the job: %d", i+1), 63 | }); err != nil { 64 | log.Fatal(err) 65 | } 66 | }(i) 67 | } 68 | 69 | // wait until all tasks done 70 | for i := 0; i < taskN; i++ { 71 | fmt.Println("message:", <-rets) 72 | time.Sleep(50 * time.Millisecond) 73 | } 74 | 75 | // shutdown the service and notify all the worker 76 | q.Release() 77 | } 78 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/golang-queue/nats 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/golang-queue/queue v0.3.0 7 | github.com/nats-io/nats.go v1.39.1 8 | github.com/stretchr/testify v1.10.0 9 | github.com/testcontainers/testcontainers-go v0.35.0 10 | go.uber.org/goleak v1.3.0 11 | ) 12 | 13 | require ( 14 | dario.cat/mergo v1.0.0 // indirect 15 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect 16 | github.com/Microsoft/go-winio v0.6.2 // indirect 17 | github.com/cenkalti/backoff/v4 v4.2.1 // indirect 18 | github.com/containerd/containerd v1.7.18 // indirect 19 | github.com/containerd/log v0.1.0 // indirect 20 | github.com/containerd/platforms v0.2.1 // indirect 21 | github.com/cpuguy83/dockercfg v0.3.2 // indirect 22 | github.com/davecgh/go-spew v1.1.1 // indirect 23 | github.com/distribution/reference v0.6.0 // indirect 24 | github.com/docker/docker v27.1.1+incompatible // indirect 25 | github.com/docker/go-connections v0.5.0 // indirect 26 | github.com/docker/go-units v0.5.0 // indirect 27 | github.com/felixge/httpsnoop v1.0.4 // indirect 28 | github.com/go-logr/logr v1.4.1 // indirect 29 | github.com/go-logr/stdr v1.2.2 // indirect 30 | github.com/go-ole/go-ole v1.2.6 // indirect 31 | github.com/gogo/protobuf v1.3.2 // indirect 32 | github.com/google/uuid v1.6.0 // indirect 33 | github.com/jpillora/backoff v1.0.0 // indirect 34 | github.com/klauspost/compress v1.18.0 // indirect 35 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect 36 | github.com/magiconair/properties v1.8.7 // indirect 37 | github.com/moby/docker-image-spec v1.3.1 // indirect 38 | github.com/moby/patternmatcher v0.6.0 // indirect 39 | github.com/moby/sys/sequential v0.5.0 // indirect 40 | github.com/moby/sys/user v0.1.0 // indirect 41 | github.com/moby/term v0.5.0 // indirect 42 | github.com/morikuni/aec v1.0.0 // indirect 43 | github.com/nats-io/nkeys v0.4.10 // indirect 44 | github.com/nats-io/nuid v1.0.1 // indirect 45 | github.com/opencontainers/go-digest v1.0.0 // indirect 46 | github.com/opencontainers/image-spec v1.1.0 // indirect 47 | github.com/pkg/errors v0.9.1 // indirect 48 | github.com/pmezard/go-difflib v1.0.0 // indirect 49 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect 50 | github.com/shirou/gopsutil/v3 v3.23.12 // indirect 51 | github.com/shoenig/go-m1cpu v0.1.6 // indirect 52 | github.com/sirupsen/logrus v1.9.3 // indirect 53 | github.com/tklauser/go-sysconf v0.3.12 // indirect 54 | github.com/tklauser/numcpus v0.6.1 // indirect 55 | github.com/yusufpapurcu/wmi v1.2.3 // indirect 56 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect 57 | go.opentelemetry.io/otel v1.24.0 // indirect 58 | go.opentelemetry.io/otel/metric v1.24.0 // indirect 59 | go.opentelemetry.io/otel/trace v1.24.0 // indirect 60 | golang.org/x/crypto v0.34.0 // indirect 61 | golang.org/x/sys v0.30.0 // indirect 62 | gopkg.in/yaml.v3 v3.0.1 // indirect 63 | ) 64 | -------------------------------------------------------------------------------- /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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= 12 | github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 13 | github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= 14 | github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= 15 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 16 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 17 | github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= 18 | github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= 19 | github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= 20 | github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= 21 | github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 22 | github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 23 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 24 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 25 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 27 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 28 | github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= 29 | github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 30 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 31 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 32 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 33 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 34 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 35 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 36 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 37 | github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 38 | github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 39 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 40 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 41 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 42 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 43 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 44 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 45 | github.com/golang-queue/queue v0.3.0 h1:gyBLNT9EDOsChazYScp8iLiwLfG0SdnCDmNUybcHig4= 46 | github.com/golang-queue/queue v0.3.0/go.mod h1:SkjMwz1TjxZOrF7kABvbar1CagcMxwRtXt5Tx00wb4g= 47 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 48 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 49 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 50 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= 54 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= 55 | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= 56 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 57 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 58 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 59 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 60 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 61 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 62 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 63 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 64 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 65 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 66 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 67 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 68 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 69 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 70 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 71 | github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= 72 | github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 73 | github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= 74 | github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= 75 | github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= 76 | github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= 77 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 78 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 79 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 80 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 81 | github.com/nats-io/nats.go v1.39.1 h1:oTkfKBmz7W047vRxV762M67ZdXeOtUgvbBaNoQ+3PPk= 82 | github.com/nats-io/nats.go v1.39.1/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM= 83 | github.com/nats-io/nkeys v0.4.10 h1:glmRrpCmYLHByYcePvnTBEAwawwapjCPMjy2huw20wc= 84 | github.com/nats-io/nkeys v0.4.10/go.mod h1:OjRrnIKnWBFl+s4YK5ChQfvHP2fxqZexrKJoVVyWB3U= 85 | github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= 86 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 87 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 88 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 89 | github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= 90 | github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 91 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 92 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 93 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 94 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 95 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 96 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 97 | github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= 98 | github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= 99 | github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= 100 | github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= 101 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 102 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 103 | github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= 104 | github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 105 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 106 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 107 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 108 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 109 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 110 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 111 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 112 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 113 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 114 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 115 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 116 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 117 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 118 | github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= 119 | github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= 120 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 121 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 122 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 123 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 124 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 125 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 126 | github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= 127 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 128 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 129 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= 130 | go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= 131 | go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= 132 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= 133 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= 134 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= 135 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= 136 | go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= 137 | go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= 138 | go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= 139 | go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= 140 | go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= 141 | go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= 142 | go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= 143 | go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= 144 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 145 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 146 | go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= 147 | go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 148 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 149 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 150 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 151 | golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA= 152 | golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= 153 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 154 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 155 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 156 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 157 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 158 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 159 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 160 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 161 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 162 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 163 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 164 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 165 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 166 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 167 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 168 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 169 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 170 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 171 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 172 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 173 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 174 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 175 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 176 | golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= 177 | golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= 178 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 179 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 180 | golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= 181 | golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= 182 | golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= 183 | golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 184 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 185 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 186 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 187 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 188 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 189 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 190 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 191 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 192 | google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0= 193 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI= 194 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 195 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= 196 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= 197 | google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= 198 | google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= 199 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 200 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 201 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 202 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 203 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 204 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 205 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 206 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 207 | gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= 208 | gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= 209 | -------------------------------------------------------------------------------- /nats.go: -------------------------------------------------------------------------------- 1 | package nats 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "sync" 7 | "sync/atomic" //nolint:typecheck,nolintlint 8 | "time" 9 | 10 | "github.com/golang-queue/queue" 11 | "github.com/golang-queue/queue/core" 12 | "github.com/golang-queue/queue/job" 13 | 14 | nats "github.com/nats-io/nats.go" 15 | ) 16 | 17 | var _ core.Worker = (*Worker)(nil) 18 | 19 | // Worker for NSQ 20 | type Worker struct { 21 | client *nats.Conn 22 | stop chan struct{} 23 | exit chan struct{} 24 | stopFlag int32 25 | stopOnce sync.Once 26 | startOnce sync.Once 27 | opts options 28 | subscription *nats.Subscription 29 | tasks chan *nats.Msg 30 | } 31 | 32 | // NewWorker for struc 33 | func NewWorker(opts ...Option) *Worker { 34 | var err error 35 | w := &Worker{ 36 | opts: newOptions(opts...), 37 | stop: make(chan struct{}), 38 | exit: make(chan struct{}), 39 | tasks: make(chan *nats.Msg), 40 | } 41 | 42 | w.client, err = nats.Connect(w.opts.addr) 43 | if err != nil { 44 | w.opts.logger.Fatal("can't connect to nats:", err) 45 | } 46 | 47 | if err := w.startConsumer(); err != nil { 48 | w.opts.logger.Fatal("can't start consumer:", err) 49 | } 50 | 51 | return w 52 | } 53 | 54 | func (w *Worker) startConsumer() (err error) { 55 | w.startOnce.Do(func() { 56 | w.subscription, err = w.client.QueueSubscribe(w.opts.subj, w.opts.queue, func(msg *nats.Msg) { 57 | select { 58 | case w.tasks <- msg: 59 | case <-w.stop: 60 | if msg != nil { 61 | // re-queue the task if worker has been shutdown. 62 | w.opts.logger.Info("re-queue the current task") 63 | if err := w.client.Publish(w.opts.subj, msg.Data); err != nil { 64 | w.opts.logger.Errorf("error to re-queue the current task: %s", err.Error()) 65 | } 66 | } 67 | close(w.exit) 68 | } 69 | }) 70 | if err != nil { 71 | w.opts.logger.Errorf("error subscribing to queue: %s", err.Error()) 72 | close(w.exit) 73 | } 74 | }) 75 | 76 | return err 77 | } 78 | 79 | // Run start the worker 80 | func (w *Worker) Run(ctx context.Context, task core.TaskMessage) error { 81 | return w.opts.runFunc(ctx, task) 82 | } 83 | 84 | // Shutdown worker 85 | func (w *Worker) Shutdown() error { 86 | if !atomic.CompareAndSwapInt32(&w.stopFlag, 0, 1) { 87 | return queue.ErrQueueShutdown 88 | } 89 | 90 | w.stopOnce.Do(func() { 91 | // unsubscribe channel if start the consumer 92 | if w.subscription != nil { 93 | _ = w.subscription.Unsubscribe() 94 | } 95 | 96 | close(w.stop) 97 | select { 98 | case <-w.exit: 99 | case <-time.After(50 * time.Millisecond): 100 | } 101 | w.client.Close() 102 | close(w.tasks) 103 | }) 104 | return nil 105 | } 106 | 107 | // Queue send notification to queue 108 | func (w *Worker) Queue(job core.TaskMessage) error { 109 | if atomic.LoadInt32(&w.stopFlag) == 1 { 110 | return queue.ErrQueueShutdown 111 | } 112 | 113 | err := w.client.Publish(w.opts.subj, job.Bytes()) 114 | if err != nil { 115 | return err 116 | } 117 | 118 | return nil 119 | } 120 | 121 | // Request a new task 122 | func (w *Worker) Request() (core.TaskMessage, error) { 123 | _ = w.startConsumer() 124 | clock := 0 125 | loop: 126 | for { 127 | select { 128 | case task, ok := <-w.tasks: 129 | if !ok { 130 | return nil, queue.ErrQueueHasBeenClosed 131 | } 132 | var data job.Message 133 | _ = json.Unmarshal(task.Data, &data) 134 | if err := task.Ack(); err != nil { 135 | return nil, err 136 | } 137 | return &data, nil 138 | case <-time.After(1 * time.Second): 139 | if clock == 5 { 140 | break loop 141 | } 142 | clock += 1 143 | } 144 | } 145 | 146 | return nil, queue.ErrNoTaskInQueue 147 | } 148 | -------------------------------------------------------------------------------- /nats_test.go: -------------------------------------------------------------------------------- 1 | package nats 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "runtime" 9 | "testing" 10 | "time" 11 | 12 | "github.com/golang-queue/queue" 13 | "github.com/golang-queue/queue/core" 14 | "github.com/golang-queue/queue/job" 15 | 16 | "github.com/nats-io/nats.go" 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 | var host = nats.DefaultURL 25 | 26 | func TestMain(m *testing.M) { 27 | goleak.VerifyTestMain(m) 28 | } 29 | 30 | type mockMessage struct { 31 | Message string 32 | } 33 | 34 | func (m mockMessage) Bytes() []byte { 35 | return []byte(m.Message) 36 | } 37 | 38 | func setupNatsContainer(ctx context.Context, t *testing.T) (testcontainers.Container, string) { 39 | req := testcontainers.ContainerRequest{ 40 | Image: "nats:2.10", 41 | ExposedPorts: []string{ 42 | "4222/tcp", // client port 43 | "6222/tcp", // cluster port 44 | "8222/tcp", // monitoring port 45 | }, 46 | WaitingFor: wait.ForLog("Server is ready"), 47 | } 48 | natsC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ 49 | ContainerRequest: req, 50 | Started: true, 51 | }) 52 | require.NoError(t, err) 53 | 54 | endpoint, err := natsC.Endpoint(ctx, "") 55 | require.NoError(t, err) 56 | 57 | return natsC, endpoint 58 | } 59 | 60 | func TestDefaultFlow(t *testing.T) { 61 | ctx := context.Background() 62 | natsC, endpoint := setupNatsContainer(ctx, t) 63 | defer testcontainers.CleanupContainer(t, natsC) 64 | 65 | m := &mockMessage{ 66 | Message: "foo", 67 | } 68 | w := NewWorker( 69 | WithAddr(endpoint), 70 | WithSubj("test"), 71 | WithQueue("test"), 72 | ) 73 | q, err := queue.NewQueue( 74 | queue.WithWorker(w), 75 | queue.WithWorkerCount(1), 76 | ) 77 | assert.NoError(t, err) 78 | assert.NoError(t, q.Queue(m)) 79 | assert.NoError(t, q.Queue(m)) 80 | q.Start() 81 | time.Sleep(500 * time.Millisecond) 82 | q.Release() 83 | } 84 | 85 | func TestClusteredHost(t *testing.T) { 86 | m := &mockMessage{ 87 | Message: "foo", 88 | } 89 | w := NewWorker( 90 | WithAddr(host, "nats://localhost:4223"), 91 | WithSubj("test"), 92 | WithQueue("cluster"), 93 | ) 94 | q, err := queue.NewQueue( 95 | queue.WithWorker(w), 96 | queue.WithWorkerCount(1), 97 | ) 98 | assert.NoError(t, err) 99 | assert.NoError(t, q.Queue(m)) 100 | assert.NoError(t, q.Queue(m)) 101 | q.Start() 102 | time.Sleep(500 * time.Millisecond) 103 | q.Release() 104 | } 105 | 106 | func TestShutdown(t *testing.T) { 107 | w := NewWorker( 108 | WithAddr(host), 109 | WithSubj("test"), 110 | WithQueue("test"), 111 | ) 112 | q, err := queue.NewQueue( 113 | queue.WithWorker(w), 114 | queue.WithWorkerCount(2), 115 | ) 116 | assert.NoError(t, err) 117 | q.Start() 118 | time.Sleep(1 * time.Second) 119 | q.Shutdown() 120 | // check shutdown once 121 | q.Shutdown() 122 | q.Wait() 123 | } 124 | 125 | func TestCustomFuncAndWait(t *testing.T) { 126 | ctx := context.Background() 127 | natsC, endpoint := setupNatsContainer(ctx, t) 128 | defer testcontainers.CleanupContainer(t, natsC) 129 | m := &mockMessage{ 130 | Message: "foo", 131 | } 132 | w := NewWorker( 133 | WithAddr(endpoint), 134 | WithSubj("test"), 135 | WithQueue("test"), 136 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 137 | log.Println("show message: " + string(m.Payload())) 138 | time.Sleep(500 * time.Millisecond) 139 | return nil 140 | }), 141 | ) 142 | q, err := queue.NewQueue( 143 | queue.WithWorker(w), 144 | queue.WithWorkerCount(2), 145 | ) 146 | assert.NoError(t, err) 147 | q.Start() 148 | time.Sleep(100 * time.Millisecond) 149 | assert.NoError(t, q.Queue(m)) 150 | assert.NoError(t, q.Queue(m)) 151 | assert.NoError(t, q.Queue(m)) 152 | assert.NoError(t, q.Queue(m)) 153 | time.Sleep(600 * time.Millisecond) 154 | q.Shutdown() 155 | q.Wait() 156 | // you will see the execute time > 1000ms 157 | } 158 | 159 | func TestEnqueueJobAfterShutdown(t *testing.T) { 160 | ctx := context.Background() 161 | natsC, endpoint := setupNatsContainer(ctx, t) 162 | defer testcontainers.CleanupContainer(t, natsC) 163 | m := mockMessage{ 164 | Message: "foo", 165 | } 166 | w := NewWorker( 167 | WithAddr(endpoint), 168 | ) 169 | q, err := queue.NewQueue( 170 | queue.WithWorker(w), 171 | queue.WithWorkerCount(2), 172 | ) 173 | assert.NoError(t, err) 174 | q.Start() 175 | time.Sleep(50 * time.Millisecond) 176 | q.Shutdown() 177 | // can't queue task after shutdown 178 | err = q.Queue(m) 179 | assert.Error(t, err) 180 | assert.Equal(t, queue.ErrQueueShutdown, err) 181 | q.Wait() 182 | } 183 | 184 | func TestJobReachTimeout(t *testing.T) { 185 | ctx := context.Background() 186 | natsC, endpoint := setupNatsContainer(ctx, t) 187 | defer testcontainers.CleanupContainer(t, natsC) 188 | m := mockMessage{ 189 | Message: "foo", 190 | } 191 | w := NewWorker( 192 | WithAddr(endpoint), 193 | WithSubj("JobReachTimeout"), 194 | WithQueue("test"), 195 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 196 | for { 197 | select { 198 | case <-ctx.Done(): 199 | log.Println("get data:", string(m.Payload())) 200 | if errors.Is(ctx.Err(), context.Canceled) { 201 | log.Println("queue has been shutdown and cancel the job") 202 | } else if errors.Is(ctx.Err(), context.DeadlineExceeded) { 203 | log.Println("job deadline exceeded") 204 | } 205 | return nil 206 | default: 207 | } 208 | time.Sleep(50 * time.Millisecond) 209 | } 210 | }), 211 | ) 212 | q, err := queue.NewQueue( 213 | queue.WithWorker(w), 214 | queue.WithWorkerCount(2), 215 | ) 216 | assert.NoError(t, err) 217 | q.Start() 218 | time.Sleep(50 * time.Millisecond) 219 | assert.NoError(t, q.Queue(m, job.AllowOption{ 220 | Timeout: job.Time(20 * time.Millisecond), 221 | })) 222 | time.Sleep(100 * time.Millisecond) 223 | q.Shutdown() 224 | q.Wait() 225 | } 226 | 227 | func TestCancelJobAfterShutdown(t *testing.T) { 228 | ctx := context.Background() 229 | natsC, endpoint := setupNatsContainer(ctx, t) 230 | defer testcontainers.CleanupContainer(t, natsC) 231 | m := mockMessage{ 232 | Message: "test", 233 | } 234 | w := NewWorker( 235 | WithAddr(endpoint), 236 | WithSubj("CancelJob"), 237 | WithQueue("test"), 238 | WithLogger(queue.NewLogger()), 239 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 240 | for { 241 | select { 242 | case <-ctx.Done(): 243 | log.Println("get data:", string(m.Payload())) 244 | if errors.Is(ctx.Err(), context.Canceled) { 245 | log.Println("queue has been shutdown and cancel the job") 246 | } else if errors.Is(ctx.Err(), context.DeadlineExceeded) { 247 | log.Println("job deadline exceeded") 248 | } 249 | return nil 250 | default: 251 | } 252 | time.Sleep(50 * time.Millisecond) 253 | } 254 | }), 255 | ) 256 | q, err := queue.NewQueue( 257 | queue.WithWorker(w), 258 | queue.WithWorkerCount(2), 259 | ) 260 | assert.NoError(t, err) 261 | q.Start() 262 | time.Sleep(50 * time.Millisecond) 263 | assert.NoError(t, q.Queue(m, job.AllowOption{ 264 | Timeout: job.Time(150 * time.Millisecond), 265 | })) 266 | time.Sleep(100 * time.Millisecond) 267 | q.Shutdown() 268 | q.Wait() 269 | } 270 | 271 | func TestGoroutineLeak(t *testing.T) { 272 | ctx := context.Background() 273 | natsC, endpoint := setupNatsContainer(ctx, t) 274 | defer testcontainers.CleanupContainer(t, natsC) 275 | m := mockMessage{ 276 | Message: "foo", 277 | } 278 | w := NewWorker( 279 | WithAddr(endpoint), 280 | WithSubj("GoroutineLeak"), 281 | WithQueue("test"), 282 | WithLogger(queue.NewEmptyLogger()), 283 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 284 | for { 285 | select { 286 | case <-ctx.Done(): 287 | log.Println("get data:", string(m.Payload())) 288 | if errors.Is(ctx.Err(), context.Canceled) { 289 | log.Println("queue has been shutdown and cancel the job") 290 | } else if errors.Is(ctx.Err(), context.DeadlineExceeded) { 291 | log.Println("job deadline exceeded") 292 | } 293 | return nil 294 | default: 295 | log.Println("get data:", string(m.Payload())) 296 | time.Sleep(50 * time.Millisecond) 297 | return nil 298 | } 299 | } 300 | }), 301 | ) 302 | q, err := queue.NewQueue( 303 | queue.WithLogger(queue.NewEmptyLogger()), 304 | queue.WithWorker(w), 305 | queue.WithWorkerCount(10), 306 | ) 307 | assert.NoError(t, err) 308 | q.Start() 309 | time.Sleep(50 * time.Millisecond) 310 | for i := 0; i < 500; i++ { 311 | m.Message = fmt.Sprintf("foobar: %d", i+1) 312 | assert.NoError(t, q.Queue(m)) 313 | } 314 | time.Sleep(400 * time.Millisecond) 315 | q.Shutdown() 316 | q.Wait() 317 | fmt.Println("number of goroutines:", runtime.NumGoroutine()) 318 | } 319 | 320 | func TestGoroutinePanic(t *testing.T) { 321 | ctx := context.Background() 322 | natsC, endpoint := setupNatsContainer(ctx, t) 323 | defer testcontainers.CleanupContainer(t, natsC) 324 | m := mockMessage{ 325 | Message: "foo", 326 | } 327 | w := NewWorker( 328 | WithAddr(endpoint), 329 | WithSubj("GoroutinePanic"), 330 | WithRunFunc(func(ctx context.Context, m core.TaskMessage) error { 331 | panic("missing something") 332 | }), 333 | ) 334 | q, err := queue.NewQueue( 335 | queue.WithWorker(w), 336 | queue.WithWorkerCount(2), 337 | ) 338 | assert.NoError(t, err) 339 | q.Start() 340 | time.Sleep(50 * time.Millisecond) 341 | assert.NoError(t, q.Queue(m)) 342 | assert.NoError(t, q.Queue(m)) 343 | time.Sleep(2 * time.Second) 344 | q.Shutdown() 345 | assert.Error(t, q.Queue(m)) 346 | q.Wait() 347 | } 348 | 349 | func TestReQueueTaskInWorkerBeforeShutdown(t *testing.T) { 350 | ctx := context.Background() 351 | natsC, endpoint := setupNatsContainer(ctx, t) 352 | defer testcontainers.CleanupContainer(t, natsC) 353 | job := &job.Message{ 354 | Body: []byte("foo"), 355 | } 356 | w := NewWorker( 357 | WithAddr(endpoint), 358 | WithSubj("test02"), 359 | WithQueue("test02"), 360 | ) 361 | 362 | assert.NoError(t, w.Queue(job)) 363 | time.Sleep(500 * time.Millisecond) 364 | // see "re-queue the current task" message 365 | assert.NoError(t, w.Shutdown()) 366 | } 367 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package nats 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | 7 | "github.com/golang-queue/queue" 8 | "github.com/golang-queue/queue/core" 9 | 10 | "github.com/nats-io/nats.go" 11 | ) 12 | 13 | // Option for queue system 14 | type Option func(*options) 15 | 16 | type options struct { 17 | runFunc func(context.Context, core.TaskMessage) error 18 | logger queue.Logger 19 | addr string 20 | subj string 21 | queue string 22 | } 23 | 24 | // WithAddr setup the addr of NATS 25 | func WithAddr(addrs ...string) Option { 26 | return func(w *options) { 27 | if len(addrs) > 0 { 28 | w.addr = strings.Join(addrs, ",") 29 | } 30 | } 31 | } 32 | 33 | // WithSubj setup the subject of NATS 34 | func WithSubj(subj string) Option { 35 | return func(w *options) { 36 | w.subj = subj 37 | } 38 | } 39 | 40 | // WithQueue setup the queue of NATS 41 | func WithQueue(queue string) Option { 42 | return func(w *options) { 43 | w.queue = queue 44 | } 45 | } 46 | 47 | // WithRunFunc setup the run func of queue 48 | func WithRunFunc(fn func(context.Context, core.TaskMessage) error) Option { 49 | return func(w *options) { 50 | w.runFunc = fn 51 | } 52 | } 53 | 54 | // WithLogger set custom logger 55 | func WithLogger(l queue.Logger) Option { 56 | return func(w *options) { 57 | w.logger = l 58 | } 59 | } 60 | 61 | func newOptions(opts ...Option) options { 62 | defaultOpts := options{ 63 | addr: nats.DefaultURL, 64 | subj: "foobar", 65 | queue: "foobar", 66 | logger: queue.NewLogger(), 67 | runFunc: func(context.Context, core.TaskMessage) error { 68 | return nil 69 | }, 70 | } 71 | 72 | // Loop through each option 73 | for _, opt := range opts { 74 | // Call the option giving the instantiated 75 | opt(&defaultOpts) 76 | } 77 | 78 | return defaultOpts 79 | } 80 | --------------------------------------------------------------------------------