Skip to content

Commit b4cc4cc

Browse files
Add PROTOCOL_VERSION_LEGACY constant and configurable encoder version
Go defines VersionLegacy = 0x86104dd760433fe5 for pre-1.0 dqlite servers and falls back to it when version 1 is rejected. Added the legacy constant and made MessageEncoder accept a version parameter so callers can implement the same fallback strategy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 parents 39e8c65 + 087c231 commit b4cc4cc

File tree

4 files changed

+27
-3
lines changed

4 files changed

+27
-3
lines changed

src/dqlitewire/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from dqlitewire.codec import MessageDecoder, MessageEncoder
55
from dqlitewire.constants import (
66
PROTOCOL_VERSION,
7+
PROTOCOL_VERSION_LEGACY,
78
ROW_DONE_BYTE,
89
ROW_DONE_MARKER,
910
ROW_PART_BYTE,
@@ -18,6 +19,7 @@
1819
"MessageDecoder",
1920
"MessageEncoder",
2021
"PROTOCOL_VERSION",
22+
"PROTOCOL_VERSION_LEGACY",
2123
"ProtocolError",
2224
"DecodeError",
2325
"EncodeError",

src/dqlitewire/codec.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@
9393
class MessageEncoder:
9494
"""Encodes messages to wire protocol format."""
9595

96+
def __init__(self, version: int = PROTOCOL_VERSION) -> None:
97+
"""Initialize encoder.
98+
99+
Args:
100+
version: Protocol version to use in handshake. Defaults to
101+
PROTOCOL_VERSION (1). Use PROTOCOL_VERSION_LEGACY
102+
(0x86104dd760433fe5) for pre-1.0 dqlite servers.
103+
"""
104+
self._version = version
105+
96106
def encode(self, message: Message) -> bytes:
97107
"""Encode a message to bytes."""
98108
return message.encode()
@@ -102,7 +112,7 @@ def encode_handshake(self) -> bytes:
102112
103113
Must be sent before any other message.
104114
"""
105-
return PROTOCOL_VERSION.to_bytes(8, "little")
115+
return self._version.to_bytes(8, "little")
106116

107117

108118
class MessageDecoder:

src/dqlitewire/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
from enum import IntEnum
44

5-
# Protocol version
5+
# Protocol versions
66
PROTOCOL_VERSION = 1
7+
PROTOCOL_VERSION_LEGACY = 0x86104DD760433FE5 # Pre-1.0 dqlite servers
78

89
# Word size in bytes (all messages are padded to 8-byte boundaries)
910
WORD_SIZE = 8

tests/test_codec.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
decode_message,
99
encode_message,
1010
)
11-
from dqlitewire.constants import PROTOCOL_VERSION
11+
from dqlitewire.constants import PROTOCOL_VERSION, PROTOCOL_VERSION_LEGACY
1212
from dqlitewire.exceptions import DecodeError
1313
from dqlitewire.messages import (
1414
ClientRequest,
@@ -31,6 +31,17 @@ def test_encode_handshake(self) -> None:
3131
assert len(handshake) == 8
3232
assert int.from_bytes(handshake, "little") == PROTOCOL_VERSION
3333

34+
def test_encode_handshake_legacy(self) -> None:
35+
"""Encoder with legacy version should produce the legacy handshake word."""
36+
encoder = MessageEncoder(version=PROTOCOL_VERSION_LEGACY)
37+
handshake = encoder.encode_handshake()
38+
assert len(handshake) == 8
39+
assert int.from_bytes(handshake, "little") == PROTOCOL_VERSION_LEGACY
40+
41+
def test_legacy_version_constant_matches_go(self) -> None:
42+
"""PROTOCOL_VERSION_LEGACY must match Go's VersionLegacy = 0x86104dd760433fe5."""
43+
assert PROTOCOL_VERSION_LEGACY == 0x86104DD760433FE5
44+
3445
def test_encoder_has_no_buffer_attribute(self) -> None:
3546
"""MessageEncoder should not have unused _buffer attribute."""
3647
encoder = MessageEncoder()

0 commit comments

Comments
 (0)