kpumuk-thrift 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +201 -0
  3. data/benchmark/Benchmark.thrift +24 -0
  4. data/benchmark/benchmark.rb +285 -0
  5. data/benchmark/client.rb +117 -0
  6. data/benchmark/server.rb +120 -0
  7. data/benchmark/thin_server.rb +45 -0
  8. data/ext/binary_protocol_accelerated.c +517 -0
  9. data/ext/binary_protocol_accelerated.h +20 -0
  10. data/ext/bytes.c +36 -0
  11. data/ext/bytes.h +31 -0
  12. data/ext/compact_protocol.c +703 -0
  13. data/ext/compact_protocol.h +20 -0
  14. data/ext/constants.h +111 -0
  15. data/ext/extconf.rb +35 -0
  16. data/ext/macros.h +41 -0
  17. data/ext/memory_buffer.c +134 -0
  18. data/ext/memory_buffer.h +20 -0
  19. data/ext/protocol.c +29 -0
  20. data/ext/protocol.h +35 -0
  21. data/ext/strlcpy.c +41 -0
  22. data/ext/strlcpy.h +34 -0
  23. data/ext/struct.c +751 -0
  24. data/ext/struct.h +25 -0
  25. data/ext/thrift_native.c +242 -0
  26. data/lib/thrift/bytes.rb +98 -0
  27. data/lib/thrift/client.rb +131 -0
  28. data/lib/thrift/exceptions.rb +87 -0
  29. data/lib/thrift/multiplexed_processor.rb +76 -0
  30. data/lib/thrift/processor.rb +75 -0
  31. data/lib/thrift/protocol/base_protocol.rb +409 -0
  32. data/lib/thrift/protocol/binary_protocol.rb +260 -0
  33. data/lib/thrift/protocol/binary_protocol_accelerated.rb +47 -0
  34. data/lib/thrift/protocol/compact_protocol.rb +467 -0
  35. data/lib/thrift/protocol/header_protocol.rb +320 -0
  36. data/lib/thrift/protocol/json_protocol.rb +796 -0
  37. data/lib/thrift/protocol/multiplexed_protocol.rb +44 -0
  38. data/lib/thrift/protocol/protocol_decorator.rb +202 -0
  39. data/lib/thrift/serializer/deserializer.rb +33 -0
  40. data/lib/thrift/serializer/serializer.rb +33 -0
  41. data/lib/thrift/server/base_server.rb +37 -0
  42. data/lib/thrift/server/mongrel_http_server.rb +60 -0
  43. data/lib/thrift/server/nonblocking_server.rb +305 -0
  44. data/lib/thrift/server/simple_server.rb +47 -0
  45. data/lib/thrift/server/thin_http_server.rb +91 -0
  46. data/lib/thrift/server/thread_pool_server.rb +79 -0
  47. data/lib/thrift/server/threaded_server.rb +51 -0
  48. data/lib/thrift/struct.rb +237 -0
  49. data/lib/thrift/struct_union.rb +202 -0
  50. data/lib/thrift/thrift_native.rb +24 -0
  51. data/lib/thrift/transport/base_server_transport.rb +37 -0
  52. data/lib/thrift/transport/base_transport.rb +117 -0
  53. data/lib/thrift/transport/buffered_transport.rb +122 -0
  54. data/lib/thrift/transport/framed_transport.rb +125 -0
  55. data/lib/thrift/transport/header_transport.rb +516 -0
  56. data/lib/thrift/transport/http_client_transport.rb +63 -0
  57. data/lib/thrift/transport/io_stream_transport.rb +42 -0
  58. data/lib/thrift/transport/memory_buffer_transport.rb +129 -0
  59. data/lib/thrift/transport/server_socket.rb +71 -0
  60. data/lib/thrift/transport/socket.rb +170 -0
  61. data/lib/thrift/transport/ssl_server_socket.rb +41 -0
  62. data/lib/thrift/transport/ssl_socket.rb +51 -0
  63. data/lib/thrift/transport/unix_server_socket.rb +64 -0
  64. data/lib/thrift/transport/unix_socket.rb +44 -0
  65. data/lib/thrift/types.rb +104 -0
  66. data/lib/thrift/union.rb +182 -0
  67. data/lib/thrift/uuid.rb +49 -0
  68. data/lib/thrift.rb +72 -0
  69. data/spec/BaseService.thrift +27 -0
  70. data/spec/ExtendedService.thrift +25 -0
  71. data/spec/Referenced.thrift +44 -0
  72. data/spec/ThriftNamespacedSpec.thrift +53 -0
  73. data/spec/ThriftSpec.thrift +187 -0
  74. data/spec/base_protocol_spec.rb +224 -0
  75. data/spec/base_transport_spec.rb +387 -0
  76. data/spec/binary_protocol_accelerated_spec.rb +46 -0
  77. data/spec/binary_protocol_spec.rb +72 -0
  78. data/spec/binary_protocol_spec_shared.rb +445 -0
  79. data/spec/bytes_spec.rb +99 -0
  80. data/spec/client_spec.rb +166 -0
  81. data/spec/compact_protocol_spec.rb +196 -0
  82. data/spec/constants_demo_spec.rb +101 -0
  83. data/spec/exception_spec.rb +140 -0
  84. data/spec/flat_spec.rb +62 -0
  85. data/spec/header_protocol_spec.rb +475 -0
  86. data/spec/header_transport_spec.rb +386 -0
  87. data/spec/http_client_spec.rb +159 -0
  88. data/spec/json_protocol_spec.rb +552 -0
  89. data/spec/namespaced_spec.rb +66 -0
  90. data/spec/nonblocking_server_spec.rb +361 -0
  91. data/spec/processor_spec.rb +79 -0
  92. data/spec/serializer_spec.rb +66 -0
  93. data/spec/server_socket_spec.rb +84 -0
  94. data/spec/server_spec.rb +186 -0
  95. data/spec/socket_spec.rb +67 -0
  96. data/spec/socket_spec_shared.rb +167 -0
  97. data/spec/spec_helper.rb +69 -0
  98. data/spec/ssl_server_socket_spec.rb +45 -0
  99. data/spec/ssl_socket_spec.rb +87 -0
  100. data/spec/struct_nested_containers_spec.rb +190 -0
  101. data/spec/struct_spec.rb +397 -0
  102. data/spec/support/header_protocol_helper.rb +54 -0
  103. data/spec/thin_http_server_spec.rb +126 -0
  104. data/spec/types_spec.rb +117 -0
  105. data/spec/union_spec.rb +272 -0
  106. data/spec/unix_socket_spec.rb +115 -0
  107. data/spec/uuid_validation_spec.rb +238 -0
  108. data/test/fuzz/Makefile.am +173 -0
  109. data/test/fuzz/README.md +149 -0
  110. data/test/fuzz/fuzz_common.rb +95 -0
  111. data/test/fuzz/fuzz_parse_binary_protocol.rb +22 -0
  112. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +22 -0
  113. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +22 -0
  114. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +22 -0
  115. data/test/fuzz/fuzz_parse_compact_protocol.rb +22 -0
  116. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +22 -0
  117. data/test/fuzz/fuzz_parse_json_protocol.rb +22 -0
  118. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +22 -0
  119. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +22 -0
  120. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +22 -0
  121. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +22 -0
  122. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +22 -0
  123. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +22 -0
  124. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +22 -0
  125. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +22 -0
  126. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +22 -0
  127. data/test/fuzz/fuzz_tracer.rb +28 -0
  128. metadata +460 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f0dfce780ae29e9f1b5b569e545ccc69b0c157ee66a419d3a33d1d1ce80fb3e1
