mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +00:00
This patch updates all build tag constraints to add the new format, alongside the old one, to maintain backwards compatibility. This was done by using `go fmt`. See https://go.dev/doc/go1.17#gofmt and https://golang.org/design/draft-gobuild for more details.
209 lines
3.7 KiB
Go
209 lines
3.7 KiB
Go
//go:build ignore
|
|
// +build ignore
|
|
|
|
// SMTP load generator, for testing purposes.
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"net"
|
|
"net/http"
|
|
"net/textproto"
|
|
"runtime"
|
|
"sync"
|
|
"time"
|
|
|
|
_ "net/http/pprof"
|
|
|
|
"golang.org/x/net/trace"
|
|
|
|
"blitiri.com.ar/go/chasquid/internal/smtp"
|
|
"blitiri.com.ar/go/log"
|
|
)
|
|
|
|
var (
|
|
addr = flag.String("addr", "",
|
|
"server address")
|
|
httpAddr = flag.String("http_addr", "localhost:8011",
|
|
"monitoring HTTP server listening address")
|
|
parallel = flag.Int("parallel", 0,
|
|
"how many sending loops to run in parallel")
|
|
runFor = flag.Duration("run_for", 0,
|
|
"how long to run for (0 = forever)")
|
|
wait = flag.Bool("wait", false,
|
|
"don't exit after --run_for has lapsed")
|
|
noop = flag.Bool("noop", false,
|
|
"don't send an email, just connect and run a NOOP")
|
|
)
|
|
|
|
var (
|
|
host string
|
|
exit bool
|
|
|
|
globalCount int64 = 0
|
|
globalRuntime time.Duration
|
|
globalMu = &sync.Mutex{}
|
|
)
|
|
|
|
func main() {
|
|
var err error
|
|
|
|
flag.Parse()
|
|
log.Init()
|
|
|
|
host, _, err = net.SplitHostPort(*addr)
|
|
if err != nil {
|
|
log.Fatalf("failed to split --addr=%q: %v", *addr, err)
|
|
}
|
|
|
|
if *wait {
|
|
go http.ListenAndServe(*httpAddr, nil)
|
|
log.Infof("monitoring address: http://%v/debug/requests?fam=one&b=11",
|
|
*httpAddr)
|
|
}
|
|
|
|
if *parallel == 0 {
|
|
*parallel = runtime.GOMAXPROCS(0)
|
|
}
|
|
|
|
lt := "full"
|
|
if *noop {
|
|
lt = "noop"
|
|
}
|
|
|
|
log.Infof("launching %d %s sending loops in parallel", *parallel, lt)
|
|
for i := 0; i < *parallel; i++ {
|
|
go serial(i)
|
|
}
|
|
|
|
var totalCount int64
|
|
var totalRuntime time.Duration
|
|
start := time.Now()
|
|
for range time.Tick(1 * time.Second) {
|
|
globalMu.Lock()
|
|
totalCount += globalCount
|
|
totalRuntime += globalRuntime
|
|
count := globalCount
|
|
runtime := globalRuntime
|
|
globalCount = 0
|
|
globalRuntime = 0
|
|
globalMu.Unlock()
|
|
|
|
if count == 0 {
|
|
log.Infof("0 ops")
|
|
} else {
|
|
log.Infof("%d ops, %v /op", count,
|
|
time.Duration(runtime.Nanoseconds()/count).Truncate(time.Microsecond))
|
|
}
|
|
|
|
if *runFor > 0 && time.Since(start) > *runFor {
|
|
exit = true
|
|
break
|
|
}
|
|
}
|
|
|
|
end := time.Now()
|
|
window := end.Sub(start)
|
|
log.Infof("total: %d ops, %v wall, %v run",
|
|
totalCount,
|
|
window.Truncate(time.Millisecond),
|
|
totalRuntime.Truncate(time.Millisecond))
|
|
|
|
avgLat := time.Duration(totalRuntime.Nanoseconds() / totalCount)
|
|
log.Infof("avg: %v /op, %.0f ops/s",
|
|
avgLat.Truncate(time.Microsecond),
|
|
float64(totalCount)/window.Seconds(),
|
|
)
|
|
|
|
if *wait {
|
|
for {
|
|
time.Sleep(24 * time.Hour)
|
|
}
|
|
}
|
|
}
|
|
|
|
func serial(id int) {
|
|
var count int64
|
|
start := time.Now()
|
|
for {
|
|
count += 1
|
|
err := one()
|
|
if err != nil {
|
|
log.Fatalf("%v", err)
|
|
}
|
|
|
|
if count == 5 {
|
|
globalMu.Lock()
|
|
globalCount += count
|
|
globalRuntime += time.Since(start)
|
|
globalMu.Unlock()
|
|
count = 0
|
|
start = time.Now()
|
|
|
|
if exit {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func one() error {
|
|
tr := trace.New("one", *addr)
|
|
defer tr.Finish()
|
|
|
|
conn, err := net.Dial("tcp", *addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
|
|
client, err := smtp.NewClient(conn, host)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer client.Close()
|
|
|
|
if *noop {
|
|
err = client.Noop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
err = client.MailAndRcpt("test@test", "null@testserver")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
retry:
|
|
w, err := client.Data()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = w.Write(body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = w.Close()
|
|
if err != nil {
|
|
// If we are sending too fast we might hit chasquid's queue size
|
|
// limit. In that case, wait and try again.
|
|
// We detect it with error code 451 which is used for this
|
|
// situation.
|
|
if terr, ok := err.(*textproto.Error); ok {
|
|
if terr.Code == 451 {
|
|
time.Sleep(10 * time.Millisecond)
|
|
goto retry
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var body = []byte(`Subject: Load test
|
|
|
|
This is the body of the load test email.
|
|
`)
|