Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/atomicgo.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
on: push

name: AtomicGo

on:
push:
branches: [ main ]

jobs:
docs:
if: "!contains(github.event.head_commit.message, 'autoupdate')"
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
Expand Down
20 changes: 0 additions & 20 deletions .github/workflows/golangci.yml

This file was deleted.

17 changes: 17 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Code analysis

on: [pull_request]

jobs:
golangci-lint:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Linting with golangci-lint
uses: reviewdog/action-golangci-lint@v2
with:
github_token: ${{ secrets.ACCESS_TOKEN }}
reporter: github-pr-review
2 changes: 1 addition & 1 deletion .github/workflows/tweet-release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: tweet-release
name: Tweet release

# Listen to the `release` event
on:
Expand Down
51 changes: 39 additions & 12 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,81 @@ linters-settings:
- ptrToRefParam
- paramTypeCombine
- unnamedResult
misspell:
locale: US
linters:
disable-all: true
enable:
# default linters
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- typecheck
- unused
# additional linters
- asasalint
- asciicheck
- bidichk
- bodyclose
- containedctx
- contextcheck
- decorder
- dupl
- durationcheck
- errchkjson
- errname
- errorlint
- exhaustive
- gci
- gocognit
- exhaustruct
- exportloopref
- forcetypeassert
- gocheckcompilerdirectives
- gocritic
- godot
- godox
- goerr113
- gofmt
- goimports
- goprintffuncname
- misspell
- gosec
- gosmopolitan
- importas
- ireturn
- nakedret
- nestif
- nilerr
- noctx
- nilnil
- prealloc
- predeclared
- revive
- rowserrcheck
- tagalign
- tenv
- thelper
- tparallel
- unconvert
- unparam
- usestdlibvars
- wastedassign
- whitespace
- wrapcheck
- wsl
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
include:
- EXC0012
- EXC0014
exclude-rules:
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
- gocritic
- wrapcheck
- goerr113
# https://github.com/go-critic/go-critic/issues/926
- linters:
- gocritic
text: "unnecessaryDefer:"
- linters:
- gocritic
text: "preferDecodeRune:"
service:
golangci-lint-version: 1.39.x # use the fixed version to not introduce new linters unexpectedly
golangci-lint-version: 1.53.x
68 changes: 18 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</a>

<a href="https://codecov.io/gh/atomicgo/robin">
<!-- unittestcount:start --><img src="https://img.shields.io/badge/Unit_Tests-11-magenta?style=flat-square" alt="Unit test count"><!-- unittestcount:end -->
<!-- unittestcount:start --><img src="https://img.shields.io/badge/Unit_Tests-10-magenta?style=flat-square" alt="Unit test count"><!-- unittestcount:end -->
</a>

