Compare commits

...

16 Commits

Author SHA1 Message Date
aed304b7e3 rework message options
Some checks failed
sync / sync (push) Successful in 1m2s
coverage / build (push) Successful in 3m31s
test / test (push) Failing after 18m28s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-06-30 16:08:59 +03:00
b796c43257 [v4] hide access to internal mutex (#185)
Some checks failed
coverage / build (push) Failing after 2m4s
test / test (push) Successful in 3m53s
sync / sync (push) Successful in 8s
* changed embedded mutex to private field

* update ci

* fix tests
2025-06-10 16:28:17 +03:00
b0691edf82 disable message pool by default
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-06-10 16:28:16 +03:00
d43b59f2f3 disable message pool by default
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-06-10 16:28:03 +03:00
f976516d6b new kgo version
update deps

fixup race conditions

add kfake usage

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-06-10 16:27:40 +03:00
8f5c03ad60 [v4] hide access to internal mutex (#185)
* changed embedded mutex to private field

* update ci

* fix tests
2025-06-10 16:26:52 +03:00
vtolstov
9e9c1434ff Apply Code Coverage Badge 2025-06-09 14:30:14 +00:00
fe81a14413 update for latest micro
Some checks failed
coverage / build (push) Successful in 3m22s
test / test (push) Failing after 19m17s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-06-09 17:27:45 +03:00
eb56a48dda update kgo
Some checks failed
coverage / build (push) Has been cancelled
test / test (push) Has been cancelled
sync / sync (push) Failing after 28s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-25 01:25:56 +03:00
71e8a13c70 new kgo version
Some checks failed
sync / sync (push) Has been cancelled
coverage / build (push) Failing after 1m31s
test / test (push) Failing after 18m29s
update deps

fixup race conditions

add kfake usage

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-25 01:24:50 +03:00
b538ef82b5 [v4] hide access to internal mutex (#185)
* changed embedded mutex to private field

* update ci

* fix tests
2025-05-25 01:23:46 +03:00
44e2d5f9a4 update ci (#188) 2025-05-25 01:23:11 +03:00
vtolstov
9426208e1c Apply Code Coverage Badge 2025-05-21 08:14:38 +00:00
d6e73a3419 minimize checks on publish
Some checks failed
coverage / build (push) Successful in 4m51s
sync / sync (push) Has been skipped
test / test (push) Failing after 15m31s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-21 09:54:16 +03:00
969e459e3d add tracer enabled status
Some checks failed
test / test (push) Failing after 17m28s
coverage / build (push) Failing after 17m37s
sync / sync (push) Has been cancelled
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-19 09:34:24 +03:00
vtolstov
b2cd7d8b8e Apply Code Coverage Badge 2025-05-18 16:02:55 +00:00
12 changed files with 185 additions and 140 deletions

View File

@@ -8,12 +8,11 @@ on:
- '.gitea/**' - '.gitea/**'
pull_request: pull_request:
branches: [ main, v3, v4 ] branches: [ main, v3, v4 ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs: jobs:
build: build:
if: github.server_url != 'https://github.com'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout code - name: checkout code
@@ -42,8 +41,8 @@ jobs:
name: autocommit name: autocommit
with: with:
commit_message: Apply Code Coverage Badge commit_message: Apply Code Coverage Badge
skip_fetch: true skip_fetch: false
skip_checkout: true skip_checkout: false
file_pattern: ./README.md file_pattern: ./README.md
- name: push - name: push

View File

@@ -24,6 +24,6 @@ jobs:
- name: setup deps - name: setup deps
run: go get -v ./... run: go get -v ./...
- name: run lint - name: run lint
uses: https://github.com/golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v6
with: with:
version: 'latest' version: 'latest'

View File

@@ -8,7 +8,7 @@ on:
jobs: jobs:
sync: sync:
if: github.server_url == 'zhttps://github.com' if: github.server_url != 'https://github.com'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: init - name: init
@@ -18,34 +18,76 @@ jobs:
echo "machine git.unistack.org login vtolstov password ${{ secrets.TOKEN_GITEA }}" >> /root/.netrc echo "machine git.unistack.org login vtolstov password ${{ secrets.TOKEN_GITEA }}" >> /root/.netrc
echo "machine github.com login vtolstov password ${{ secrets.TOKEN_GITHUB }}" >> /root/.netrc echo "machine github.com login vtolstov password ${{ secrets.TOKEN_GITHUB }}" >> /root/.netrc
- name: check master
id: check_master
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync master - name: sync master
if: steps.check_master.outputs.sync_needed == 'true'
run: | run: |
git clone --filter=blob:none --filter=tree:0 --branch master --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo git clone --filter=blob:none --filter=tree:0 --branch master --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo cd repo
git remote add --no-tags --fetch --track master upstream https://github.com/${GITHUB_REPOSITORY} git remote add --no-tags --fetch --track master upstream https://github.com/${GITHUB_REPOSITORY}
git merge upstream/master git pull --rebase upstream master
git push upstream master --progress git push upstream master --progress
git push origin master --progress git push origin master --progress
cd ../ cd ../
rm -rf repo rm -rf repo
- name: check v3
id: check_v3
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync v3 - name: sync v3
if: steps.check_v3.outputs.sync_needed == 'true'
run: | run: |
git clone --filter=blob:none --filter=tree:0 --branch v3 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo git clone --filter=blob:none --filter=tree:0 --branch v3 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo cd repo
git remote add --no-tags --fetch --track v3 upstream https://github.com/${GITHUB_REPOSITORY} git remote add --no-tags --fetch --track v3 upstream https://github.com/${GITHUB_REPOSITORY}
git merge upstream/v3 git pull --rebase upstream v3
git push upstream v3 --progress git push upstream v3 --progress
git push origin v3 --progress git push origin v3 --progress
cd ../ cd ../
rm -rf repo rm -rf repo
- name: check v4
id: check_v4
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync v4 - name: sync v4
if: steps.check_v4.outputs.sync_needed == 'true'
run: | run: |
git clone --filter=blob:none --filter=tree:0 --branch v4 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo git clone --filter=blob:none --filter=tree:0 --branch v4 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo cd repo
git remote add --no-tags --fetch --track v4 upstream https://github.com/${GITHUB_REPOSITORY} git remote add --no-tags --fetch --track v4 upstream https://github.com/${GITHUB_REPOSITORY}
git merge upstream/v4 git pull --rebase upstream v4
git push upstream v4 --progress git push upstream v4 --progress
git push origin v4 --progress git push origin v4 --progress
cd ../ cd ../

View File

@@ -32,19 +32,19 @@ jobs:
go-version: 'stable' go-version: 'stable'
- name: setup go work - name: setup go work
env: env:
GOWORK: /workspace/${{ github.repository_owner }}/go.work GOWORK: ${{ github.workspace }}/go.work
run: | run: |
go work init go work init
go work use . go work use .
go work use micro-tests go work use micro-tests
- name: setup deps - name: setup deps
env: env:
GOWORK: /workspace/${{ github.repository_owner }}/go.work GOWORK: ${{ github.workspace }}/go.work
run: go get -v ./... run: go get -v ./...
- name: run tests - name: run tests
env: env:
INTEGRATION_TESTS: yes INTEGRATION_TESTS: yes
GOWORK: /workspace/${{ github.repository_owner }}/go.work GOWORK: ${{ github.workspace }}/go.work
run: | run: |
cd micro-tests cd micro-tests
go test -mod readonly -v ./... || true go test -mod readonly -v ./... || true

View File

@@ -1,2 +1,2 @@
# micro-broker-kgo # micro-broker-kgo
![Coverage](https://img.shields.io/badge/Coverage-66.1%25-yellow) ![Coverage](https://img.shields.io/badge/Coverage-63.0%25-yellow)

11
go.mod
View File

@@ -3,13 +3,12 @@ module go.unistack.org/micro-broker-kgo/v4
go 1.23.8 go 1.23.8
require ( require (
github.com/twmb/franz-go v1.19.1 github.com/twmb/franz-go v1.19.5
github.com/twmb/franz-go/pkg/kadm v1.16.0 github.com/twmb/franz-go/pkg/kadm v1.16.0
github.com/twmb/franz-go/pkg/kfake v0.0.0-20250508175730-72e1646135e3 github.com/twmb/franz-go/pkg/kfake v0.0.0-20250508175730-72e1646135e3
github.com/twmb/franz-go/pkg/kmsg v1.11.2 github.com/twmb/franz-go/pkg/kmsg v1.11.2
go.opentelemetry.io/otel v1.35.0 go.opentelemetry.io/otel v1.36.0
go.unistack.org/micro-codec-json/v4 v4.1.0 go.unistack.org/micro/v4 v4.1.17
go.unistack.org/micro/v4 v4.1.13
) )
require ( require (
@@ -17,8 +16,8 @@ require (
github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/compress v1.18.0 // indirect
github.com/matoous/go-nanoid v1.5.1 // indirect github.com/matoous/go-nanoid v1.5.1 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/spf13/cast v1.8.0 // indirect github.com/spf13/cast v1.9.2 // indirect
go.unistack.org/micro-proto/v4 v4.1.0 // indirect go.unistack.org/micro-proto/v4 v4.1.0 // indirect
golang.org/x/crypto v0.38.0 // indirect golang.org/x/crypto v0.39.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
) )

24
go.sum
View File

@@ -20,30 +20,26 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twmb/franz-go v1.19.0 h1:FzBAPUeaip68X9cbLDesgQesa5zxKVaZMk+du98vj3c= github.com/twmb/franz-go v1.19.5 h1:W7+o8D0RsQsedqib71OVlLeZ0zI6CbFra7yTYhZTs5Y=
github.com/twmb/franz-go v1.19.0/go.mod h1:4kFJ5tmbbl7asgwAGVuyG1ZMx0NNpYk7EqflvWfPCpM= github.com/twmb/franz-go v1.19.5/go.mod h1:4kFJ5tmbbl7asgwAGVuyG1ZMx0NNpYk7EqflvWfPCpM=
github.com/twmb/franz-go v1.19.1 h1:cOhDFUkGvUFHSQ7UYW6bO77BJa2fYEk5mA2AX+1NIdE=
github.com/twmb/franz-go v1.19.1/go.mod h1:4kFJ5tmbbl7asgwAGVuyG1ZMx0NNpYk7EqflvWfPCpM=
github.com/twmb/franz-go/pkg/kadm v1.16.0 h1:STMs1t5lYR5mR974PSiwNzE5TvsosByTp+rKXLOhAjE= github.com/twmb/franz-go/pkg/kadm v1.16.0 h1:STMs1t5lYR5mR974PSiwNzE5TvsosByTp+rKXLOhAjE=
github.com/twmb/franz-go/pkg/kadm v1.16.0/go.mod h1:MUdcUtnf9ph4SFBLLA/XxE29rvLhWYLM9Ygb8dfSCvw= github.com/twmb/franz-go/pkg/kadm v1.16.0/go.mod h1:MUdcUtnf9ph4SFBLLA/XxE29rvLhWYLM9Ygb8dfSCvw=
github.com/twmb/franz-go/pkg/kfake v0.0.0-20250508175730-72e1646135e3 h1:p24opKWPySAy8xSl8NqRgOv7Q+bX7kdrQirBVRJzQfo= github.com/twmb/franz-go/pkg/kfake v0.0.0-20250508175730-72e1646135e3 h1:p24opKWPySAy8xSl8NqRgOv7Q+bX7kdrQirBVRJzQfo=
github.com/twmb/franz-go/pkg/kfake v0.0.0-20250508175730-72e1646135e3/go.mod h1:7uQs3Ae6HkWT1Y9elMbqtAcNFCI0y6+iS+Phw49L49U= github.com/twmb/franz-go/pkg/kfake v0.0.0-20250508175730-72e1646135e3/go.mod h1:7uQs3Ae6HkWT1Y9elMbqtAcNFCI0y6+iS+Phw49L49U=
github.com/twmb/franz-go/pkg/kmsg v1.11.2 h1:hIw75FpwcAjgeyfIGFqivAvwC5uNIOWRGvQgZhH4mhg= github.com/twmb/franz-go/pkg/kmsg v1.11.2 h1:hIw75FpwcAjgeyfIGFqivAvwC5uNIOWRGvQgZhH4mhg=
github.com/twmb/franz-go/pkg/kmsg v1.11.2/go.mod h1:CFfkkLysDNmukPYhGzuUcDtf46gQSqCZHMW1T4Z+wDE= github.com/twmb/franz-go/pkg/kmsg v1.11.2/go.mod h1:CFfkkLysDNmukPYhGzuUcDtf46gQSqCZHMW1T4Z+wDE=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.unistack.org/micro-codec-json/v4 v4.1.0 h1:iydeSkt3ee7IPU0dHHKlGN97lw+YFQasBk9rdv0woYA=
go.unistack.org/micro-codec-json/v4 v4.1.0/go.mod h1:aUg86elSlURSynTAetDAAXj/VzFDwwcg92QNrRzcvrM=
go.unistack.org/micro-proto/v4 v4.1.0 h1:qPwL2n/oqh9RE3RTTDgt28XK3QzV597VugQPaw9lKUk= go.unistack.org/micro-proto/v4 v4.1.0 h1:qPwL2n/oqh9RE3RTTDgt28XK3QzV597VugQPaw9lKUk=
go.unistack.org/micro-proto/v4 v4.1.0/go.mod h1:ArmK7o+uFvxSY3dbJhKBBX4Pm1rhWdLEFf3LxBrMtec= go.unistack.org/micro-proto/v4 v4.1.0/go.mod h1:ArmK7o+uFvxSY3dbJhKBBX4Pm1rhWdLEFf3LxBrMtec=
go.unistack.org/micro/v4 v4.1.13 h1:1IEQwiIwHdypZN4dWmsWN83Plq9bdkQ4U8aYZsT17s8= go.unistack.org/micro/v4 v4.1.17 h1:26QDtRSYVpozYuassyvLP4sEQRo3dxgD3sVILRXmIPo=
go.unistack.org/micro/v4 v4.1.13/go.mod h1:xleO2M5Yxh4s6I+RUcLrEpUjobefh+71ctrdIfn7TUs= go.unistack.org/micro/v4 v4.1.17/go.mod h1:xleO2M5Yxh4s6I+RUcLrEpUjobefh+71ctrdIfn7TUs=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

84
kgo.go
View File

@@ -74,7 +74,7 @@ type Broker struct {
opts broker.Options opts broker.Options
sync.RWMutex mu sync.RWMutex
init bool init bool
} }
@@ -108,7 +108,7 @@ type kgoMessage struct {
ctx context.Context ctx context.Context
body []byte body []byte
hdr metadata.Metadata hdr metadata.Metadata
opts broker.PublishOptions opts broker.MessageOptions
ack bool ack bool
} }
@@ -141,17 +141,17 @@ func (b *Broker) newCodec(ct string) (codec.Codec, error) {
if idx := strings.IndexRune(ct, ';'); idx >= 0 { if idx := strings.IndexRune(ct, ';'); idx >= 0 {
ct = ct[:idx] ct = ct[:idx]
} }
b.RLock() b.mu.RLock()
c, ok := b.opts.Codecs[ct] c, ok := b.opts.Codecs[ct]
b.RUnlock() b.mu.RUnlock()
if ok { if ok {
return c, nil return c, nil
} }
return nil, codec.ErrUnknownContentType return nil, codec.ErrUnknownContentType
} }
func (b *Broker) NewMessage(ctx context.Context, hdr metadata.Metadata, body interface{}, opts ...broker.PublishOption) (broker.Message, error) { func (b *Broker) NewMessage(ctx context.Context, hdr metadata.Metadata, body interface{}, opts ...broker.MessageOption) (broker.Message, error) {
options := broker.NewPublishOptions(opts...) options := broker.NewMessageOptions(opts...)
if options.ContentType == "" { if options.ContentType == "" {
options.ContentType = b.opts.ContentType options.ContentType = b.opts.ContentType
} }
@@ -238,10 +238,10 @@ func (k *Broker) Connect(ctx context.Context) error {
return err return err
} }
k.Lock() k.mu.Lock()
k.c = c k.c = c
k.connected.Store(1) k.connected.Store(1)
k.Unlock() k.mu.Unlock()
return nil return nil
} }
@@ -259,8 +259,8 @@ func (k *Broker) Disconnect(ctx context.Context) error {
ctx, span = k.opts.Tracer.Start(ctx, "Disconnect") ctx, span = k.opts.Tracer.Start(ctx, "Disconnect")
defer span.Finish() defer span.Finish()
k.Lock() k.mu.Lock()
defer k.Unlock() defer k.mu.Unlock()
select { select {
case <-nctx.Done(): case <-nctx.Done():
return nctx.Err() return nctx.Err()
@@ -284,8 +284,8 @@ func (k *Broker) Disconnect(ctx context.Context) error {
} }
func (k *Broker) Init(opts ...broker.Option) error { func (k *Broker) Init(opts ...broker.Option) error {
k.Lock() k.mu.Lock()
defer k.Unlock() defer k.mu.Unlock()
if len(opts) == 0 && k.init { if len(opts) == 0 && k.init {
return nil return nil
@@ -344,51 +344,31 @@ func (b *Broker) fnPublish(ctx context.Context, topic string, messages ...broker
} }
func (b *Broker) publish(ctx context.Context, topic string, messages ...broker.Message) error { func (b *Broker) publish(ctx context.Context, topic string, messages ...broker.Message) error {
if b.connected.Load() == 0 { var records []*kgo.Record
c, _, err := b.connect(ctx, b.kopts...)
if err != nil {
return err
}
b.Lock()
b.c = c
b.Unlock()
}
records := make([]*kgo.Record, 0, len(messages))
var errs []string
var key []byte
var promise func(*kgo.Record, error)
for _, msg := range messages { for _, msg := range messages {
if mctx := msg.Context(); mctx != nil {
if k, ok := mctx.Value(publishKey{}).([]byte); ok && k != nil {
key = k
}
if p, ok := mctx.Value(publishPromiseKey{}).(func(*kgo.Record, error)); ok && p != nil {
promise = p
}
}
rec := &kgo.Record{ rec := &kgo.Record{
Context: ctx, Context: msg.Context(),
Key: key,
Topic: topic, Topic: topic,
Value: msg.Body(), Value: msg.Body(),
} }
b.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", rec.Topic, "topic", rec.Topic).Inc() var promise func(*kgo.Record, error)
if rec.Context != nil {
if k, ok := rec.Context.Value(messageKey{}).([]byte); ok && k != nil {
rec.Key = k
}
if p, ok := rec.Context.Value(messagePromiseKey{}).(func(*kgo.Record, error)); ok && p != nil {
promise = p
}
}
setHeaders(rec, msg.Header()) setHeaders(rec, msg.Header())
records = append(records, rec)
}
ts := time.Now()
if promise != nil { if promise != nil {
ts := time.Now()
for _, rec := range records { b.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", rec.Topic, "topic", rec.Topic).Inc()
b.c.Produce(ctx, rec, func(r *kgo.Record, err error) { b.c.Produce(ctx, rec, func(r *kgo.Record, err error) {
te := time.Since(ts) te := time.Since(ts)
b.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", rec.Topic, "topic", rec.Topic).Dec() b.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", rec.Topic, "topic", rec.Topic).Dec()
@@ -401,12 +381,17 @@ func (b *Broker) publish(ctx context.Context, topic string, messages ...broker.M
} }
promise(r, err) promise(r, err)
}) })
continue
} else {
records = append(records, rec)
} }
return nil
} }
if len(records) > 0 {
var errs []string
ts := time.Now()
b.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", topic, "topic", topic).Set(uint64(len(records)))
results := b.c.ProduceSync(ctx, records...) results := b.c.ProduceSync(ctx, records...)
te := time.Since(ts) te := time.Since(ts)
for _, result := range results { for _, result := range results {
b.opts.Meter.Summary(semconv.PublishMessageLatencyMicroseconds, "endpoint", result.Record.Topic, "topic", result.Record.Topic).Update(te.Seconds()) b.opts.Meter.Summary(semconv.PublishMessageLatencyMicroseconds, "endpoint", result.Record.Topic, "topic", result.Record.Topic).Update(te.Seconds())
@@ -423,6 +408,7 @@ func (b *Broker) publish(ctx context.Context, topic string, messages ...broker.M
if len(errs) > 0 { if len(errs) > 0 {
return fmt.Errorf("publish error: %s", strings.Join(errs, "\n")) return fmt.Errorf("publish error: %s", strings.Join(errs, "\n"))
} }
}
return nil return nil
} }
@@ -549,9 +535,9 @@ func (b *Broker) fnSubscribe(ctx context.Context, topic string, handler interfac
go sub.poll(ctx) go sub.poll(ctx)
b.Lock() b.mu.Lock()
b.subs = append(b.subs, sub) b.subs = append(b.subs, sub)
b.Unlock() b.mu.Unlock()
return sub, nil return sub, nil
} }

View File

@@ -81,7 +81,7 @@ func TestFail(t *testing.T) {
go func() { go func() {
for _, msg := range msgs { for _, msg := range msgs {
// t.Logf("broker publish") // t.Logf("broker publish")
if err := b.Publish(ctx, "test", msg); err != nil { if err := b.Publish(ctx, "test.fail", msg); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@@ -96,7 +96,7 @@ func TestFail(t *testing.T) {
return msg.Ack() return msg.Ack()
} }
sub, err := b.Subscribe(ctx, "test", fn, sub, err := b.Subscribe(ctx, "test.fail", fn,
broker.SubscribeAutoAck(true), broker.SubscribeAutoAck(true),
broker.SubscribeGroup(group), broker.SubscribeGroup(group),
broker.SubscribeBodyOnly(true)) broker.SubscribeBodyOnly(true))
@@ -184,7 +184,7 @@ func TestPubSub(t *testing.T) {
msgs = append(msgs, m) msgs = append(msgs, m)
} }
if err := b.Publish(ctx, "test", msgs...); err != nil { if err := b.Publish(ctx, "test.pubsub", msgs...); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// t.Skip() // t.Skip()
@@ -197,7 +197,7 @@ func TestPubSub(t *testing.T) {
return msg.Ack() return msg.Ack()
} }
sub, err := b.Subscribe(ctx, "test", fn, sub, err := b.Subscribe(ctx, "test.pubsub", fn,
broker.SubscribeAutoAck(true), broker.SubscribeAutoAck(true),
broker.SubscribeGroup(group), broker.SubscribeGroup(group),
broker.SubscribeBodyOnly(true)) broker.SubscribeBodyOnly(true))
@@ -222,7 +222,7 @@ func TestPubSub(t *testing.T) {
if prc := atomic.LoadInt64(&idx); prc == msgcnt { if prc := atomic.LoadInt64(&idx); prc == msgcnt {
close(done) close(done)
} else { } else {
t.Logf("processed %v\n", prc) t.Logf("processed %v of %v\n", prc, msgcnt)
} }
case <-ticker.C: case <-ticker.C:
close(done) close(done)

View File

@@ -27,11 +27,11 @@ func SubscribeContext(ctx context.Context) broker.SubscribeOption {
return broker.SetSubscribeOption(subscribeContextKey{}, ctx) return broker.SetSubscribeOption(subscribeContextKey{}, ctx)
} }
type publishKey struct{} type messageKey struct{}
// PublishKey set the kafka message key (broker option) // MessageKey set the kafka message key (broker option)
func PublishKey(key []byte) broker.PublishOption { func MessageKey(key []byte) broker.MessageOption {
return broker.SetPublishOption(publishKey{}, key) return broker.SetMessageOption(messageKey{}, key)
} }
type optionsKey struct{} type optionsKey struct{}
@@ -103,11 +103,11 @@ func SubscribeFatalOnError(b bool) broker.SubscribeOption {
return broker.SetSubscribeOption(fatalOnErrorKey{}, b) return broker.SetSubscribeOption(fatalOnErrorKey{}, b)
} }
type publishPromiseKey struct{} type messagePromiseKey struct{}
// PublishPromise set the kafka promise func for Produce // MessagePromise set the kafka promise func for Produce
func PublishPromise(fn func(*kgo.Record, error)) broker.PublishOption { func MessagePromise(fn func(*kgo.Record, error)) broker.MessageOption {
return broker.SetPublishOption(publishPromiseKey{}, fn) return broker.SetMessageOption(messagePromiseKey{}, fn)
} }
type subscribeMessagePoolKey struct{} type subscribeMessagePoolKey struct{}

View File

@@ -48,9 +48,8 @@ type Subscriber struct {
done chan struct{} done chan struct{}
kopts broker.Options kopts broker.Options
opts broker.SubscribeOptions opts broker.SubscribeOptions
connected *atomic.Uint32 connected *atomic.Uint32
sync.RWMutex mu sync.RWMutex
closed bool closed bool
fatalOnError bool fatalOnError bool
} }
@@ -118,11 +117,11 @@ func (s *Subscriber) poll(ctx context.Context) {
continue continue
} }
s.Lock() s.mu.Lock()
for p, l := range lmap { for p, l := range lmap {
s.kopts.Meter.Counter(semconv.BrokerGroupLag, "topic", s.topic, "group", s.opts.Group, "partition", strconv.Itoa(int(p))).Set(uint64(l.Lag)) s.kopts.Meter.Counter(semconv.BrokerGroupLag, "topic", s.topic, "group", s.opts.Group, "partition", strconv.Itoa(int(p))).Set(uint64(l.Lag))
} }
s.Unlock() s.mu.Unlock()
} }
} }
@@ -147,9 +146,9 @@ func (s *Subscriber) poll(ctx context.Context) {
fetches.EachPartition(func(p kgo.FetchTopicPartition) { fetches.EachPartition(func(p kgo.FetchTopicPartition) {
tps := tp{p.Topic, p.Partition} tps := tp{p.Topic, p.Partition}
s.Lock() s.mu.Lock()
c := s.consumers[tps] c := s.consumers[tps]
s.Unlock() s.mu.Unlock()
if c != nil { if c != nil {
c.recs <- p c.recs <- p
} }
@@ -166,15 +165,15 @@ func (s *Subscriber) killConsumers(ctx context.Context, lost map[string][]int32)
for topic, partitions := range lost { for topic, partitions := range lost {
for _, partition := range partitions { for _, partition := range partitions {
tps := tp{topic, partition} tps := tp{topic, partition}
s.Lock() s.mu.Lock()
pc, ok := s.consumers[tps] pc, ok := s.consumers[tps]
s.Unlock() s.mu.Unlock()
if !ok { if !ok {
continue continue
} }
s.Lock() s.mu.Lock()
delete(s.consumers, tps) delete(s.consumers, tps)
s.Unlock() s.mu.Unlock()
close(pc.quit) close(pc.quit)
if s.kopts.Logger.V(logger.DebugLevel) { if s.kopts.Logger.V(logger.DebugLevel) {
s.kopts.Logger.Debug(ctx, fmt.Sprintf("[kgo] waiting for work to finish topic %s partition %d", topic, partition)) s.kopts.Logger.Debug(ctx, fmt.Sprintf("[kgo] waiting for work to finish topic %s partition %d", topic, partition))
@@ -230,9 +229,9 @@ func (s *Subscriber) assigned(_ context.Context, c *kgo.Client, assigned map[str
opts: s.opts, opts: s.opts,
connected: s.connected, connected: s.connected,
} }
s.Lock() s.mu.Lock()
s.consumers[tp{topic, partition}] = pc s.consumers[tp{topic, partition}] = pc
s.Unlock() s.mu.Unlock()
go pc.consume() go pc.consume()
} }
} }
@@ -282,7 +281,9 @@ func (pc *consumer) consume() {
pc.kopts.Meter.Counter(semconv.SubscribeMessageInflight, "endpoint", record.Topic, "topic", record.Topic).Dec() pc.kopts.Meter.Counter(semconv.SubscribeMessageInflight, "endpoint", record.Topic, "topic", record.Topic).Dec()
if err != nil { if err != nil {
if sp != nil {
sp.SetStatus(tracer.SpanStatusError, err.Error()) sp.SetStatus(tracer.SpanStatusError, err.Error())
}
pc.kopts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", record.Topic, "topic", record.Topic, "status", "failure").Inc() pc.kopts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", record.Topic, "topic", record.Topic, "status", "failure").Inc()
} else if pc.opts.AutoAck { } else if pc.opts.AutoAck {
pm.ack = true pm.ack = true
@@ -299,14 +300,17 @@ func (pc *consumer) consume() {
if ack { if ack {
pc.c.MarkCommitRecords(record) pc.c.MarkCommitRecords(record)
} else { } else {
if sp != nil {
sp.Finish() sp.Finish()
}
// pc.connected.Store(0) // pc.connected.Store(0)
pc.kopts.Logger.Fatal(pc.kopts.Context, "[kgo] message not commited") pc.kopts.Logger.Fatal(pc.kopts.Context, "[kgo] message not commited")
return return
} }
if sp != nil {
sp.Finish() sp.Finish()
} }
} }
} }
} }
}

View File

@@ -32,6 +32,9 @@ var (
// the record's context, so it can be ended in the OnProduceRecordUnbuffered // the record's context, so it can be ended in the OnProduceRecordUnbuffered
// hook. // hook.
func (m *hookTracer) OnProduceRecordBuffered(r *kgo.Record) { func (m *hookTracer) OnProduceRecordBuffered(r *kgo.Record) {
if !m.tracer.Enabled() {
return
}
// Set up span options. // Set up span options.
attrs := []interface{}{ attrs := []interface{}{
messagingSystem, messagingSystem,
@@ -77,6 +80,9 @@ func (m *hookTracer) OnProduceRecordBuffered(r *kgo.Record) {
// It sets attributes with values unset when producing and records any error // It sets attributes with values unset when producing and records any error
// that occurred during the publish operation. // that occurred during the publish operation.
func (m *hookTracer) OnProduceRecordUnbuffered(r *kgo.Record, err error) { func (m *hookTracer) OnProduceRecordUnbuffered(r *kgo.Record, err error) {
if !m.tracer.Enabled() {
return
}
if span, ok := tracer.SpanFromContext(r.Context); ok { if span, ok := tracer.SpanFromContext(r.Context); ok {
span.AddLabels( span.AddLabels(
semconv.MessagingKafkaDestinationPartition(int(r.Partition)), semconv.MessagingKafkaDestinationPartition(int(r.Partition)),
@@ -96,6 +102,9 @@ func (m *hookTracer) OnProduceRecordUnbuffered(r *kgo.Record, err error) {
// OnFetchRecordUnbuffered hook and can be used in downstream consumer // OnFetchRecordUnbuffered hook and can be used in downstream consumer
// processing. // processing.
func (m *hookTracer) OnFetchRecordBuffered(r *kgo.Record) { func (m *hookTracer) OnFetchRecordBuffered(r *kgo.Record) {
if !m.tracer.Enabled() {
return
}
// Set up the span options. // Set up the span options.
attrs := []interface{}{ attrs := []interface{}{
messagingSystem, messagingSystem,
@@ -141,6 +150,9 @@ func (m *hookTracer) OnFetchRecordBuffered(r *kgo.Record) {
// OnFetchRecordUnbuffered continues and ends the "receive" span for an // OnFetchRecordUnbuffered continues and ends the "receive" span for an
// unbuffered record. // unbuffered record.
func (m *hookTracer) OnFetchRecordUnbuffered(r *kgo.Record, _ bool) { func (m *hookTracer) OnFetchRecordUnbuffered(r *kgo.Record, _ bool) {
if !m.tracer.Enabled() {
return
}
span, _ := tracer.SpanFromContext(r.Context) span, _ := tracer.SpanFromContext(r.Context)
span.Finish() span.Finish()
} }
@@ -155,6 +167,13 @@ func (m *hookTracer) OnFetchRecordUnbuffered(r *kgo.Record, _ bool) {
// not a record which has been created for producing, so call this at the start of each // not a record which has been created for producing, so call this at the start of each
// iteration of your processing for the record. // iteration of your processing for the record.
func (m *hookTracer) WithProcessSpan(r *kgo.Record) (context.Context, tracer.Span) { func (m *hookTracer) WithProcessSpan(r *kgo.Record) (context.Context, tracer.Span) {
if r.Context == nil {
r.Context = context.Background()
}
if !m.tracer.Enabled() {
return r.Context, nil
}
// Set up the span options. // Set up the span options.
attrs := []interface{}{ attrs := []interface{}{
messagingSystem, messagingSystem,