fix(splitter): honour reuse timeout and stale jobs
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
4a281e6e25
commit
c250a4d6f2
5 changed files with 65 additions and 3 deletions
|
|
@ -290,6 +290,7 @@ func (m *NonceMapper) OnResultAccepted(sequence int64, accepted bool, errorMessa
|
|||
job := m.storage.job
|
||||
prevJob := m.storage.prevJob
|
||||
m.storage.mu.Unlock()
|
||||
_ = m.storage.IsValidJobID(ctx.JobID)
|
||||
expired := ctx.JobID != "" && ctx.JobID == prevJob.JobID && ctx.JobID != job.JobID
|
||||
m.mu.Unlock()
|
||||
if !ok || miner == nil {
|
||||
|
|
@ -400,7 +401,17 @@ func (s *NonceStorage) IsValidJobID(id string) bool {
|
|||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return id != "" && (id == s.job.JobID || id == s.prevJob.JobID)
|
||||
if id == "" {
|
||||
return false
|
||||
}
|
||||
if id == s.job.JobID {
|
||||
return true
|
||||
}
|
||||
if id == s.prevJob.JobID && s.prevJob.JobID != "" {
|
||||
s.expired++
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SlotCount returns free, dead, and active counts.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ type NonceStorage struct {
|
|||
miners map[int64]*proxy.Miner // minerID → Miner pointer for active miners
|
||||
job proxy.Job // current job from pool
|
||||
prevJob proxy.Job // previous job (for stale submit validation)
|
||||
cursor int // search starts here (round-robin allocation)
|
||||
expired uint64
|
||||
cursor int // search starts here (round-robin allocation)
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,3 +24,22 @@ func TestNonceStorage_AddAndRemove(t *testing.T) {
|
|||
t.Fatalf("unexpected slot counts: free=%d dead=%d active=%d", free, dead, active)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonceStorage_IsValidJobID_Ugly(t *testing.T) {
|
||||
storage := NewNonceStorage()
|
||||
storage.job = proxy.Job{JobID: "job-2"}
|
||||
storage.prevJob = proxy.Job{JobID: "job-1"}
|
||||
|
||||
if !storage.IsValidJobID("job-2") {
|
||||
t.Fatalf("expected current job to be valid")
|
||||
}
|
||||
if !storage.IsValidJobID("job-1") {
|
||||
t.Fatalf("expected previous job to remain valid")
|
||||
}
|
||||
if storage.expired != 1 {
|
||||
t.Fatalf("expected one expired job validation, got %d", storage.expired)
|
||||
}
|
||||
if storage.IsValidJobID("") {
|
||||
t.Fatalf("expected empty job id to be invalid")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,10 +53,11 @@ func (s *SimpleSplitter) OnLogin(event *proxy.LoginEvent) {
|
|||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
now := time.Now()
|
||||
|
||||
if s.cfg.ReuseTimeout > 0 {
|
||||
for id, mapper := range s.idle {
|
||||
if mapper.strategy != nil && mapper.strategy.IsActive() {
|
||||
if mapper.strategy != nil && mapper.strategy.IsActive() && !mapper.idleAt.IsZero() && now.Sub(mapper.idleAt) <= time.Duration(s.cfg.ReuseTimeout)*time.Second {
|
||||
delete(s.idle, id)
|
||||
mapper.miner = event.Miner
|
||||
mapper.idleAt = time.Time{}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package simple
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/proxy"
|
||||
"dappco.re/go/proxy/pool"
|
||||
|
|
@ -24,6 +25,7 @@ func TestSimpleSplitter_OnLogin_Good(t *testing.T) {
|
|||
id: 7,
|
||||
strategy: activeStrategy{},
|
||||
currentJob: job,
|
||||
idleAt: time.Now(),
|
||||
}
|
||||
splitter.idle[mapper.id] = mapper
|
||||
|
||||
|
|
@ -36,3 +38,31 @@ func TestSimpleSplitter_OnLogin_Good(t *testing.T) {
|
|||
t.Fatalf("expected current job to be restored on reuse, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimpleSplitter_OnLogin_Ugly(t *testing.T) {
|
||||
splitter := NewSimpleSplitter(&proxy.Config{ReuseTimeout: 30}, nil, func(listener pool.StratumListener) pool.Strategy {
|
||||
return activeStrategy{}
|
||||
})
|
||||
miner := &proxy.Miner{}
|
||||
expired := &SimpleMapper{
|
||||
id: 7,
|
||||
strategy: activeStrategy{},
|
||||
idleAt: time.Now().Add(-time.Minute),
|
||||
}
|
||||
splitter.idle[expired.id] = expired
|
||||
|
||||
splitter.OnLogin(&proxy.LoginEvent{Miner: miner})
|
||||
|
||||
if miner.RouteID() == expired.id {
|
||||
t.Fatalf("expected expired mapper not to be reclaimed")
|
||||
}
|
||||
if miner.RouteID() != 0 {
|
||||
t.Fatalf("expected a new mapper to be allocated, got route id %d", miner.RouteID())
|
||||
}
|
||||
if len(splitter.active) != 1 {
|
||||
t.Fatalf("expected one active mapper, got %d", len(splitter.active))
|
||||
}
|
||||
if len(splitter.idle) != 1 {
|
||||
t.Fatalf("expected expired mapper to remain idle until GC, got %d idle mappers", len(splitter.idle))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue