restructure: console→client-console, add admin-console skeleton, move docs to doc/
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package billing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// pgStore — реализация Store поверх PostgreSQL через pgx connection pool.
|
||||
type pgStore struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
// NewPostgresStore создаёт Store с пулом соединений к PostgreSQL.
|
||||
// dsn — строка вида postgres://user:pass@host:5432/dbname
|
||||
// При ошибке подключения — возвращает ошибку, вызывающий код решает как реагировать.
|
||||
func NewPostgresStore(ctx context.Context, dsn string) (Store, error) {
|
||||
cfg, err := pgxpool.ParseConfig(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.MaxConns = 4
|
||||
cfg.MinConns = 1
|
||||
cfg.MaxConnIdleTime = 5 * time.Minute
|
||||
|
||||
pool, err := pgxpool.NewWithConfig(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Проверяем живость соединения
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
pool.Close()
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("billing: connected to PostgreSQL")
|
||||
return &pgStore{pool: pool}, nil
|
||||
}
|
||||
|
||||
// RecordInvocation вставляет запись асинхронно — не блокирует основной поток.
|
||||
func (s *pgStore) RecordInvocation(inv Invocation) {
|
||||
go func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := s.pool.Exec(ctx, `
|
||||
INSERT INTO invocations (
|
||||
namespace, function_name, trigger_type, route, http_method,
|
||||
started_at, duration_ms, status_code, cold_start,
|
||||
request_bytes, response_bytes, error_msg, recorded_by, event_type
|
||||
) VALUES (
|
||||
$1,$2,$3,$4,$5,
|
||||
$6,$7,$8,$9,
|
||||
$10,$11,$12,$13,$14
|
||||
)`,
|
||||
inv.Namespace,
|
||||
inv.FunctionName,
|
||||
inv.TriggerType,
|
||||
inv.Route,
|
||||
inv.HTTPMethod,
|
||||
inv.StartedAt,
|
||||
inv.DurationMS,
|
||||
inv.StatusCode,
|
||||
inv.ColdStart,
|
||||
inv.RequestBytes,
|
||||
inv.ResponseBytes,
|
||||
nullIfEmpty(inv.ErrorMsg),
|
||||
inv.RecordedBy,
|
||||
nullIfEmpty(inv.EventType),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("billing: insert invocation: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Close закрывает пул соединений.
|
||||
func (s *pgStore) Close() {
|
||||
s.pool.Close()
|
||||
log.Printf("billing: connection pool closed")
|
||||
}
|
||||
|
||||
// nullIfEmpty возвращает nil для пустой строки (SQL NULL вместо пустой строки).
|
||||
func nullIfEmpty(s string) interface{} {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user