4
+ data.tar.gz: '08590bca4dc532c304d00cd855ff0c6c8833da44c5f6f70d74e8e84140492129'
5
+ SHA512:
6
+ metadata.gz: 220d0f33b4ea153ab47d777cee1105e11e109cc7f35272e91abad94ffa6387050ef6b0a1db928f4d31cdfa41c339898ce8d8e7e002288a9cc7a01aa6caf69443
7
+ data.tar.gz: e5e21dc48a3fc494478d280d304d25614a66b85bf3e8b440c21474bf022b71b3a11b705274beefe97828bc288b7f68da9eb3437ac23be21a2627fa7c8ad43ecb
data/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # Thrift Ruby Software Library
2
+
3
+ ## License
4
+
5
+ Licensed to the Apache Software Foundation (ASF) under one
6
+ or more contributor license agreements. See the NOTICE file
7
+ distributed with this work for additional information
8
+ regarding copyright ownership. The ASF licenses this file
9
+ to you under the Apache License, Version 2.0 (the
10
+ "License"); you may not use this file except in compliance
11
+ with the License. You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing,
16
+ software distributed under the License is distributed on an
17
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18
+ KIND, either express or implied. See the License for the
19
+ specific language governing permissions and limitations
20
+ under the License.
21
+
22
+ # Using Thrift with Ruby
23
+
24
+ Ruby bindings for the Apache Thrift RPC system. The gem contains the runtime
25
+ types, transports, protocols, and servers used by generated Ruby code for both
26
+ clients and services.
27
+
28
+ ## Compatibility
29
+
30
+ - Ruby MRI >= 2.7 (tested against current supported releases).
31
+ - JRuby works with the pure-Ruby implementation; the native extension is
32
+ skipped automatically.
33
+ - For the repo-wide transport, protocol, and server support matrix, see
34
+ [Language Feature Matrix](https://github.com/apache/thrift/blob/master/LANGUAGES.md). This README focuses on Ruby-specific
35
+ behavior and migration notes.
36
+
37
+ ## Installation
38
+
39
+ - Requirements: Ruby >= 2.7.
40
+ - From RubyGems: `gem install thrift`
41
+ - From source: `bundle install`, `gem build thrift.gemspec`, then install the
42
+ resulting `thrift-*.gem`. The native accelerator is built when the gem is
43
+ installed on supported runtimes.
44
+
45
+ ## Generating Ruby Code
46
+
47
+ The Ruby library does not include the Thrift compiler. Use a compiler built
48
+ from the root of this repository to generate Ruby bindings:
49
+
50
+ thrift --gen rb path/to/service.thrift
51
+ # with namespaced modules
52
+ thrift --gen rb:namespaced --recurse path/to/service.thrift
53
+
54
+ Generated files are typically written to `gen-rb/` and can be required
55
+ directly from your application.
56
+
57
+ ## Basic Client Usage
58
+
59
+ $:.push File.expand_path('gen-rb', __dir__)
60
+ require 'thrift'
61
+ require 'calculator'
62
+
63
+ socket = Thrift::Socket.new('localhost', 9090)
64
+ transport = Thrift::BufferedTransport.new(socket)
65
+ protocol = Thrift::BinaryProtocol.new(transport)
66
+ client = Calculator::Client.new(protocol)
67
+
68
+ transport.open
69
+ puts client.add(1, 1)
70
+ transport.close
71
+
72
+ ## Basic Server Usage
73
+
74
+ $:.push File.expand_path('gen-rb', __dir__)
75
+ require 'thrift'
76
+ require 'calculator'
77
+
78
+ class CalculatorHandler
79
+ def add(a, b)
80
+ a + b
81
+ end
82
+ end
83
+
84
+ handler = CalculatorHandler.new
85
+ processor = Calculator::Processor.new(handler)
86
+ server_transport = Thrift::ServerSocket.new(9090)
87
+ transport_factory = Thrift::BufferedTransportFactory.new
88
+ protocol_factory = Thrift::BinaryProtocolFactory.new
89
+
90
+ server = Thrift::ThreadedServer.new(processor, server_transport,
91
+ transport_factory, protocol_factory)
92
+ server.serve
93
+
94
+ ## Development and Tests
95
+
96
+ - `bundle exec rake spec` runs the Ruby specs. It expects a built Thrift
97
+ compiler at `../../compiler/cpp/thrift`.
98
+ - `bundle exec rake test` runs the cross-language test suite; it must be
99
+ executed from a full Thrift checkout.
100
+ - `bundle exec rake build_ext` (implicit in the tasks above) compiles the
101
+ optional native extension that accelerates protocols and buffers.
102
+
103
+ ## More Ruby Code
104
+
105
+ - Tutorial client and server: `tutorial/rb/RubyClient.rb` and `tutorial/rb/RubyServer.rb`
106
+ - Runtime benchmarks: `lib/rb/benchmark`
107
+ - Protocol benchmark: `test/rb/benchmarks/protocol_benchmark.rb`
108
+ - Library specs: `lib/rb/spec`
109
+ - Fuzzing harnesses and notes: `lib/rb/test/fuzz`
110
+ - Cross-language and integration tests: `test/rb`
111
+
112
+ ## Breaking Changes
113
+
114
+ ### 0.23.0
115
+
116
+ The documented source-build flow now effectively requires Ruby `2.7+`.
117
+ The committed development bundle no longer resolves on Ruby `2.6`
118
+ (`json-2.18.1 requires ruby version >= 2.7`), so building and testing this
119
+ library from source should be treated as `2.7+`.
120
+
121
+ Generated structs and unions now consistently raise
122
+ `Thrift::ProtocolException::INVALID_DATA` for invalid payloads such as unset
123
+ required fields, invalid enum values, or invalid union state. If your
124
+ application or tests matched older exception types or messages, update them.
125
+
126
+ Regenerated Ruby clients now validate replies more strictly. Mismatched reply
127
+ message types, method names, or sequence IDs raise
128
+ `Thrift::ApplicationException::INVALID_MESSAGE_TYPE`,
129
+ `Thrift::ApplicationException::WRONG_METHOD_NAME`, or
130
+ `Thrift::ApplicationException::BAD_SEQUENCE_ID`. If you relied on older,
131
+ looser reply handling in servers, proxies, or tests, regenerate and update
132
+ those call paths together.
133
+
134
+ Generated Ruby clients have never been safe to share across concurrent
135
+ threads. A client tracks pending sequence IDs on a single reply stream, so use
136
+ one client/transport pair per thread or serialize access yourself.
137
+
138
+ Treat `Thrift::ApplicationException::BAD_SEQUENCE_ID` as a correctness bug
139
+ that needs immediate attention. It means the client read a reply whose
140
+ sequence ID did not match the next pending request, so the connection may
141
+ already be out of sync and you may be reading a reply intended for a
142
+ different call. The most common cause is sharing one client across threads,
143
+ but a buggy proxy or server can also cause it.
144
+
145
+ ### 0.13.0
146
+
147
+ Ruby development and CI moved to Ruby `2.4+`, but the runtime still claimed
148
+ support for older interpreters. Treat Ruby `< 2.4` on the `0.13.x` line as
149
+ best-effort, not guaranteed.
150
+
151
+ - Historical note for very old releases: the Ruby runtime was rearranged to use
152
+ more Ruby-like names, and generated files switched to underscored filenames.
153
+ If you are upgrading very old code, regenerate your Ruby bindings and update
154
+ any old `T*` constants or legacy require paths such as
155
+ `TBinaryProtocol` -> `Thrift::BinaryProtocol`.
156
+ - `rb:namespaced` changes the generated file layout. Flat output from
157
+ `thrift --gen rb` and namespaced output from `thrift --gen rb:namespaced`
158
+ use different require paths, so switch them atomically with regenerated code.
159
+
160
+ # --gen rb
161
+ require 'calculator'
162
+
163
+ # --gen rb:namespaced
164
+ require 'my_namespace/calculator'
165
+
166
+ ## Migration Notes
167
+
168
+ - If you upgrade across the stricter reply-validation changes, regenerate all
169
+ Ruby stubs and deploy them with the matching Ruby runtime. Do not mix old
170
+ generated code, new generated code, and new runtime code on the same client
171
+ path without testing that combination.
172
+ - If you receive `Thrift::ApplicationException::BAD_SEQUENCE_ID`, treat the
173
+ connection as out of sync. Close it, create a new client/transport pair, and
174
+ investigate the root cause before retrying.
175
+ - Do not share one generated Ruby client across concurrent threads. Use one
176
+ client/transport pair per thread, or serialize access to a shared client.
177
+ - If you switch between `thrift --gen rb` and `thrift --gen rb:namespaced`,
178
+ regenerate all Ruby output and update `require` paths in the same change.
179
+
180
+ ## Runtime Notes
181
+
182
+ - Loading the `thrift_native` extension changes which implementation you are
183
+ running. It replaces
184
+ `Thrift::Struct`, `Thrift::Union`, and `Thrift::CompactProtocol` methods
185
+ with C implementations in place. `Thrift::BinaryProtocol` remains available
186
+ in pure Ruby, and the C-backed binary protocol is opt-in through
187
+ `Thrift::BinaryProtocolAcceleratedFactory` or
188
+ `Thrift::BinaryProtocolAccelerated` when that class is available.
189
+ - The native extension is optional. If it cannot be built or loaded, Thrift
190
+ falls back to the pure-Ruby implementation. This mainly changes performance
191
+ and implementation details.
192
+ - JRuby skips the native extension automatically and uses the pure-Ruby path.
193
+ - Do not share one client instance across concurrent threads. A client tracks
194
+ request and reply state on a single transport stream.
195
+ - `Thrift::NonblockingServer` expects framed input. Use
196
+ `Thrift::FramedTransport` with it on the wire.
197
+ - Client and server must agree on transport and protocol choices. If you
198
+ switch to SSL, HTTP, header transport, compact protocol, or namespaced
199
+ generated code, update both ends together.
200
+ - HTTPS client transport verifies peers by default, and `Thrift::SSLSocket`
201
+ performs a hostname check against the host you pass in.
@@ -0,0 +1,24 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ namespace rb ThriftBenchmark
21
+
22
+ service BenchmarkService {
23
+ i32 fibonacci(1:byte n)
24
+ }
@@ -0,0 +1,285 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ require 'rubygems'
21
+ $:.unshift File.dirname(__FILE__) + '/../lib'
22
+ $:.unshift File.dirname(__FILE__) + '/../ext'
23
+ require 'thrift'
24
+ require 'stringio'
25
+
26
+ HOST = '127.0.0.1'
27
+ PORT = 42587
28
+
29
+ ###############
30
+ ## Server
31
+ ###############
32
+
33
+ class Server
34
+ attr_accessor :serverclass
35
+ attr_accessor :interpreter
36
+ attr_accessor :host
37
+ attr_accessor :port
38
+ attr_accessor :protocol_type
39
+
40
+ def initialize(opts)
41
+ @serverclass = opts.fetch(:class, Thrift::NonblockingServer)
42
+ @interpreter = opts.fetch(:interpreter, "ruby")
43
+ @host = opts.fetch(:host, ::HOST)
44
+ @port = opts.fetch(:port, ::PORT)
45
+ @protocol_type = opts.fetch(:protocol_type, 'binary')
46
+ @tls = opts.fetch(:tls, false)
47
+ end
48
+
49
+ def start
50
+ return if @serverclass == Object
51
+ args = (File.basename(@interpreter) == "jruby" ? "-J-server" : "")
52
+ @pipe = IO.popen("#{@interpreter} #{args} #{File.dirname(__FILE__)}/server.rb #{"-tls" if @tls} #{@host} #{@port} #{@serverclass.name} #{@protocol_type}", "r+")
53
+ Marshal.load(@pipe) # wait until the server has started
54
+ sleep 0.4 # give the server time to actually start spawning sockets
55
+ end
56
+
57
+ def shutdown
58
+ return unless @pipe
59
+ Marshal.dump(:shutdown, @pipe)
60
+ begin
61
+ @pipe.read(10) # block until the server shuts down
62
+ rescue EOFError
63
+ end
64
+ @pipe.close
65
+ @pipe = nil
66
+ end
67
+ end
68
+
69
+ class BenchmarkManager
70
+ def initialize(opts, server)
71
+ @socket = opts.fetch(:socket) do
72
+ @host = opts.fetch(:host, 'localhost')
73
+ @port = opts.fetch(:port)
74
+ nil
75
+ end
76
+ @num_processes = opts.fetch(:num_processes, 40)
77
+ @clients_per_process = opts.fetch(:clients_per_process, 10)
78
+ @calls_per_client = opts.fetch(:calls_per_client, 50)
79
+ @interpreter = opts.fetch(:interpreter, "ruby")
80
+ @server = server
81
+ @log_exceptions = opts.fetch(:log_exceptions, false)
82
+ @protocol_type = opts.fetch(:protocol_type, 'binary')
83
+ @tls = opts.fetch(:tls, false)
84
+ end
85
+
86
+ def run
87
+ @pool = []
88
+ @benchmark_start = Time.now
89
+ puts "Spawning benchmark processes..."
90
+ @num_processes.times do
91
+ spawn
92
+ sleep 0.02 # space out spawns
93
+ end
94
+ collect_output
95
+ @benchmark_end = Time.now # we know the procs are done here
96
+ translate_output
97
+ analyze_output
98
+ report_output
99
+ end
100
+
101
+ def spawn
102
+ pipe = IO.popen("#{@interpreter} #{File.dirname(__FILE__)}/client.rb #{"-log-exceptions" if @log_exceptions} #{"-tls" if @tls} #{@host} #{@port} #{@clients_per_process} #{@calls_per_client} #{@protocol_type}")
103
+ @pool << pipe
104
+ end
105
+
106
+ def socket_class
107
+ if @socket
108
+ Thrift::UNIXSocket
109
+ elsif @tls
110
+ Thrift::SSLSocket
111
+ else
112
+ Thrift::Socket
113
+ end
114
+ end
115
+
116
+ def collect_output
117
+ puts "Collecting output..."
118
+ # read from @pool until all sockets are closed
119
+ @buffers = Hash.new { |h, k| h[k] = '' }
120
+ until @pool.empty?
121
+ rd, = select(@pool)
122
+ next if rd.nil?
123
+ rd.each do |fd|
124
+ begin
125
+ @buffers[fd] << fd.readpartial(4096)
126
+ rescue EOFError
127
+ @pool.delete fd
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ def translate_output
134
+ puts "Translating output..."
135
+ @output = []
136
+ @buffers.each do |fd, buffer|
137
+ strio = StringIO.new(buffer)
138
+ logs = []
139
+ begin
140
+ loop do
141
+ logs << Marshal.load(strio)
142
+ end
143
+ rescue EOFError
144
+ @output << logs
145
+ end
146
+ end
147
+ end
148
+
149
+ def analyze_output
150
+ puts "Analyzing output..."
151
+ call_times = []
152
+ client_times = []
153
+ connection_failures = []
154
+ connection_errors = []
155
+ shortest_call = 0
156
+ shortest_client = 0
157
+ longest_call = 0
158
+ longest_client = 0
159
+ @output.each do |logs|
160
+ cur_call, cur_client = nil
161
+ logs.each do |tok, time|
162
+ case tok
163
+ when :start
164
+ cur_client = time
165
+ when :call_start
166
+ cur_call = time
167
+ when :call_end
168
+ delta = time - cur_call
169
+ call_times << delta
170
+ longest_call = delta unless longest_call > delta
171
+ shortest_call = delta if shortest_call == 0 or delta < shortest_call
172
+ cur_call = nil
173
+ when :end
174
+ delta = time - cur_client
175
+ client_times << delta
176
+ longest_client = delta unless longest_client > delta
177
+ shortest_client = delta if shortest_client == 0 or delta < shortest_client
178
+ cur_client = nil
179
+ when :connection_failure
180
+ connection_failures << time
181
+ when :connection_error
182
+ connection_errors << time
183
+ end
184
+ end
185
+ end
186
+ @report = {}
187
+ @report[:total_calls] = call_times.inject(0.0) { |a, t| a += t }
188
+ @report[:avg_calls] = @report[:total_calls] / call_times.size
189
+ @report[:total_clients] = client_times.inject(0.0) { |a, t| a += t }
190
+ @report[:avg_clients] = @report[:total_clients] / client_times.size
191
+ @report[:connection_failures] = connection_failures.size
192
+ @report[:connection_errors] = connection_errors.size
193
+ @report[:shortest_call] = shortest_call
194
+ @report[:shortest_client] = shortest_client
195
+ @report[:longest_call] = longest_call
196
+ @report[:longest_client] = longest_client
197
+ @report[:total_benchmark_time] = @benchmark_end - @benchmark_start
198
+ @report[:fastthread] = $".include?('fastthread.bundle')
199
+ end
200
+
201
+ def report_output
202
+ fmt = "%.4f seconds"
203
+ puts
204
+ tabulate "%d",
205
+ [["Server class", "%s"], @server.serverclass == Object ? "" : @server.serverclass],
206
+ [["Server interpreter", "%s"], @server.interpreter],
207
+ [["Client interpreter", "%s"], @interpreter],
208
+ [["Protocol type", "%s"], @protocol_type],
209
+ [["Socket class", "%s"], socket_class],
210
+ ["Number of processes", @num_processes],
211
+ ["Clients per process", @clients_per_process],
212
+ ["Calls per client", @calls_per_client],
213
+ [["Using fastthread", "%s"], @report[:fastthread] ? "yes" : "no"]
214
+ puts
215
+ failures = (@report[:connection_failures] > 0)
216
+ tabulate fmt,
217
+ [["Connection failures", "%d", [:red, :bold]], @report[:connection_failures]],
218
+ [["Connection errors", "%d", [:red, :bold]], @report[:connection_errors]],
219
+ ["Average time per call", @report[:avg_calls]],
220
+ ["Average time per client (%d calls)" % @calls_per_client, @report[:avg_clients]],
221
+ ["Total time for all calls", @report[:total_calls]],
222
+ ["Real time for benchmarking", @report[:total_benchmark_time]],
223
+ ["Shortest call time", @report[:shortest_call]],
224
+ ["Longest call time", @report[:longest_call]],
225
+ ["Shortest client time (%d calls)" % @calls_per_client, @report[:shortest_client]],
226
+ ["Longest client time (%d calls)" % @calls_per_client, @report[:longest_client]]
227
+ end
228
+
229
+ ANSI = {
230
+ :reset => 0,
231
+ :bold => 1,
232
+ :black => 30,
233
+ :red => 31,
234
+ :green => 32,
235
+ :yellow => 33,
236
+ :blue => 34,
237
+ :magenta => 35,
238
+ :cyan => 36,
239
+ :white => 37
240
+ }
241
+
242
+ def tabulate(fmt, *labels_and_values)
243
+ labels = labels_and_values.map { |l| Array === l ? l.first : l }
244
+ label_width = labels.inject(0) { |w, l| l.size > w ? l.size : w }
245
+ labels_and_values.each do |(l, v)|
246
+ f = fmt
247
+ l, f, c = l if Array === l
248
+ fmtstr = "%-#{label_width+1}s #{f}"
249
+ if STDOUT.tty? and c and v.to_i > 0
250
+ fmtstr = "\e[#{[*c].map { |x| ANSI[x] } * ";"}m" + fmtstr + "\e[#{ANSI[:reset]}m"
251
+ end
252
+ puts fmtstr % [l+":", v]
253
+ end
254
+ end
255
+ end
256
+
257
+ def resolve_const(const)
258
+ const and const.split('::').inject(Object) { |k, c| k.const_get(c) }
259
+ end
260
+
261
+ puts "Starting server..."
262
+ protocol_type = ENV['THRIFT_PROTOCOL'] || 'binary'
263
+ args = {}
264
+ args[:interpreter] = ENV['THRIFT_SERVER_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby"
265
+ args[:class] = resolve_const(ENV['THRIFT_SERVER']) || Thrift::NonblockingServer
266
+ args[:host] = ENV['THRIFT_HOST'] || HOST
267
+ args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i
268
+ args[:tls] = ENV['THRIFT_TLS'] == 'true'
269
+ args[:protocol_type] = protocol_type
270
+ server = Server.new(args)
271
+ server.start
272
+
273
+ args = {}
274
+ args[:host] = ENV['THRIFT_HOST'] || HOST
275
+ args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i
276
+ args[:tls] = ENV['THRIFT_TLS'] == 'true'
277
+ args[:num_processes] = (ENV['THRIFT_NUM_PROCESSES'] || 40).to_i
278
+ args[:clients_per_process] = (ENV['THRIFT_NUM_CLIENTS'] || 5).to_i
279
+ args[:calls_per_client] = (ENV['THRIFT_NUM_CALLS'] || 50).to_i
280
+ args[:interpreter] = ENV['THRIFT_CLIENT_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby"
281
+ args[:log_exceptions] = !!ENV['THRIFT_LOG_EXCEPTIONS']
282
+ args[:protocol_type] = protocol_type
283
+ BenchmarkManager.new(args, server).run
284
+
285
+ server.shutdown
@@ -0,0 +1,117 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ $:.unshift File.dirname(__FILE__) + '/../lib'
21
+ $:.unshift File.dirname(__FILE__) + '/../ext'
22
+ require 'thrift'
23
+ require 'openssl'
24
+ $:.unshift File.dirname(__FILE__) + "/gen-rb"
25
+ require 'benchmark_service'
26
+
27
+ class Client
28
+ def initialize(host, port, clients_per_process, calls_per_client, log_exceptions, tls, protocol_type)
29
+ @host = host
30
+ @port = port
31
+ @clients_per_process = clients_per_process
32
+ @calls_per_client = calls_per_client
33
+ @log_exceptions = log_exceptions
34
+ @tls = tls
35
+ @protocol_type = protocol_type || 'binary'
36
+ end
37
+
38
+ def create_protocol(socket)
39
+ case @protocol_type
40
+ when 'binary'
41
+ transport = Thrift::FramedTransport.new(socket)
42
+ Thrift::BinaryProtocol.new(transport)
43
+ when 'compact'
44
+ transport = Thrift::FramedTransport.new(socket)
45
+ Thrift::CompactProtocol.new(transport)
46
+ when 'header'
47
+ Thrift::HeaderProtocol.new(socket)
48
+ when 'header-compact'
49
+ Thrift::HeaderProtocol.new(socket, nil, Thrift::HeaderSubprotocolID::COMPACT)
50
+ when 'header-zlib'
51
+ protocol = Thrift::HeaderProtocol.new(socket)
52
+ protocol.add_transform(Thrift::HeaderTransformID::ZLIB)
53
+ protocol
54
+ else
55
+ transport = Thrift::FramedTransport.new(socket)
56
+ Thrift::BinaryProtocol.new(transport)
57
+ end
58
+ end
59
+
60
+ def run
61
+ @clients_per_process.times do
62
+ socket = if @tls
63
+ ssl_context = OpenSSL::SSL::SSLContext.new.tap do |ctx|
64
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
65
+ ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
66
+
67
+ keys_dir = File.expand_path("../../../test/keys", __dir__)
68
+ ctx.ca_file = File.join(keys_dir, "CA.pem")
69
+ ctx.cert = OpenSSL::X509::Certificate.new(File.open(File.join(keys_dir, "client.crt")))
70
+ ctx.cert_store = OpenSSL::X509::Store.new
71
+ ctx.cert_store.add_file(File.join(keys_dir, 'server.pem'))
72
+ ctx.key = OpenSSL::PKey::RSA.new(File.open(File.join(keys_dir, "client.key")))
73
+ end
74
+
75
+ Thrift::SSLSocket.new(@host, @port, 5, ssl_context)
76
+ else
77
+ Thrift::Socket.new(@host, @port, 5)
78
+ end
79
+ protocol = create_protocol(socket)
80
+ transport = protocol.trans
81
+ client = ThriftBenchmark::BenchmarkService::Client.new(protocol)
82
+ begin
83
+ start = Time.now
84
+ transport.open
85
+ Marshal.dump [:start, start], STDOUT
86
+ rescue => e
87
+ Marshal.dump [:connection_failure, Time.now], STDOUT
88
+ print_exception e if @log_exceptions
89
+ else
90
+ begin
91
+ @calls_per_client.times do
92
+ Marshal.dump [:call_start, Time.now], STDOUT
93
+ client.fibonacci(15)
94
+ Marshal.dump [:call_end, Time.now], STDOUT
95
+ end
96
+ transport.close
97
+ Marshal.dump [:end, Time.now], STDOUT
98
+ rescue Thrift::TransportException => e
99
+ Marshal.dump [:connection_error, Time.now], STDOUT
100
+ print_exception e if @log_exceptions
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def print_exception(e)
107
+ STDERR.puts "ERROR: #{e.message}"
108
+ STDERR.puts "\t#{e.backtrace * "\n\t"}"
109
+ end
110
+ end
111
+
112
+ log_exceptions = true if ARGV[0] == '-log-exceptions' and ARGV.shift
113
+ tls = true if ARGV[0] == '-tls' and ARGV.shift
114
+
115
+ host, port, clients_per_process, calls_per_client, protocol_type = ARGV
116
+
117
+ Client.new(host, port.to_i, clients_per_process.to_i, calls_per_client.to_i, log_exceptions, tls, protocol_type).run