diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index 4396ecbb55a..420d6e33c13 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -142,8 +142,11 @@ type Session struct { SystemVariables map[string]string `protobuf:"bytes,14,rep,name=system_variables,json=systemVariables,proto3" json:"system_variables,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // row_count keeps track of the last seen rows affected for this session RowCount int64 `protobuf:"varint,15,opt,name=row_count,json=rowCount,proto3" json:"row_count,omitempty"` - // savepoints stores all the savepoints call for this session - Savepoints []string `protobuf:"bytes,16,rep,name=savepoints,proto3" json:"savepoints,omitempty"` + // Stores savepoint and release savepoint calls inside a transaction + // and is reset once transaction is committed or rolled back. + Savepoints []string `protobuf:"bytes,16,rep,name=savepoints,proto3" json:"savepoints,omitempty"` + // in_reserved_conn is set to true if the session should be using reserved connections. + InReservedConn bool `protobuf:"varint,17,opt,name=in_reserved_conn,json=inReservedConn,proto3" json:"in_reserved_conn,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -279,6 +282,13 @@ func (m *Session) GetSavepoints() []string { return nil } +func (m *Session) GetInReservedConn() bool { + if m != nil { + return m.InReservedConn + } + return false +} + type Session_ShardSession struct { Target *query.Target `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` TransactionId int64 `protobuf:"varint,2,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` @@ -988,78 +998,79 @@ func init() { func init() { proto.RegisterFile("vtgate.proto", fileDescriptor_aab96496ceaf1ebb) } var fileDescriptor_aab96496ceaf1ebb = []byte{ - // 1157 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xdd, 0x6e, 0x1b, 0x45, - 0x14, 0xee, 0xfa, 0xdf, 0xc7, 0x7f, 0xcb, 0xd4, 0x2d, 0x5b, 0x53, 0xc0, 0x72, 0x5b, 0xd5, 0x0d, - 0xc8, 0x46, 0x41, 0xa0, 0x0a, 0x81, 0x50, 0xe2, 0xb8, 0x95, 0xab, 0x24, 0x0e, 0x63, 0x27, 0x91, - 0x10, 0x68, 0xb5, 0xf1, 0x4e, 0x9c, 0x51, 0x9d, 0x9d, 0xed, 0xcc, 0xd8, 0xc1, 0x4f, 0xc1, 0x3d, - 0x2f, 0xc0, 0x23, 0xf0, 0x0e, 0xdc, 0xf1, 0x3a, 0x5c, 0xa1, 0x99, 0x59, 0xdb, 0x1b, 0x13, 0x68, - 0x9a, 0x2a, 0x37, 0xab, 0x3d, 0x3f, 0x73, 0xe6, 0x9c, 0xef, 0x3b, 0x67, 0x66, 0xa0, 0x38, 0x93, - 0x63, 0x4f, 0x92, 0x56, 0xc8, 0x99, 0x64, 0x28, 0x63, 0xa4, 0x9a, 0x7d, 0x42, 0x83, 0x09, 0x1b, - 0xfb, 0x9e, 0xf4, 0x8c, 0xa5, 0x56, 0x78, 0x33, 0x25, 0x7c, 0x1e, 0x09, 0x65, 0xc9, 0x42, 0x16, - 0x37, 0xce, 0x24, 0x0f, 0x47, 0x46, 0x68, 0xfc, 0x9d, 0x83, 0xec, 0x80, 0x08, 0x41, 0x59, 0x80, - 0x9e, 0x40, 0x99, 0x06, 0xae, 0xe4, 0x5e, 0x20, 0xbc, 0x91, 0xa4, 0x2c, 0x70, 0xac, 0xba, 0xd5, - 0xcc, 0xe1, 0x12, 0x0d, 0x86, 0x2b, 0x25, 0xea, 0x40, 0x59, 0x9c, 0x79, 0xdc, 0x77, 0x85, 0x59, - 0x27, 0x9c, 0x44, 0x3d, 0xd9, 0x2c, 0x6c, 0x3e, 0x6c, 0x45, 0xd9, 0x45, 0xf1, 0x5a, 0x03, 0xe5, - 0x15, 0x09, 0xb8, 0x24, 0x62, 0x92, 0x40, 0x9f, 0x00, 0x78, 0x53, 0xc9, 0x46, 0xec, 0xfc, 0x9c, - 0x4a, 0x27, 0xa5, 0xf7, 0x89, 0x69, 0xd0, 0x23, 0x28, 0x49, 0x8f, 0x8f, 0x89, 0x74, 0x85, 0xe4, - 0x34, 0x18, 0x3b, 0xe9, 0xba, 0xd5, 0xcc, 0xe3, 0xa2, 0x51, 0x0e, 0xb4, 0x0e, 0xb5, 0x21, 0xcb, - 0x42, 0xa9, 0x53, 0xc8, 0xd4, 0xad, 0x66, 0x61, 0xf3, 0x5e, 0xcb, 0x14, 0xde, 0xfd, 0x85, 0x8c, - 0xa6, 0x92, 0xf4, 0x8d, 0x11, 0x2f, 0xbc, 0xd0, 0x36, 0xd8, 0xb1, 0xf2, 0xdc, 0x73, 0xe6, 0x13, - 0x27, 0x5b, 0xb7, 0x9a, 0xe5, 0xcd, 0x0f, 0x17, 0xc9, 0xc7, 0x2a, 0xdd, 0x63, 0x3e, 0xc1, 0x15, - 0x79, 0x59, 0x81, 0xda, 0x90, 0xbb, 0xf0, 0x78, 0x40, 0x83, 0xb1, 0x70, 0x72, 0xba, 0xf0, 0xbb, - 0xd1, 0xae, 0x3f, 0xa8, 0xef, 0xb1, 0xb1, 0xe1, 0xa5, 0x13, 0xfa, 0x1e, 0x8a, 0x21, 0x27, 0x2b, - 0xb4, 0xf2, 0xd7, 0x40, 0xab, 0x10, 0x72, 0xb2, 0xc4, 0x6a, 0x0b, 0x4a, 0x21, 0x13, 0x72, 0x15, - 0x01, 0xae, 0x11, 0xa1, 0xa8, 0x96, 0x2c, 0x43, 0x3c, 0x86, 0xf2, 0xc4, 0x13, 0xd2, 0xa5, 0x81, - 0x20, 0x5c, 0xba, 0xd4, 0x77, 0x0a, 0x75, 0xab, 0x99, 0xc2, 0x45, 0xa5, 0xed, 0x69, 0x65, 0xcf, - 0x47, 0x1f, 0x03, 0x9c, 0xb2, 0x69, 0xe0, 0xbb, 0x9c, 0x5d, 0x08, 0xa7, 0xa8, 0x3d, 0xf2, 0x5a, - 0x83, 0xd9, 0x85, 0x40, 0x2e, 0xdc, 0x9f, 0x0a, 0xc2, 0x5d, 0x9f, 0x9c, 0xd2, 0x80, 0xf8, 0xee, - 0xcc, 0xe3, 0xd4, 0x3b, 0x99, 0x10, 0xe1, 0x94, 0x74, 0x42, 0xcf, 0xd6, 0x13, 0x3a, 0x14, 0x84, - 0xef, 0x18, 0xe7, 0xa3, 0x85, 0x6f, 0x37, 0x90, 0x7c, 0x8e, 0xab, 0xd3, 0x2b, 0x4c, 0xa8, 0x0f, - 0xb6, 0x98, 0x0b, 0x49, 0xce, 0x63, 0xa1, 0xcb, 0x3a, 0xf4, 0xe3, 0x7f, 0xd5, 0xaa, 0xfd, 0xd6, - 0xa2, 0x56, 0xc4, 0x65, 0x2d, 0xfa, 0x08, 0xf2, 0x9c, 0x5d, 0xb8, 0x23, 0x36, 0x0d, 0xa4, 0x53, - 0xa9, 0x5b, 0xcd, 0x24, 0xce, 0x71, 0x76, 0xd1, 0x51, 0xb2, 0x6a, 0x41, 0xe1, 0xcd, 0x48, 0xc8, - 0x68, 0x20, 0x85, 0x63, 0xd7, 0x93, 0xcd, 0x3c, 0x8e, 0x69, 0x6a, 0x7f, 0x58, 0x50, 0x8c, 0x43, - 0x8a, 0x9e, 0x40, 0xc6, 0xb4, 0x9f, 0x9e, 0x8b, 0xc2, 0x66, 0x29, 0xe2, 0x7d, 0xa8, 0x95, 0x38, - 0x32, 0xaa, 0x31, 0x8a, 0x37, 0x19, 0xf5, 0x9d, 0x84, 0xde, 0xb9, 0x14, 0xd3, 0xf6, 0x7c, 0xf4, - 0x1c, 0x8a, 0x52, 0x65, 0x29, 0x5d, 0x6f, 0x42, 0x3d, 0xe1, 0x24, 0xa3, 0x0e, 0x5e, 0x4e, 0xeb, - 0x50, 0x5b, 0xb7, 0x94, 0x11, 0x17, 0xe4, 0x4a, 0x40, 0x9f, 0x42, 0x81, 0x13, 0x41, 0xf8, 0x8c, - 0xf8, 0x2a, 0x7a, 0x4a, 0x47, 0x87, 0x85, 0xaa, 0xe7, 0xd7, 0x7e, 0x82, 0x07, 0xff, 0x09, 0x3d, - 0xb2, 0x21, 0xf9, 0x9a, 0xcc, 0x75, 0x09, 0x79, 0xac, 0x7e, 0xd1, 0x33, 0x48, 0xcf, 0xbc, 0xc9, - 0x94, 0xe8, 0x3c, 0x57, 0xed, 0xbc, 0x4d, 0x83, 0xe5, 0x5a, 0x6c, 0x3c, 0xbe, 0x49, 0x3c, 0xb7, - 0x6a, 0xdb, 0x50, 0xbd, 0x0a, 0xfd, 0x2b, 0x02, 0x57, 0xe3, 0x81, 0xf3, 0xb1, 0x18, 0xaf, 0x52, - 0xb9, 0xa4, 0x9d, 0x6a, 0xfc, 0x9e, 0x80, 0x72, 0x34, 0xaa, 0x98, 0xbc, 0x99, 0x12, 0x21, 0xd1, - 0xe7, 0x90, 0x1f, 0x79, 0x93, 0x09, 0xe1, 0xaa, 0x32, 0x03, 0x73, 0xa5, 0x65, 0x0e, 0xac, 0x8e, - 0xd6, 0xf7, 0x76, 0x70, 0xce, 0x78, 0xf4, 0x7c, 0xf4, 0x0c, 0xb2, 0xd1, 0x50, 0x44, 0xb9, 0x57, - 0xd6, 0xfa, 0x04, 0x2f, 0xec, 0xe8, 0x29, 0xa4, 0x75, 0x59, 0x11, 0xce, 0x1f, 0x2c, 0x8a, 0x54, - 0xdd, 0xad, 0x07, 0x17, 0x1b, 0x3b, 0xfa, 0x0a, 0x22, 0xb0, 0x5d, 0x39, 0x0f, 0x89, 0x46, 0xb7, - 0xbc, 0x59, 0x5d, 0xa7, 0x65, 0x38, 0x0f, 0x09, 0x06, 0xb9, 0xfc, 0x57, 0xac, 0xbf, 0x26, 0x73, - 0x11, 0x7a, 0x23, 0xe2, 0xea, 0xa3, 0x4e, 0x1f, 0x49, 0x79, 0x5c, 0x5a, 0x68, 0x75, 0x2b, 0xc5, - 0x8f, 0xac, 0xec, 0x75, 0x8e, 0xac, 0x57, 0xa9, 0x5c, 0xda, 0xce, 0x34, 0x7e, 0xb5, 0xa0, 0xb2, - 0x44, 0x4a, 0x84, 0x2c, 0x10, 0x6a, 0xc7, 0x34, 0xe1, 0x9c, 0xf1, 0x35, 0x98, 0xf0, 0x41, 0xa7, - 0xab, 0xd4, 0xd8, 0x58, 0xdf, 0x05, 0xa3, 0x0d, 0xc8, 0x70, 0x22, 0xa6, 0x13, 0x19, 0x81, 0x84, - 0xe2, 0x07, 0x1b, 0xd6, 0x16, 0x1c, 0x79, 0x34, 0xfe, 0x4a, 0xc0, 0xdd, 0x28, 0xa3, 0x6d, 0x4f, - 0x8e, 0xce, 0x6e, 0x9d, 0xc0, 0xcf, 0x20, 0xab, 0xb2, 0xa1, 0x44, 0x8d, 0x4a, 0xf2, 0x6a, 0x0a, - 0x17, 0x1e, 0xef, 0x41, 0xa2, 0x27, 0x2e, 0xdd, 0x80, 0x69, 0x73, 0x03, 0x7a, 0x22, 0x7e, 0x03, - 0xde, 0x12, 0xd7, 0x8d, 0xdf, 0x2c, 0xa8, 0x5e, 0xc6, 0xf4, 0xd6, 0xa8, 0xfe, 0x02, 0xb2, 0x86, - 0xc8, 0x05, 0x9a, 0xf7, 0xa3, 0xdc, 0x0c, 0xcd, 0xc7, 0x54, 0x9e, 0x99, 0xd0, 0x0b, 0x37, 0x35, - 0xac, 0xd5, 0x81, 0xe4, 0xc4, 0x3b, 0x7f, 0xaf, 0x91, 0x5d, 0xce, 0x61, 0xe2, 0xdd, 0xe6, 0x30, - 0x79, 0xe3, 0x39, 0x4c, 0xbd, 0x85, 0x9b, 0xf4, 0xb5, 0x9e, 0x0e, 0x31, 0x6c, 0x33, 0xff, 0x8f, - 0x6d, 0xa3, 0x03, 0xf7, 0xd6, 0x80, 0x8a, 0x68, 0x5c, 0xcd, 0x97, 0xf5, 0xd6, 0xf9, 0xfa, 0x19, - 0x1e, 0x60, 0x22, 0xd8, 0x64, 0x46, 0x62, 0x9d, 0x77, 0x33, 0xc8, 0x11, 0xa4, 0x7c, 0x19, 0x5d, - 0x43, 0x79, 0xac, 0xff, 0x1b, 0x0f, 0xa1, 0x76, 0x55, 0x78, 0x93, 0x68, 0xe3, 0x4f, 0x0b, 0xca, - 0x47, 0xa6, 0x86, 0x9b, 0x6d, 0xb9, 0x46, 0x5e, 0xe2, 0x9a, 0xe4, 0x3d, 0x85, 0xf4, 0x6c, 0xac, - 0x52, 0x5d, 0x1c, 0xd2, 0xb1, 0x97, 0xed, 0xd1, 0x4b, 0x49, 0x7d, 0x6c, 0xec, 0x0a, 0xc9, 0x53, - 0x3a, 0x91, 0x84, 0x6b, 0x76, 0x15, 0x92, 0x31, 0xcf, 0x17, 0xda, 0x82, 0x23, 0x8f, 0xc6, 0x77, - 0x50, 0x59, 0xd6, 0xb2, 0x22, 0x82, 0xcc, 0x88, 0xba, 0xf6, 0x2d, 0xdd, 0xfc, 0x97, 0x96, 0x1f, - 0x75, 0x95, 0x09, 0x47, 0x1e, 0x1b, 0x3b, 0x50, 0x59, 0x7b, 0x13, 0xa2, 0x0a, 0x14, 0x0e, 0xf7, - 0x07, 0x07, 0xdd, 0x4e, 0xef, 0x45, 0xaf, 0xbb, 0x63, 0xdf, 0x41, 0x00, 0x99, 0x41, 0x6f, 0xff, - 0xe5, 0x6e, 0xd7, 0xb6, 0x50, 0x1e, 0xd2, 0x7b, 0x87, 0xbb, 0xc3, 0x9e, 0x9d, 0x50, 0xbf, 0xc3, - 0xe3, 0xfe, 0x41, 0xc7, 0x4e, 0x6e, 0x7c, 0x0b, 0x85, 0x8e, 0x7e, 0xd9, 0xf6, 0xb9, 0x4f, 0xb8, - 0x5a, 0xb0, 0xdf, 0xc7, 0x7b, 0x5b, 0xbb, 0xf6, 0x1d, 0x94, 0x85, 0xe4, 0x01, 0x56, 0x2b, 0x73, - 0x90, 0x3a, 0xe8, 0x0f, 0x86, 0x76, 0x02, 0x95, 0x01, 0xb6, 0x0e, 0x87, 0xfd, 0x4e, 0x7f, 0x6f, - 0xaf, 0x37, 0xb4, 0x93, 0xdb, 0x5f, 0x43, 0x85, 0xb2, 0xd6, 0x8c, 0x4a, 0x22, 0x84, 0x79, 0xb8, - 0xff, 0xf8, 0x28, 0x92, 0x28, 0x6b, 0x9b, 0xbf, 0xf6, 0x98, 0xb5, 0x67, 0xb2, 0xad, 0xad, 0x6d, - 0xd3, 0x9a, 0x27, 0x19, 0x2d, 0x7d, 0xf9, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5f, 0xb1, 0x56, - 0x1a, 0x38, 0x0c, 0x00, 0x00, + // 1181 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xdd, 0x8e, 0x1b, 0xb5, + 0x17, 0xef, 0x64, 0xf2, 0x35, 0x27, 0x5f, 0x53, 0x37, 0xed, 0x7f, 0x9a, 0x7f, 0x81, 0x28, 0x6d, + 0xd5, 0xb4, 0xa0, 0x04, 0x2d, 0x02, 0x55, 0x08, 0x84, 0x76, 0xb3, 0x69, 0x95, 0xaa, 0xdb, 0x2c, + 0x4e, 0x76, 0x2b, 0x21, 0xd0, 0x68, 0x9a, 0x71, 0x53, 0xab, 0xd9, 0xf1, 0xd4, 0x76, 0xb2, 0xe4, + 0x29, 0xb8, 0xe7, 0x1a, 0x89, 0x47, 0xe0, 0x1d, 0xb8, 0xe3, 0x8d, 0x90, 0xed, 0x49, 0x32, 0x1b, + 0x16, 0xba, 0x6d, 0xb5, 0x37, 0x23, 0x9f, 0x0f, 0x9f, 0x39, 0xe7, 0xf7, 0x3b, 0xc7, 0x36, 0x94, + 0x17, 0x72, 0x1a, 0x48, 0xd2, 0x89, 0x39, 0x93, 0x0c, 0xe5, 0x8d, 0xd4, 0x70, 0x5f, 0xd0, 0x68, + 0xc6, 0xa6, 0x61, 0x20, 0x03, 0x63, 0x69, 0x94, 0xde, 0xcc, 0x09, 0x5f, 0x26, 0x42, 0x55, 0xb2, + 0x98, 0xa5, 0x8d, 0x0b, 0xc9, 0xe3, 0x89, 0x11, 0x5a, 0xbf, 0x39, 0x50, 0x18, 0x11, 0x21, 0x28, + 0x8b, 0xd0, 0x5d, 0xa8, 0xd2, 0xc8, 0x97, 0x3c, 0x88, 0x44, 0x30, 0x91, 0x94, 0x45, 0x9e, 0xd5, + 0xb4, 0xda, 0x45, 0x5c, 0xa1, 0xd1, 0x78, 0xa3, 0x44, 0x3d, 0xa8, 0x8a, 0x57, 0x01, 0x0f, 0x7d, + 0x61, 0xf6, 0x09, 0x2f, 0xd3, 0xb4, 0xdb, 0xa5, 0x9d, 0x5b, 0x9d, 0x24, 0xbb, 0x24, 0x5e, 0x67, + 0xa4, 0xbc, 0x12, 0x01, 0x57, 0x44, 0x4a, 0x12, 0xe8, 0x63, 0x80, 0x60, 0x2e, 0xd9, 0x84, 0x9d, + 0x9c, 0x50, 0xe9, 0x65, 0xf5, 0x7f, 0x52, 0x1a, 0x74, 0x1b, 0x2a, 0x32, 0xe0, 0x53, 0x22, 0x7d, + 0x21, 0x39, 0x8d, 0xa6, 0x5e, 0xae, 0x69, 0xb5, 0x1d, 0x5c, 0x36, 0xca, 0x91, 0xd6, 0xa1, 0x2e, + 0x14, 0x58, 0x2c, 0x75, 0x0a, 0xf9, 0xa6, 0xd5, 0x2e, 0xed, 0x5c, 0xef, 0x98, 0xc2, 0xfb, 0x3f, + 0x93, 0xc9, 0x5c, 0x92, 0xa1, 0x31, 0xe2, 0x95, 0x17, 0xda, 0x03, 0x37, 0x55, 0x9e, 0x7f, 0xc2, + 0x42, 0xe2, 0x15, 0x9a, 0x56, 0xbb, 0xba, 0xf3, 0xbf, 0x55, 0xf2, 0xa9, 0x4a, 0x0f, 0x58, 0x48, + 0x70, 0x4d, 0x9e, 0x55, 0xa0, 0x2e, 0x14, 0x4f, 0x03, 0x1e, 0xd1, 0x68, 0x2a, 0xbc, 0xa2, 0x2e, + 0xfc, 0x5a, 0xf2, 0xd7, 0xef, 0xd5, 0xf7, 0xb9, 0xb1, 0xe1, 0xb5, 0x13, 0xfa, 0x0e, 0xca, 0x31, + 0x27, 0x1b, 0xb4, 0x9c, 0x0b, 0xa0, 0x55, 0x8a, 0x39, 0x59, 0x63, 0xb5, 0x0b, 0x95, 0x98, 0x09, + 0xb9, 0x89, 0x00, 0x17, 0x88, 0x50, 0x56, 0x5b, 0xd6, 0x21, 0xee, 0x40, 0x75, 0x16, 0x08, 0xe9, + 0xd3, 0x48, 0x10, 0x2e, 0x7d, 0x1a, 0x7a, 0xa5, 0xa6, 0xd5, 0xce, 0xe2, 0xb2, 0xd2, 0x0e, 0xb4, + 0x72, 0x10, 0xa2, 0x8f, 0x00, 0x5e, 0xb2, 0x79, 0x14, 0xfa, 0x9c, 0x9d, 0x0a, 0xaf, 0xac, 0x3d, + 0x1c, 0xad, 0xc1, 0xec, 0x54, 0x20, 0x1f, 0x6e, 0xcc, 0x05, 0xe1, 0x7e, 0x48, 0x5e, 0xd2, 0x88, + 0x84, 0xfe, 0x22, 0xe0, 0x34, 0x78, 0x31, 0x23, 0xc2, 0xab, 0xe8, 0x84, 0xee, 0x6f, 0x27, 0x74, + 0x24, 0x08, 0xdf, 0x37, 0xce, 0xc7, 0x2b, 0xdf, 0x7e, 0x24, 0xf9, 0x12, 0xd7, 0xe7, 0xe7, 0x98, + 0xd0, 0x10, 0x5c, 0xb1, 0x14, 0x92, 0x9c, 0xa4, 0x42, 0x57, 0x75, 0xe8, 0x3b, 0xff, 0xa8, 0x55, + 0xfb, 0x6d, 0x45, 0xad, 0x89, 0xb3, 0x5a, 0xf4, 0x7f, 0x70, 0x38, 0x3b, 0xf5, 0x27, 0x6c, 0x1e, + 0x49, 0xaf, 0xd6, 0xb4, 0xda, 0x36, 0x2e, 0x72, 0x76, 0xda, 0x53, 0xb2, 0x6a, 0x41, 0x11, 0x2c, + 0x48, 0xcc, 0x68, 0x24, 0x85, 0xe7, 0x36, 0xed, 0xb6, 0x83, 0x53, 0x1a, 0xd4, 0x06, 0x97, 0x46, + 0x3e, 0x27, 0x82, 0xf0, 0x05, 0x09, 0xfd, 0x09, 0x8b, 0x22, 0xef, 0xaa, 0x6e, 0xd4, 0x2a, 0x8d, + 0x70, 0xa2, 0xee, 0xb1, 0x28, 0x6a, 0xfc, 0x61, 0x41, 0x39, 0x0d, 0x3e, 0xba, 0x0b, 0x79, 0xd3, + 0xa8, 0x7a, 0x82, 0x4a, 0x3b, 0x95, 0xa4, 0x43, 0xc6, 0x5a, 0x89, 0x13, 0xa3, 0x1a, 0xb8, 0x74, + 0x3b, 0xd2, 0xd0, 0xcb, 0xe8, 0x1c, 0x2b, 0x29, 0xed, 0x20, 0x44, 0x0f, 0xa1, 0x2c, 0x55, 0x3d, + 0xd2, 0x0f, 0x66, 0x34, 0x10, 0x9e, 0x9d, 0xf4, 0xfa, 0x7a, 0xae, 0xc7, 0xda, 0xba, 0xab, 0x8c, + 0xb8, 0x24, 0x37, 0x02, 0xfa, 0x04, 0x4a, 0xeb, 0xfc, 0x69, 0xa8, 0xc7, 0xcc, 0xc6, 0xb0, 0x52, + 0x0d, 0xc2, 0xc6, 0x8f, 0x70, 0xf3, 0x5f, 0x49, 0x42, 0x2e, 0xd8, 0xaf, 0xc9, 0x52, 0x97, 0xe0, + 0x60, 0xb5, 0x44, 0xf7, 0x21, 0xb7, 0x08, 0x66, 0x73, 0xa2, 0xf3, 0xdc, 0x34, 0xfe, 0x1e, 0x8d, + 0xd6, 0x7b, 0xb1, 0xf1, 0xf8, 0x3a, 0xf3, 0xd0, 0x6a, 0xec, 0x41, 0xfd, 0x3c, 0x9e, 0xce, 0x09, + 0x5c, 0x4f, 0x07, 0x76, 0x52, 0x31, 0x9e, 0x64, 0x8b, 0xb6, 0x9b, 0x6d, 0xfd, 0x9e, 0x81, 0x6a, + 0x32, 0xd4, 0x98, 0xbc, 0x99, 0x13, 0x21, 0xd1, 0x67, 0xe0, 0x4c, 0x82, 0xd9, 0x8c, 0x70, 0x55, + 0x99, 0x81, 0xb9, 0xd6, 0x31, 0x47, 0x5b, 0x4f, 0xeb, 0x07, 0xfb, 0xb8, 0x68, 0x3c, 0x06, 0x21, + 0xba, 0x0f, 0x85, 0x64, 0x7c, 0x92, 0xdc, 0x6b, 0x5b, 0x1d, 0x85, 0x57, 0x76, 0x74, 0x0f, 0x72, + 0xba, 0xac, 0x04, 0xe7, 0xab, 0xab, 0x22, 0xd5, 0x1c, 0xe8, 0x11, 0xc7, 0xc6, 0x8e, 0xbe, 0x84, + 0x04, 0x6c, 0x5f, 0x2e, 0x63, 0xa2, 0xd1, 0xad, 0xee, 0xd4, 0xb7, 0x69, 0x19, 0x2f, 0x63, 0x82, + 0x41, 0xae, 0xd7, 0x8a, 0xf5, 0xd7, 0x64, 0x29, 0xe2, 0x60, 0x42, 0x7c, 0x7d, 0x28, 0xea, 0xc3, + 0xcb, 0xc1, 0x95, 0x95, 0x56, 0xb7, 0x52, 0xfa, 0x70, 0x2b, 0x5c, 0xe4, 0x70, 0x7b, 0x92, 0x2d, + 0xe6, 0xdc, 0x7c, 0xeb, 0x17, 0x0b, 0x6a, 0x6b, 0xa4, 0x44, 0xcc, 0x22, 0xa1, 0xfe, 0x98, 0x23, + 0x9c, 0x33, 0xbe, 0x05, 0x13, 0x3e, 0xec, 0xf5, 0x95, 0x1a, 0x1b, 0xeb, 0xbb, 0x60, 0xf4, 0x00, + 0xf2, 0x9c, 0x88, 0xf9, 0x4c, 0x26, 0x20, 0xa1, 0xf4, 0x11, 0x88, 0xb5, 0x05, 0x27, 0x1e, 0xad, + 0xbf, 0x32, 0x70, 0x2d, 0xc9, 0x68, 0x2f, 0x90, 0x93, 0x57, 0x97, 0x4e, 0xe0, 0xa7, 0x50, 0x50, + 0xd9, 0x50, 0xa2, 0x46, 0xc5, 0x3e, 0x9f, 0xc2, 0x95, 0xc7, 0x07, 0x90, 0x18, 0x88, 0x33, 0x77, + 0x65, 0xce, 0xdc, 0x95, 0x81, 0x48, 0xdf, 0x95, 0x97, 0xc4, 0x75, 0xeb, 0x57, 0x0b, 0xea, 0x67, + 0x31, 0xbd, 0x34, 0xaa, 0x3f, 0x87, 0x82, 0x21, 0x72, 0x85, 0xe6, 0x8d, 0x24, 0x37, 0x43, 0xf3, + 0x73, 0x2a, 0x5f, 0x99, 0xd0, 0x2b, 0x37, 0x35, 0xac, 0xf5, 0x91, 0xe4, 0x24, 0x38, 0xf9, 0xa0, + 0x91, 0x5d, 0xcf, 0x61, 0xe6, 0xdd, 0xe6, 0xd0, 0x7e, 0xef, 0x39, 0xcc, 0xbe, 0x85, 0x9b, 0xdc, + 0x85, 0x1e, 0x19, 0x29, 0x6c, 0xf3, 0xff, 0x8d, 0x6d, 0xab, 0x07, 0xd7, 0xb7, 0x80, 0x4a, 0x68, + 0xdc, 0xcc, 0x97, 0xf5, 0xd6, 0xf9, 0xfa, 0x09, 0x6e, 0x62, 0x22, 0xd8, 0x6c, 0x41, 0x52, 0x9d, + 0xf7, 0x7e, 0x90, 0x23, 0xc8, 0x86, 0x32, 0xb9, 0x86, 0x1c, 0xac, 0xd7, 0xad, 0x5b, 0xd0, 0x38, + 0x2f, 0xbc, 0x49, 0xb4, 0xf5, 0xa7, 0x05, 0xd5, 0x63, 0x53, 0xc3, 0xfb, 0xfd, 0x72, 0x8b, 0xbc, + 0xcc, 0x05, 0xc9, 0xbb, 0x07, 0xb9, 0xc5, 0x54, 0xa5, 0xba, 0x3a, 0xa4, 0x53, 0x6f, 0xe0, 0xe3, + 0xc7, 0x92, 0x86, 0xd8, 0xd8, 0x15, 0x92, 0x2f, 0xe9, 0x4c, 0x12, 0xae, 0xd9, 0x55, 0x48, 0xa6, + 0x3c, 0x1f, 0x69, 0x0b, 0x4e, 0x3c, 0x5a, 0xdf, 0x42, 0x6d, 0x5d, 0xcb, 0x86, 0x08, 0xb2, 0x20, + 0xea, 0x81, 0x60, 0xe9, 0xe6, 0x3f, 0xb3, 0xfd, 0xb8, 0xaf, 0x4c, 0x38, 0xf1, 0x78, 0xb0, 0x0f, + 0xb5, 0xad, 0xd7, 0x23, 0xaa, 0x41, 0xe9, 0xe8, 0xd9, 0xe8, 0xb0, 0xdf, 0x1b, 0x3c, 0x1a, 0xf4, + 0xf7, 0xdd, 0x2b, 0x08, 0x20, 0x3f, 0x1a, 0x3c, 0x7b, 0xfc, 0xb4, 0xef, 0x5a, 0xc8, 0x81, 0xdc, + 0xc1, 0xd1, 0xd3, 0xf1, 0xc0, 0xcd, 0xa8, 0xe5, 0xf8, 0xf9, 0xf0, 0xb0, 0xe7, 0xda, 0x0f, 0xbe, + 0x81, 0x52, 0x4f, 0xbf, 0x81, 0x87, 0x3c, 0x24, 0x5c, 0x6d, 0x78, 0x36, 0xc4, 0x07, 0xbb, 0x4f, + 0xdd, 0x2b, 0xa8, 0x00, 0xf6, 0x21, 0x56, 0x3b, 0x8b, 0x90, 0x3d, 0x1c, 0x8e, 0xc6, 0x6e, 0x06, + 0x55, 0x01, 0x76, 0x8f, 0xc6, 0xc3, 0xde, 0xf0, 0xe0, 0x60, 0x30, 0x76, 0xed, 0xbd, 0xaf, 0xa0, + 0x46, 0x59, 0x67, 0x41, 0x25, 0x11, 0xc2, 0x3c, 0xf1, 0x7f, 0xb8, 0x9d, 0x48, 0x94, 0x75, 0xcd, + 0xaa, 0x3b, 0x65, 0xdd, 0x85, 0xec, 0x6a, 0x6b, 0xd7, 0xb4, 0xe6, 0x8b, 0xbc, 0x96, 0xbe, 0xf8, + 0x3b, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x94, 0x91, 0x45, 0x62, 0x0c, 0x00, 0x00, } diff --git a/go/vt/vtcombo/tablet_map.go b/go/vt/vtcombo/tablet_map.go index 4dd58d8556d..e586d59bad5 100644 --- a/go/vt/vtcombo/tablet_map.go +++ b/go/vt/vtcombo/tablet_map.go @@ -474,14 +474,16 @@ func (itc *internalTabletConn) HandlePanic(err *error) { } //ReserveBeginExecute is part of the QueryService interface. -func (itc *internalTabletConn) ReserveBeginExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { - res, transactionID, reservedID, alias, err := itc.tablet.qsc.QueryService().ReserveBeginExecute(ctx, target, sql, preQueries, bindVariables, options) +func (itc *internalTabletConn) ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { + bindVariables = sqltypes.CopyBindVariables(bindVariables) + res, transactionID, reservedID, alias, err := itc.tablet.qsc.QueryService().ReserveBeginExecute(ctx, target, preQueries, sql, bindVariables, options) return res, transactionID, reservedID, alias, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err)) } //ReserveBeginExecute is part of the QueryService interface. -func (itc *internalTabletConn) ReserveExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, txID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { - res, reservedID, alias, err := itc.tablet.qsc.QueryService().ReserveExecute(ctx, target, sql, preQueries, bindVariables, txID, options) +func (itc *internalTabletConn) ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { + bindVariables = sqltypes.CopyBindVariables(bindVariables) + res, reservedID, alias, err := itc.tablet.qsc.QueryService().ReserveExecute(ctx, target, preQueries, sql, bindVariables, transactionID, options) return res, reservedID, alias, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err)) } diff --git a/go/vt/vterrors/aggregate.go b/go/vt/vterrors/aggregate.go index 232d778908b..afe48ae8d41 100644 --- a/go/vt/vterrors/aggregate.go +++ b/go/vt/vterrors/aggregate.go @@ -80,6 +80,9 @@ func Aggregate(errors []error) error { if len(errors) == 0 { return nil } + if len(errors) == 1 { + return errors[0] + } return New(aggregateCodes(errors), aggregateErrors(errors)) } diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 0ff7d427846..caf86f8bfa8 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -269,7 +269,7 @@ func (e *Executor) addNeededBindVars(bindVarNeeds sqlparser.BindVarNeeds, bindVa } func (e *Executor) destinationExec(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { - return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession, false /* notInTransaction */, safeSession.Options, logStats, false /* canAutocommit */) + return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession, safeSession.Options, logStats, false /* canAutocommit */) } func (e *Executor) handleBegin(ctx context.Context, safeSession *SafeSession, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { @@ -333,7 +333,7 @@ func (e *Executor) handleSavepoint(ctx context.Context, safeSession *SafeSession queries[i] = &querypb.BoundQuery{Sql: sql} } - qr, errs := e.ExecuteMultiShard(ctx, rss, queries, safeSession, false, false) + qr, errs := e.ExecuteMultiShard(ctx, rss, queries, safeSession, false /*autocommit*/) err := vterrors.Aggregate(errs) if err != nil { return nil, err @@ -1653,8 +1653,8 @@ func (e *Executor) handlePrepare(ctx context.Context, safeSession *SafeSession, } // ExecuteMultiShard implements the IExecutor interface -func (e *Executor) ExecuteMultiShard(ctx context.Context, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, notInTransaction bool, autocommit bool) (qr *sqltypes.Result, errs []error) { - return e.scatterConn.ExecuteMultiShard(ctx, rss, queries, session, notInTransaction, autocommit) +func (e *Executor) ExecuteMultiShard(ctx context.Context, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool) (qr *sqltypes.Result, errs []error) { + return e.scatterConn.ExecuteMultiShard(ctx, rss, queries, session, autocommit) } // StreamExecuteMulti implements the IExecutor interface diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 4437f77da83..92e811e38d5 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -300,7 +300,7 @@ func TestExecutorAutocommit(t *testing.T) { assert.NotEqual(t, uint64(0), logStats.RowsAffected, "logstats: expected non-zero RowsAffected") // autocommit = 1, "begin" - session.Reset() + session.ResetTx() startCount = sbclookup.CommitCount.Get() _, err = executor.Execute(ctx, "TestExecute", session, "begin", nil) require.NoError(t, err) diff --git a/go/vt/vtgate/resolver.go b/go/vt/vtgate/resolver.go index 25cedde9ab9..437ffd095a4 100644 --- a/go/vt/vtgate/resolver.go +++ b/go/vt/vtgate/resolver.go @@ -63,7 +63,6 @@ func (res *Resolver) Execute( tabletType topodatapb.TabletType, destination key.Destination, session *SafeSession, - notInTransaction bool, options *querypb.ExecuteOptions, logStats *LogStats, canAutocommit bool, @@ -78,17 +77,25 @@ func (res *Resolver) Execute( autocommit := len(rss) == 1 && canAutocommit && session.AutocommitApproval() + queries := make([]*querypb.BoundQuery, len(rss)) + for i := range rss { + queries[i] = &querypb.BoundQuery{ + Sql: sql, + BindVariables: bindVars, + } + } + + session.SetOptions(options) + for { - qr, err := res.scatterConn.Execute( + qr, errors := res.scatterConn.ExecuteMultiShard( ctx, - sql, - bindVars, rss, + queries, session, - notInTransaction, - options, autocommit, ) + err = vterrors.Aggregate(errors) if isRetryableError(err) { newRss, err := res.resolver.ResolveDestination(ctx, keyspace, tabletType, destination) if err != nil { diff --git a/go/vt/vtgate/safe_session.go b/go/vt/vtgate/safe_session.go index 0d58e38d70f..29329cc44f3 100644 --- a/go/vt/vtgate/safe_session.go +++ b/go/vt/vtgate/safe_session.go @@ -30,7 +30,7 @@ import ( // SafeSession is a mutex-protected version of the Session. // It is thread-safe if each thread only accesses one shard. -// (the use pattern is 'Find', if not found, then 'Append', +// (the use pattern is 'Find', if not found, then 'AppendOrUpdate', // for a single shard) type SafeSession struct { mu sync.Mutex @@ -85,6 +85,22 @@ func NewAutocommitSession(sessn *vtgatepb.Session) *SafeSession { return NewSafeSession(newSession) } +// ResetTx clears the session +func (session *SafeSession) ResetTx() { + session.mu.Lock() + defer session.mu.Unlock() + session.mustRollback = false + session.autocommitState = notAutocommittable + session.Session.InTransaction = false + session.commitOrder = vtgatepb.CommitOrder_NORMAL + session.Savepoints = nil + if !session.Session.InReservedConn { + session.ShardSessions = nil + session.PreSessions = nil + session.PostSessions = nil + } +} + // Reset clears the session func (session *SafeSession) Reset() { session.mu.Lock() @@ -92,11 +108,12 @@ func (session *SafeSession) Reset() { session.mustRollback = false session.autocommitState = notAutocommittable session.Session.InTransaction = false + session.commitOrder = vtgatepb.CommitOrder_NORMAL + session.Savepoints = nil session.ShardSessions = nil session.PreSessions = nil session.PostSessions = nil - session.commitOrder = vtgatepb.CommitOrder_NORMAL - session.Savepoints = nil + session.Session.InReservedConn = false } // SetAutocommittable sets the state to autocommitable if true. @@ -151,7 +168,7 @@ func (session *SafeSession) InTransaction() bool { } // Find returns the transactionId and tabletAlias, if any, for a session -func (session *SafeSession) Find(keyspace, shard string, tabletType topodatapb.TabletType) (transactionID int64, alias *topodatapb.TabletAlias) { +func (session *SafeSession) Find(keyspace, shard string, tabletType topodatapb.TabletType) (transactionID int64, reservedID int64, alias *topodatapb.TabletAlias) { session.mu.Lock() defer session.mu.Unlock() sessions := session.ShardSessions @@ -163,41 +180,80 @@ func (session *SafeSession) Find(keyspace, shard string, tabletType topodatapb.T } for _, shardSession := range sessions { if keyspace == shardSession.Target.Keyspace && tabletType == shardSession.Target.TabletType && shard == shardSession.Target.Shard { - return shardSession.TransactionId, shardSession.TabletAlias + return shardSession.TransactionId, shardSession.ReservedId, shardSession.TabletAlias + } + } + return 0, 0, nil +} + +func addOrUpdate(shardSession *vtgatepb.Session_ShardSession, sessions []*vtgatepb.Session_ShardSession) ([]*vtgatepb.Session_ShardSession, error) { + appendSession := true + for i, sess := range sessions { + targetedAtSameTablet := sess.Target.Keyspace == shardSession.Target.Keyspace && + sess.Target.TabletType == shardSession.Target.TabletType && + sess.Target.Shard == shardSession.Target.Shard + if targetedAtSameTablet { + if sess.TabletAlias != shardSession.TabletAlias { + return nil, vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "got a different alias for the same target") + } + // replace the old info with the new one + sessions[i] = shardSession + appendSession = false + break } } - return 0, nil + if appendSession { + sessions = append(sessions, shardSession) + } + + return sessions, nil } -// Append adds a new ShardSession -func (session *SafeSession) Append(shardSession *vtgatepb.Session_ShardSession, txMode vtgatepb.TransactionMode) error { +// AppendOrUpdate adds a new ShardSession, or updates an existing one if one already exists for the given shard session +func (session *SafeSession) AppendOrUpdate(shardSession *vtgatepb.Session_ShardSession, txMode vtgatepb.TransactionMode) error { session.mu.Lock() defer session.mu.Unlock() if session.autocommitState == autocommitted { - // Unreachable. - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: SafeSession.Append: unexpected autocommit state") + // Should be unreachable + return vterrors.New(vtrpcpb.Code_INTERNAL, "BUG: SafeSession.AppendOrUpdate: unexpected autocommit state") } - if !session.Session.InTransaction { - // Unreachable. - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: SafeSession.Append: not in transaction") + if !(session.Session.InTransaction || session.Session.InReservedConn) { + // Should be unreachable + return vterrors.New(vtrpcpb.Code_INTERNAL, "BUG: SafeSession.AppendOrUpdate: not in transaction and not in reserved connection") } session.autocommitState = notAutocommittable // Always append, in order for rollback to succeed. switch session.commitOrder { case vtgatepb.CommitOrder_NORMAL: - session.ShardSessions = append(session.ShardSessions, shardSession) + newSessions, err := addOrUpdate(shardSession, session.ShardSessions) + if err != nil { + return err + } + session.ShardSessions = newSessions // isSingle is enforced only for normmal commit order operations. if session.isSingleDB(txMode) && len(session.ShardSessions) > 1 { session.mustRollback = true return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "multi-db transaction attempted: %v", session.ShardSessions) } case vtgatepb.CommitOrder_PRE: - session.PreSessions = append(session.PreSessions, shardSession) + newSessions, err := addOrUpdate(shardSession, session.PreSessions) + if err != nil { + return err + } + session.PreSessions = newSessions case vtgatepb.CommitOrder_POST: - session.PostSessions = append(session.PostSessions, shardSession) + newSessions, err := addOrUpdate(shardSession, session.PostSessions) + if err != nil { + return err + } + session.PostSessions = newSessions + default: + // Should be unreachable + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: SafeSession.AppendOrUpdate: unexpected commitOrder") } + return nil } @@ -264,9 +320,23 @@ func (session *SafeSession) SetSystemVariable(name string, expr string) { session.SystemVariables[name] = expr } +//SetOptions sets the options +func (session *SafeSession) SetOptions(options *querypb.ExecuteOptions) { + session.mu.Lock() + defer session.mu.Unlock() + session.Options = options +} + //StoreSavepoint stores the savepoint and release savepoint queries in the session func (session *SafeSession) StoreSavepoint(sql string) { session.mu.Lock() defer session.mu.Unlock() session.Savepoints = append(session.Savepoints, sql) } + +//InReservedConn returns true if the session needs to execute on a dedicated connection +func (session *SafeSession) InReservedConn() bool { + session.mu.Lock() + defer session.mu.Unlock() + return session.Session.InReservedConn +} diff --git a/go/vt/vtgate/safe_session_test.go b/go/vt/vtgate/safe_session_test.go new file mode 100644 index 00000000000..66131132fc1 --- /dev/null +++ b/go/vt/vtgate/safe_session_test.go @@ -0,0 +1,48 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtgate + +import ( + "testing" + + "github.com/stretchr/testify/require" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" +) + +func TestFailToMultiShardWhenSetToSingleDb(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{ + InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE, + }) + + sess0 := &vtgatepb.Session_ShardSession{ + Target: &querypb.Target{Keyspace: "keyspace", Shard: "0"}, + TabletAlias: &topodatapb.TabletAlias{Cell: "cell", Uid: 0}, + TransactionId: 1, + } + sess1 := &vtgatepb.Session_ShardSession{ + Target: &querypb.Target{Keyspace: "keyspace", Shard: "1"}, + TabletAlias: &topodatapb.TabletAlias{Cell: "cell", Uid: 1}, + TransactionId: 1, + } + + err := session.AppendOrUpdate(sess0, vtgatepb.TransactionMode_SINGLE) + require.NoError(t, err) + err = session.AppendOrUpdate(sess1, vtgatepb.TransactionMode_SINGLE) + require.Error(t, err) +} diff --git a/go/vt/vtgate/scatter_conn.go b/go/vt/vtgate/scatter_conn.go index 3eb81cd82c8..c6a7fc7e79f 100644 --- a/go/vt/vtgate/scatter_conn.go +++ b/go/vt/vtgate/scatter_conn.go @@ -69,7 +69,7 @@ type shardActionFunc func(rs *srvtopo.ResolvedShard, i int) error // multiGoTransaction is capable of executing multiple // shardActionTransactionFunc actions in parallel and consolidating // the results and errors for the caller. -type shardActionTransactionFunc func(rs *srvtopo.ResolvedShard, i int, shouldBegin bool, transactionID int64, alias *topodatapb.TabletAlias) (int64, *topodatapb.TabletAlias, error) +type shardActionTransactionFunc func(rs *srvtopo.ResolvedShard, i int, shardActionInfo *shardActionInfo) (*shardActionInfo, error) // NewLegacyScatterConn creates a new ScatterConn. func NewLegacyScatterConn(statsName string, txConn *TxConn, gw Gateway, hc discovery.LegacyHealthCheck) *ScatterConn { @@ -138,80 +138,6 @@ func (stc *ScatterConn) endAction(startTime time.Time, allErrors *concurrency.Al stc.timings.Record(statsKey, startTime) } -// Execute executes a non-streaming query on the specified shards. -func (stc *ScatterConn) Execute( - ctx context.Context, - query string, - bindVars map[string]*querypb.BindVariable, - rss []*srvtopo.ResolvedShard, - session *SafeSession, - notInTransaction bool, - options *querypb.ExecuteOptions, - autocommit bool, -) (*sqltypes.Result, error) { - - // mu protects qr - var mu sync.Mutex - qr := new(sqltypes.Result) - - allErrors := stc.multiGoTransaction( - ctx, - "Execute", - rss, - session, - notInTransaction, - func(rs *srvtopo.ResolvedShard, i int, shouldBegin bool, transactionID int64, alias *topodatapb.TabletAlias) (int64, *topodatapb.TabletAlias, error) { - var ( - innerqr *sqltypes.Result - err error - opts *querypb.ExecuteOptions - ) - switch { - case autocommit: - // As this is auto-commit, the transactionID is supposed to be zero. - if transactionID != 0 { - return 0, nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "In autocommit mode, transactionID is non-zero: %d", transactionID) - } - innerqr, err = rs.Gateway.Execute(ctx, rs.Target, query, bindVars, 0, 0, opts) - case shouldBegin: - innerqr, transactionID, alias, err = rs.Gateway.BeginExecute(ctx, rs.Target, session.Savepoints, query, bindVars, 0, options) - default: - var qs queryservice.QueryService - _, usingLegacy := rs.Gateway.(*DiscoveryGateway) - if transactionID != 0 && usingLegacy && rs.Target.TabletType != topodatapb.TabletType_MASTER { - return 0, nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "replica transactions not supported using the legacy healthcheck") - } - - if usingLegacy || transactionID == 0 { - qs = rs.Gateway - } else { - qs, err = rs.Gateway.QueryServiceByAlias(alias) - } - if err == nil { - innerqr, err = qs.Execute(ctx, rs.Target, query, bindVars, transactionID, 0, options) - } - } - if err != nil { - return transactionID, alias, err - } - - mu.Lock() - defer mu.Unlock() - // Don't append more rows if row count is exceeded. - if len(qr.Rows) <= *maxMemoryRows { - qr.AppendResult(innerqr) - } - return transactionID, alias, nil - }, - ) - - if len(qr.Rows) > *maxMemoryRows { - return nil, vterrors.Errorf(vtrpcpb.Code_RESOURCE_EXHAUSTED, "in-memory row count exceeded allowed limit of %d", *maxMemoryRows) - } - - return qr, allErrors.AggrError(vterrors.Aggregate) -} - // ExecuteMultiShard is like Execute, // but each shard gets its own Sql Queries and BindVariables. // @@ -223,10 +149,13 @@ func (stc *ScatterConn) ExecuteMultiShard( rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, - notInTransaction bool, autocommit bool, ) (qr *sqltypes.Result, errs []error) { + if len(rss) != len(queries) { + return nil, []error{vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: got mismatched number of queries and shards")} + } + // mu protects qr var mu sync.Mutex qr = new(sqltypes.Result) @@ -236,13 +165,16 @@ func (stc *ScatterConn) ExecuteMultiShard( "Execute", rss, session, - notInTransaction, - func(rs *srvtopo.ResolvedShard, i int, shouldBegin bool, transactionID int64, alias *topodatapb.TabletAlias) (int64, *topodatapb.TabletAlias, error) { + func(rs *srvtopo.ResolvedShard, i int, info *shardActionInfo) (*shardActionInfo, error) { var ( innerqr *sqltypes.Result err error opts *querypb.ExecuteOptions + alias *topodatapb.TabletAlias ) + transactionID := info.transactionID + reservedID := info.reserveID + if session != nil && session.Session != nil { opts = session.Session.Options } @@ -250,35 +182,55 @@ func (stc *ScatterConn) ExecuteMultiShard( switch { case autocommit: // As this is auto-commit, the transactionID is supposed to be zero. - if transactionID != 0 { - return 0, nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "In autocommit mode, transactionID is non-zero: %d", transactionID) + if info.transactionID != int64(0) { + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "in autocommit mode, transactionID should be zero but was: %d", info.transactionID) } innerqr, err = rs.Gateway.Execute(ctx, rs.Target, queries[i].Sql, queries[i].BindVariables, 0, 0, opts) - case shouldBegin: - innerqr, transactionID, alias, err = rs.Gateway.BeginExecute(ctx, rs.Target, session.Savepoints, queries[i].Sql, queries[i].BindVariables, 0, opts) - default: - var qs queryservice.QueryService - _, usingLegacy := rs.Gateway.(*DiscoveryGateway) - if usingLegacy || transactionID == 0 { - qs = rs.Gateway - } else { - qs, err = rs.Gateway.QueryServiceByAlias(alias) + if err != nil { + return nil, err } - if err == nil { - innerqr, err = qs.Execute(ctx, rs.Target, queries[i].Sql, queries[i].BindVariables, transactionID, 0, opts) + case nothing == info.actionNeeded: + qs, err := getQueryService(rs, info) + if err != nil { + return nil, err } + innerqr, err = qs.Execute(ctx, rs.Target, queries[i].Sql, queries[i].BindVariables, info.transactionID, info.reserveID, opts) + if err != nil { + return nil, err + } + case begin == info.actionNeeded: + qs, err := getQueryService(rs, info) + if err != nil { + return nil, err + } + innerqr, transactionID, alias, err = qs.BeginExecute(ctx, rs.Target, session.Savepoints, queries[i].Sql, queries[i].BindVariables, info.reserveID, opts) + if err != nil { + return info.updateTransactionID(transactionID, alias), err + } + case reserve == info.actionNeeded: + qs, err := getQueryService(rs, info) + if err != nil { + return nil, err + } + innerqr, reservedID, alias, err = qs.ReserveExecute(ctx, rs.Target, nil, queries[i].Sql, queries[i].BindVariables, info.transactionID, opts) + if err != nil { + return info.updateReservedID(reservedID, alias), err + } + case reserveBegin == info.actionNeeded: + innerqr, transactionID, reservedID, alias, err = rs.Gateway.ReserveBeginExecute(ctx, rs.Target, nil, queries[i].Sql, queries[i].BindVariables, opts) + if err != nil { + return info.updateTransactionAndReservedID(transactionID, reservedID, alias), err + } + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: unexpected actionNeeded on ScatterConn#ExecuteMultiShard %v", info.actionNeeded) } - if err != nil { - return transactionID, alias, err - } - mu.Lock() defer mu.Unlock() // Don't append more rows if row count is exceeded. if len(qr.Rows) <= *maxMemoryRows { qr.AppendResult(innerqr) } - return transactionID, alias, nil + return info.updateTransactionAndReservedID(transactionID, reservedID, alias), nil }, ) @@ -289,6 +241,20 @@ func (stc *ScatterConn) ExecuteMultiShard( return qr, allErrors.GetErrors() } +func getQueryService(rs *srvtopo.ResolvedShard, info *shardActionInfo) (queryservice.QueryService, error) { + _, usingLegacyGw := rs.Gateway.(*DiscoveryGateway) + //if usingLegacyGw { + // switch info.actionNeeded { + // case reserve, reserveBegin: + // return nil, vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "reserved connections are not supported on old gen gateway") + // } + //} + if usingLegacyGw || info.alias == nil { + return rs.Gateway, nil + } + return rs.Gateway.QueryServiceByAlias(info.alias) +} + func (stc *ScatterConn) processOneStreamingResult(mu *sync.Mutex, fieldSent *bool, qr *sqltypes.Result, callback func(*sqltypes.Result) error) error { mu.Lock() defer mu.Unlock() @@ -527,7 +493,6 @@ func (stc *ScatterConn) multiGoTransaction( name string, rss []*srvtopo.ResolvedShard, session *SafeSession, - notInTransaction bool, action shardActionTransactionFunc, ) (allErrors *concurrency.AllErrorRecorder) { @@ -542,14 +507,19 @@ func (stc *ScatterConn) multiGoTransaction( startTime, statsKey := stc.startAction(name, rs.Target) defer stc.endAction(startTime, allErrors, statsKey, &err, session) - shouldBegin, transactionID, alias := transactionInfo(rs.Target, session, notInTransaction) - transactionID, alias, err = action(rs, i, shouldBegin, transactionID, alias) - if shouldBegin && transactionID != 0 { - if appendErr := session.Append(&vtgatepb.Session_ShardSession{ + shardActionInfo := transactionInfo(rs.Target, session) + updated, err := action(rs, i, shardActionInfo) + if updated == nil { + return + } + if updated.actionNeeded != nothing && (updated.transactionID != 0 || updated.reserveID != 0) { + appendErr := session.AppendOrUpdate(&vtgatepb.Session_ShardSession{ Target: rs.Target, - TransactionId: transactionID, - TabletAlias: alias, - }, stc.txConn.mode); appendErr != nil { + TransactionId: updated.transactionID, + ReservedId: updated.reserveID, + TabletAlias: updated.alias, + }, stc.txConn.mode) + if appendErr != nil { err = appendErr } } @@ -578,31 +548,65 @@ func (stc *ScatterConn) multiGoTransaction( return allErrors } -// transactionInfo looks at the current session, and returns: -// - shouldBegin: if we should call 'Begin' to get a transactionID -// - transactionID: the transactionID to use, or 0 if not in a transaction. -func transactionInfo( - target *querypb.Target, - session *SafeSession, - notInTransaction bool, -) (shouldBegin bool, transactionID int64, alias *topodatapb.TabletAlias) { - if !session.InTransaction() { - return false, 0, nil +// transactionInfo looks at the current session, and returns information about what needs to be done for this tablet +func transactionInfo(target *querypb.Target, session *SafeSession) *shardActionInfo { + if !(session.InTransaction() || session.InReservedConn()) { + return &shardActionInfo{} } // No need to protect ourselves from the race condition between - // Find and Append. The higher level functions ensure that no + // Find and AppendOrUpdate. The higher level functions ensure that no // duplicate (target) tuples can execute // this at the same time. - transactionID, alias = session.Find(target.Keyspace, target.Shard, target.TabletType) - if transactionID != 0 { - return false, transactionID, alias + transactionID, reserveID, alias := session.Find(target.Keyspace, target.Shard, target.TabletType) + + shouldReserve := session.InReservedConn() && reserveID == 0 + shouldBegin := session.InTransaction() && transactionID == 0 + + var act = nothing + switch { + case shouldBegin && shouldReserve: + act = reserveBegin + case shouldReserve: + act = reserve + case shouldBegin: + act = begin } - // We are in a transaction at higher level, - // but client requires not to start a transaction for this query. - // If a transaction was started on this conn, we will use it (as above). - if notInTransaction { - return false, 0, nil + + return &shardActionInfo{ + actionNeeded: act, + transactionID: transactionID, + reserveID: reserveID, + alias: alias, } +} - return true, 0, nil +type shardActionInfo struct { + actionNeeded actionNeeded + reserveID, transactionID int64 + alias *topodatapb.TabletAlias } + +func (sai *shardActionInfo) updateTransactionID(txID int64, alias *topodatapb.TabletAlias) *shardActionInfo { + return sai.updateTransactionAndReservedID(txID, sai.reserveID, alias) +} + +func (sai *shardActionInfo) updateReservedID(rID int64, alias *topodatapb.TabletAlias) *shardActionInfo { + return sai.updateTransactionAndReservedID(sai.transactionID, rID, alias) +} + +func (sai *shardActionInfo) updateTransactionAndReservedID(txID int64, rID int64, alias *topodatapb.TabletAlias) *shardActionInfo { + newInfo := *sai + newInfo.reserveID = rID + newInfo.transactionID = txID + newInfo.alias = alias + return &newInfo +} + +type actionNeeded int + +const ( + nothing actionNeeded = iota + reserveBegin + reserve + begin +) diff --git a/go/vt/vtgate/scatter_conn_test.go b/go/vt/vtgate/scatter_conn_test.go index 8a5a5f50478..4ed65a573fd 100644 --- a/go/vt/vtgate/scatter_conn_test.go +++ b/go/vt/vtgate/scatter_conn_test.go @@ -28,7 +28,6 @@ import ( "golang.org/x/net/context" - "github.com/golang/protobuf/proto" "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/discovery" @@ -43,18 +42,6 @@ import ( // This file uses the sandbox_test framework. -func TestScatterConnExecute(t *testing.T) { - testScatterConnGeneric(t, "TestScatterConnExecute", func(sc *ScatterConn, shards []string) (*sqltypes.Result, error) { - res := srvtopo.NewResolver(&sandboxTopo{}, sc.gateway, "aa") - rss, err := res.ResolveDestination(ctx, "TestScatterConnExecute", topodatapb.TabletType_REPLICA, key.DestinationShards(shards)) - if err != nil { - return nil, err - } - - return sc.Execute(ctx, "query", nil, rss, NewSafeSession(nil), false, nil, false) - }) -} - func TestExecuteFailOnAutocommit(t *testing.T) { createSandbox("TestExecuteFailOnAutocommit") @@ -110,8 +97,10 @@ func TestExecuteFailOnAutocommit(t *testing.T) { }, Autocommit: false, } - _, errs := sc.ExecuteMultiShard(ctx, rss, queries, NewSafeSession(session), false, true) - require.Error(t, vterrors.Aggregate(errs)) + _, errs := sc.ExecuteMultiShard(ctx, rss, queries, NewSafeSession(session), true /*autocommit*/) + err := vterrors.Aggregate(errs) + require.Error(t, err) + require.Contains(t, err.Error(), "in autocommit mode, transactionID should be zero but was: 123") utils.MustMatch(t, 0, len(sbc0.Queries), "") utils.MustMatch(t, []*querypb.BoundQuery{queries[1]}, sbc1.Queries, "") } @@ -132,7 +121,7 @@ func TestScatterConnExecuteMulti(t *testing.T) { } } - qr, errs := sc.ExecuteMultiShard(ctx, rss, queries, NewSafeSession(nil), false, false) + qr, errs := sc.ExecuteMultiShard(ctx, rss, queries, NewSafeSession(nil), false /*autocommit*/) return qr, vterrors.Aggregate(errs) }) } @@ -190,12 +179,10 @@ func testScatterConnGeneric(t *testing.T, name string, f func(sc *ScatterConn, s s := createSandbox(name) sc := newTestLegacyScatterConn(hc, new(sandboxTopo), "aa") qr, err := f(sc, nil) + require.NoError(t, err) if qr.RowsAffected != 0 { t.Errorf("want 0, got %v", qr.RowsAffected) } - if err != nil { - t.Errorf("want nil, got %v", err) - } // single shard s.Reset() @@ -319,10 +306,7 @@ func TestMaxMemoryRows(t *testing.T) { session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - _, err = sc.Execute(ctx, "query1", nil, rss, session, true, nil, false) want := "in-memory row count exceeded allowed limit of 3" - assert.EqualError(t, err, want) - queries := []*querypb.BoundQuery{{ Sql: "query1", BindVariables: map[string]*querypb.BindVariable{}, @@ -330,10 +314,211 @@ func TestMaxMemoryRows(t *testing.T) { Sql: "query1", BindVariables: map[string]*querypb.BindVariable{}, }} - _, errs := sc.ExecuteMultiShard(ctx, rss, queries, session, false, false) + _, errs := sc.ExecuteMultiShard(ctx, rss, queries, session, false) assert.EqualError(t, errs[0], want) } +func TestReservedBeginTableDriven(t *testing.T) { + type testAction struct { + transaction, reserved bool + shards []string + sbc0Reserve, sbc1Reserve int64 + sbc0Begin, sbc1Begin int64 + } + type testCase struct { + name string + actions []testAction + } + + tests := []testCase{{ + name: "begin", + actions: []testAction{ + { + shards: []string{"0"}, + transaction: true, + sbc0Begin: 1, + }, { + shards: []string{"0", "1"}, + transaction: true, + sbc1Begin: 1, + }, { + shards: []string{"0", "1"}, + transaction: true, + // nothing needs to be done + }}, + }, { + name: "reserve", + actions: []testAction{ + { + shards: []string{"1"}, + reserved: true, + sbc1Reserve: 1, + }, { + shards: []string{"0", "1"}, + reserved: true, + sbc0Reserve: 1, + }, { + shards: []string{"0", "1"}, + reserved: true, + // nothing needs to be done + }}, + }, { + name: "reserve everywhere", + actions: []testAction{ + { + shards: []string{"0", "1"}, + reserved: true, + sbc0Reserve: 1, + sbc1Reserve: 1, + }}, + }, { + name: "begin then reserve", + actions: []testAction{ + { + shards: []string{"0"}, + transaction: true, + sbc0Begin: 1, + }, { + shards: []string{"0", "1"}, + transaction: true, + reserved: true, + sbc0Reserve: 1, + sbc1Reserve: 1, + sbc1Begin: 1, + }}, + }, { + name: "reserve then begin", + actions: []testAction{ + { + shards: []string{"1"}, + reserved: true, + sbc1Reserve: 1, + }, { + shards: []string{"0"}, + transaction: true, + reserved: true, + sbc0Reserve: 1, + sbc0Begin: 1, + }, { + shards: []string{"0", "1"}, + transaction: true, + reserved: true, + sbc1Begin: 1, + }}, + }, { + name: "reserveBegin", + actions: []testAction{ + { + shards: []string{"1"}, + transaction: true, + reserved: true, + sbc1Reserve: 1, + sbc1Begin: 1, + }, { + shards: []string{"0"}, + transaction: true, + reserved: true, + sbc0Reserve: 1, + sbc0Begin: 1, + }, { + shards: []string{"0", "1"}, + transaction: true, + reserved: true, + // nothing needs to be done + }}, + }, { + name: "reserveBegin everywhere", + actions: []testAction{ + { + shards: []string{"0", "1"}, + transaction: true, + reserved: true, + sbc0Reserve: 1, + sbc0Begin: 1, + sbc1Reserve: 1, + sbc1Begin: 1, + }}, + }} + for _, test := range tests { + keyspace := "keyspace" + createSandbox(keyspace) + hc := discovery.NewFakeLegacyHealthCheck() + sc := newTestLegacyScatterConn(hc, new(sandboxTopo), "aa") + sbc0 := hc.AddTestTablet("aa", "0", 1, keyspace, "0", topodatapb.TabletType_REPLICA, true, 1, nil) + sbc1 := hc.AddTestTablet("aa", "1", 1, keyspace, "1", topodatapb.TabletType_REPLICA, true, 1, nil) + + // empty results + sbc0.SetResults([]*sqltypes.Result{{}}) + sbc1.SetResults([]*sqltypes.Result{{}}) + + res := srvtopo.NewResolver(&sandboxTopo{}, sc.gateway, "aa") + + t.Run(test.name, func(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{}) + for _, action := range test.actions { + session.Session.InTransaction = action.transaction + session.Session.InReservedConn = action.reserved + var destinations []key.Destination + for _, shard := range action.shards { + destinations = append(destinations, key.DestinationShard(shard)) + } + executeOnShards(t, res, keyspace, sc, session, destinations) + assert.EqualValues(t, action.sbc0Reserve, sbc0.ReserveCount.Get(), "sbc0 reserve count") + assert.EqualValues(t, action.sbc0Begin, sbc0.BeginCount.Get(), "sbc0 begin count") + assert.EqualValues(t, action.sbc1Reserve, sbc1.ReserveCount.Get(), "sbc1 reserve count") + assert.EqualValues(t, action.sbc1Begin, sbc1.BeginCount.Get(), "sbc1 begin count") + sbc0.BeginCount.Set(0) + sbc0.ReserveCount.Set(0) + sbc1.BeginCount.Set(0) + sbc1.ReserveCount.Set(0) + } + }) + } +} + +// TODO (harshit): This test should actual fail. +func TestReservedOnMultiReplica(t *testing.T) { + keyspace := "keyspace" + createSandbox(keyspace) + hc := discovery.NewFakeLegacyHealthCheck() + sc := newTestLegacyScatterConn(hc, new(sandboxTopo), "aa") + sbc0_1 := hc.AddTestTablet("aa", "0", 1, keyspace, "0", topodatapb.TabletType_REPLICA, true, 1, nil) + sbc0_2 := hc.AddTestTablet("aa", "2", 1, keyspace, "0", topodatapb.TabletType_REPLICA, true, 1, nil) + // sbc1 := hc.AddTestTablet("aa", "1", 1, keyspace, "1", topodatapb.TabletType_REPLICA, true, 1, nil) + + // empty results + sbc0_1.SetResults([]*sqltypes.Result{{}}) + sbc0_2.SetResults([]*sqltypes.Result{{}}) + + res := srvtopo.NewResolver(&sandboxTopo{}, sc.gateway, "aa") + + session := NewSafeSession(&vtgatepb.Session{InTransaction: false, InReservedConn: true}) + destinations := []key.Destination{key.DestinationShard("0")} + for i := 0; i < 10; i++ { + executeOnShards(t, res, keyspace, sc, session, destinations) + assert.EqualValues(t, 1, sbc0_1.ReserveCount.Get()+sbc0_2.ReserveCount.Get(), "sbc0 reserve count") + assert.EqualValues(t, 0, sbc0_1.BeginCount.Get()+sbc0_2.BeginCount.Get(), "sbc0 begin count") + } +} + +func executeOnShards(t *testing.T, res *srvtopo.Resolver, keyspace string, sc *ScatterConn, session *SafeSession, destinations []key.Destination) { + t.Helper() + rss, _, err := res.ResolveDestinations(ctx, keyspace, topodatapb.TabletType_REPLICA, nil, destinations) + require.NoError(t, err) + + var queries []*querypb.BoundQuery + + for range rss { + queries = append(queries, &querypb.BoundQuery{ + Sql: "query1", + BindVariables: map[string]*querypb.BindVariable{}, + }) + } + + _, errs := sc.ExecuteMultiShard(ctx, rss, queries, session, false) + require.Empty(t, errs) +} + func TestMultiExecs(t *testing.T) { createSandbox("TestMultiExecs") hc := discovery.NewFakeLegacyHealthCheck() @@ -372,7 +557,7 @@ func TestMultiExecs(t *testing.T) { }, } - _, _ = sc.ExecuteMultiShard(ctx, rss, queries, NewSafeSession(nil), false, false) + _, _ = sc.ExecuteMultiShard(ctx, rss, queries, NewSafeSession(nil), false) if len(sbc0.Queries) == 0 || len(sbc1.Queries) == 0 { t.Fatalf("didn't get expected query") } @@ -446,162 +631,6 @@ func TestScatterConnStreamExecuteSendError(t *testing.T) { } } -func TestScatterConnQueryNotInTransaction(t *testing.T) { - s := createSandbox("TestScatterConnQueryNotInTransaction") - hc := discovery.NewFakeLegacyHealthCheck() - - // case 1: read query (not in transaction) followed by write query, not in the same shard. - hc.Reset() - sc := newTestLegacyScatterConn(hc, new(sandboxTopo), "aa") - sbc0 := hc.AddTestTablet("aa", "0", 1, "TestScatterConnQueryNotInTransaction", "0", topodatapb.TabletType_REPLICA, true, 1, nil) - sbc1 := hc.AddTestTablet("aa", "1", 1, "TestScatterConnQueryNotInTransaction", "1", topodatapb.TabletType_REPLICA, true, 1, nil) - - res := srvtopo.NewResolver(&sandboxTopo{}, sc.gateway, "aa") - rss0, err := res.ResolveDestination(ctx, "TestScatterConnQueryNotInTransaction", topodatapb.TabletType_REPLICA, key.DestinationShard("0")) - if err != nil { - t.Fatalf("ResolveDestination(0) failed: %v", err) - } - rss1, err := res.ResolveDestination(ctx, "TestScatterConnQueryNotInTransaction", topodatapb.TabletType_REPLICA, key.DestinationShard("1")) - if err != nil { - t.Fatalf("ResolveDestination(1) failed: %v", err) - } - - session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, true, nil, false) - sc.Execute(ctx, "query1", nil, rss1, session, false, nil, false) - - wantSession := vtgatepb.Session{ - InTransaction: true, - ShardSessions: []*vtgatepb.Session_ShardSession{{ - Target: &querypb.Target{ - Keyspace: "TestScatterConnQueryNotInTransaction", - Shard: "1", - TabletType: topodatapb.TabletType_REPLICA, - }, - TransactionId: 1, - TabletAlias: sbc0.Tablet().Alias, - }}, - } - if !proto.Equal(&wantSession, session.Session) { - t.Errorf("want\n%+v\ngot\n%+v", wantSession, *session.Session) - } - sc.txConn.Commit(ctx, session) - { - execCount0 := sbc0.ExecCount.Get() - execCount1 := sbc1.ExecCount.Get() - if execCount0 != 1 || execCount1 != 1 { - t.Errorf("want 1/1, got %d/%d", execCount0, execCount1) - } - } - if commitCount := sbc0.CommitCount.Get(); commitCount != 0 { - t.Errorf("want 0, got %d", commitCount) - } - if commitCount := sbc1.CommitCount.Get(); commitCount != 1 { - t.Errorf("want 1, got %d", commitCount) - } - - // case 2: write query followed by read query (not in transaction), not in the same shard. - s.Reset() - hc.Reset() - sc = newTestLegacyScatterConn(hc, new(sandboxTopo), "aa") - sbc0 = hc.AddTestTablet("aa", "0", 1, "TestScatterConnQueryNotInTransaction", "0", topodatapb.TabletType_REPLICA, true, 1, nil) - sbc1 = hc.AddTestTablet("aa", "1", 1, "TestScatterConnQueryNotInTransaction", "1", topodatapb.TabletType_REPLICA, true, 1, nil) - session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) - - res = srvtopo.NewResolver(&sandboxTopo{}, sc.gateway, "aa") - rss0, err = res.ResolveDestination(ctx, "TestScatterConnQueryNotInTransaction", topodatapb.TabletType_REPLICA, key.DestinationShard("0")) - if err != nil { - t.Fatalf("ResolveDestination(0) failed: %v", err) - } - rss1, err = res.ResolveDestination(ctx, "TestScatterConnQueryNotInTransaction", topodatapb.TabletType_REPLICA, key.DestinationShard("1")) - if err != nil { - t.Fatalf("ResolveDestination(1) failed: %v", err) - } - - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - sc.Execute(ctx, "query1", nil, rss1, session, true, nil, false) - - wantSession = vtgatepb.Session{ - InTransaction: true, - ShardSessions: []*vtgatepb.Session_ShardSession{{ - Target: &querypb.Target{ - Keyspace: "TestScatterConnQueryNotInTransaction", - Shard: "0", - TabletType: topodatapb.TabletType_REPLICA, - }, - TransactionId: 1, - TabletAlias: sbc0.Tablet().Alias, - }}, - } - if !proto.Equal(&wantSession, session.Session) { - t.Errorf("want\n%+v\ngot\n%+v", wantSession, *session.Session) - } - sc.txConn.Commit(ctx, session) - { - execCount0 := sbc0.ExecCount.Get() - execCount1 := sbc1.ExecCount.Get() - if execCount0 != 1 || execCount1 != 1 { - t.Errorf("want 1/1, got %d/%d", execCount0, execCount1) - } - } - if commitCount := sbc0.CommitCount.Get(); commitCount != 1 { - t.Errorf("want 1, got %d", commitCount) - } - if commitCount := sbc1.CommitCount.Get(); commitCount != 0 { - t.Errorf("want 0, got %d", commitCount) - } - - // case 3: write query followed by read query, in the same shard. - s.Reset() - hc.Reset() - sc = newTestLegacyScatterConn(hc, new(sandboxTopo), "aa") - sbc0 = hc.AddTestTablet("aa", "0", 1, "TestScatterConnQueryNotInTransaction", "0", topodatapb.TabletType_REPLICA, true, 1, nil) - sbc1 = hc.AddTestTablet("aa", "1", 1, "TestScatterConnQueryNotInTransaction", "1", topodatapb.TabletType_REPLICA, true, 1, nil) - session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) - noTxSession := NewSafeSession(&vtgatepb.Session{InTransaction: false}) - - res = srvtopo.NewResolver(&sandboxTopo{}, sc.gateway, "aa") - rss0, err = res.ResolveDestination(ctx, "TestScatterConnQueryNotInTransaction", topodatapb.TabletType_REPLICA, key.DestinationShard("0")) - require.NoError(t, err) - rss1, err = res.ResolveDestination(ctx, "TestScatterConnQueryNotInTransaction", topodatapb.TabletType_REPLICA, key.DestinationShards([]string{"0", "1"})) - require.NoError(t, err) - - _, err = sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - require.NoError(t, err) - _, err = sc.Execute(ctx, "query1", nil, rss1, noTxSession, true, nil, false) - require.NoError(t, err) - - wantSession = vtgatepb.Session{ - InTransaction: true, - ShardSessions: []*vtgatepb.Session_ShardSession{{ - Target: &querypb.Target{ - Keyspace: "TestScatterConnQueryNotInTransaction", - Shard: "0", - TabletType: topodatapb.TabletType_REPLICA, - }, - TransactionId: 1, - TabletAlias: sbc0.Tablet().Alias, - }}, - } - if !proto.Equal(&wantSession, session.Session) { - t.Errorf("want\n%+v\ngot\n%+v", wantSession, *session.Session) - } - err = sc.txConn.Commit(ctx, session) - require.NoError(t, err) - - execCount0 := sbc0.ExecCount.Get() - execCount1 := sbc1.ExecCount.Get() - if execCount0 != 2 || execCount1 != 1 { - t.Errorf("want 2/1, got %d/%d", execCount0, execCount1) - } - if commitCount := sbc0.CommitCount.Get(); commitCount != 1 { - t.Errorf("want 1, got %d", commitCount) - } - if commitCount := sbc1.CommitCount.Get(); commitCount != 0 { - t.Errorf("want 0, got %d", commitCount) - } -} - func TestScatterConnSingleDB(t *testing.T) { createSandbox("TestScatterConnSingleDB") hc := discovery.NewFakeLegacyHealthCheck() @@ -613,42 +642,37 @@ func TestScatterConnSingleDB(t *testing.T) { res := srvtopo.NewResolver(&sandboxTopo{}, sc.gateway, "aa") rss0, err := res.ResolveDestination(ctx, "TestScatterConnSingleDB", topodatapb.TabletType_MASTER, key.DestinationShard("0")) - if err != nil { - t.Fatalf("ResolveDestination(0) failed: %v", err) - } + require.NoError(t, err) rss1, err := res.ResolveDestination(ctx, "TestScatterConnSingleDB", topodatapb.TabletType_MASTER, key.DestinationShard("1")) - if err != nil { - t.Fatalf("ResolveDestination(1) failed: %v", err) - } + require.NoError(t, err) want := "multi-db transaction attempted" // TransactionMode_SINGLE in session session := NewSafeSession(&vtgatepb.Session{InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}) - _, err = sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - require.NoError(t, err) - _, err = sc.Execute(ctx, "query1", nil, rss1, session, false, nil, false) - if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("Multi DB exec: %v, must contain %s", err, want) - } + queries := []*querypb.BoundQuery{{Sql: "query1"}} + _, errors := sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + require.Empty(t, errors) + _, errors = sc.ExecuteMultiShard(ctx, rss1, queries, session, false) + require.Error(t, errors[0]) + assert.Contains(t, errors[0].Error(), want) // TransactionMode_SINGLE in txconn sc.txConn.mode = vtgatepb.TransactionMode_SINGLE session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) - _, err = sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - require.NoError(t, err) - _, err = sc.Execute(ctx, "query1", nil, rss1, session, false, nil, false) - if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("Multi DB exec: %v, must contain %s", err, want) - } + _, errors = sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + require.Empty(t, errors) + _, errors = sc.ExecuteMultiShard(ctx, rss1, queries, session, false) + require.Error(t, errors[0]) + assert.Contains(t, errors[0].Error(), want) // TransactionMode_MULTI in txconn. Should not fail. sc.txConn.mode = vtgatepb.TransactionMode_MULTI session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) - _, err = sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - require.NoError(t, err) - _, err = sc.Execute(ctx, "query1", nil, rss1, session, false, nil, false) - require.NoError(t, err) + _, errors = sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + require.Empty(t, errors) + _, errors = sc.ExecuteMultiShard(ctx, rss1, queries, session, false) + require.Empty(t, errors) } func TestAppendResult(t *testing.T) { diff --git a/go/vt/vtgate/tx_conn.go b/go/vt/vtgate/tx_conn.go index aca938c9cf8..2dd50364c38 100644 --- a/go/vt/vtgate/tx_conn.go +++ b/go/vt/vtgate/tx_conn.go @@ -64,7 +64,7 @@ func (txc *TxConn) Begin(ctx context.Context, session *SafeSession) error { // Commit commits the current transaction. The type of commit can be // best effort or 2pc depending on the session setting. func (txc *TxConn) Commit(ctx context.Context, session *SafeSession) error { - defer session.Reset() + defer session.ResetTx() if !session.InTransaction() { return nil } @@ -74,7 +74,7 @@ func (txc *TxConn) Commit(ctx context.Context, session *SafeSession) error { case vtgatepb.TransactionMode_TWOPC: twopc = true case vtgatepb.TransactionMode_UNSPECIFIED: - twopc = (txc.mode == vtgatepb.TransactionMode_TWOPC) + twopc = txc.mode == vtgatepb.TransactionMode_TWOPC } if twopc { return txc.commit2PC(ctx, session) @@ -91,40 +91,45 @@ func (txc *TxConn) queryService(alias *topodatapb.TabletAlias) (queryservice.Que } func (txc *TxConn) commitShard(ctx context.Context, s *vtgatepb.Session_ShardSession) error { + if s.TransactionId == 0 { + return nil + } var qs queryservice.QueryService var err error qs, err = txc.queryService(s.TabletAlias) if err != nil { return err } - // TODO(reserve-conn): Add logic for returned reservedID. - _, err = qs.Commit(ctx, s.Target, s.TransactionId) - return err + reservedID, err := qs.Commit(ctx, s.Target, s.TransactionId) + if err != nil { + return err + } + s.TransactionId = 0 + s.ReservedId = reservedID + return nil } func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error { if err := txc.runSessions(ctx, session.PreSessions, txc.commitShard); err != nil { - _ = txc.Rollback(ctx, session) + _ = txc.Release(ctx, session) return err } - // Close all PreSessions - for _, shardSession := range session.PreSessions { - shardSession.TransactionId = 0 - } - // Retain backward compatibility on commit order for the normal session. for _, shardSession := range session.ShardSessions { if err := txc.commitShard(ctx, shardSession); err != nil { - _ = txc.Rollback(ctx, session) + _ = txc.Release(ctx, session) return err } - shardSession.TransactionId = 0 } if err := txc.runSessions(ctx, session.PostSessions, txc.commitShard); err != nil { // If last commit fails, there will be nothing to rollback. session.RecordWarning(&querypb.QueryWarning{Message: fmt.Sprintf("post-operation transaction had an error: %v", err)}) + // With reserved connection we should release them. + if session.InReservedConn() { + _ = txc.Release(ctx, session) + } } return nil } @@ -196,22 +201,61 @@ func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { if !session.InTransaction() { return nil } + defer session.ResetTx() + + allsessions := append(session.PreSessions, session.ShardSessions...) + allsessions = append(allsessions, session.PostSessions...) + + err := txc.runSessions(ctx, allsessions, func(ctx context.Context, s *vtgatepb.Session_ShardSession) error { + if s.TransactionId == 0 { + return nil + } + qs, err := txc.queryService(s.TabletAlias) + if err != nil { + return err + } + reservedID, err := qs.Rollback(ctx, s.Target, s.TransactionId) + if err != nil { + return err + } + s.TransactionId = 0 + s.ReservedId = reservedID + return nil + }) + if err != nil { + session.RecordWarning(&querypb.QueryWarning{Message: fmt.Sprintf("rollback encountered an error and connection to all shard for this session is released: %v", err)}) + if session.InReservedConn() { + _ = txc.Release(ctx, session) + } + } + return err +} + +//Release releases the reserved connection and/or rollbacks the transaction +func (txc *TxConn) Release(ctx context.Context, session *SafeSession) error { + if !session.InTransaction() && !session.InReservedConn() { + return nil + } defer session.Reset() allsessions := append(session.PreSessions, session.ShardSessions...) allsessions = append(allsessions, session.PostSessions...) return txc.runSessions(ctx, allsessions, func(ctx context.Context, s *vtgatepb.Session_ShardSession) error { - if s.TransactionId == 0 { + if s.ReservedId == 0 && s.TransactionId == 0 { return nil } qs, err := txc.queryService(s.TabletAlias) if err != nil { return err } - // TODO(reserve-conn): Add logic for returned reservedID. - _, err = qs.Rollback(ctx, s.Target, s.TransactionId) - return err + err = qs.Release(ctx, s.Target, s.TransactionId, s.ReservedId) + if err != nil { + return err + } + s.TransactionId = 0 + s.ReservedId = 0 + return nil }) } diff --git a/go/vt/vtgate/tx_conn_test.go b/go/vt/vtgate/tx_conn_test.go index 11a04051a8e..5f9b660dbc8 100644 --- a/go/vt/vtgate/tx_conn_test.go +++ b/go/vt/vtgate/tx_conn_test.go @@ -37,21 +37,25 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) +var queries = []*querypb.BoundQuery{{Sql: "query1"}} +var twoQueries = []*querypb.BoundQuery{{Sql: "query1"}, {Sql: "query1"}} + func TestTxConnBegin(t *testing.T) { sc, sbc0, _, rss0, _, _ := newTestTxConnEnv(t, "TestTxConn") session := &vtgatepb.Session{} // begin - err := sc.txConn.Begin(ctx, NewSafeSession(session)) + safeSession := NewSafeSession(session) + err := sc.txConn.Begin(ctx, safeSession) require.NoError(t, err) wantSession := vtgatepb.Session{InTransaction: true} utils.MustMatch(t, &wantSession, session, "Session") - _, err = sc.Execute(ctx, "query1", nil, rss0, NewSafeSession(session), false, nil, false) - require.NoError(t, err) + _, errors := sc.ExecuteMultiShard(ctx, rss0, queries, safeSession, false) + require.Empty(t, errors) // Begin again should cause a commit and a new begin. require.NoError(t, - sc.txConn.Begin(ctx, NewSafeSession(session))) + sc.txConn.Begin(ctx, safeSession)) utils.MustMatch(t, &wantSession, session, "Session") assert.EqualValues(t, 1, sbc0.CommitCount.Get(), "sbc0.CommitCount") } @@ -62,7 +66,7 @@ func TestTxConnCommitSuccess(t *testing.T) { // Sequence the executes to ensure commit order session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) wantSession := vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{{ @@ -76,7 +80,7 @@ func TestTxConnCommitSuccess(t *testing.T) { }}, } utils.MustMatch(t, &wantSession, session.Session, "Session") - sc.Execute(ctx, "query1", nil, rss01, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) wantSession = vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{{ @@ -107,6 +111,274 @@ func TestTxConnCommitSuccess(t *testing.T) { assert.EqualValues(t, 1, sbc1.CommitCount.Get(), "sbc1.CommitCount") } +func TestTxConnReservedCommitSuccess(t *testing.T) { + sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConn") + sc.txConn.mode = vtgatepb.TransactionMode_MULTI + + // Sequence the executes to ensure commit order + session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + wantSession := vtgatepb.Session{ + InTransaction: true, + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) + wantSession = vtgatepb.Session{ + InTransaction: true, + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + + require.NoError(t, + sc.txConn.Commit(ctx, session)) + wantSession = vtgatepb.Session{ + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 2, + TabletAlias: sbc0.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 2, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + assert.EqualValues(t, 1, sbc0.CommitCount.Get(), "sbc0.CommitCount") + assert.EqualValues(t, 1, sbc1.CommitCount.Get(), "sbc1.CommitCount") + + require.NoError(t, + sc.txConn.Release(ctx, session)) + wantSession = vtgatepb.Session{} + utils.MustMatch(t, &wantSession, session.Session, "Session") + assert.EqualValues(t, 1, sbc0.ReleaseCount.Get(), "sbc0.ReleaseCount") + assert.EqualValues(t, 1, sbc1.ReleaseCount.Get(), "sbc1.ReleaseCount") +} + +func TestTxConnReservedOn2ShardTxOn1ShardAndCommit(t *testing.T) { + keyspace := "TestTxConn" + sc, sbc0, sbc1, rss0, rss1, _ := newTestTxConnEnv(t, keyspace) + sc.txConn.mode = vtgatepb.TransactionMode_MULTI + + // Sequence the executes to ensure shard session order + session := NewSafeSession(&vtgatepb.Session{InReservedConn: true}) + + // this will create reserved connections against all tablets + _, errs := sc.ExecuteMultiShard(ctx, rss1, queries, session, false) + require.Empty(t, errs) + _, errs = sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + require.Empty(t, errs) + + wantSession := vtgatepb.Session{ + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 1, + TabletAlias: sbc1.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + + session.Session.InTransaction = true + + // start a transaction against rss0 + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + wantSession = vtgatepb.Session{ + InTransaction: true, + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 1, + TabletAlias: sbc1.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + + utils.MustMatch(t, &wantSession, session.Session, "Session") + + require.NoError(t, + sc.txConn.Commit(ctx, session)) + + wantSession = vtgatepb.Session{ + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 1, + TabletAlias: sbc1.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 2, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + assert.EqualValues(t, 1, sbc0.CommitCount.Get(), "sbc0.CommitCount") + assert.EqualValues(t, 0, sbc1.CommitCount.Get(), "sbc1.CommitCount") +} + +func TestTxConnReservedOn2ShardTxOn1ShardAndRollback(t *testing.T) { + keyspace := "TestTxConn" + sc, sbc0, sbc1, rss0, rss1, _ := newTestTxConnEnv(t, keyspace) + sc.txConn.mode = vtgatepb.TransactionMode_MULTI + + // Sequence the executes to ensure shard session order + session := NewSafeSession(&vtgatepb.Session{InReservedConn: true}) + + // this will create reserved connections against all tablets + _, errs := sc.ExecuteMultiShard(ctx, rss1, queries, session, false) + require.Empty(t, errs) + _, errs = sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + require.Empty(t, errs) + + wantSession := vtgatepb.Session{ + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 1, + TabletAlias: sbc1.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + + session.Session.InTransaction = true + + // start a transaction against rss0 + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + wantSession = vtgatepb.Session{ + InTransaction: true, + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 1, + TabletAlias: sbc1.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + + utils.MustMatch(t, &wantSession, session.Session, "Session") + + require.NoError(t, + sc.txConn.Rollback(ctx, session)) + + wantSession = vtgatepb.Session{ + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 1, + TabletAlias: sbc1.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: keyspace, + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 2, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + assert.EqualValues(t, 1, sbc0.RollbackCount.Get(), "sbc0.RollbackCount") + assert.EqualValues(t, 0, sbc1.RollbackCount.Get(), "sbc1.RollbackCount") +} + func TestTxConnCommitOrderFailure1(t *testing.T) { sc, sbc0, sbc1, rss0, rss1, _ := newTestTxConnEnv(t, "TestTxConn") sc.txConn.mode = vtgatepb.TransactionMode_MULTI @@ -115,13 +387,13 @@ func TestTxConnCommitOrderFailure1(t *testing.T) { // Sequence the executes to ensure commit order session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.ExecuteMultiShard(ctx, rss0, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) - sc.ExecuteMultiShard(ctx, rss0, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) session.SetCommitOrder(vtgatepb.CommitOrder_POST) - sc.ExecuteMultiShard(ctx, rss1, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss1, queries, session, false) sbc0.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1 err := sc.txConn.Commit(ctx, session) @@ -134,8 +406,8 @@ func TestTxConnCommitOrderFailure1(t *testing.T) { // first commit failed so we don't try to commit the second shard assert.EqualValues(t, 0, sbc1.CommitCount.Get(), "sbc1.CommitCount") // When the commit fails, we try to clean up by issuing a rollback - assert.EqualValues(t, 2, sbc0.RollbackCount.Get(), "sbc0.RollbackCount") - assert.EqualValues(t, 1, sbc1.RollbackCount.Get(), "sbc1.RollbackCount") + assert.EqualValues(t, 2, sbc0.ReleaseCount.Get(), "sbc0.ReleaseCount") + assert.EqualValues(t, 1, sbc1.ReleaseCount.Get(), "sbc1.ReleaseCount") } func TestTxConnCommitOrderFailure2(t *testing.T) { @@ -148,13 +420,13 @@ func TestTxConnCommitOrderFailure2(t *testing.T) { // Sequence the executes to ensure commit order session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.ExecuteMultiShard(context.Background(), rss1, queries, session, false, false) + sc.ExecuteMultiShard(context.Background(), rss1, queries, session, false) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) - sc.ExecuteMultiShard(context.Background(), rss0, queries, session, false, false) + sc.ExecuteMultiShard(context.Background(), rss0, queries, session, false) session.SetCommitOrder(vtgatepb.CommitOrder_POST) - sc.ExecuteMultiShard(context.Background(), rss1, queries, session, false, false) + sc.ExecuteMultiShard(context.Background(), rss1, queries, session, false) sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1 err := sc.txConn.Commit(ctx, session) @@ -166,8 +438,8 @@ func TestTxConnCommitOrderFailure2(t *testing.T) { assert.EqualValues(t, 1, sbc0.CommitCount.Get(), "sbc0.CommitCount") assert.EqualValues(t, 1, sbc1.CommitCount.Get(), "sbc1.CommitCount") // When the commit fails, we try to clean up by issuing a rollback - assert.EqualValues(t, 0, sbc0.RollbackCount.Get(), "sbc0.RollbackCount") - assert.EqualValues(t, 2, sbc1.RollbackCount.Get(), "sbc1.RollbackCount") + assert.EqualValues(t, 0, sbc0.ReleaseCount.Get(), "sbc0.ReleaseCount") + assert.EqualValues(t, 2, sbc1.ReleaseCount.Get(), "sbc1.ReleaseCount") } func TestTxConnCommitOrderFailure3(t *testing.T) { @@ -180,13 +452,13 @@ func TestTxConnCommitOrderFailure3(t *testing.T) { // Sequence the executes to ensure commit order session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.ExecuteMultiShard(ctx, rss0, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) session.SetCommitOrder(vtgatepb.CommitOrder_PRE) - sc.ExecuteMultiShard(ctx, rss0, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) session.SetCommitOrder(vtgatepb.CommitOrder_POST) - sc.ExecuteMultiShard(ctx, rss1, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss1, queries, session, false) sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1 require.NoError(t, @@ -201,6 +473,8 @@ func TestTxConnCommitOrderFailure3(t *testing.T) { utils.MustMatch(t, &wantSession, session.Session, "Session") assert.EqualValues(t, 2, sbc0.CommitCount.Get(), "sbc0.CommitCount") assert.EqualValues(t, 1, sbc1.CommitCount.Get(), "sbc1.CommitCount") + assert.EqualValues(t, 0, sbc0.RollbackCount.Get(), "sbc0.RollbackCount") + assert.EqualValues(t, 0, sbc1.RollbackCount.Get(), "sbc1.RollbackCount") } func TestTxConnCommitOrderSuccess(t *testing.T) { @@ -213,7 +487,7 @@ func TestTxConnCommitOrderSuccess(t *testing.T) { // Sequence the executes to ensure commit order session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.ExecuteMultiShard(ctx, rss0, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) wantSession := vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{{ @@ -229,7 +503,7 @@ func TestTxConnCommitOrderSuccess(t *testing.T) { utils.MustMatch(t, &wantSession, session.Session, "Session") session.SetCommitOrder(vtgatepb.CommitOrder_PRE) - sc.ExecuteMultiShard(ctx, rss0, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) wantSession = vtgatepb.Session{ InTransaction: true, PreSessions: []*vtgatepb.Session_ShardSession{{ @@ -254,7 +528,7 @@ func TestTxConnCommitOrderSuccess(t *testing.T) { utils.MustMatch(t, &wantSession, session.Session, "Session") session.SetCommitOrder(vtgatepb.CommitOrder_POST) - sc.ExecuteMultiShard(ctx, rss1, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss1, queries, session, false) wantSession = vtgatepb.Session{ InTransaction: true, PreSessions: []*vtgatepb.Session_ShardSession{{ @@ -288,7 +562,7 @@ func TestTxConnCommitOrderSuccess(t *testing.T) { utils.MustMatch(t, &wantSession, session.Session, "Session") // Ensure nothing changes if we reuse a transaction. - sc.ExecuteMultiShard(ctx, rss1, queries, session, false, false) + sc.ExecuteMultiShard(ctx, rss1, queries, session, false) utils.MustMatch(t, &wantSession, session.Session, "Session") require.NoError(t, @@ -299,12 +573,153 @@ func TestTxConnCommitOrderSuccess(t *testing.T) { assert.EqualValues(t, 1, sbc1.CommitCount.Get(), "sbc1.CommitCount") } +func TestTxConnReservedCommitOrderSuccess(t *testing.T) { + sc, sbc0, sbc1, rss0, rss1, _ := newTestTxConnEnv(t, "TestTxConn") + sc.txConn.mode = vtgatepb.TransactionMode_MULTI + + queries := []*querypb.BoundQuery{{ + Sql: "query1", + }} + + // Sequence the executes to ensure commit order + session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + wantSession := vtgatepb.Session{ + InTransaction: true, + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + + session.SetCommitOrder(vtgatepb.CommitOrder_PRE) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + wantSession = vtgatepb.Session{ + InTransaction: true, + InReservedConn: true, + PreSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 2, + ReservedId: 2, + TabletAlias: sbc0.Tablet().Alias, + }}, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + + session.SetCommitOrder(vtgatepb.CommitOrder_POST) + sc.ExecuteMultiShard(ctx, rss1, queries, session, false) + wantSession = vtgatepb.Session{ + InTransaction: true, + InReservedConn: true, + PreSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 2, + ReservedId: 2, + TabletAlias: sbc0.Tablet().Alias, + }}, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }}, + PostSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + TransactionId: 1, + ReservedId: 1, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + + // Ensure nothing changes if we reuse a transaction. + sc.ExecuteMultiShard(ctx, rss1, queries, session, false) + utils.MustMatch(t, &wantSession, session.Session, "Session") + + require.NoError(t, + sc.txConn.Commit(ctx, session)) + wantSession = vtgatepb.Session{ + InReservedConn: true, + PreSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 3, + TabletAlias: sbc0.Tablet().Alias, + }}, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 4, + TabletAlias: sbc0.Tablet().Alias, + }}, + PostSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 2, + TabletAlias: sbc0.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + assert.EqualValues(t, 2, sbc0.CommitCount.Get(), "sbc0.CommitCount") + assert.EqualValues(t, 1, sbc1.CommitCount.Get(), "sbc1.CommitCount") + + require.NoError(t, + sc.txConn.Release(ctx, session)) + wantSession = vtgatepb.Session{} + utils.MustMatch(t, &wantSession, session.Session, "Session") + assert.EqualValues(t, 2, sbc0.ReleaseCount.Get(), "sbc0.ReleaseCount") + assert.EqualValues(t, 1, sbc1.ReleaseCount.Get(), "sbc1.ReleaseCount") +} + func TestTxConnCommit2PC(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PC") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - sc.Execute(ctx, "query1", nil, rss01, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) session.TransactionMode = vtgatepb.TransactionMode_TWOPC require.NoError(t, sc.txConn.Commit(ctx, session)) @@ -318,7 +733,7 @@ func TestTxConnCommit2PC(t *testing.T) { func TestTxConnCommit2PCOneParticipant(t *testing.T) { sc, sbc0, _, rss0, _, _ := newTestTxConnEnv(t, "TestTxConnCommit2PCOneParticipant") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) session.TransactionMode = vtgatepb.TransactionMode_TWOPC require.NoError(t, sc.txConn.Commit(ctx, session)) @@ -329,8 +744,8 @@ func TestTxConnCommit2PCCreateTransactionFail(t *testing.T) { sc, sbc0, sbc1, rss0, rss1, _ := newTestTxConnEnv(t, "TestTxConnCommit2PCCreateTransactionFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - sc.Execute(ctx, "query1", nil, rss1, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss1, queries, session, false) sbc0.MustFailCreateTransaction = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -351,8 +766,8 @@ func TestTxConnCommit2PCPrepareFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PCPrepareFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - sc.Execute(ctx, "query1", nil, rss01, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) sbc1.MustFailPrepare = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -371,8 +786,8 @@ func TestTxConnCommit2PCStartCommitFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PCStartCommitFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - sc.Execute(ctx, "query1", nil, rss01, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) sbc0.MustFailStartCommit = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -391,8 +806,8 @@ func TestTxConnCommit2PCCommitPreparedFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PCCommitPreparedFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - sc.Execute(ctx, "query1", nil, rss01, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) sbc1.MustFailCommitPrepared = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -411,8 +826,8 @@ func TestTxConnCommit2PCConcludeTransactionFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PCConcludeTransactionFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - sc.Execute(ctx, "query1", nil, rss01, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) sbc0.MustFailConcludeTransaction = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -431,8 +846,8 @@ func TestTxConnRollback(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TxConnRollback") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(ctx, "query1", nil, rss0, session, false, nil, false) - sc.Execute(ctx, "query1", nil, rss01, session, false, nil, false) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) require.NoError(t, sc.txConn.Rollback(ctx, session)) wantSession := vtgatepb.Session{} @@ -441,6 +856,63 @@ func TestTxConnRollback(t *testing.T) { assert.EqualValues(t, 1, sbc1.RollbackCount.Get(), "sbc1.RollbackCount") } +func TestTxConnReservedRollback(t *testing.T) { + sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TxConnReservedRollback") + + session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) + require.NoError(t, + sc.txConn.Rollback(ctx, session)) + wantSession := vtgatepb.Session{ + InReservedConn: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TxConnReservedRollback", + Shard: "0", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 2, + TabletAlias: sbc0.Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: "TxConnReservedRollback", + Shard: "1", + TabletType: topodatapb.TabletType_MASTER, + }, + ReservedId: 2, + TabletAlias: sbc1.Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + assert.EqualValues(t, 1, sbc0.RollbackCount.Get(), "sbc0.RollbackCount") + assert.EqualValues(t, 1, sbc1.RollbackCount.Get(), "sbc1.RollbackCount") + assert.EqualValues(t, 0, sbc0.ReleaseCount.Get(), "sbc0.ReleaseCount") + assert.EqualValues(t, 0, sbc1.ReleaseCount.Get(), "sbc1.ReleaseCount") +} + +func TestTxConnReservedRollbackFailure(t *testing.T) { + sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TxConnReservedRollback") + + session := NewSafeSession(&vtgatepb.Session{InTransaction: true, InReservedConn: true}) + sc.ExecuteMultiShard(ctx, rss0, queries, session, false) + sc.ExecuteMultiShard(ctx, rss01, twoQueries, session, false) + + sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1 + require.Error(t, + sc.txConn.Rollback(ctx, session)) + wantSession := vtgatepb.Session{ + Warnings: []*querypb.QueryWarning{{ + Message: "rollback encountered an error and connection to all shard for this session is released: Code: INVALID_ARGUMENT\nINVALID_ARGUMENT error\n\ntarget: TxConnReservedRollback.1.master, used tablet: aa-0 (1)", + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + assert.EqualValues(t, 1, sbc0.RollbackCount.Get(), "sbc0.RollbackCount") + assert.EqualValues(t, 1, sbc1.RollbackCount.Get(), "sbc1.RollbackCount") + assert.EqualValues(t, 1, sbc0.ReleaseCount.Get(), "sbc0.ReleaseCount") + assert.EqualValues(t, 1, sbc1.ReleaseCount.Get(), "sbc1.ReleaseCount") +} + func TestTxConnResolveOnPrepare(t *testing.T) { sc, sbc0, sbc1, _, _, _ := newTestTxConnEnv(t, "TestTxConn") diff --git a/go/vt/vtgate/vcursor_impl.go b/go/vt/vtgate/vcursor_impl.go index 2b739bd06d9..45e20a3c009 100644 --- a/go/vt/vtgate/vcursor_impl.go +++ b/go/vt/vtgate/vcursor_impl.go @@ -58,7 +58,7 @@ var _ iExecute = (*Executor)(nil) // vcursor_impl needs these facilities to be able to be able to execute queries for vindexes type iExecute interface { Execute(ctx context.Context, method string, session *SafeSession, s string, vars map[string]*querypb.BindVariable) (*sqltypes.Result, error) - ExecuteMultiShard(ctx context.Context, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, notInTransaction bool, autocommit bool) (qr *sqltypes.Result, errs []error) + ExecuteMultiShard(ctx context.Context, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, session *SafeSession, autocommit bool) (qr *sqltypes.Result, errs []error) StreamExecuteMulti(ctx context.Context, s string, rss []*srvtopo.ResolvedShard, vars []map[string]*querypb.BindVariable, options *querypb.ExecuteOptions, callback func(reply *sqltypes.Result) error) error // TODO: remove when resolver is gone @@ -284,7 +284,7 @@ func (vc *vcursorImpl) Execute(method string, query string, bindVars map[string] // ExecuteMultiShard is part of the engine.VCursor interface. func (vc *vcursorImpl) ExecuteMultiShard(rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, rollbackOnError, autocommit bool) (*sqltypes.Result, []error) { atomic.AddUint32(&vc.logStats.ShardQueries, uint32(len(queries))) - qr, errs := vc.executor.ExecuteMultiShard(vc.ctx, rss, commentedShardQueries(queries, vc.marginComments), vc.safeSession, false, autocommit) + qr, errs := vc.executor.ExecuteMultiShard(vc.ctx, rss, commentedShardQueries(queries, vc.marginComments), vc.safeSession, autocommit) if errs == nil && rollbackOnError { vc.rollbackOnPartialExec = true @@ -308,7 +308,7 @@ func (vc *vcursorImpl) ExecuteStandalone(query string, bindVars map[string]*quer } // The autocommit flag is always set to false because we currently don't // execute DMLs through ExecuteStandalone. - qr, errs := vc.executor.ExecuteMultiShard(vc.ctx, rss, bqs, NewAutocommitSession(vc.safeSession.Session), false, false /* autocommit */) + qr, errs := vc.executor.ExecuteMultiShard(vc.ctx, rss, bqs, NewAutocommitSession(vc.safeSession.Session), false /* autocommit */) return qr, vterrors.Aggregate(errs) } diff --git a/go/vt/vttablet/endtoend/framework/client.go b/go/vt/vttablet/endtoend/framework/client.go index 2dfd48cf828..b38553a0e93 100644 --- a/go/vt/vttablet/endtoend/framework/client.go +++ b/go/vt/vttablet/endtoend/framework/client.go @@ -300,7 +300,7 @@ func (client *QueryClient) ReserveExecute(query string, preQueries []string, bin if client.reservedID != 0 { return nil, errors.New("already reserved a connection") } - qr, reservedID, _, err := client.server.ReserveExecute(client.ctx, &client.target, query, preQueries, bindvars, client.transactionID, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}) + qr, reservedID, _, err := client.server.ReserveExecute(client.ctx, &client.target, preQueries, query, bindvars, client.transactionID, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}) client.reservedID = reservedID if err != nil { return nil, err @@ -316,14 +316,7 @@ func (client *QueryClient) ReserveBeginExecute(query string, preQueries []string if client.transactionID != 0 { return nil, errors.New("already in transaction") } - qr, transactionID, reservedID, _, err := client.server.ReserveBeginExecute( - client.ctx, - &client.target, - query, - preQueries, - bindvars, - &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}, - ) + qr, transactionID, reservedID, _, err := client.server.ReserveBeginExecute(client.ctx, &client.target, preQueries, query, bindvars, &querypb.ExecuteOptions{IncludedFields: querypb.ExecuteOptions_ALL}) client.transactionID = transactionID client.reservedID = reservedID if err != nil { diff --git a/go/vt/vttablet/grpctabletconn/conn.go b/go/vt/vttablet/grpctabletconn/conn.go index dabc4a5ace3..53b75533826 100644 --- a/go/vt/vttablet/grpctabletconn/conn.go +++ b/go/vt/vttablet/grpctabletconn/conn.go @@ -733,7 +733,7 @@ func (conn *gRPCQueryClient) HandlePanic(err *error) { } //ReserveBeginExecute implements the queryservice interface -func (conn *gRPCQueryClient) ReserveBeginExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { +func (conn *gRPCQueryClient) ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { @@ -763,7 +763,7 @@ func (conn *gRPCQueryClient) ReserveBeginExecute(ctx context.Context, target *qu } //ReserveBeginExecute implements the queryservice interface -func (conn *gRPCQueryClient) ReserveExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, txID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { +func (conn *gRPCQueryClient) ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { @@ -778,7 +778,7 @@ func (conn *gRPCQueryClient) ReserveExecute(ctx context.Context, target *querypb Sql: sql, BindVariables: bindVariables, }, - TransactionId: txID, + TransactionId: transactionID, Options: options, PreQueries: preQueries, } diff --git a/go/vt/vttablet/queryservice/queryservice.go b/go/vt/vttablet/queryservice/queryservice.go index 8dc69215a42..ffbe2af46e5 100644 --- a/go/vt/vttablet/queryservice/queryservice.go +++ b/go/vt/vttablet/queryservice/queryservice.go @@ -111,9 +111,9 @@ type QueryService interface { // HandlePanic will be called if any of the functions panic. HandlePanic(err *error) - ReserveBeginExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) + ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) - ReserveExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) + ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) Release(ctx context.Context, target *querypb.Target, transactionID, reservedID int64) error diff --git a/go/vt/vttablet/queryservice/wrapped.go b/go/vt/vttablet/queryservice/wrapped.go index 3d714319bdf..039d0958518 100644 --- a/go/vt/vttablet/queryservice/wrapped.go +++ b/go/vt/vttablet/queryservice/wrapped.go @@ -280,27 +280,28 @@ func (ws *wrappedService) HandlePanic(err *error) { // No-op. Wrappers must call HandlePanic. } -func (ws *wrappedService) ReserveBeginExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { +func (ws *wrappedService) ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { var res *sqltypes.Result var transactionID, reservedID int64 var alias *topodatapb.TabletAlias err := ws.wrapper(ctx, target, ws.impl, "ReserveBeginExecute", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (bool, error) { var err error - res, transactionID, reservedID, alias, err = conn.ReserveBeginExecute(ctx, target, sql, preQueries, bindVariables, options) + res, transactionID, reservedID, alias, err = conn.ReserveBeginExecute(ctx, target, preQueries, sql, bindVariables, options) return canRetry(ctx, err), err }) return res, transactionID, reservedID, alias, err } -func (ws *wrappedService) ReserveExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, txID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { +func (ws *wrappedService) ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { + notInTransaction := (transactionID == 0) var res *sqltypes.Result var reservedID int64 var alias *topodatapb.TabletAlias err := ws.wrapper(ctx, target, ws.impl, "ReserveExecute", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (bool, error) { var err error - res, reservedID, alias, err = conn.ReserveExecute(ctx, target, sql, preQueries, bindVariables, txID, options) - return canRetry(ctx, err), err + res, reservedID, alias, err = conn.ReserveExecute(ctx, target, preQueries, sql, bindVariables, transactionID, options) + return canRetry(ctx, err) && notInTransaction, err }) return res, reservedID, alias, err diff --git a/go/vt/vttablet/sandboxconn/sandboxconn.go b/go/vt/vttablet/sandboxconn/sandboxconn.go index 6da51987dce..1c1b000a967 100644 --- a/go/vt/vttablet/sandboxconn/sandboxconn.go +++ b/go/vt/vttablet/sandboxconn/sandboxconn.go @@ -66,6 +66,8 @@ type SandboxConn struct { SetRollbackCount sync2.AtomicInt64 ConcludeTransactionCount sync2.AtomicInt64 ReadTransactionCount sync2.AtomicInt64 + ReserveCount sync2.AtomicInt64 + ReleaseCount sync2.AtomicInt64 // Queries stores the non-batch requests received. Queries []*querypb.BoundQuery @@ -95,6 +97,11 @@ type SandboxConn struct { // transaction id generator TransactionID sync2.AtomicInt64 + // reserve id generator + ReserveID sync2.AtomicInt64 + + txIDToRID map[int64]int64 + sExecMu sync.Mutex execMu sync.Mutex } @@ -106,6 +113,7 @@ func NewSandboxConn(t *topodatapb.Tablet) *SandboxConn { return &SandboxConn{ tablet: t, MustFailCodes: make(map[vtrpcpb.Code]int), + txIDToRID: make(map[int64]int64), } } @@ -215,13 +223,21 @@ func (sbc *SandboxConn) begin(ctx context.Context, target *querypb.Target, preQu // Commit is part of the QueryService interface. func (sbc *SandboxConn) Commit(ctx context.Context, target *querypb.Target, transactionID int64) (int64, error) { sbc.CommitCount.Add(1) - return 0, sbc.getError() + reservedID := sbc.txIDToRID[transactionID] + if reservedID != 0 { + reservedID = sbc.ReserveID.Add(1) + } + return reservedID, sbc.getError() } // Rollback is part of the QueryService interface. func (sbc *SandboxConn) Rollback(ctx context.Context, target *querypb.Target, transactionID int64) (int64, error) { sbc.RollbackCount.Add(1) - return 0, sbc.getError() + reservedID := sbc.txIDToRID[transactionID] + if reservedID != 0 { + reservedID = sbc.ReserveID.Add(1) + } + return reservedID, sbc.getError() } // Prepare prepares the specified transaction. @@ -314,6 +330,9 @@ func (sbc *SandboxConn) ReadTransaction(ctx context.Context, target *querypb.Tar // BeginExecute is part of the QueryService interface. func (sbc *SandboxConn) BeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, query string, bindVars map[string]*querypb.BindVariable, reservedID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { transactionID, alias, err := sbc.begin(ctx, target, preQueries, reservedID, options) + if transactionID != 0 { + sbc.txIDToRID[transactionID] = reservedID + } if err != nil { return nil, 0, nil, err } @@ -411,18 +430,44 @@ func (sbc *SandboxConn) HandlePanic(err *error) { } //ReserveBeginExecute implements the QueryService interface -func (sbc *SandboxConn) ReserveBeginExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { - panic("implement me") +func (sbc *SandboxConn) ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { + reservedID := sbc.reserve(ctx, target, preQueries, bindVariables, 0, options) + result, transactionID, alias, err := sbc.BeginExecute(ctx, target, preQueries, sql, bindVariables, reservedID, options) + if transactionID != 0 { + sbc.txIDToRID[transactionID] = reservedID + } + if err != nil { + return nil, transactionID, reservedID, alias, err + } + return result, transactionID, reservedID, alias, nil } //ReserveExecute implements the QueryService interface -func (sbc *SandboxConn) ReserveExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, txID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { - panic("implement me") +func (sbc *SandboxConn) ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { + reservedID := sbc.reserve(ctx, target, preQueries, bindVariables, transactionID, options) + result, err := sbc.Execute(ctx, target, sql, bindVariables, transactionID, reservedID, options) + if transactionID != 0 { + sbc.txIDToRID[transactionID] = reservedID + } + if err != nil { + return nil, 0, nil, err + } + return result, reservedID, sbc.tablet.Alias, nil +} + +func (sbc *SandboxConn) reserve(ctx context.Context, target *querypb.Target, preQueries []string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) int64 { + sbc.ReserveCount.Add(1) + for _, query := range preQueries { + sbc.Execute(ctx, target, query, bindVariables, transactionID, 0, options) + } + reservedID := sbc.ReserveID.Add(1) + return reservedID } //Release implements the QueryService interface func (sbc *SandboxConn) Release(ctx context.Context, target *querypb.Target, transactionID, reservedID int64) error { - panic("implement me") + sbc.ReleaseCount.Add(1) + return sbc.getError() } // Close does not change ExecCount diff --git a/go/vt/vttablet/tabletconntest/fakequeryservice.go b/go/vt/vttablet/tabletconntest/fakequeryservice.go index eb9a9786897..b61fbefa50f 100644 --- a/go/vt/vttablet/tabletconntest/fakequeryservice.go +++ b/go/vt/vttablet/tabletconntest/fakequeryservice.go @@ -724,12 +724,12 @@ func (f *FakeQueryService) QueryServiceByAlias(_ *topodatapb.TabletAlias) (query } // ReserveBeginExecute satisfies the Gateway interface -func (f *FakeQueryService) ReserveBeginExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { +func (f *FakeQueryService) ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { panic("implement me") } //ReserveExecute implements the QueryService interface -func (f *FakeQueryService) ReserveExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, txID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { +func (f *FakeQueryService) ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { panic("implement me") } diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 6c9a8865210..ae1dfa4c176 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -1025,7 +1025,7 @@ func (tsv *TabletServer) VStreamResults(ctx context.Context, target *querypb.Tar } //ReserveBeginExecute implements the QueryService interface -func (tsv *TabletServer) ReserveBeginExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { +func (tsv *TabletServer) ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, int64, *topodatapb.TabletAlias, error) { var connID int64 var err error @@ -1055,7 +1055,7 @@ func (tsv *TabletServer) ReserveBeginExecute(ctx context.Context, target *queryp } //ReserveExecute implements the QueryService interface -func (tsv *TabletServer) ReserveExecute(ctx context.Context, target *querypb.Target, sql string, preQueries []string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { +func (tsv *TabletServer) ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, *topodatapb.TabletAlias, error) { var connID int64 var err error diff --git a/go/vt/vttablet/tabletserver/tabletserver_test.go b/go/vt/vttablet/tabletserver/tabletserver_test.go index 718d08e9de9..e72c376327c 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_test.go @@ -484,7 +484,7 @@ func TestTabletServerReserveConnection(t *testing.T) { options := &querypb.ExecuteOptions{} // reserve a connection - _, rID, _, err := tsv.ReserveExecute(ctx, &target, "select 42", nil, nil, 0, options) + _, rID, _, err := tsv.ReserveExecute(ctx, &target, nil, "select 42", nil, 0, options) require.NoError(t, err) // run a query in it @@ -533,7 +533,7 @@ func TestMakeSureToCloseDbConnWhenBeginQueryFails(t *testing.T) { options := &querypb.ExecuteOptions{} // run a query with a non-existent reserved id - _, _, _, _, err := tsv.ReserveBeginExecute(ctx, &target, "select 42", []string{}, nil, options) + _, _, _, _, err := tsv.ReserveBeginExecute(ctx, &target, []string{}, "select 42", nil, options) require.Error(t, err) } @@ -547,7 +547,7 @@ func TestTabletServerReserveAndBeginCommit(t *testing.T) { options := &querypb.ExecuteOptions{} // reserve a connection and a transaction - _, txID, rID, _, err := tsv.ReserveBeginExecute(ctx, &target, "select 42", nil, nil, options) + _, txID, rID, _, err := tsv.ReserveBeginExecute(ctx, &target, nil, "select 42", nil, options) require.NoError(t, err) defer func() { // fallback so the test finishes quickly @@ -1970,7 +1970,7 @@ func TestReserveBeginExecute(t *testing.T) { defer db.Close() target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} - _, transactionID, reservedID, _, err := tsv.ReserveBeginExecute(ctx, &target, "select 42", []string{"select 43"}, nil, &querypb.ExecuteOptions{}) + _, transactionID, reservedID, _, err := tsv.ReserveBeginExecute(ctx, &target, []string{"select 43"}, "select 42", nil, &querypb.ExecuteOptions{}) require.NoError(t, err) assert.Greater(t, transactionID, int64(0), "transactionID") @@ -1993,7 +1993,7 @@ func TestReserveExecute_WithoutTx(t *testing.T) { target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} - _, reservedID, _, err := tsv.ReserveExecute(ctx, &target, "select 42", []string{"select 43"}, nil, 0, &querypb.ExecuteOptions{}) + _, reservedID, _, err := tsv.ReserveExecute(ctx, &target, []string{"select 43"}, "select 42", nil, 0, &querypb.ExecuteOptions{}) require.NoError(t, err) assert.NotEqual(t, int64(0), reservedID, "reservedID should not be zero") expected := []string{ @@ -2017,7 +2017,7 @@ func TestReserveExecute_WithTx(t *testing.T) { require.NotEqual(t, int64(0), transactionID) db.ResetQueryLog() - _, reservedID, _, err := tsv.ReserveExecute(ctx, &target, "select 42", []string{"select 43"}, nil, transactionID, &querypb.ExecuteOptions{}) + _, reservedID, _, err := tsv.ReserveExecute(ctx, &target, []string{"select 43"}, "select 42", nil, transactionID, &querypb.ExecuteOptions{}) require.NoError(t, err) defer tsv.Release(ctx, &target, transactionID, reservedID) assert.Equal(t, transactionID, reservedID, "reservedID should be equal to transactionID") @@ -2075,14 +2075,14 @@ func TestRelease(t *testing.T) { switch { case test.begin && test.reserve: - _, transactionID, reservedID, _, err = tsv.ReserveBeginExecute(ctx, &target, "select 42", []string{"select 1212"}, nil, &querypb.ExecuteOptions{}) + _, transactionID, reservedID, _, err = tsv.ReserveBeginExecute(ctx, &target, []string{"select 1212"}, "select 42", nil, &querypb.ExecuteOptions{}) require.NotEqual(t, int64(0), transactionID) require.NotEqual(t, int64(0), reservedID) case test.begin: _, transactionID, _, err = tsv.BeginExecute(ctx, &target, nil, "select 42", nil, 0, &querypb.ExecuteOptions{}) require.NotEqual(t, int64(0), transactionID) case test.reserve: - _, reservedID, _, err = tsv.ReserveExecute(ctx, &target, "select 42", nil, nil, 0, &querypb.ExecuteOptions{}) + _, reservedID, _, err = tsv.ReserveExecute(ctx, &target, nil, "select 42", nil, 0, &querypb.ExecuteOptions{}) require.NotEqual(t, int64(0), reservedID) } require.NoError(t, err) @@ -2113,12 +2113,12 @@ func TestReserveStats(t *testing.T) { ctx := callerid.NewContext(context.Background(), nil, callerID) // Starts reserved connection and transaction - _, rbeTxID, rbeRID, _, err := tsv.ReserveBeginExecute(ctx, &target, "select 42", nil, nil, &querypb.ExecuteOptions{}) + _, rbeTxID, rbeRID, _, err := tsv.ReserveBeginExecute(ctx, &target, nil, "select 42", nil, &querypb.ExecuteOptions{}) require.NoError(t, err) assert.EqualValues(t, 1, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) // Starts reserved connection - _, reRID, _, err := tsv.ReserveExecute(ctx, &target, "select 42", nil, nil, 0, &querypb.ExecuteOptions{}) + _, reRID, _, err := tsv.ReserveExecute(ctx, &target, nil, "select 42", nil, 0, &querypb.ExecuteOptions{}) require.NoError(t, err) assert.EqualValues(t, 2, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) @@ -2133,7 +2133,7 @@ func TestReserveStats(t *testing.T) { assert.EqualValues(t, 2, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) // Reserved the connection on previous transaction - _, beReRID, _, err := tsv.ReserveExecute(ctx, &target, "select 42", nil, nil, beTxID, &querypb.ExecuteOptions{}) + _, beReRID, _, err := tsv.ReserveExecute(ctx, &target, nil, "select 42", nil, beTxID, &querypb.ExecuteOptions{}) require.NoError(t, err) assert.EqualValues(t, 3, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) diff --git a/proto/vtgate.proto b/proto/vtgate.proto index 719b3e696d2..13be289b2c8 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -125,6 +125,9 @@ message Session { // Stores savepoint and release savepoint calls inside a transaction // and is reset once transaction is committed or rolled back. repeated string savepoints = 16; + + // in_reserved_conn is set to true if the session should be using reserved connections. + bool in_reserved_conn = 17; } // ExecuteRequest is the payload to Execute.