| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 1 | # Thrift Ruby Software Library |
| Mark Slee | 54b7ab9 | 2007-03-06 00:06:27 +0000 | [diff] [blame] | 2 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 3 | ## License |
| Bryan Duxbury | def30a6 | 2009-04-08 00:19:37 +0000 | [diff] [blame] | 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 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 22 | # Using Thrift with Ruby |
| Mark Slee | 54b7ab9 | 2007-03-06 00:06:27 +0000 | [diff] [blame] | 23 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 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. |
| Kevin Clark | 1b8fec1 | 2008-06-24 01:06:08 +0000 | [diff] [blame] | 27 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 28 | ## Compatibility |
| Kevin Clark | 1b8fec1 | 2008-06-24 01:06:08 +0000 | [diff] [blame] | 29 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 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. |
| Kevin Clark | 1b8fec1 | 2008-06-24 01:06:08 +0000 | [diff] [blame] | 36 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 37 | ## Installation |
| Kevin Clark | 1b8fec1 | 2008-06-24 01:06:08 +0000 | [diff] [blame] | 38 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 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. |
| Kevin Clark | 1b8fec1 | 2008-06-24 01:06:08 +0000 | [diff] [blame] | 44 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 45 | ## Generating Ruby Code |
| Kevin Clark | 1b8fec1 | 2008-06-24 01:06:08 +0000 | [diff] [blame] | 46 | |
| Dmytro Shteflyuk | 3f81e22 | 2025-12-17 10:17:37 -0500 | [diff] [blame] | 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. |