<a href="https://opensource.org/licenses/MIT" target="_blank">
Expand Down Expand Up @@ -69,55 +69,53 @@
import "atomicgo.dev/robin"
```

Package robin is a simple, generic round\-robin load balancer for Go.
Package robin is a simple, generic and thread\-safe round\-robin load balancer for Go.

It can be used to load balance any type of data. It is not limited to HTTP requests.

Robin takes any slice as an input and returns the next item in the slice. When the end of the slice is reached, it starts again from the beginning.

There are two versions of Robin: a thread\-safe version \(NewThreadSafeLoadbalancer\) and a non\-thread\-safe \(NewLoadbalancer\) version. The thread\-safe version is slower than the non\-thread\-safe version, but it is guaranteed that two concurrent calls to Loadbalancer.Next will not return the same item, if the slice contains more than one item.
Thread\-safety is achieved by using atomic operations amd guarantees that two concurrent calls to Loadbalancer.Next will not return the same item, if the slice contains more than one item.

Benchmark:

```
BenchmarkLoadbalancer_Next 225866620 5.274 ns/op
BenchmarkLoadbalancer_Next-2 227712583 5.285 ns/op
BenchmarkLoadbalancer_Next-32 228792201 5.273 ns/op
BenchmarkLoadbalancer_Next_ThreadSafe 100000000 10.15 ns/op
BenchmarkLoadbalancer_Next_ThreadSafe-2 100000000 10.02 ns/op
BenchmarkLoadbalancer_Next_ThreadSafe-32 100000000 10.06 ns/op
BenchmarkLoadbalancer_Next 251751190 4.772 ns/op
BenchmarkLoadbalancer_Next-2 250728889 4.834 ns/op
BenchmarkLoadbalancer_Next-4 253328150 4.773 ns/op
BenchmarkLoadbalancer_Next-8 248147372 4.783 ns/op
BenchmarkLoadbalancer_Next-16 249468267 4.773 ns/op
BenchmarkLoadbalancer_Next-32 247134729 4.802 ns/op
```

## Index

- [type Loadbalancer](<#type-loadbalancer>)
- [func NewLoadbalancer[T any](items []T) *Loadbalancer[T]](<#func-newloadbalancer>)
- [func NewThreadSafeLoadbalancer[T any](items []T) *Loadbalancer[T]](<#func-newthreadsafeloadbalancer>)
- [func (l *Loadbalancer[T]) AddItems(items ...T)](<#func-loadbalancert-additems>)
- [func (l *Loadbalancer[T]) Current() T](<#func-loadbalancert-current>)
- [func (l *Loadbalancer[T]) Next() T](<#func-loadbalancert-next>)
- [func (l *Loadbalancer[T]) Reset()](<#func-loadbalancert-reset>)


## type [Loadbalancer](<https://github.com/atomicgo/robin/blob/main/robin.go#L6-L12>)
## type [Loadbalancer](<https://github.com/atomicgo/robin/blob/main/robin.go#L8-L12>)

Loadbalancer is a simple, generic round\-robin load balancer for Go.
Loadbalancer is a simple, generic and thread\-safe round\-robin load balancer for Go.

```go
type Loadbalancer[T any] struct {
Items []T
ThreadSafe bool
Items []T
// contains filtered or unexported fields
}
```

### func [NewLoadbalancer](<https://github.com/atomicgo/robin/blob/main/robin.go#L17>)
### func [NewLoadbalancer](<https://github.com/atomicgo/robin/blob/main/robin.go#L16>)

```go
func NewLoadbalancer[T any](items []T) *Loadbalancer[T]
```

NewLoadbalancer creates a new Loadbalancer. For maximum speed, this is not thread\-safe. Use NewThreadSafeLoadbalancer if you need thread\-safety. If two goroutines call Loadbalancer.Next at the exact same time, it can happen that they both return the same item.
NewLoadbalancer creates a new Loadbalancer. It is guaranteed that two concurrent calls to Loadbalancer.Next will not return the same item, if the slice contains more than one item.

<details><summary>Example</summary>
<p>
Expand All @@ -141,37 +139,7 @@ object1
</p>
</details>

### func [NewThreadSafeLoadbalancer](<https://github.com/atomicgo/robin/blob/main/robin.go#L26>)

```go
func NewThreadSafeLoadbalancer[T any](items []T) *Loadbalancer[T]
```

NewThreadSafeLoadbalancer creates a new Loadbalancer. This is thread\-safe, but slower than NewLoadbalancer. It is guaranteed that two concurrent calls to Loadbalancer.Next will not return the same item, if the slice contains more than one item.

<details><summary>Example</summary>
<p>

```go
{
set := []string{"object1", "object2", "object3"}
lb := NewThreadSafeLoadbalancer(set)

fmt.Println(lb.Current())

}
```

#### Output

```
object1
```

</p>
</details>

### func \(\*Loadbalancer\[T\]\) [AddItems](<https://github.com/atomicgo/robin/blob/main/robin.go#L67>)
### func \(\*Loadbalancer\[T\]\) [AddItems](<https://github.com/atomicgo/robin/blob/main/robin.go#L40>)

```go
func (l *Loadbalancer[T]) AddItems(items ...T)
Expand Down Expand Up @@ -203,7 +171,7 @@ AddItems adds items to the Loadbalancer.
</p>
</details>

