perf: expand gateway capacity and prevent receipt replay
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package upstream
|
||||
|
||||
import (
|
||||
"cmpp-platform/gateway/internal/protocollog"
|
||||
"cmpp-platform/gateway/internal/queue"
|
||||
"context"
|
||||
"fmt"
|
||||
@@ -19,6 +20,8 @@ const (
|
||||
defaultReconnectInitialDelay = 5 * time.Second
|
||||
defaultReconnectMaximumDelay = 5 * time.Minute
|
||||
defaultAuthReconnectDelay = 5 * time.Minute
|
||||
maximumConnections = 8
|
||||
maximumWindowSize = 64
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
@@ -26,6 +29,14 @@ type Manager struct {
|
||||
EventAPIBaseURL string
|
||||
HTTPClient *http.Client
|
||||
SubmitSegmentPublisher SubmitSegmentPublisher
|
||||
EventPublisher interface {
|
||||
PublishReceipt(context.Context, queue.ReceiptEvent) error
|
||||
PublishUplink(context.Context, queue.UplinkEvent) error
|
||||
}
|
||||
ProtocolLogPublisher interface {
|
||||
Publish(context.Context, protocollog.Event) error
|
||||
}
|
||||
GatewayInstanceID string
|
||||
|
||||
mu sync.Mutex
|
||||
conns map[string]*connectionPool
|
||||
@@ -72,8 +83,14 @@ func (m *Manager) ConnectChannel(ctx context.Context, command queue.ConnectChann
|
||||
WindowSize: command.Channel.WindowSize,
|
||||
HeartbeatIntervalSeconds: command.Channel.HeartbeatIntervalSeconds,
|
||||
HeartbeatMissThreshold: command.Channel.HeartbeatMissThreshold,
|
||||
ConnectionWarmupSeconds: command.Channel.ConnectionWarmupSeconds,
|
||||
ConnectionDrainSeconds: command.Channel.ConnectionDrainSeconds,
|
||||
SubmitTimeoutSeconds: command.Channel.SubmitTimeoutSeconds,
|
||||
FailureCooldownSeconds: command.Channel.FailureCooldownSeconds,
|
||||
})
|
||||
if pool == nil || !pool.matches(config) {
|
||||
if pool != nil && !pool.matches(config) && pool.sameEndpoint(config) {
|
||||
pool.reconfigure(config)
|
||||
} else if pool == nil || !pool.matches(config) {
|
||||
if pool != nil {
|
||||
pool.close()
|
||||
}
|
||||
@@ -130,7 +147,9 @@ func (m *Manager) connectionFor(cmd queue.SubmitCommand) (*connectionPool, error
|
||||
m.ensureDefaultsLocked()
|
||||
|
||||
pool := m.conns[cmd.ChannelID]
|
||||
if pool == nil || !pool.matches(cmd.Upstream) {
|
||||
if pool != nil && !pool.matches(cmd.Upstream) && pool.sameEndpoint(cmd.Upstream) {
|
||||
pool.reconfigure(cmd.Upstream)
|
||||
} else if pool == nil || !pool.matches(cmd.Upstream) {
|
||||
if pool != nil {
|
||||
pool.close()
|
||||
}
|
||||
@@ -170,6 +189,27 @@ func (m *Manager) ConnectionCounts() (desired int, connected int) {
|
||||
return desired, connected
|
||||
}
|
||||
|
||||
func (m *Manager) WindowCounts() (configured int, inFlight int) {
|
||||
m.mu.Lock()
|
||||
pools := make([]*connectionPool, 0, len(m.conns))
|
||||
for _, pool := range m.conns {
|
||||
pools = append(pools, pool)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
for _, pool := range pools {
|
||||
pool.mu.Lock()
|
||||
conns := append([]*connection(nil), pool.conns...)
|
||||
pool.mu.Unlock()
|
||||
for _, conn := range conns {
|
||||
conn.mu.Lock()
|
||||
configured += max(1, conn.windowLimit)
|
||||
inFlight += len(conn.window)
|
||||
conn.mu.Unlock()
|
||||
}
|
||||
}
|
||||
return configured, inFlight
|
||||
}
|
||||
|
||||
func (m *Manager) newConnectionPool(channelID string, connectionID string, config queue.UpstreamConfig) *connectionPool {
|
||||
eventAPIBaseURL := m.EventAPIBaseURL
|
||||
if eventAPIBaseURL == "" {
|
||||
@@ -184,8 +224,11 @@ func (m *Manager) newConnectionPool(channelID string, connectionID string, confi
|
||||
reporter: func(ctx context.Context, state ConnectionState) error {
|
||||
return m.post(ctx, "/admin/gateway/connections", state)
|
||||
},
|
||||
reconnectSignal: make(chan struct{}, 1),
|
||||
stopCh: make(chan struct{}),
|
||||
protocolLogPublisher: m.ProtocolLogPublisher,
|
||||
gatewayInstanceID: m.GatewayInstanceID,
|
||||
eventPublisher: m.EventPublisher,
|
||||
reconnectSignal: make(chan struct{}, 1),
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +245,12 @@ func validateConnectChannelCommand(command queue.ConnectChannelCommand) error {
|
||||
if command.Channel.Account == "" || command.Channel.PasswordCipher == "" {
|
||||
return fmt.Errorf("account and passwordCipher are required")
|
||||
}
|
||||
if command.DesiredConnections < 1 || command.DesiredConnections > maximumConnections {
|
||||
return fmt.Errorf("desiredConnections must be between 1 and %d", maximumConnections)
|
||||
}
|
||||
if command.Channel.WindowSize != 0 && (command.Channel.WindowSize < 1 || command.Channel.WindowSize > maximumWindowSize) {
|
||||
return fmt.Errorf("windowSize must be between 1 and %d", maximumWindowSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -222,5 +271,17 @@ func normalizeUpstreamConfig(config queue.UpstreamConfig) queue.UpstreamConfig {
|
||||
if config.HeartbeatMissThreshold <= 0 {
|
||||
config.HeartbeatMissThreshold = defaultHeartbeatMissThreshold
|
||||
}
|
||||
if config.ConnectionWarmupSeconds < 0 {
|
||||
config.ConnectionWarmupSeconds = 30
|
||||
}
|
||||
if config.ConnectionDrainSeconds <= 0 {
|
||||
config.ConnectionDrainSeconds = 60
|
||||
}
|
||||
if config.SubmitTimeoutSeconds <= 0 {
|
||||
config.SubmitTimeoutSeconds = 60
|
||||
}
|
||||
if config.FailureCooldownSeconds <= 0 {
|
||||
config.FailureCooldownSeconds = 30
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user