Skip to content

Commit 2da268f

Browse files
authored
[bug] proxy: session cannot be transferred after load data local infile (#18787)
In the case of "load data local infile" statement, client sends the first packet, then server sends response, which is "0xFB + filename", after that, client sends content of filename and an empty packet, at last, server sends OK packet. The sequence ID of this OK packet is not 1, and will cause the session cannot be transferred after this stmt finished. So, the solution is: when server sends 0xFB and the sequence ID of next packet is 3 bigger than last one, the next packet MUST be an OK packet, and the transfer is allowed. Approved by: @aylei, @sukki37, @zhangxu19830126
1 parent e74f4b9 commit 2da268f

File tree

4 files changed

+112
-28
lines changed

4 files changed

+112
-28
lines changed

pkg/proxy/tunnel.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,9 @@ func (p *pipe) kickoff(ctx context.Context, peer *pipe) (e error) {
520520
p.mu.started = false
521521
p.mu.cond.Broadcast()
522522
}
523+
524+
var firstCond bool
525+
var currSeq int16
523526
var lastSeq int16 = -1
524527
var rotated bool
525528
prepareNextMessage := func() (terminate bool, err error) {
@@ -557,8 +560,6 @@ func (p *pipe) kickoff(ctx context.Context, peer *pipe) (e error) {
557560
// set txn status and cmd time within the mutex together.
558561
// only server->client pipe need to set the txn status.
559562
if p.name == pipeServerToClient {
560-
var currSeq int16
561-
562563
// issue#16042
563564
if len(tempBuf) > 3 {
564565
currSeq = int16(tempBuf[3])
@@ -576,7 +577,27 @@ func (p *pipe) kickoff(ctx context.Context, peer *pipe) (e error) {
576577
rotated = false
577578
}
578579

579-
inTxn, ok := checkTxnStatus(tempBuf)
580+
// seqID is mainly used for server side. It records the sequence ID of
581+
// each packet.
582+
// In the case of "load data local infile" statement, client sends the
583+
// first packet, then server sends response, which is "0xFB + filename",
584+
// after that, client sends content of filename and an empty packet, at
585+
// last, server sends OK packet. The sequence ID of this OK packet is not
586+
// 1, and will cause the session cannot be transferred after this stmt
587+
// finished.
588+
// So, the solution is: when server sends 0xFB and the sequence ID of
589+
// next packet is 3 bigger than last one, the next packet MUST be an
590+
// OK packet, and the transfer is allowed.
591+
// Related issue: https://github.com/matrixorigin/mo-cloud/issues/4088
592+
var mustOK bool
593+
if !firstCond {
594+
firstCond = isLoadDataLocalInfileRespPacket(tempBuf)
595+
} else {
596+
mustOK = currSeq-lastSeq == 3
597+
firstCond = false
598+
}
599+
600+
inTxn, ok := checkTxnStatus(tempBuf, mustOK)
580601
if ok {
581602
p.mu.inTxn = inTxn
582603
}
@@ -721,10 +742,10 @@ func txnStatus(status uint16) bool {
721742
}
722743

723744
// handleOKPacket handles the OK packet from server to update the txn state.
724-
func handleOKPacket(msg []byte) bool {
745+
func handleOKPacket(msg []byte, mustOK bool) bool {
725746
var mp *frontend.MysqlProtocolImpl
726-
// the sequence ID should be 1 for OK packet.
727-
if msg[3] != 1 {
747+
// if the mustOK is false, then the sequence ID should be 1 for OK packet.
748+
if !mustOK && msg[3] != 1 {
728749
return txnStatus(0)
729750
}
730751
pos := 5
@@ -754,14 +775,14 @@ func handleEOFPacket(msg []byte) bool {
754775
// the first return value is the txn status, and the second return value
755776
// indicates if we can get the txn status from the packet. If it is a ERROR
756777
// packet, the second return value is false.
757-
func checkTxnStatus(msg []byte) (bool, bool) {
778+
func checkTxnStatus(msg []byte, mustOK bool) (bool, bool) {
758779
ok := true
759780
inTxn := true
760781
// For the server->client pipe, we get the transaction status from the
761782
// OK and EOF packet, which is used in connection transfer. If the session
762783
// is in a transaction, a transfer should not start.
763784
if isOKPacket(msg) {
764-
inTxn = handleOKPacket(msg)
785+
inTxn = handleOKPacket(msg, mustOK)
765786
} else if isEOFPacket(msg) {
766787
inTxn = handleEOFPacket(msg)
767788
} else if isErrPacket(msg) {

pkg/proxy/tunnel_test.go

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -691,24 +691,64 @@ func TestReplaceServerConn(t *testing.T) {
691691
}
692692

693693
func TestCheckTxnStatus(t *testing.T) {
694-
inTxn, ok := checkTxnStatus(nil)
695-
require.True(t, ok)
696-
require.True(t, inTxn)
697-
698-
inTxn, ok = checkTxnStatus(makeErrPacket(8))
699-
require.False(t, ok)
700-
require.True(t, inTxn)
701-
702-
p1 := makeOKPacket(5)
703-
value := frontend.SERVER_QUERY_WAS_SLOW | frontend.SERVER_STATUS_NO_GOOD_INDEX_USED
704-
binary.LittleEndian.PutUint16(p1[7:], value)
705-
inTxn, ok = checkTxnStatus(p1)
706-
require.True(t, ok)
707-
require.False(t, inTxn)
708-
709-
value |= frontend.SERVER_STATUS_IN_TRANS
710-
binary.LittleEndian.PutUint16(p1[7:], value)
711-
inTxn, ok = checkTxnStatus(p1)
712-
require.True(t, ok)
713-
require.True(t, inTxn)
694+
t.Run("mustOK false", func(t *testing.T) {
695+
inTxn, ok := checkTxnStatus(nil, false)
696+
require.True(t, ok)
697+
require.True(t, inTxn)
698+
699+
inTxn, ok = checkTxnStatus(makeErrPacket(8), false)
700+
require.False(t, ok)
701+
require.True(t, inTxn)
702+
703+
p1 := makeOKPacket(5)
704+
value := frontend.SERVER_QUERY_WAS_SLOW | frontend.SERVER_STATUS_NO_GOOD_INDEX_USED
705+
binary.LittleEndian.PutUint16(p1[7:], value)
706+
inTxn, ok = checkTxnStatus(p1, false)
707+
require.True(t, ok)
708+
require.False(t, inTxn)
709+
710+
value |= frontend.SERVER_STATUS_IN_TRANS
711+
binary.LittleEndian.PutUint16(p1[7:], value)
712+
inTxn, ok = checkTxnStatus(p1, false)
713+
require.True(t, ok)
714+
require.True(t, inTxn)
715+
})
716+
717+
t.Run("mustOK true", func(t *testing.T) {
718+
inTxn, ok := checkTxnStatus(nil, true)
719+
require.True(t, ok)
720+
require.True(t, inTxn)
721+
722+
inTxn, ok = checkTxnStatus(makeErrPacket(8), true)
723+
require.False(t, ok)
724+
require.True(t, inTxn)
725+
726+
p1 := makeOKPacket(5)
727+
value := frontend.SERVER_QUERY_WAS_SLOW | frontend.SERVER_STATUS_NO_GOOD_INDEX_USED
728+
binary.LittleEndian.PutUint16(p1[7:], value)
729+
inTxn, ok = checkTxnStatus(p1, true)
730+
require.True(t, ok)
731+
require.False(t, inTxn)
732+
733+
value |= frontend.SERVER_STATUS_IN_TRANS
734+
binary.LittleEndian.PutUint16(p1[7:], value)
735+
inTxn, ok = checkTxnStatus(p1, true)
736+
require.True(t, ok)
737+
require.True(t, inTxn)
738+
739+
value ^= frontend.SERVER_STATUS_IN_TRANS
740+
binary.LittleEndian.PutUint16(p1[7:], value)
741+
inTxn, ok = checkTxnStatus(p1, true)
742+
require.True(t, ok)
743+
require.False(t, inTxn)
744+
745+
p1[3] = 4
746+
inTxn, ok = checkTxnStatus(p1, false)
747+
require.True(t, ok)
748+
require.True(t, inTxn)
749+
750+
inTxn, ok = checkTxnStatus(p1, true)
751+
require.True(t, ok)
752+
require.False(t, inTxn)
753+
})
714754
}

pkg/proxy/util.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ func isErrPacket(p []byte) bool {
9898
return false
9999
}
100100

101+
// isLoadDataLocalInfileRespPacket returns true if []byte is a packet
102+
// of load data local infile response.
103+
func isLoadDataLocalInfileRespPacket(p []byte) bool {
104+
if len(p) > 4 && p[4] == 0xFB {
105+
return true
106+
}
107+
return false
108+
}
109+
101110
// isEmptyPacket returns true if []byte is an empty packet.
102111
func isEmptyPacket(p []byte) bool {
103112
return len(p) == 0

pkg/proxy/util_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,20 @@ func TestIsErrPacket(t *testing.T) {
200200
require.True(t, ret)
201201
}
202202

203+
func TestIsLoadDataLocalInfileRespPacket(t *testing.T) {
204+
var data []byte
205+
ret := isLoadDataLocalInfileRespPacket(data)
206+
require.False(t, ret)
207+
208+
data = []byte{0, 0, 0, 0, 2, 0}
209+
ret = isLoadDataLocalInfileRespPacket(data)
210+
require.False(t, ret)
211+
212+
data = []byte{0, 0, 0, 0, 0xFB, 0}
213+
ret = isLoadDataLocalInfileRespPacket(data)
214+
require.True(t, ret)
215+
}
216+
203217
func TestIsDeallocatePacket(t *testing.T) {
204218
var data []byte
205219
ret := isDeallocatePacket(data)

0 commit comments

Comments
 (0)