alt/services/worker/internal/storage/postgres/store.go
toki f762d2abd9 feat: worker persistence layer and socket session updates
- Add worker storage layer with PostgreSQL/sqlc setup
- Add migration files for worker backbone schema
- Add job runner and built-in jobs implementation
- Add Redis key definitions for worker state management
- Archive socket-session-loop milestone (completed/renamed)
- Update roadmap current.md and foundation-alignment phase
- Add socket endpoint integration and runtime smoke tests
- Add infra-check, worker-storage-check, worker-storage-gen CLI tools
- Update API socket server and config
- Add pubspec dependencies and client socket endpoint changes
- Add config tests for API and worker services
2026-05-28 19:05:10 +09:00

133 lines
3.8 KiB
Go

package postgres
import (
"context"
"fmt"
"time"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgxpool"
"git.toki-labs.com/toki/alt/packages/domain/backtest"
"git.toki-labs.com/toki/alt/packages/domain/market"
"git.toki-labs.com/toki/alt/services/worker/internal/storage"
"git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres/sqlc"
)
var _ storage.InstrumentStore = (*Store)(nil)
var _ storage.BarStore = (*Store)(nil)
var _ storage.BacktestRunStore = (*Store)(nil)
type Store struct {
pool *pgxpool.Pool
queries *sqlc.Queries
}
func NewStore(pool *pgxpool.Pool) *Store {
return &Store{
pool: pool,
queries: sqlc.New(pool),
}
}
func (s *Store) UpsertInstrument(ctx context.Context, inst market.Instrument) error {
params, err := mapInstrumentToParams(inst)
if err != nil {
return fmt.Errorf("failed to map instrument: %w", err)
}
if err := s.queries.UpsertInstrument(ctx, params); err != nil {
return fmt.Errorf("failed to upsert instrument: %w", err)
}
return nil
}
func (s *Store) GetInstrument(ctx context.Context, id market.InstrumentID) (market.Instrument, error) {
row, err := s.queries.GetInstrument(ctx, string(id))
if err != nil {
return market.Instrument{}, fmt.Errorf("failed to get instrument: %w", err)
}
inst, err := mapRowToInstrument(row)
if err != nil {
return market.Instrument{}, fmt.Errorf("failed to map instrument row: %w", err)
}
return inst, nil
}
func (s *Store) ListInstruments(ctx context.Context) ([]market.Instrument, error) {
rows, err := s.queries.ListInstruments(ctx)
if err != nil {
return nil, fmt.Errorf("failed to list instruments: %w", err)
}
insts := make([]market.Instrument, len(rows))
for i, row := range rows {
inst, err := mapRowToInstrument(row)
if err != nil {
return nil, fmt.Errorf("failed to map instrument row at index %d: %w", i, err)
}
insts[i] = inst
}
return insts, nil
}
func (s *Store) UpsertBar(ctx context.Context, bar market.Bar) error {
params, err := mapBarToParams(bar)
if err != nil {
return fmt.Errorf("failed to map bar: %w", err)
}
if err := s.queries.UpsertBar(ctx, params); err != nil {
return fmt.Errorf("failed to upsert bar: %w", err)
}
return nil
}
func (s *Store) GetBars(ctx context.Context, id market.InstrumentID, timeframe market.Timeframe, from, to time.Time) ([]market.Bar, error) {
// First, fetch the instrument to know its currency
instRow, err := s.queries.GetInstrument(ctx, string(id))
if err != nil {
return nil, fmt.Errorf("failed to get instrument for bar currency: %w", err)
}
params := sqlc.GetBarsParams{
InstrumentID: string(id),
Timeframe: string(timeframe),
Timestamp: pgtype.Timestamptz{Time: from, Valid: !from.IsZero()},
Timestamp_2: pgtype.Timestamptz{Time: to, Valid: !to.IsZero()},
}
rows, err := s.queries.GetBars(ctx, params)
if err != nil {
return nil, fmt.Errorf("failed to get bars: %w", err)
}
bars := make([]market.Bar, len(rows))
for i, row := range rows {
bar, err := mapRowToBar(row, market.Currency(instRow.Currency))
if err != nil {
return nil, fmt.Errorf("failed to map bar row at index %d: %w", i, err)
}
bars[i] = bar
}
return bars, nil
}
func (s *Store) UpsertRun(ctx context.Context, run backtest.Run) error {
params, err := mapRunToParams(run)
if err != nil {
return fmt.Errorf("failed to map run: %w", err)
}
if err := s.queries.UpsertRun(ctx, params); err != nil {
return fmt.Errorf("failed to upsert run: %w", err)
}
return nil
}
func (s *Store) GetRun(ctx context.Context, id backtest.RunID) (backtest.Run, error) {
row, err := s.queries.GetRun(ctx, string(id))
if err != nil {
return backtest.Run{}, fmt.Errorf("failed to get run: %w", err)
}
run, err := mapRowToRun(row)
if err != nil {
return backtest.Run{}, fmt.Errorf("failed to map run row: %w", err)
}
return run, nil
}