### func \(\*Loadbalancer\[T\]\) [Current](<https://github.com/atomicgo/robin/blob/main/robin.go#L34>)
### func \(\*Loadbalancer\[T\]\) [Current](<https://github.com/atomicgo/robin/blob/main/robin.go#L23>)

```go
func (l *Loadbalancer[T]) Current() T
Expand Down Expand Up @@ -233,7 +201,7 @@ Current returns the current item in the slice, without advancing the Loadbalance
</p>
</details>

### func \(\*Loadbalancer\[T\]\) [Next](<https://github.com/atomicgo/robin/blob/main/robin.go#L43>)
### func \(\*Loadbalancer\[T\]\) [Next](<https://github.com/atomicgo/robin/blob/main/robin.go#L29>)

```go
func (l *Loadbalancer[T]) Next() T
Expand Down Expand Up @@ -274,7 +242,7 @@ Next returns the next item in the slice. When the end of the slice is reached, i
</p>
</details>

### func \(\*Loadbalancer\[T\]\) [Reset](<https://github.com/atomicgo/robin/blob/main/robin.go#L58>)
### func \(\*Loadbalancer\[T\]\) [Reset](<https://github.com/atomicgo/robin/blob/main/robin.go#L35>)

```go
func (l *Loadbalancer[T]) Reset()
Expand Down
9 changes: 0 additions & 9 deletions benchmarks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,3 @@ func BenchmarkLoadbalancer_Next(b *testing.B) {
lb.Next()
}
}

func BenchmarkLoadbalancer_Next_ThreadSafe(b *testing.B) {
set := []int{1, 2, 3}
lb := NewThreadSafeLoadbalancer(set)
b.ResetTimer()
for i := 0; i < b.N; i++ {
lb.Next()
}
}
17 changes: 8 additions & 9 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
/*
Package robin is a simple, generic round-robin load balancer for Go.
Package robin is a simple, generic and thread-safe round-robin load balancer for Go.

It can be used to load balance any type of data. It is not limited to HTTP requests.

Robin takes any slice as an input and returns the next item in the slice. When the end of the slice is reached, it starts again from the beginning.

There are two versions of Robin: a thread-safe version (NewThreadSafeLoadbalancer) and a non-thread-safe (NewLoadbalancer) version.
The thread-safe version is slower than the non-thread-safe version, but it is guaranteed that two concurrent calls to Loadbalancer.Next will not return the same item, if the slice contains more than one item.
Thread-safety is achieved by using atomic operations amd guarantees that two concurrent calls to Loadbalancer.Next will not return the same item, if the slice contains more than one item.

Benchmark:

BenchmarkLoadbalancer_Next 225866620 5.274 ns/op
BenchmarkLoadbalancer_Next-2 227712583 5.285 ns/op
BenchmarkLoadbalancer_Next-32 228792201 5.273 ns/op
BenchmarkLoadbalancer_Next_ThreadSafe 100000000 10.15 ns/op
BenchmarkLoadbalancer_Next_ThreadSafe-2 100000000 10.02 ns/op
BenchmarkLoadbalancer_Next_ThreadSafe-32 100000000 10.06 ns/op
BenchmarkLoadbalancer_Next 251751190 4.772 ns/op
BenchmarkLoadbalancer_Next-2 250728889 4.834 ns/op
BenchmarkLoadbalancer_Next-4 253328150 4.773 ns/op
BenchmarkLoadbalancer_Next-8 248147372 4.783 ns/op
BenchmarkLoadbalancer_Next-16 249468267 4.773 ns/op
BenchmarkLoadbalancer_Next-32 247134729 4.802 ns/op
*/
package robin
Loading