feat(go): add 100k ingest hardening baseline
This commit is contained in:
@@ -74,7 +74,7 @@ func main() {
|
||||
Metrics: registry,
|
||||
ReadBufferSize: envInt("TCP_READ_BUFFER_BYTES", 64*1024),
|
||||
IdleTimeout: time.Duration(envInt("TCP_IDLE_TIMEOUT_SECONDS", 180)) * time.Second,
|
||||
MaxConnections: envInt("TCP_MAX_CONNECTIONS", 20_000),
|
||||
MaxConnections: envInt("TCP_MAX_CONNECTIONS", 120_000),
|
||||
PublishUnified: publishUnified,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -46,6 +46,16 @@ func TestGatewayConfiguresIdentityLookupCacheTTL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatewayDefaultsTo100KConnectionCeiling(t *testing.T) {
|
||||
source, err := os.ReadFile("main.go")
|
||||
if err != nil {
|
||||
t.Fatalf("read main.go: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(source), `envInt("TCP_MAX_CONNECTIONS", 120_000)`) {
|
||||
t.Fatal("gateway should default TCP_MAX_CONNECTIONS to a 100K-ready ceiling")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNATSSinkConfigFromEnvDefaultsToGoSubjects(t *testing.T) {
|
||||
t.Setenv("NATS_URL", "nats://172.17.111.56:4222")
|
||||
|
||||
|
||||
45
go/vehicle-gateway/cmd/load-sim/main.go
Normal file
45
go/vehicle-gateway/cmd/load-sim/main.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/loadsim"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
||||
flagCfg := loadsim.RegisterFlags(flag.CommandLine)
|
||||
if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cfg, err := flagCfg.Build()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
log.Printf("load simulation started protocol=%s addr=%s connections=%d connect_rate=%d send_interval=%s duration=%s template=%s",
|
||||
cfg.Protocol, cfg.Addr, cfg.Connections, cfg.ConnectRatePerSecond, cfg.SendInterval, cfg.Duration, cfg.Template)
|
||||
stats, err := (loadsim.Runner{}).Run(ctx, cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Print(formatStats(stats))
|
||||
}
|
||||
|
||||
func formatStats(stats loadsim.Stats) string {
|
||||
return fmt.Sprintf("connections_opened=%d connections_failed=%d frames_written=%d write_errors=%d",
|
||||
stats.ConnectionsOpened,
|
||||
stats.ConnectionsFailed,
|
||||
stats.FramesWritten,
|
||||
stats.WriteErrors,
|
||||
)
|
||||
}
|
||||
28
go/vehicle-gateway/cmd/load-sim/main_test.go
Normal file
28
go/vehicle-gateway/cmd/load-sim/main_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/loadsim"
|
||||
)
|
||||
|
||||
func TestFormatStatsIncludesCapacityCounters(t *testing.T) {
|
||||
out := formatStats(loadsim.Stats{
|
||||
ConnectionsOpened: 10,
|
||||
ConnectionsFailed: 2,
|
||||
FramesWritten: 300,
|
||||
WriteErrors: 1,
|
||||
})
|
||||
|
||||
for _, want := range []string{
|
||||
"connections_opened=10",
|
||||
"connections_failed=2",
|
||||
"frames_written=300",
|
||||
"write_errors=1",
|
||||
} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Fatalf("formatStats() = %q, missing %q", out, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,6 +134,35 @@ func TestAsyncSecondaryRealtimeUpdaterDoesNotBlockPrimaryPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsyncSecondaryQueueDropDoesNotFailPrimaryUpdate(t *testing.T) {
|
||||
env := envelope.FrameEnvelope{Protocol: envelope.ProtocolJT808, VIN: "VIN001"}
|
||||
primary := &contextCheckingRealtimeUpdater{}
|
||||
registry := metrics.NewRegistry()
|
||||
updater := &asyncSecondaryRealtimeUpdater{
|
||||
primary: primary,
|
||||
secondary: &contextCheckingRealtimeUpdater{},
|
||||
queue: make(chan envelope.FrameEnvelope, 1),
|
||||
registry: registry,
|
||||
}
|
||||
updater.queue <- envelope.FrameEnvelope{Protocol: envelope.ProtocolJT808, VIN: "VIN000"}
|
||||
|
||||
if err := updater.Update(context.Background(), env); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
if primary.count != 1 {
|
||||
t.Fatalf("primary updates = %d, want 1", primary.count)
|
||||
}
|
||||
text := registry.Render()
|
||||
for _, want := range []string{
|
||||
`vehicle_realtime_async_queue_total{protocol="JT808",status="dropped",store="mysql"} 1`,
|
||||
`vehicle_realtime_async_queue_depth{protocol="JT808",store="mysql"} 1`,
|
||||
} {
|
||||
if !strings.Contains(text, want) {
|
||||
t.Fatalf("async queue drop metric missing %s:\n%s", want, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreMetricUpdaterRecordsStoreUpdateResults(t *testing.T) {
|
||||
registry := metrics.NewRegistry()
|
||||
env := envelope.FrameEnvelope{Protocol: envelope.ProtocolJT808, VIN: "VIN001"}
|
||||
|
||||
Reference in New Issue
Block a user