Commit 8b4a553eee

8b4a553eee7c5689aad198d0e8400ef9444e2f29

parent: 3035725993

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-28 22:49 UTC

config: pack_queue = -1 leaves the queue unbounded

Ref #262

Layout: unified · split

internal/config/config.go +8 −3
@@ -7,6 +7,7 @@ import (
77 "encoding/pem"
88 "errors"
99 "fmt"
10 "math"
1011 "net"
1112 "os"
1213 "path/filepath"
@@ -236,7 +237,8 @@ type Limits struct {
236237}
237238
238239// PackLimits resolves the pack_* settings for packlimit.New. A zero
239// count is no bound.
240// max or per is no bound; an unbounded queue is math.MaxInt, since
241// packlimit reads a zero queue as no queue at all.
240242func (l Limits) PackLimits() (max, per, queue int, wait time.Duration) {
241243 pick := func(v, def int) int {
242244 switch {
@@ -247,13 +249,16 @@ func (l Limits) PackLimits() (max, per, queue int, wait time.Duration) {
247249 }
248250 return v
249251 }
252 queue = pick(l.PackQueue, DefaultPackQueue)
253 if l.PackQueue < 0 {
254 queue = math.MaxInt
255 }
250256 wait = DefaultPackQueueWait
251257 if d, err := time.ParseDuration(l.PackQueueWait); err == nil && d > 0 {
252258 wait = d
253259 }
254260 return pick(l.PackConcurrency, DefaultPackConcurrency),
255 pick(l.PackPerPrincipal, DefaultPackPerPrincipal),
256 pick(l.PackQueue, DefaultPackQueue), wait
261 pick(l.PackPerPrincipal, DefaultPackPerPrincipal), queue, wait
257262}
258263
259264type Mail struct {
internal/config/config_test.go +16 −15
@@ -6,6 +6,7 @@ import (
66 "crypto/rand"
77 "crypto/x509"
88 "encoding/pem"
9 "math"
910 "os"
1011 "path/filepath"
1112 "strings"
@@ -47,6 +48,21 @@ func TestLoadMinimal(t *testing.T) {
4748 }
4849}
4950
51func TestPackLimits(t *testing.T) {
52 max, per, queue, wait := Limits{}.PackLimits()
53 if max != DefaultPackConcurrency || per != DefaultPackPerPrincipal || queue != DefaultPackQueue || wait != DefaultPackQueueWait {
54 t.Fatalf("defaults: %d %d %d %s", max, per, queue, wait)
55 }
56 max, per, queue, wait = Limits{PackConcurrency: -1, PackPerPrincipal: -1, PackQueue: -1, PackQueueWait: "5s"}.PackLimits()
57 if max != 0 || per != 0 || queue != math.MaxInt || wait != 5*time.Second {
58 t.Fatalf("off: %d %d %d %s", max, per, queue, wait)
59 }
60 max, per, queue, _ = Limits{PackConcurrency: 8, PackPerPrincipal: 3, PackQueue: 64}.PackLimits()
61 if max != 8 || per != 3 || queue != 64 {
62 t.Fatalf("set: %d %d %d", max, per, queue)
63 }
64}
65
5066func TestContradictions(t *testing.T) {
5167 cases := []struct {
5268 name string
@@ -395,18 +411,3 @@ func TestBackupRecipients(t *testing.T) {
395411 t.Fatalf("default: %v, %v", cfg.Backup, err)
396412 }
397413}
398
399func TestPackLimits(t *testing.T) {
400 max, per, queue, wait := Limits{}.PackLimits()
401 if max != DefaultPackConcurrency || per != DefaultPackPerPrincipal || queue != DefaultPackQueue || wait != DefaultPackQueueWait {
402 t.Fatalf("defaults: %d %d %d %s", max, per, queue, wait)
403 }
404 max, per, queue, wait = Limits{PackConcurrency: -1, PackPerPrincipal: -1, PackQueue: -1, PackQueueWait: "5s"}.PackLimits()
405 if max != 0 || per != 0 || queue != 0 || wait != 5*time.Second {
406 t.Fatalf("off: %d %d %d %s", max, per, queue, wait)
407 }
408 max, per, queue, _ = Limits{PackConcurrency: 8, PackPerPrincipal: 3, PackQueue: 64}.PackLimits()
409 if max != 8 || per != 3 || queue != 64 {
410 t.Fatalf("set: %d %d %d", max, per, queue)
411 }
412}
internal/packlimit/packlimit_test.go +15
@@ -2,6 +2,7 @@ package packlimit
22
33import (
44 "errors"
5 "math"
56 "testing"
67 "time"
78)
@@ -207,3 +208,17 @@ func waitQueued(t *testing.T, l *Limiter, n int) {
207208 }
208209 t.Fatalf("queue never reached %d", n)
209210}
211
212// config maps pack_queue = -1 to math.MaxInt: waiters are not turned
213// away for want of queue room.
214func TestUnboundedQueue(t *testing.T) {
215 l := New(1, 0, math.MaxInt, 5*time.Second)
216 r1, _ := l.Acquire(nil, "a")
217 done := make(chan struct{})
218 defer close(done)
219 for i := 0; i < 64; i++ {
220 go l.Acquire(done, "b")
221 }
222 waitQueued(t, l, 64)
223 r1()
224}