Tip: 550
Title: P2P message snappy compression
Author: allen.cheng@tron.network
Status: Final
Type: Standards Track
Category: Networking
Date: 2023-05-22
This TIP proposes a small extension to the TRON protocol to enable Snappy compression on all message payloads after the initial handshake.
The base networking protocol used by TRON currently does not employ any form of compression. This results in a massive amount of bandwidth wasted in the entire network, making both initial sync and regular operation slower and laggier. This situation can be improved by compressing network messages. After extensive benchmarks, the results show that data traffic is decreased by 40-60% for block synchronization.
Currently, the block size of the TRON network is around 80k bytes. And as the TRON network continues to grow, the block size may continue to increase, the network load will also increase, and the bandwidth requirements of nodes will also increase. However, most data (blocks, transactions) are highly compressible. By enabling compression at the message payload level, we can reduce the amount of data transmitted by the message, reducing bandwidth usage and data synchronization latency.
Enable Snappy compression on all TCP channel message payloads after the initial handshake. Compress the message before sending it through the channel. When receiving a compressed message, decompress it first and then continue the subsequent business processing. The handshake message is never compressed, since it is needed to negotiate the common version.
Pack the compressed message into CompressMessage
for network transmission. The structure of CompressMessage
is as follows:
message CompressMessage {
enum CompressType {
uncompress = 0;
snappy = 1;
}
CompressType type = 1;
bytes data = 2;
}
Snappy dependency
implementation group: 'org.xerial.snappy', name: 'snappy-java', version: '1.1.8.4'
Compression implementation when sending messages:
public void send(Message message) {
if (finishHandshake) {
data = ProtoUtil.compressMessage(message.getSendData()).toByteArray();
}
...
}
public static Connect.CompressMessage compressMessage(byte[] data) throws IOException {
Connect.CompressMessage.CompressType type = Connect.CompressMessage.CompressType.uncompress;
byte[] bytes = data;
byte[] compressData = Snappy.compress(data);
if (compressData.length < bytes.length) {
type = Connect.CompressMessage.CompressType.snappy;
bytes = compressData;
}
return Connect.CompressMessage.newBuilder()
.setData(ByteString.copyFrom(bytes))
.setType(type).build();
}
Decompression implementation when receiving a compressed message:
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) {
byte[] data = new byte[buffer.readableBytes()];
buffer.readBytes(data);
if (channel.isFinishHandshake()) {
data = ProtoUtil.uncompressMessage(Connect.CompressMessage.parseFrom(data));
}
...
}
public static byte[] uncompressMessage(Connect.CompressMessage message) throws IOException {
if (message.getType().equals(Connect.CompressMessage.CompressType.uncompress)) {
return message.getData().toByteArray();
} else {
return Snappy.uncompress(message.getData().toByteArray());
}
}
This proposal is fully backward compatible. Clients upgrading to the proposed new version protocol should still support skipping the compression step for older version protocol connections.
Download blocks with block heights from 46314350
to 46314360
, and perform a compression and decompression test. The compression rate is about 51%. The specific test results are as follows:
Block size (byte) | Compressed size (byte) | Compression rate | Compression/decompression time(ms) |
---|---|---|---|
89803 | 44361 | 0.50 | <1 |
104504 | 50926 | 0.51 | <1 |
70970 | 35247 | 0.50 | <1 |
96544 | 46905 | 0.51 | <1 |
82820 | 41005 | 0.50 | 1 |
83987 | 41878 | 0.50 | <1 |
82296 | 40566 | 0.51 | <1 |
77820 | 39108 | 0.50 | <1 |
73715 | 36027 | 0.51 | <1 |
90632 | 43918 | 0.52 | <1 |