daemon/active: implement watchdog to handle stuck replication / pruners
ActiveSide.do() can only run sequentially, i.e. we cannot run replication and pruning in parallel. Why? * go-streamrpc only allows one active request at a time (this is bad design and should be fixed at some point) * replication and pruning are implemented independently, but work on the same resources (snapshots) A: pruning might destroy a snapshot that is planned to be replicated B: replication might replicate snapshots that should be pruned We do not have any resource management / locking for A and B, but we have a use case where users don't want their machine fill up with snapshots if replication does not work. That means we _have_ to run the pruners. A further complication is that we cannot just cancel the replication context after a timeout and move on to the pruner: it could be initial replication and we don't know how long it will take. (And we don't have resumable send & recv yet). With the previous commits, we can implement the watchdog using context cancellation. Note that the 'MadeProgress()' calls can only be placed right before non-error state transition. Otherwise, we could end up in a live-lock.
This commit is contained in:
+20
-1
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NetConnLogger struct {
|
||||
@@ -101,6 +102,14 @@ func (c *ChainedReader) Read(buf []byte) (n int, err error) {
|
||||
|
||||
type ByteCounterReader struct {
|
||||
reader io.ReadCloser
|
||||
|
||||
// called & accessed synchronously during Read, no external access
|
||||
cb func(full int64)
|
||||
cbEvery time.Duration
|
||||
lastCbAt time.Time
|
||||
bytesSinceLastCb int64
|
||||
|
||||
// set atomically because it may be read by multiple threads
|
||||
bytes int64
|
||||
}
|
||||
|
||||
@@ -110,13 +119,23 @@ func NewByteCounterReader(reader io.ReadCloser) *ByteCounterReader {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *ByteCounterReader) SetCallback(every time.Duration, cb func(full int64)) {
|
||||
b.cbEvery = every
|
||||
b.cb = cb
|
||||
}
|
||||
|
||||
func (b *ByteCounterReader) Close() error {
|
||||
return b.reader.Close()
|
||||
}
|
||||
|
||||
func (b *ByteCounterReader) Read(p []byte) (n int, err error) {
|
||||
n, err = b.reader.Read(p)
|
||||
atomic.AddInt64(&b.bytes, int64(n))
|
||||
full := atomic.AddInt64(&b.bytes, int64(n))
|
||||
now := time.Now()
|
||||
if b.cb != nil && now.Sub(b.lastCbAt) > b.cbEvery {
|
||||
b.cb(full)
|
||||
b.lastCbAt = now
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user