@@ -65,50 +65,64 @@ async def write_payload(self, payload: bytes, packet_type: str = "", reset_seque
6565 Write payload with MariaDB packet framing (async version)
6666
6767 Args:
68- payload: Payload bytes to send
68+ payload: Payload bytes with first 4 bytes reserved for header
6969 packet_type: Packet type for logging (e.g., "COM_QUERY")
7070 reset_sequence: Whether to reset sequence number before sending
7171 """
7272 if reset_sequence :
7373 self .sequence .set (- 1 )
7474
75- payload_len = len (payload )
76- offset = 0
75+ # Payload has 4 bytes reserved at start for header
76+ payload_len = len (payload ) - 4
77+ data_offset = 4 # Data starts after reserved header space
7778
7879 # Handle empty payload - still need to send header
7980 if payload_len == 0 :
8081 seq = self .sequence .increment_and_get ()
81- header = b'\x00 \x00 \x00 ' + bytes ([seq ])
82+ # Write header into first 4 bytes
83+ payload_buf = bytearray (payload )
84+ payload_buf [0 :4 ] = b'\x00 \x00 \x00 ' + bytes ([seq ])
8285
8386 if logger .isEnabledFor (logging .DEBUG ):
8487 conn_id_str = f"[conn_id={ self .connection_id } ]" if self .connection_id >= 0 else ""
8588 packet_type_str = f" { packet_type } " if packet_type else ""
86- logger .debug (hex_dump (header , f"SEND async: { conn_id_str } { packet_type_str } " ))
89+ logger .debug (hex_dump (bytes ( payload_buf [ 0 : 4 ]) , f"SEND async: { conn_id_str } { packet_type_str } " ))
8790
88- self .writer .write (header )
91+ self .writer .write (payload_buf [ 0 : 4 ] )
8992 await self .writer .drain ()
9093 return
9194
95+ # Convert to bytearray for in-place header writing
96+ payload_buf = bytearray (payload )
97+
9298 # Handle packet splitting for large payloads
93- while offset < payload_len :
94- chunk_size = min (MAX_PACKET_SIZE , payload_len - offset )
99+ sent = 0
100+
101+ while sent < payload_len :
102+ chunk_size = min (MAX_PACKET_SIZE , payload_len - sent )
95103 seq = self .sequence .increment_and_get ()
96104
97- # Build header: 3-byte length + 1-byte sequence
98- header = chunk_size .to_bytes (3 , 'little' ) + bytes ([seq ])
105+ # Data for this chunk starts at data_offset + sent
106+ chunk_start = data_offset + sent
107+ chunk_end = chunk_start + chunk_size
108+
109+ # Write header 4 bytes before the chunk data
110+ header_pos = chunk_start - 4
111+ payload_buf [header_pos ] = chunk_size & 0xff
112+ payload_buf [header_pos + 1 ] = (chunk_size >> 8 ) & 0xff
113+ payload_buf [header_pos + 2 ] = (chunk_size >> 16 ) & 0xff
114+ payload_buf [header_pos + 3 ] = seq
99115
100- # Log if debug enabled (need to build full packet for logging)
116+ # Log if debug enabled
101117 if logger .isEnabledFor (logging .DEBUG ):
102- chunk = payload [offset :offset + chunk_size ]
103- packet = header + chunk
118+ packet = bytes (payload_buf [header_pos :chunk_end ])
104119 conn_id_str = f"[conn_id={ self .connection_id } ]" if self .connection_id >= 0 else ""
105120 packet_type_str = f" { packet_type } " if packet_type else ""
106121 logger .debug (hex_dump (packet , f"SEND async: { conn_id_str } { packet_type_str } " ))
107122
108- # Send header and chunk separately (more efficient - no concatenation)
109- self .writer .write (header )
110- self .writer .write (payload [offset :offset + chunk_size ])
111- offset += chunk_size
123+ # Send packet: header + chunk data
124+ self .writer .write (payload_buf [header_pos :chunk_end ])
125+ sent += chunk_size
112126
113127 # Flush all buffered data
114128 await self .writer .drain ()
@@ -124,71 +138,79 @@ async def write_payload(self, payload: bytes, packet_type: str = "", reset_seque
124138class SyncWriteStream (BaseWriteStream ):
125139 """Sync write stream implementation using blocking socket operations"""
126140
127- def __init__ (self , sock : socket .socket , connection_id : int = - 1 ):
128- """
129- Initialize sync write stream
130-
131- Args:
132- sock: Blocking socket
133- connection_id: Connection ID for logging
134- """
135- self .socket : socket .socket = sock
136- super ().__init__ (connection_id )
141+ def __init__ (self , socket : socket .socket , connection_id : int = - 1 ):
142+ """Initialize write stream with socket"""
143+ self .socket = socket
144+ self .sequence = MutableInt (- 1 )
145+ self .connection_id = connection_id
146+ # Check once if sendmsg is supported (Unix) or if we need sendall (Windows)
147+ self .has_sendmsg = hasattr (socket , 'sendmsg' )
137148
138149 def write_payload (self , payload : bytes , packet_type : str = "" , reset_sequence : bool = True ) -> None :
139150 """
140151 Write payload with MariaDB packet framing (sync version)
141152
142153 Args:
143- payload: Payload bytes to send
154+ payload: Payload bytes with first 4 bytes reserved for header
144155 packet_type: Packet type for logging (e.g., "COM_QUERY")
145156 reset_sequence: Whether to reset sequence number before sending
146157 """
147158 if reset_sequence :
148159 self .sequence .set (- 1 )
149160
150- payload_len = len (payload )
151- offset = 0
161+ # Payload has 4 bytes reserved at start for header
162+ payload_len = len (payload ) - 4
163+ data_offset = 4 # Data starts after reserved header space
152164
153165 # Handle empty payload - still need to send header
154166 if payload_len == 0 :
155167 seq = self .sequence .increment_and_get ()
156- header = b'\x00 \x00 \x00 ' + bytes ([seq ])
168+ # Write header into first 4 bytes
169+ payload_buf = bytearray (payload )
170+ payload_buf [0 :4 ] = b'\x00 \x00 \x00 ' + bytes ([seq ])
157171
158172 if logger .isEnabledFor (logging .DEBUG ):
159173 conn_id_str = f"[conn_id={ self .connection_id } ]" if self .connection_id >= 0 else ""
160174 packet_type_str = f" { packet_type } " if packet_type else ""
161- logger .debug (hex_dump (header , f"SEND sync: { conn_id_str } { packet_type_str } " ))
175+ logger .debug (hex_dump (bytes ( payload_buf [ 0 : 4 ]) , f"SEND sync: { conn_id_str } { packet_type_str } " ))
162176
163- self .socket .sendall (header )
177+ self .socket .sendall (payload_buf [ 0 : 4 ] )
164178 return
165179
180+ # Convert to bytearray for in-place header writing
181+ payload_buf = bytearray (payload )
182+
166183 # Handle packet splitting for large payloads
167- while offset < payload_len :
168- chunk_size = min (MAX_PACKET_SIZE , payload_len - offset )
184+ sent = 0 # Track how much data we've sent
185+
186+
187+ while sent < payload_len :
188+ chunk_size = min (MAX_PACKET_SIZE , payload_len - sent )
169189 seq = self .sequence .increment_and_get ()
170190
171- # Build header: 3-byte length + 1-byte sequence
172- header = chunk_size .to_bytes (3 , 'little' ) + bytes ([seq ])
173- chunk = payload [offset :offset + chunk_size ]
191+ # Data for this chunk starts at data_offset + sent
192+ chunk_start = data_offset + sent
193+ chunk_end = chunk_start + chunk_size
194+
195+ # Write header 4 bytes before the chunk data
196+ header_pos = chunk_start - 4
197+
198+ payload_buf [header_pos ] = chunk_size & 0xff
199+ payload_buf [header_pos + 1 ] = (chunk_size >> 8 ) & 0xff
200+ payload_buf [header_pos + 2 ] = (chunk_size >> 16 ) & 0xff
201+ payload_buf [header_pos + 3 ] = seq
174202
175- # Log if debug enabled (need full packet for logging)
203+ # Log if debug enabled
176204 if logger .isEnabledFor (logging .DEBUG ):
177- packet = header + chunk
205+ packet = bytes ( payload_buf [ header_pos : chunk_end ])
178206 conn_id_str = f"[conn_id={ self .connection_id } ]" if self .connection_id >= 0 else ""
179207 packet_type_str = f" { packet_type } " if packet_type else ""
180208 logger .debug (hex_dump (packet , f"SEND sync: { conn_id_str } { packet_type_str } " ))
181209
182- # Send header and chunk in a single syscall using scatter-gather I/O
183- # sendmsg() is available on Unix and sends multiple buffers efficiently
184- try :
185- self .socket .sendmsg ([header , chunk ])
186- except Exception :
187- # Fallback for platforms without sendmsg (e.g., Windows)
188- self .socket .sendall (header )
189- self .socket .sendall (chunk )
210+ # Send packet: header + chunk data
211+ self .socket .sendall (payload_buf [header_pos :chunk_end ])
190212
191- offset += chunk_size
213+ sent += chunk_size
192214
193215 # If last packet was exactly MAX_PACKET_SIZE, send empty packet to signal end
194216 if payload_len % MAX_PACKET_SIZE == 0 :
0 commit comments