go-proxy/splitter/simple/mapper.go
Virgil 3376cea600 fix(pool): restore failover and stratum job handling
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-04 11:13:41 +00:00

135 lines
2.9 KiB
Go

package simple
import (
"sync"
"time"
"dappco.re/go/core/proxy"
"dappco.re/go/core/proxy/pool"
)
// SimpleMapper holds one outbound pool connection and serves at most one active miner
// at a time. It becomes idle when the miner disconnects and may be reclaimed for the
// next login.
//
// m := simple.NewSimpleMapper(id, strategy)
type SimpleMapper struct {
id int64
miner *proxy.Miner // nil when idle
strategy pool.Strategy
events *proxy.EventBus
pending map[int64]simpleSubmitContext
job proxy.Job
prevJob proxy.Job
idleAt time.Time // zero when active
stopped bool
mu sync.Mutex
}
type simpleSubmitContext struct {
RequestID int64
Expired bool
SubmittedAt time.Time
}
// NewSimpleMapper stores the mapper ID and strategy.
//
// mapper := simple.NewSimpleMapper(1, strategy)
func NewSimpleMapper(id int64, strategy pool.Strategy) *SimpleMapper {
return &SimpleMapper{id: id, strategy: strategy, pending: make(map[int64]simpleSubmitContext)}
}
func (m *SimpleMapper) OnJob(job proxy.Job) {
if !job.IsValid() {
return
}
m.mu.Lock()
if m.job.ClientID == job.ClientID || m.job.ClientID == "" {
m.prevJob = m.job
} else {
m.prevJob = proxy.Job{}
}
m.job = job
miner := m.miner
m.mu.Unlock()
if miner != nil {
miner.ForwardJob(job, job.Algo)
}
}
func (m *SimpleMapper) JobStatus(id string) (valid bool, expired bool) {
m.mu.Lock()
defer m.mu.Unlock()
if id == "" {
return false, false
}
if id == m.job.JobID {
return true, false
}
if id == m.prevJob.JobID {
return true, true
}
return false, false
}
func (m *SimpleMapper) OnResultAccepted(sequence int64, accepted bool, errorMessage string) {
m.mu.Lock()
context, exists := m.pending[sequence]
miner := m.miner
if !exists {
m.mu.Unlock()
return
}
delete(m.pending, sequence)
m.mu.Unlock()
if miner == nil {
return
}
if accepted {
latency := shareLatency(context.SubmittedAt)
if m.events != nil {
m.events.Dispatch(proxy.Event{Type: proxy.EventAccept, Miner: miner, Job: m.currentJob(), Diff: miner.Diff(), Latency: latency, Expired: context.Expired})
}
miner.Success(context.RequestID, "OK")
return
}
latency := shareLatency(context.SubmittedAt)
if m.events != nil {
m.events.Dispatch(proxy.Event{Type: proxy.EventReject, Miner: miner, Job: m.currentJob(), Diff: miner.Diff(), Error: errorMessage, Latency: latency, Expired: context.Expired})
}
miner.ReplyWithError(context.RequestID, errorMessage)
}
func (m *SimpleMapper) OnDisconnect() {
m.stopped = true
}
func (m *SimpleMapper) currentJob() *proxy.Job {
m.mu.Lock()
defer m.mu.Unlock()
if !m.job.IsValid() {
return nil
}
job := m.job
return &job
}
func shareLatency(submittedAt time.Time) uint16 {
if submittedAt.IsZero() {
return 0
}
elapsed := time.Since(submittedAt).Milliseconds()
if elapsed <= 0 {
return 0
}
if elapsed > int64(^uint16(0)) {
return ^uint16(0)
}
return uint16(elapsed)
}