Issue #22128 has been updated by byroot (Jean Boussier). For the record, the recent `make_shareable` optimization (https://github.com/ruby/ruby/pull/17483) barely moved the needle: ``` ruby 4.1.0dev (2026-06-24T13:17:28Z expose-ractor-set-.. f9d7dd50cd) +YJIT +PRISM [arm64-darwin25] Warming up -------------------------------------- baseline 35.000 i/100ms shareable 67.000 i/100ms Calculating ------------------------------------- baseline 225.092 (±40.0%) i/s (4.44 ms/i) - 1.155k in 5.131236s shareable 645.024 (± 7.0%) i/s (1.55 ms/i) - 3.283k in 5.089737s Comparison: baseline: 225.1 i/s shareable: 645.0 i/s - 2.87x faster ``` Ultimately, having to recursively walk an arbitrary object tree is always going to be costly. I really think we need to be able to build shareable object trees efficiently. If really `RB_OBJ_SET_FROZEN_SHAREABLE` is deemed too dangerous, then at least we could have shareable versions of APIs such as `rb_ary_new_from_values` (and a similar one for hash). Such API would know that the top level array (or hash) isn't referenced by anyone, so assuming you build it with only scalar values or already shareable arrays and hashes, it wouldn't need to recurse. It might even be able to beat `RB_OBJ_SET_FROZEN_SHAREABLE` as it would inline better. ---------------------------------------- Feature #22128: C API: Expose RB_OBJ_SET_FROZEN_SHAREABLE https://bugs.ruby-lang.org/issues/22128#change-117880 * Author: byroot (Jean Boussier) * Status: Open ---------------------------------------- ### Context I'm trying to experiment with adapting Active Record for a Ractor architecture. Since database connections can't possibly be Ractor shareable, the idea is to warp each connection inside its own ractor, and then send SQL queries and responses through a port. But for this to perform well, I'd like to directly build the query response as a fully shareable object, so that it can be pushed into the port for free, instead of having Ruby need to recursively walk the potentially large response to mark objects as shareable. Here's an example of how it would work in trilogy: https://github.com/trilogy-libraries/trilogy/pull/299 ### Problem Unfortunately, the necessary API isn't currently exposed in the C API: - `RB_OBJ_SET_FROZEN_SHAREABLE` - `RB_OBJ_SET_SHAREABLE` / `rb_obj_set_shareable` I understand that this API could potentially be misused, but given it's a C API, I believe it's acceptable to require care from the caller. ### Benchmark ```ruby # frozen_string_literal: true require 'trilogy' require 'benchmark/ips' baseline = Trilogy.new(database: "test") shareable = Trilogy.new(database: "test", shareable: true) values = { null_test: "test", bit_test: "test", single_bit_test: 1, tiny_int_test: 2, bool_cast_test: true, small_int_test: 4, medium_int_test: 23434, int_test: 324234, big_int_test: 234234, unsigned_big_int_test: 23423423, float_test: 234234, float_zero_test: 213.23, double_test: 23123.12323, decimal_test: 123213.12312, decimal_zero_test: 213213.21323, date_test: "2026-01-30", date_time_test: "2026-01-30 14:03:56", date_time_with_precision_test: "2026-01-30 14:03:56.12", time_with_precision_test: "2026-01-30 14:03:56.12", timestamp_test: "2026-01-30 14:03:56.12", varchar_test: "VARCHAR" } baseline.query("DELETE FROM trilogy_test") insert = "INSERT INTO trilogy_test(#{values.keys.join(", ")}) VALUES (#{values.values.map(&:inspect).join(", ")})" 1000.times do baseline.query(insert) end p shareable.query("SELECT * FROM test.trilogy_test").to_a.size Benchmark.ips do |x| x.report("baseline") { Ractor.make_shareable(baseline.query("SELECT * FROM trilogy_test")) } x.report("shareable") { Ractor.make_shareable(shareable.query("SELECT * FROM trilogy_test")) } x.compare!(order: :baseline) end ``` ``` ruby 4.1.0dev (2026-06-24T13:17:28Z expose-ractor-set-.. f9d7dd50cd) +PRISM [arm64-darwin25] Warming up -------------------------------------- baseline 42.000 i/100ms shareable 66.000 i/100ms Calculating ------------------------------------- baseline 220.024 (±43.2%) i/s (4.54 ms/i) - 1.134k in 5.153976s shareable 677.487 (± 2.4%) i/s (1.48 ms/i) - 3.432k in 5.065782s Comparison: baseline: 220.0 i/s shareable: 677.5 i/s - 3.08x faster ``` -- https://bugs.ruby-lang.org/