merged with fixes
This commit is contained in:
parent
a9e758b712
commit
2f1818d656
176 changed files with 9000 additions and 907 deletions
190
internal/mtprotoedge/bulk_rpc_scheduler.go
Normal file
190
internal/mtprotoedge/bulk_rpc_scheduler.go
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
package mtprotoedge
|
||||
|
||||
import "sync"
|
||||
|
||||
// bulkRPCScheduler limits runnable file handlers before they enter the shared
|
||||
// inbound worker pool. Waiters remain behind inboundRPCGate, so bulk backend
|
||||
// latency cannot occupy every worker needed by bootstrap and control RPCs.
|
||||
type bulkRPCScheduler struct {
|
||||
mu sync.Mutex
|
||||
max int
|
||||
inUse int
|
||||
waiters []*bulkRPCLease
|
||||
closed bool
|
||||
}
|
||||
|
||||
type bulkRPCLease struct {
|
||||
scheduler *bulkRPCScheduler
|
||||
granted bool
|
||||
released bool
|
||||
notified bool
|
||||
notify func(bool)
|
||||
}
|
||||
|
||||
// bulkRPCAdmission serializes the two bulk prerequisites: a request may enter
|
||||
// the process-wide handler scheduler only after its logical session owns an ACK
|
||||
// window credit. This prevents credit-blocked requests from parking all global
|
||||
// handler slots.
|
||||
type bulkRPCAdmission struct {
|
||||
mu sync.Mutex
|
||||
scheduler *bulkRPCScheduler
|
||||
lease *bulkRPCLease
|
||||
released bool
|
||||
}
|
||||
|
||||
func newBulkRPCScheduler(max int) *bulkRPCScheduler {
|
||||
if max <= 0 {
|
||||
max = 1
|
||||
}
|
||||
return &bulkRPCScheduler{max: max}
|
||||
}
|
||||
|
||||
func (s *bulkRPCScheduler) reserve() *bulkRPCLease {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
lease := &bulkRPCLease{scheduler: s}
|
||||
s.mu.Lock()
|
||||
if s.closed {
|
||||
lease.released = true
|
||||
} else if s.inUse < s.max {
|
||||
s.inUse++
|
||||
lease.granted = true
|
||||
} else {
|
||||
s.waiters = append(s.waiters, lease)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return lease
|
||||
}
|
||||
|
||||
func (l *bulkRPCLease) subscribe(notify func(bool)) {
|
||||
if l == nil || l.scheduler == nil || notify == nil {
|
||||
return
|
||||
}
|
||||
s := l.scheduler
|
||||
s.mu.Lock()
|
||||
l.notify = notify
|
||||
granted, released := l.granted, l.released
|
||||
shouldNotify := (granted || released) && !l.notified
|
||||
if shouldNotify {
|
||||
l.notified = true
|
||||
}
|
||||
s.mu.Unlock()
|
||||
if !shouldNotify {
|
||||
return
|
||||
}
|
||||
if granted {
|
||||
notify(true)
|
||||
} else {
|
||||
notify(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *bulkRPCLease) release() {
|
||||
if l == nil || l.scheduler == nil {
|
||||
return
|
||||
}
|
||||
s := l.scheduler
|
||||
var notifications []func(bool)
|
||||
s.mu.Lock()
|
||||
if l.released {
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
l.released = true
|
||||
if l.granted {
|
||||
l.granted = false
|
||||
s.inUse--
|
||||
}
|
||||
for !s.closed && s.inUse < s.max && len(s.waiters) > 0 {
|
||||
next := s.waiters[0]
|
||||
s.waiters[0] = nil
|
||||
s.waiters = s.waiters[1:]
|
||||
if next == nil || next.released {
|
||||
continue
|
||||
}
|
||||
next.granted = true
|
||||
s.inUse++
|
||||
if next.notify != nil && !next.notified {
|
||||
next.notified = true
|
||||
notifications = append(notifications, next.notify)
|
||||
}
|
||||
}
|
||||
s.mu.Unlock()
|
||||
for _, notify := range notifications {
|
||||
notify(true)
|
||||
}
|
||||
}
|
||||
|
||||
func newBulkRPCAdmission(scheduler *bulkRPCScheduler) *bulkRPCAdmission {
|
||||
if scheduler == nil {
|
||||
return nil
|
||||
}
|
||||
return &bulkRPCAdmission{scheduler: scheduler}
|
||||
}
|
||||
|
||||
func (a *bulkRPCAdmission) subscribeAfter(credit *outboundBulkCredit, notify func(bool)) {
|
||||
if a == nil || a.scheduler == nil || credit == nil || notify == nil {
|
||||
if notify != nil {
|
||||
notify(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
credit.subscribe(func(success bool) {
|
||||
if !success {
|
||||
notify(false)
|
||||
return
|
||||
}
|
||||
lease := a.scheduler.reserve()
|
||||
a.mu.Lock()
|
||||
if a.released {
|
||||
a.mu.Unlock()
|
||||
lease.release()
|
||||
notify(false)
|
||||
return
|
||||
}
|
||||
a.lease = lease
|
||||
a.mu.Unlock()
|
||||
lease.subscribe(notify)
|
||||
})
|
||||
}
|
||||
|
||||
func (a *bulkRPCAdmission) release() {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
a.mu.Lock()
|
||||
if a.released {
|
||||
a.mu.Unlock()
|
||||
return
|
||||
}
|
||||
a.released = true
|
||||
lease := a.lease
|
||||
a.lease = nil
|
||||
a.mu.Unlock()
|
||||
lease.release()
|
||||
}
|
||||
|
||||
func (s *bulkRPCScheduler) close() {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
var notifications []func(bool)
|
||||
s.mu.Lock()
|
||||
s.closed = true
|
||||
for _, lease := range s.waiters {
|
||||
if lease == nil || lease.released {
|
||||
continue
|
||||
}
|
||||
lease.released = true
|
||||
if lease.notify != nil && !lease.notified {
|
||||
lease.notified = true
|
||||
notifications = append(notifications, lease.notify)
|
||||
}
|
||||
}
|
||||
s.waiters = nil
|
||||
s.mu.Unlock()
|
||||
for _, notify := range notifications {
|
||||
notify(false)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue