notify/file_transfer_monitor.go
starainrt 09d972c7b7
feat(notify): 重构通信内核并补齐 stream/bulk/record/transfer 能力
- 引入 LogicalConn/TransportConn 分层,ClientConn 保留兼容适配层
  - 新增 Stream、Bulk、RecordStream 三条数据面能力及对应控制路径
  - 完成 transfer/file 传输内核与状态快照、诊断能力
  - 补齐 reconnect、inbound dispatcher、modern psk 等基础模块
  - 增加大规模回归、并发与基准测试覆盖
  - 更新依赖库
2026-04-15 15:24:36 +08:00

175 lines
4.8 KiB
Go

package notify
import "sync"
const defaultFileTransferCompletedLimit = 128
type fileTransferMonitor struct {
mu sync.Mutex
active map[string]fileTransferSnapshot
completed map[string]fileTransferSnapshot
runtimeActive map[string]fileTransferSnapshot
runtimeCompleted map[string]fileTransferSnapshot
completedLimit int
}
func newFileTransferMonitor() *fileTransferMonitor {
return newFileTransferMonitorWithConfig(defaultFileTransferConfig())
}
func newFileTransferMonitorWithConfig(cfg fileTransferConfig) *fileTransferMonitor {
cfg = normalizeFileTransferConfig(cfg)
return newFileTransferMonitorWithCompletedLimit(cfg.MonitorCompletedLimit)
}
func newFileTransferMonitorWithCompletedLimit(limit int) *fileTransferMonitor {
if limit <= 0 {
limit = defaultFileTransferCompletedLimit
}
return &fileTransferMonitor{
active: make(map[string]fileTransferSnapshot),
completed: make(map[string]fileTransferSnapshot),
runtimeActive: make(map[string]fileTransferSnapshot),
runtimeCompleted: make(map[string]fileTransferSnapshot),
completedLimit: limit,
}
}
func (m *fileTransferMonitor) applyConfig(cfg fileTransferConfig) {
if m == nil {
return
}
cfg = normalizeFileTransferConfig(cfg)
m.mu.Lock()
m.completedLimit = cfg.MonitorCompletedLimit
m.trimCompletedLocked()
m.mu.Unlock()
}
func (m *fileTransferMonitor) observe(direction fileTransferDirection, event FileEvent) {
if m == nil {
return
}
if !isFileTransferObservable(event.Kind) {
return
}
snapshot := fileTransferSnapshotFromEvent(direction, event)
key := fileTransferMonitorKey(direction, snapshot.Scope, snapshot.FileID)
runtimeKey := fileTransferRuntimeMonitorKey(direction, snapshot.RuntimeScope, snapshot.FileID)
if key == "" || runtimeKey == "" {
return
}
m.mu.Lock()
defer m.mu.Unlock()
if isFileTransferTerminal(snapshot.Kind) {
delete(m.active, key)
m.completed[key] = snapshot
delete(m.runtimeActive, runtimeKey)
m.runtimeCompleted[runtimeKey] = snapshot
m.trimCompletedLocked()
return
}
delete(m.completed, key)
m.active[key] = snapshot
delete(m.runtimeCompleted, runtimeKey)
m.runtimeActive[runtimeKey] = snapshot
}
func (m *fileTransferMonitor) activeSnapshots() []fileTransferSnapshot {
if m == nil {
return nil
}
m.mu.Lock()
defer m.mu.Unlock()
return sortedFileTransferSnapshots(m.active)
}
func (m *fileTransferMonitor) activeSnapshotsByDirection(direction fileTransferDirection) []fileTransferSnapshot {
if m == nil {
return nil
}
m.mu.Lock()
defer m.mu.Unlock()
return filteredFileTransferSnapshots(m.active, direction)
}
func (m *fileTransferMonitor) completedSnapshots() []fileTransferSnapshot {
if m == nil {
return nil
}
m.mu.Lock()
defer m.mu.Unlock()
return sortedFileTransferSnapshots(m.completed)
}
func (m *fileTransferMonitor) completedSnapshotsByDirection(direction fileTransferDirection) []fileTransferSnapshot {
if m == nil {
return nil
}
m.mu.Lock()
defer m.mu.Unlock()
return filteredFileTransferSnapshots(m.completed, direction)
}
func (m *fileTransferMonitor) latestSnapshot(direction fileTransferDirection, scope string, fileID string) (fileTransferSnapshot, bool) {
if m == nil {
return fileTransferSnapshot{}, false
}
key := fileTransferMonitorKey(direction, scope, fileID)
if key == "" {
return fileTransferSnapshot{}, false
}
m.mu.Lock()
defer m.mu.Unlock()
if snapshot, ok := m.active[key]; ok {
return snapshot, true
}
snapshot, ok := m.completed[key]
return snapshot, ok
}
func (m *fileTransferMonitor) snapshotsByFileID(fileID string) []fileTransferSnapshot {
if m == nil || fileID == "" {
return nil
}
m.mu.Lock()
defer m.mu.Unlock()
latest := latestFileTransferSnapshotsLocked(m.active, m.completed)
return filterFileTransferSnapshotsByFileID(latest, fileID)
}
func (m *fileTransferMonitor) snapshotsByDirectionAndFileID(direction fileTransferDirection, fileID string) []fileTransferSnapshot {
if m == nil || fileID == "" {
return nil
}
m.mu.Lock()
defer m.mu.Unlock()
latest := latestFileTransferSnapshotsLocked(m.active, m.completed)
return filterFileTransferSnapshotsByDirectionAndFileID(latest, direction, fileID)
}
func (m *fileTransferMonitor) trimCompletedLocked() {
trimFileTransferSnapshotsLocked(m.completed, m.completedLimit)
trimFileTransferSnapshotsLocked(m.runtimeCompleted, m.completedLimit)
}
func trimFileTransferSnapshotsLocked(snapshots map[string]fileTransferSnapshot, limit int) {
if limit <= 0 || len(snapshots) <= limit {
return
}
for len(snapshots) > limit {
oldestKey := ""
oldestSnapshot := fileTransferSnapshot{}
for key, snapshot := range snapshots {
if oldestKey == "" || fileTransferSnapshotOlder(snapshot, oldestSnapshot, key, oldestKey) {
oldestKey = key
oldestSnapshot = snapshot
}
}
if oldestKey == "" {
return
}
delete(snapshots, oldestKey)
}
}