refactor(mtproto): sync redesign RPC replay ownership

This commit is contained in:
iamxvbaba 2026-08-02 01:51:03 +08:00
parent 7e0f9d1e62
commit ac0566f779
28 changed files with 1304 additions and 343 deletions

View file

@ -732,8 +732,8 @@ func preflightInboundItem(msgID int64, seqNo int32, typeID uint32, content bool,
// prepareInboundRPCBatch performs the whole container's count/byte admission
// before copying or scheduling any API RPC. Capacity exhaustion is converted
// into one consistent terminal FLOOD_WAIT result per uncached RPC; no business
// handler from the batch is allowed to start in that case.
// into one consistent local 500 WORKER_BUSY_TOO_LONG_RETRY result per uncached
// RPC; no business handler from the batch is allowed to start in that case.
func (s *Server) prepareInboundRPCBatch(ctx context.Context, c *Conn, plan *inboundPlan) error {
if s.layerRPC != nil {
return s.prepareInboundLayerRPCBatch(ctx, c, plan)
@ -784,6 +784,13 @@ func (s *Server) prepareInboundRPCBatch(ctx context.Context, c *Conn, plan *inbo
s.rpcRewrap.commit(candidate)
item.kind = inboundItemReplayRPC
item.payload = claim.encoded
case rpcResultAcquireAcknowledged:
// The client already ACKed the correlated rpc_result. Retire the
// stale rewrap candidate and keep this duplicate ACK-only; neither
// the old body nor the business handler may run again.
s.rpcRewrap.commit(candidate)
item.kind = inboundItemDuplicate
item.payload = nil
case rpcResultAcquirePending:
s.rpcRewrap.commit(candidate)
item.kind = inboundItemRewrappedRPC
@ -847,6 +854,9 @@ func (s *Server) prepareInboundRPCBatch(ctx context.Context, c *Conn, plan *inbo
)
item.kind = inboundItemReplayRPC
item.payload = claim.encoded
case rpcResultAcquireAcknowledged:
item.kind = inboundItemDuplicate
item.payload = nil
case rpcResultAcquirePending:
// A malformed/replayed container may repeat the same msg_id after this
// very plan installed its owner. More generally, any request already in
@ -1026,6 +1036,10 @@ func (s *Server) executeInboundPlan(ctx context.Context, cs *connState, c *Conn,
}); err != nil {
return err
}
// The key cannot reconnect to ACK or replay any old answer. Release every
// logical outbox and receipt only after the terminal OK is physically on
// the wire; retaining them for the offline TTL would be pure leakage.
s.conns.ForgetLogicalSessionsForRawAuthKey(c.authKeyID)
c.beginTerminalShutdown()
c.closeTransport()
return nil
@ -1037,10 +1051,7 @@ func (s *Server) executeInboundPlan(ctx context.Context, cs *connState, c *Conn,
if owner, _ := item.payload.(*rpcResultOwnerLease); owner != nil {
owner.CompleteExecution(false)
}
if err := s.sendResult(ctx, c, item.msgID, &mt.RPCError{
ErrorCode: 420,
ErrorMessage: "FLOOD_WAIT_1",
}); err != nil {
if err := s.sendResult(ctx, c, item.msgID, rpcWorkerBusyError()); err != nil {
return err
}
case inboundItemRPCAdmissionError: