This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/itworx/pulse/internal/agentstore"
|
||||
"github.com/itworx/pulse/internal/database"
|
||||
"github.com/itworx/pulse/internal/runtimeconfig"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
const (
|
||||
// storeConnectTimeout bounds the start-up reachability check. The healthcheck
|
||||
// contract wants the first heartbeat written after the database is reachable, so
|
||||
// this must stay far below the 45 second staleness threshold.
|
||||
storeConnectTimeout = 5 * time.Second
|
||||
// agentPoolMaxConns keeps the agent's footprint on the shared database small: it
|
||||
// issues one small write per capability per interval and never reads.
|
||||
agentPoolMaxConns = 2
|
||||
)
|
||||
|
||||
// errStoreNotLinked is returned by every write while no snapshot store implementation
|
||||
// is compiled into this binary. It is deliberately an error on the write path rather
|
||||
// than a silent drop: the reader turns a missing snapshot into Unknown, and the agent
|
||||
// log states plainly why nothing arrives.
|
||||
var errStoreNotLinked = errors.New("no agent snapshot store is linked into this build")
|
||||
|
||||
// openStore connects to PostgreSQL and returns the narrow Writer the agent publishes
|
||||
// through, plus a close function.
|
||||
//
|
||||
// The agent depends on the agentstore.Writer interface only; the concrete PostgreSQL
|
||||
// store owns migration 0016 and the agent_snapshots table.
|
||||
func openStore(ctx context.Context, config runtimeconfig.AgentConfig) (agentstore.Writer, func(), error) {
|
||||
pool, err := database.NewPool(ctx, database.Config{
|
||||
URL: config.DatabaseURL,
|
||||
MaxConns: agentPoolMaxConns,
|
||||
MinConns: 1,
|
||||
})
|
||||
if err != nil {
|
||||
// Never wrap the URL itself into the error: it carries the database password.
|
||||
return nil, nil, errors.New("agent database pool could not be created")
|
||||
}
|
||||
reachable, cancel := context.WithTimeout(ctx, storeConnectTimeout)
|
||||
defer cancel()
|
||||
if err := database.Ping(reachable, pool); err != nil {
|
||||
pool.Close()
|
||||
return nil, nil, fmt.Errorf("agent database is unreachable: %w", err)
|
||||
}
|
||||
writer, err := newSnapshotWriter(pool)
|
||||
if err != nil {
|
||||
pool.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
return writer, pool.Close, nil
|
||||
}
|
||||
|
||||
// newSnapshotWriter builds the store the agent writes through. A nil pool would make
|
||||
// every publish fail silently at the driver, so it is refused here instead: the agent
|
||||
// must either have a real store or fail to start.
|
||||
func newSnapshotWriter(pool *pgxpool.Pool) (agentstore.Writer, error) {
|
||||
if pool == nil {
|
||||
return nil, errStoreNotLinked
|
||||
}
|
||||
return agentstore.PostgresStore{Pool: pool}, nil
|
||||
}
|
||||
|
||||
var _ agentstore.Writer = agentstore.PostgresStore{}
|
||||
Reference in New Issue
Block a user