sless-primer/POSTGRES/code/go-pg-race/handler.go
2026-03-22 17:08:18 +04:00

95 lines
2.2 KiB
Go

// 2026-03-21 — go-pg-race: параллельные INSERT из нескольких горутин внутри одной функции.
// Тестирует: race condition устойчивость Go + PG при concurrent writes из одного пода.
// Использует pgx/v5 (pre-cached в base image).
package handler
import (
"context"
"fmt"
"os"
"sync"
"sync/atomic"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
// Handle запускает workers горутин, каждая делает n_per_worker INSERTs.
func Handle(event map[string]interface{}) interface{} {
workers := intParam(event, "workers", 5)
if workers > 20 {
workers = 20
}
nPerWorker := intParam(event, "n_per_worker", 10)
if nPerWorker > 50 {
nPerWorker = 50
}
dsn := fmt.Sprintf(
"host=%s port=%s dbname=%s user=%s password=%s sslmode=%s",
os.Getenv("PGHOST"), getenv("PGPORT", "5432"),
os.Getenv("PGDATABASE"), os.Getenv("PGUSER"),
os.Getenv("PGPASSWORD"), getenv("PGSSLMODE", "require"),
)
pool, err := pgxpool.New(context.Background(), dsn)
if err != nil {
return map[string]interface{}{"error": err.Error()}
}
defer pool.Close()
var (
wg sync.WaitGroup
ok int64
errCount int64
)
t0 := time.Now()
for w := 0; w < workers; w++ {
wg.Add(1)
go func(wid int) {
defer wg.Done()
for i := 0; i < nPerWorker; i++ {
title := fmt.Sprintf("go-race-w%d-%d-%d", wid, time.Now().UnixMilli(), i)
_, err := pool.Exec(context.Background(),
"INSERT INTO terraform_demo_table (title) VALUES ($1)", title)
if err != nil {
atomic.AddInt64(&errCount, 1)
} else {
atomic.AddInt64(&ok, 1)
}
}
}(w)
}
wg.Wait()
elapsed := time.Since(t0).Seconds()
return map[string]interface{}{
"workers": workers,
"n_per_worker": nPerWorker,
"inserted": ok,
"errors": errCount,
"elapsed_sec": elapsed,
"ops_per_sec": float64(ok) / elapsed,
}
}
func intParam(event map[string]interface{}, key string, def int) int {
v, ok := event[key]
if !ok {
return def
}
switch val := v.(type) {
case float64:
return int(val)
case int:
return val
}
return def
}
func getenv(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}