Skip to content

Commit

Permalink
switch to math/rand/v2 in most places
Browse files Browse the repository at this point in the history
this allows removing some ugly instantiations of an rng based on the current
time.

Intn is now IntN for our concurrency-safe prng wrapper to match the randv2 api.

v2 exists since go1.22, which we already require.
  • Loading branch information
mjl- committed Nov 29, 2024
1 parent 96a3ecd commit de435fc
Show file tree
Hide file tree
Showing 9 changed files with 24 additions and 30 deletions.
4 changes: 2 additions & 2 deletions dmarc/dmarc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"errors"
"fmt"
"log/slog"
mathrand "math/rand"
mathrand2 "math/rand/v2"
"time"

"github.com/mjl-/mox/dkim"
Expand Down Expand Up @@ -257,7 +257,7 @@ func Verify(ctx context.Context, elog *slog.Logger, resolver dns.Resolver, msgFr

// Record can request sampling of messages to apply policy.
// See ../rfc/7489:1432
useResult = !applyRandomPercentage || record.Percentage == 100 || mathrand.Intn(100) < record.Percentage
useResult = !applyRandomPercentage || record.Percentage == 100 || mathrand2.IntN(100) < record.Percentage

// We treat "quarantine" and "reject" the same. Thus, we also don't "downgrade"
// from reject to quarantine if this message was sampled out.
Expand Down
2 changes: 1 addition & 1 deletion dmarcdb/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ var jitterRand = mox.NewPseudoRand()
// Jitter so we don't cause load at exactly whole hours, other processes may
// already be doing that.
var jitteredTimeUntil = func(t time.Time) time.Duration {
return time.Until(t.Add(time.Duration(30+jitterRand.Intn(60)) * time.Second))
return time.Until(t.Add(time.Duration(30+jitterRand.IntN(60)) * time.Second))
}

// Start launches a goroutine that wakes up at each whole hour (plus jitter) and
Expand Down
8 changes: 4 additions & 4 deletions junk.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,12 @@ messages are shuffled, with optional random seed.`
hamFiles := listDir(hamDir)
spamFiles := listDir(spamDir)

var rand *mathrand.Rand
var seed int64
if a.seed {
rand = mathrand.New(mathrand.NewSource(time.Now().UnixMilli()))
} else {
rand = mathrand.New(mathrand.NewSource(0))
seed = time.Now().UnixMilli()
}
// Still at math/rand (v1 instead of v2) for potential comparison to earlier test results.
rand := mathrand.New(mathrand.NewSource(seed))

shuffle := func(l []string) {
count := len(l)
Expand Down
20 changes: 9 additions & 11 deletions mox-/rand.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@ import (
cryptorand "crypto/rand"
"encoding/binary"
"fmt"
mathrand "math/rand"
mathrand2 "math/rand/v2"
"sync"
)

type rand struct {
rand *mathrand.Rand
rand *mathrand2.Rand
sync.Mutex
}

// NewPseudoRand returns a new PRNG seeded with random bytes from crypto/rand. Its
// functions can be called concurrently.
func NewPseudoRand() *rand {
return &rand{rand: mathrand.New(mathrand.NewSource(CryptoRandInt()))}
var seed [32]byte
if _, err := cryptorand.Read(seed[:]); err != nil {
panic(err)
}
return &rand{rand: mathrand2.New(mathrand2.NewChaCha8(seed))}
}

func (r *rand) Float64() float64 {
Expand All @@ -25,16 +29,10 @@ func (r *rand) Float64() float64 {
return r.rand.Float64()
}

func (r *rand) Intn(n int) int {
r.Lock()
defer r.Unlock()
return r.rand.Intn(n)
}

func (r *rand) Read(buf []byte) (int, error) {
func (r *rand) IntN(n int) int {
r.Lock()
defer r.Unlock()
return r.rand.Read(buf)
return r.rand.IntN(n)
}

// CryptoRandInt returns a cryptographically random number.
Expand Down
7 changes: 3 additions & 4 deletions mtastsdb/refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"errors"
"fmt"
"log/slog"
mathrand "math/rand"
mathrand2 "math/rand/v2"
"runtime/debug"
"time"

Expand Down Expand Up @@ -71,12 +71,11 @@ func refresh1(ctx context.Context, log mlog.Log, resolver dns.Resolver, sleep fu
}

// Randomize list.
rand := mathrand.New(mathrand.NewSource(time.Now().UnixNano()))
for i := range prs {
if i == 0 {
continue
}
j := rand.Intn(i + 1)
j := mathrand2.IntN(i + 1)
prs[i], prs[j] = prs[j], prs[i]
}

Expand All @@ -87,7 +86,7 @@ func refresh1(ctx context.Context, log mlog.Log, resolver dns.Resolver, sleep fu
go refreshDomain(ctx, log, DB, resolver, pr)
if i < len(prs)-1 {
interval := 3 * int64(time.Hour) / int64(len(prs)-1)
extra := time.Duration(rand.Int63n(interval) - interval/2)
extra := time.Duration(mathrand2.Int64N(interval) - interval/2)
next := start.Add(time.Duration(int64(i+1)*interval) + extra)
d := next.Sub(timeNow())
if d > 0 {
Expand Down
2 changes: 1 addition & 1 deletion queue/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ func hookDeliver(log mlog.Log, h Hook) {
} else {
backoff = hookIntervals[len(hookIntervals)-1] * time.Duration(2)
}
backoff += time.Duration(jitter.Intn(200)-100) * backoff / 10000
backoff += time.Duration(jitter.IntN(200)-100) * backoff / 10000
h.Attempts++
now := time.Now()
h.NextAttempt = now.Add(backoff)
Expand Down
2 changes: 1 addition & 1 deletion queue/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -1370,7 +1370,7 @@ func deliver(log mlog.Log, resolver dns.Resolver, m0 Msg) {
return fmt.Errorf("get message to be delivered: %v", err)
}

backoff = time.Duration(7*60+30+jitter.Intn(10)-5) * time.Second
backoff = time.Duration(7*60+30+jitter.IntN(10)-5) * time.Second
for i := 0; i < m0.Attempts; i++ {
backoff *= time.Duration(2)
}
Expand Down
2 changes: 1 addition & 1 deletion tlsrptsend/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ var jitterRand = mox.NewPseudoRand()
// Jitter so we don't cause load at exactly midnight, other processes may
// already be doing that.
var jitteredTimeUntil = func(t time.Time) time.Duration {
return time.Until(t.Add(time.Duration(240+jitterRand.Intn(120)) * time.Second))
return time.Until(t.Add(time.Duration(240+jitterRand.IntN(120)) * time.Second))
}

// Start launches a goroutine that wakes up just after 00:00 UTC to send TLSRPT
Expand Down
7 changes: 2 additions & 5 deletions webmail/eventwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"fmt"
"io"
"log/slog"
mathrand "math/rand"
mathrand2 "math/rand/v2"
"net/http"
"runtime/debug"
"sync"
Expand Down Expand Up @@ -71,9 +71,6 @@ func (ew *eventWriter) write(name string, v any) error {
return ew.out.Flush()
}

// For random wait between min and max delay.
var waitGen = mathrand.New(mathrand.NewSource(time.Now().UnixNano()))

// Schedule an event for writing to the connection. If events get a delay, this
// function still returns immediately.
func (ew *eventWriter) xsendEvent(ctx context.Context, log mlog.Log, name string, v any) {
Expand Down Expand Up @@ -136,7 +133,7 @@ func (ew *eventWriter) xsendEvent(ctx context.Context, log mlog.Log, name string
}
// If we have an events channel, we have a goroutine that write the events, delayed.
if ew.events != nil {
wait := ew.waitMin + time.Duration(waitGen.Intn(1000))*(ew.waitMax-ew.waitMin)/1000
wait := ew.waitMin + time.Duration(mathrand2.IntN(1000))*(ew.waitMax-ew.waitMin)/1000
when := time.Now().Add(wait)
ew.events <- struct {
name string
Expand Down

0 comments on commit de435fc

Please sign in to comment.