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
2 changes: 1 addition & 1 deletion .github/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
| macOS | `NZBreX_@TAG@_darwin-arm64_purego.tar.gz` (Intel: `darwin-amd64`) |
| FreeBSD | `NZBreX_@TAG@_freebsd-amd64_purego.tar.gz` |

**purego** is the default and fine for everyone. Its yEnc decoder (go-yenc-simd) is as fast as rapidyenc's C code on x86-64 CPUs with AVX2, and faster with AVX-512. On ARM (Apple Silicon, arm64 Linux) it has no SIMD yet, so there the **rapidyenc** files, which decode with rapidyenc's C code for `-yenctest=4`, are faster. Decoding is far faster than any download either way; `NZBreX -benchmark` shows the numbers for your machine. The rapidyenc files exist for Linux amd64/arm64, Windows amd64 and macOS arm64; the Linux ones need glibc 2.35 or newer.
**purego** is the default and fine for everyone. Its yEnc decoder (go-yenc-simd) is as fast as rapidyenc's C code on x86-64 CPUs with AVX2, and faster with AVX-512. On ARM (Apple Silicon, arm64 Linux) it uses NEON: on an Apple M1 or a Neoverse N2 the decode kernel does about 5–8 GB/s on random data, a whole article with size and CRC checks 2–3.4 GB/s (plain Go: 1.9–2.7 GB/s). The **rapidyenc** files decode with rapidyenc's C code for `-yenctest=4` instead. Decoding is far faster than any download either way; `NZBreX -benchmark` shows the numbers for your machine (the rapidyenc files measure both decoders). The rapidyenc files exist for Linux amd64/arm64, Windows amd64 and macOS arm64; the Linux ones need glibc 2.34 or newer and libstdc++, so on Alpine take purego.

Each archive contains the binary, `README.md`, `LICENSE`, `cleanHeaders.txt` and the provider samples.

Expand Down
12 changes: 7 additions & 5 deletions ConnPool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,15 @@ func serveScript(t *gotesting.T, reply func(line string, w net.Conn)) string {
return ln.Addr().String()
}

func shortIdle(t *gotesting.T) {
func shortIdle(t *gotesting.T, d time.Duration) {
old := connIdleTimeout
connIdleTimeout = 300 * time.Millisecond
connIdleTimeout = d
t.Cleanup(func() { connIdleTimeout = old })
}

// A server that stops answering mid-session no longer blocks the caller forever.
func TestIdleTimeoutEndsSilentCommand(t *gotesting.T) {
shortIdle(t)
shortIdle(t, 300*time.Millisecond)
addr := serveScript(t, func(string, net.Conn) {}) // never answers
c := testPool(t, addr)
ci, err := c.connect()
Expand All @@ -227,11 +227,13 @@ func TestIdleTimeoutEndsSilentCommand(t *gotesting.T) {
}

// The timeout is per read: a slow but steady article is not cut off.
// The idle timeout is 10x the gap between lines: with 300 ms it failed on a
// busy single-CPU VM (load 45) whenever one 100 ms sleep overran by 200 ms.
func TestIdleTimeoutKeepsSlowTransfer(t *gotesting.T) {
shortIdle(t)
shortIdle(t, time.Second)
addr := serveScript(t, func(_ string, w net.Conn) {
io.WriteString(w, "222 0 <a@b> body\r\n")
for i := 0; i < 30; i++ { // 3 s in total, 10x the idle timeout
for i := 0; i < 30; i++ { // 3 s in total, 3x the idle timeout
time.Sleep(100 * time.Millisecond)
fmt.Fprintf(w, "line %d\r\n", i)
}
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ Only one mode per process.

## Installation

1. **Download pre-built binaries** from the [Releases](../../releases) page, or
1. **Download pre-built binaries** from the [Releases](../../releases) page
(the release notes say which file to take), or
2. **Compile from source** (see Build Instructions below)
3. Configure `provider.json` with your Usenet provider details.
4. Run the program from the command line:
Expand Down Expand Up @@ -114,13 +115,20 @@ go build .
No C compiler, no cgo. Cross-compiles to any target, e.g.
`CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build .` or `GOOS=darwin GOARCH=arm64`.
Built on Linux with `CGO_ENABLED=0`, the binary is static (runs on glibc and musl/Alpine).
The `purego` release files are built this way: on Alpine take those, the
`rapidyenc` Linux files need glibc 2.34 or newer and libstdc++.

For go-yenc-simd's AVX2/AVX-512 code on amd64, build with
`GOEXPERIMENT=simd go build .` (Go 1.27; an experiment, outside the Go 1
compatibility promise). Without it the decoder still works, in plain Go.

#### With rapidyenc's C decoder
Needs a C/C++ compiler and cmake; the rapidyenc library is built first.
This also works on musl (Alpine: `apk add cmake g++ make linux-headers`).
Tested from source with both variants on AlmaLinux 9/10, Alpine 3.22–3.24,
Arch, CentOS Stream 9/10, Debian 13, Gentoo, openEuler 25.03, openSUSE Leap
15.6/16.0, Rocky 9/10 and Ubuntu 24.04–26.04 (amd64). With gcc older than 8
(Leap 15.6 ships gcc 7) rapidyenc builds without its AVX-512 VBMI2 kernel.

```sh
# Linux
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.27.3
require (
github.com/go-while/go-cpu-mem-profiler v0.0.0-20240612221627-856954a5fc83
github.com/go-while/go-loggedrwmutex v0.0.0-20250601032232-b0aa20cdcb2c
github.com/go-while/go-yenc-simd v0.1.1
github.com/go-while/go-yenc-simd v0.1.2
github.com/javi11/nzbparser v0.5.5
github.com/stretchr/testify v1.12.1
golang.org/x/crypto v0.58.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ github.com/go-while/go-cpu-mem-profiler v0.0.0-20240612221627-856954a5fc83 h1:ve
github.com/go-while/go-cpu-mem-profiler v0.0.0-20240612221627-856954a5fc83/go.mod h1:31AY7BccUb7qvKi4LQCwLOYsvcza9TdQtl/zHEIOcRE=
github.com/go-while/go-loggedrwmutex v0.0.0-20250601032232-b0aa20cdcb2c h1:sDQuzVfH/JKw6k06m85cWNKsB27QSH496GZeImozJic=
github.com/go-while/go-loggedrwmutex v0.0.0-20250601032232-b0aa20cdcb2c/go.mod h1:PqQxwEostGAr7ARrGKLQATdk6t2CZBOSDNg8vRCmydw=
github.com/go-while/go-yenc-simd v0.1.1 h1:sEFhp92Q8xep7ypsFWG5R0sSH5ZTTVNgwRCQUvHJl5g=
github.com/go-while/go-yenc-simd v0.1.1/go.mod h1:aOlUl+t2OX3Qjj3nK2ollyEn+pkfMAsjI0upfld9tJ4=
github.com/go-while/go-yenc-simd v0.1.2 h1:OCsKQWDT+6TuuezumL0s0uQJQQ3GadGByg3vWk3mfOg=
github.com/go-while/go-yenc-simd v0.1.2/go.mod h1:aOlUl+t2OX3Qjj3nK2ollyEn+pkfMAsjI0upfld9tJ4=
github.com/go-while/nzbparser v0.5.6-0.20261018190657-7c20501e8eac h1:a9UhKqxKQvKZpgy90/YilvtNaALs84/KnWuHlJ7VSOs=
github.com/go-while/nzbparser v0.5.6-0.20261018190657-7c20501e8eac/go.mod h1:ikF7WI3BUGs5IHQJmKzmtTkX29NZW5nvUdo6ZWFZgL4=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
Expand Down
7 changes: 6 additions & 1 deletion scripts/build-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,18 @@ if [ "${DEB:-0}" = 1 ] && [ "$goos" = linux ]; then
mkdir -p "$root/DEBIAN" "$root/usr/bin" "$root/usr/share/doc/nzbrex"
cp "$stage/$name/$bin" "$root/usr/bin/NZBreX"
cp README.md LICENSE cleanHeaders.txt provider.sample.json provider.ygg.json "$root/usr/share/doc/nzbrex/"
# purego is static; rapidyenc links glibc (symbols up to GLIBC_2.34) and libstdc++
depends=""
if [ "$variant" = rapidyenc ]; then
depends="Depends: libc6 (>= 2.34), libstdc++6"$'\n'
fi
cat >"$root/DEBIAN/control" <<EOF
Package: nzbrex
Version: $debversion
Section: net
Priority: optional
Architecture: $goarch
Maintainer: ${DEB_MAINTAINER:-go-while <nzbrex@usenet-server.com>}
${depends}Maintainer: ${DEB_MAINTAINER:-go-while <nzbrex@usenet-server.com>}
Homepage: https://github.com/go-while/NZBreX
Description: re-upload Usenet articles missing from providers
NZBreX checks the articles of an NZB on your providers and re-uploads the
Expand Down
Loading