Skip to content

[Feature: 21869] Add Ractor#recieve_all method for Message Batching#16105

Open
synacker wants to merge 13 commits into
ruby:masterfrom
synacker:feature-21869
Open

[Feature: 21869] Add Ractor#recieve_all method for Message Batching#16105
synacker wants to merge 13 commits into
ruby:masterfrom
synacker:feature-21869

Conversation

@synacker

@synacker synacker commented Feb 7, 2026

Copy link
Copy Markdown

Summary
The Ractor API provides an excellent mechanism for inter‑thread communication, but it currently lacks a built‑in message batching technique. I propose adding a receive_all method to enable batch processing of messages, which can significantly improve performance in high‑load scenarios.

Motivation
In distributed queued systems, processing messages one‑by‑one (as with the current receive method) can introduce unnecessary overhead. Batch processing allows:

  • Reduced context‑switching overhead.

  • More efficient I/O operations (e.g., fewer file writes).

  • Better throughput in high‑concurrency environments.

Proposed Solution
Add a receive_all method to the Ractor API that:

Returns all available messages in the Ractor’s mailbox at once (as an array).

Demonstration Code
Below is a benchmark comparing individual receive vs. batch receive_all:

require 'benchmark'
class RactorsTest

  def initialize(count)
    @count = count
    @ractor1 = Ractor.new(count, 'output1.txt') do |count, filename|
      File.open(filename, 'w') do |file|
        while count.positive?
          message = receive
          file.write("Ractor 1 received message: #{message}\n")
          file.flush
          count -= 1
        end
      end
    end
    
    @ractor2 = Ractor.new(count, 'output2.txt') do |count, filename|
      File.open(filename, 'w') do |file|
        while count.positive?
          messages = receive_all
          messages.each do |message|
            file.write("Ractor 2 received message: #{message}\n")
          end
          count -= messages.length
          file.flush
        end
      end
    end
  end

  def run1
    @count.times do |i|
      @ractor1.send("Message #{i + 1}")
    end
    @ractor1.join
  end

  def run2
    @count.times do |i|
      @ractor2.send("Message #{i + 1}")
    end
    @ractor2.join
  end
end

records = 1_000_000

test = RactorsTest.new(records)

p [:once, Benchmark.realtime { test.run1 }.round(2)]
p [:all, Benchmark.realtime { test.run2 }.round(2)]

Benchmark Results
On my system, receive_all shows ~4x improvement over individual receive:

Key Observations:
Ractor1 (using receive): Processes each message individually, resulting in frequent I/O calls.

Ractor2 (using receive_all): Processes all queued messages at once, minimizing I/O overhead

@synacker
synacker force-pushed the feature-21869 branch 2 times, most recently from fba0113 to 3480ae0 Compare February 7, 2026 22:12
@cloudbees-smart-tests

This comment has been minimized.

@synacker
synacker force-pushed the feature-21869 branch 6 times, most recently from 2138067 to 7277592 Compare February 9, 2026 22:21
@synacker
synacker force-pushed the feature-21869 branch 5 times, most recently from 3d4cf72 to 20ac4d8 Compare March 1, 2026 18:50
@synacker synacker changed the title Add recieve_all to ractor api [Feature: 21869] [Feature: 21869] Add Ractor#recieve_all method Mar 1, 2026
@synacker synacker changed the title [Feature: 21869] Add Ractor#recieve_all method [Feature: 21869] Add Ractor#recieve_all method for Message Batching Mar 1, 2026
@synacker

synacker commented Mar 2, 2026

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant