Skip to content

Commit fa939ba

Browse files
Added zstd support for msgs
Signed-off-by: Athish Pranav D <[email protected]>
1 parent 7f13c7a commit fa939ba

File tree

5 files changed

+60
-33
lines changed

5 files changed

+60
-33
lines changed

fluentd.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Gem::Specification.new do |gem|
3333
gem.add_runtime_dependency("tzinfo-data", ["~> 1.0"])
3434
gem.add_runtime_dependency("strptime", [">= 0.2.4", "< 1.0.0"])
3535
gem.add_runtime_dependency("webrick", ["~> 1.4"])
36+
gem.add_runtime_dependency("zstd-ruby", ["~> 1.5"])
3637

3738
# gems that aren't default gems as of Ruby 3.4
3839
gem.add_runtime_dependency("base64", ["~> 0.2"])

lib/fluent/event.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ class MessagePackEventStream < EventStream
203203
# https://github.com/msgpack/msgpack-ruby/issues/119
204204

205205
# Keep cached_unpacker argument for existing plugins
206-
def initialize(data, cached_unpacker = nil, size = 0, unpacked_times: nil, unpacked_records: nil)
206+
def initialize(data, cached_unpacker = nil, size = 0, unpacked_times: nil, unpacked_records: nil, compress: nil)
207207
@data = data
208208
@size = size
209209
@unpacked_times = unpacked_times
@@ -268,10 +268,11 @@ def to_msgpack_stream(time_int: false, packer: nil)
268268
end
269269

270270
class CompressedMessagePackEventStream < MessagePackEventStream
271-
def initialize(data, cached_unpacker = nil, size = 0, unpacked_times: nil, unpacked_records: nil)
271+
def initialize(data, cached_unpacker = nil, size = 0, unpacked_times: nil, unpacked_records: nil, compress: :gzip)
272272
super
273273
@decompressed_data = nil
274274
@compressed_data = data
275+
@type = compress
275276
end
276277

277278
def empty?
@@ -303,7 +304,7 @@ def to_compressed_msgpack_stream(time_int: false, packer: nil)
303304

304305
def ensure_decompressed!
305306
return if @decompressed_data
306-
@data = @decompressed_data = decompress(@data)
307+
@data = @decompressed_data = decompress(@data, type: @type)
307308
end
308309
end
309310

lib/fluent/plugin/compressable.rb

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,75 +16,101 @@
1616

1717
require 'stringio'
1818
require 'zlib'
19+
require 'zstd-ruby'
1920

2021
module Fluent
2122
module Plugin
2223
module Compressable
23-
def compress(data, **kwargs)
24+
def compress(data, type: :gzip, **kwargs)
2425
output_io = kwargs[:output_io]
26+
writer = nil
2527
io = output_io || StringIO.new
26-
Zlib::GzipWriter.wrap(io) do |gz|
27-
gz.write data
28+
if type == :gzip
29+
writer = Zlib::GzipWriter.new(io)
30+
elsif type == :zstd
31+
writer = Zstd::StreamWriter.new(io)
32+
else
33+
raise ArgumentError, "Unknown compression type: #{type}"
2834
end
29-
35+
writer.write(data)
36+
writer.finish
3037
output_io || io.string
3138
end
3239

3340
# compressed_data is String like `compress(data1) + compress(data2) + ... + compress(dataN)`
3441
# https://www.ruby-forum.com/topic/971591#979503
35-
def decompress(compressed_data = nil, output_io: nil, input_io: nil)
42+
def decompress(compressed_data = nil, output_io: nil, input_io: nil, type: :gzip)
3643
case
3744
when input_io && output_io
38-
io_decompress(input_io, output_io)
45+
io_decompress(input_io, output_io, type)
3946
when input_io
4047
output_io = StringIO.new
41-
io = io_decompress(input_io, output_io)
48+
io = io_decompress(input_io, output_io, type)
4249
io.string
4350
when compressed_data.nil? || compressed_data.empty?
4451
# check compressed_data(String) is 0 length
4552
compressed_data
4653
when output_io
4754
# execute after checking compressed_data is empty or not
4855
io = StringIO.new(compressed_data)
49-
io_decompress(io, output_io)
56+
io_decompress(io, output_io, type)
5057
else
51-
string_decompress(compressed_data)
58+
string_decompress(compressed_data, type)
5259
end
5360
end
5461

5562
private
5663

57-
def string_decompress(compressed_data)
64+
def string_decompress(compressed_data, type = :gzip)
5865
io = StringIO.new(compressed_data)
5966

6067
out = ''
6168
loop do
62-
gz = Zlib::GzipReader.new(io)
63-
out << gz.read
64-
unused = gz.unused
65-
gz.finish
69+
if type == :gzip
70+
reader = Zlib::GzipReader.new(io)
71+
out << reader.read
72+
unused = reader.unused
73+
reader.finish
6674

67-
unless unused.nil?
68-
adjust = unused.length
69-
io.pos -= adjust
75+
unless unused.nil?
76+
adjust = unused.length
77+
io.pos -= adjust
78+
end
79+
elsif type == :zstd
80+
reader = Zstd::StreamReader.new(io)
81+
# Zstd::StreamReader needs to specify the size of the buffer
82+
out << reader.read(1024)
83+
# Zstd::StreamReader doesn't provide unused data, so we have to manually adjust the position
84+
else
85+
raise ArgumentError, "Unknown compression type: #{type}"
7086
end
7187
break if io.eof?
7288
end
7389

7490
out
7591
end
7692

77-
def io_decompress(input, output)
93+
def io_decompress(input, output, type = :gzip)
7894
loop do
79-
gz = Zlib::GzipReader.new(input)
80-
v = gz.read
81-
output.write(v)
82-
unused = gz.unused
83-
gz.finish
84-
85-
unless unused.nil?
86-
adjust = unused.length
87-
input.pos -= adjust
95+
reader = nil
96+
if type == :gzip
97+
reader = Zlib::GzipReader.new(input)
98+
v = reader.read
99+
output.write(v)
100+
unused = reader.unused
101+
reader.finish
102+
unless unused.nil?
103+
adjust = unused.length
104+
input.pos -= adjust
105+
end
106+
elsif type == :zstd
107+
reader = Zstd::StreamReader.new(input)
108+
# Zstd::StreamReader needs to specify the size of the buffer
109+
v = reader.read(1024)
110+
output.write(v)
111+
# Zstd::StreamReader doesn't provide unused data, so we have to manually adjust the position
112+
else
113+
raise ArgumentError, "Unknown compression type: #{type}"
88114
end
89115
break if input.eof?
90116
end

lib/fluent/plugin/in_forward.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,7 @@ def on_message(msg, chunk_size, conn)
309309
# PackedForward
310310
option = msg[2]
311311
size = (option && option['size']) || 0
312-
es_class = (option && option['compressed'] == 'gzip') ? Fluent::CompressedMessagePackEventStream : Fluent::MessagePackEventStream
313-
es = es_class.new(entries, nil, size.to_i)
312+
es = (option && option['compressed']!=nil && option['compressed']!="text") ? Fluent::CompressedMessagePackEventStream.new(entries, nil, size.to_i, compress: option['compressed'].to_sym) : Fluent::MessagePackEventStream.new(entries, nil, size.to_i)
314313
es = check_and_skip_invalid_event(tag, es, conn.remote_host) if @skip_invalid_event
315314
if @enable_field_injection
316315
es = add_source_info(es, conn)

test/plugin/test_in_forward.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ def create_driver(conf=base_config)
553553
chunk = ["tag1", entries, { 'compressed' => 'gzip' }].to_msgpack
554554

555555
# check CompressedMessagePackEventStream is created
556-
mock(Fluent::CompressedMessagePackEventStream).new(entries, nil, 0)
556+
mock(Fluent::CompressedMessagePackEventStream).new(entries, nil, 0, compress: :gzip)
557557

558558
d.run do
559559
Fluent::MessagePackFactory.msgpack_unpacker.feed_each(chunk) do |obj|

0 commit comments

Comments
 (0)