ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:124711] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching
@ 2026-02-07 20:52 synacker (Mikhail Milovidov) via ruby-core
  2026-02-07 22:41 ` [ruby-core:124712] " synacker (Mikhail Milovidov) via ruby-core
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: synacker (Mikhail Milovidov) via ruby-core @ 2026-02-07 20:52 UTC (permalink / raw)
  To: ruby-core; +Cc: synacker (Mikhail Milovidov)

Issue #21869 has been reported by synacker (Mikhail Milovidov).

----------------------------------------
Feature #21869: Add receive_all Method to Ractor API for Message Batching
https://bugs.ruby-lang.org/issues/21869

* Author: synacker (Mikhail Milovidov)
* Status: Open
----------------------------------------
**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:

``` ruby
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



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124712] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching
  2026-02-07 20:52 [ruby-core:124711] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching synacker (Mikhail Milovidov) via ruby-core
@ 2026-02-07 22:41 ` synacker (Mikhail Milovidov) via ruby-core
  2026-02-10  7:15 ` [ruby-core:124752] " ko1 (Koichi Sasada) via ruby-core
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: synacker (Mikhail Milovidov) via ruby-core @ 2026-02-07 22:41 UTC (permalink / raw)
  To: ruby-core; +Cc: synacker (Mikhail Milovidov)

Issue #21869 has been updated by synacker (Mikhail Milovidov).


PR in github: https://github.com/ruby/ruby/pull/16105

----------------------------------------
Feature #21869: Add receive_all Method to Ractor API for Message Batching
https://bugs.ruby-lang.org/issues/21869#change-116308

* Author: synacker (Mikhail Milovidov)
* Status: Open
----------------------------------------
**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:

``` ruby
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



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124752] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching
  2026-02-07 20:52 [ruby-core:124711] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching synacker (Mikhail Milovidov) via ruby-core
  2026-02-07 22:41 ` [ruby-core:124712] " synacker (Mikhail Milovidov) via ruby-core
@ 2026-02-10  7:15 ` ko1 (Koichi Sasada) via ruby-core
  2026-02-10 10:03 ` [ruby-core:124757] " Eregon (Benoit Daloze) via ruby-core
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: ko1 (Koichi Sasada) via ruby-core @ 2026-02-10  7:15 UTC (permalink / raw)
  To: ruby-core; +Cc: ko1 (Koichi Sasada)

Issue #21869 has been updated by ko1 (Koichi Sasada).


Does it block when the queue is empty or returns nil? (I think the example expects blocking)

----------------------------------------
Feature #21869: Add receive_all Method to Ractor API for Message Batching
https://bugs.ruby-lang.org/issues/21869#change-116357

* Author: synacker (Mikhail Milovidov)
* Status: Open
----------------------------------------
**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:

``` ruby
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



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124757] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching
  2026-02-07 20:52 [ruby-core:124711] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching synacker (Mikhail Milovidov) via ruby-core
  2026-02-07 22:41 ` [ruby-core:124712] " synacker (Mikhail Milovidov) via ruby-core
  2026-02-10  7:15 ` [ruby-core:124752] " ko1 (Koichi Sasada) via ruby-core
@ 2026-02-10 10:03 ` Eregon (Benoit Daloze) via ruby-core
  2026-02-10 12:11 ` [ruby-core:124759] " synacker (Mikhail Milovidov) via ruby-core
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2026-02-10 10:03 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #21869 has been updated by Eregon (Benoit Daloze).


synacker (Mikhail Milovidov) wrote:
> More efficient I/O operations (e.g., fewer file writes).

Is it? In your example you call `file.write` for each message in both cases.

But you also call `file.flush` after each `file.write` in ractor1 and only only once per batch in ractor2.
Could you benchmark without the `file.flush`s? I suspect the difference is much smaller then.

I understand the idea that batching helps in this case where you want to explicitly flush, but that's a pretty specific example, e.g. it's uncommon to even call IO#flush at all in Ruby.
One could also flush after N messages/bytes in ractor1.

----------------------------------------
Feature #21869: Add receive_all Method to Ractor API for Message Batching
https://bugs.ruby-lang.org/issues/21869#change-116363

* Author: synacker (Mikhail Milovidov)
* Status: Open
----------------------------------------
**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:

``` ruby
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



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124759] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching
  2026-02-07 20:52 [ruby-core:124711] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching synacker (Mikhail Milovidov) via ruby-core
                   ` (2 preceding siblings ...)
  2026-02-10 10:03 ` [ruby-core:124757] " Eregon (Benoit Daloze) via ruby-core
@ 2026-02-10 12:11 ` synacker (Mikhail Milovidov) via ruby-core
  2026-02-10 13:14 ` [ruby-core:124760] " synacker (Mikhail Milovidov) via ruby-core
  2026-03-07  9:09 ` [ruby-core:124947] " byroot (Jean Boussier) via ruby-core
  5 siblings, 0 replies; 7+ messages in thread
From: synacker (Mikhail Milovidov) via ruby-core @ 2026-02-10 12:11 UTC (permalink / raw)
  To: ruby-core; +Cc: synacker (Mikhail Milovidov)

Issue #21869 has been updated by synacker (Mikhail Milovidov).


ko1 (Koichi Sasada) wrote in #note-2:
> Does it block when the queue is empty or returns \[\]? (I think the example expects blocking)

Yes, it blocks if the queue empty. The method ```receive_all``` accepts a limit parameter:
1. ```limit > 0```: collects up to ```limit``` messages (may return fewer if fewer are queued). Block if the queue empty.
2. ```limit == 0```: returns an empty array immediately (no blocking)
3. ```limit <0``` or ```nil``` (default): returns all messages from the queue or blocks if the queue is empty



Eregon (Benoit Daloze) wrote in #note-3:
> I understand the idea that batching helps in this case where you want to explicitly flush, but that's a pretty specific example, e.g. it's uncommon to even call IO#flush at all in Ruby.

The example demonstrates how you can collect messages during long I/O operations and batch them together to reduce the number of subsequent I/O calls. The ```file.flush``` call simulates a long I/O operation - it could equally represent a database call or something like that

----------------------------------------
Feature #21869: Add receive_all Method to Ractor API for Message Batching
https://bugs.ruby-lang.org/issues/21869#change-116366

* Author: synacker (Mikhail Milovidov)
* Status: Open
----------------------------------------
**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:

``` ruby
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



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124760] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching
  2026-02-07 20:52 [ruby-core:124711] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching synacker (Mikhail Milovidov) via ruby-core
                   ` (3 preceding siblings ...)
  2026-02-10 12:11 ` [ruby-core:124759] " synacker (Mikhail Milovidov) via ruby-core
@ 2026-02-10 13:14 ` synacker (Mikhail Milovidov) via ruby-core
  2026-03-07  9:09 ` [ruby-core:124947] " byroot (Jean Boussier) via ruby-core
  5 siblings, 0 replies; 7+ messages in thread
From: synacker (Mikhail Milovidov) via ruby-core @ 2026-02-10 13:14 UTC (permalink / raw)
  To: ruby-core; +Cc: synacker (Mikhail Milovidov)

Issue #21869 has been updated by synacker (Mikhail Milovidov).


Eregon (Benoit Daloze) wrote in #note-3:
> Is it? In your example you call `file.write` for each message in both cases.
> 
> But you also call `file.flush` after each `file.write` in ractor1 and only only once per batch in ractor2.  

This is also a realistic scenario. To guarantee messages are saved to file, you'd normally need to call ```flush``` after each message - but that's inefficient when processing single messages.

----------------------------------------
Feature #21869: Add receive_all Method to Ractor API for Message Batching
https://bugs.ruby-lang.org/issues/21869#change-116367

* Author: synacker (Mikhail Milovidov)
* Status: Open
----------------------------------------
**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:

``` ruby
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



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124947] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching
  2026-02-07 20:52 [ruby-core:124711] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching synacker (Mikhail Milovidov) via ruby-core
                   ` (4 preceding siblings ...)
  2026-02-10 13:14 ` [ruby-core:124760] " synacker (Mikhail Milovidov) via ruby-core
@ 2026-03-07  9:09 ` byroot (Jean Boussier) via ruby-core
  5 siblings, 0 replies; 7+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2026-03-07  9:09 UTC (permalink / raw)
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #21869 has been updated by byroot (Jean Boussier).


> I understand the idea that batching helps in this case where you want to explicitly flush, but that's a pretty specific example, e.g. it's uncommon to even call IO#flush at all in Ruby.

Not specific to Ractor, but I relatively often had a similar use case with `Thread::Queue`. When implementing instrumentation libraries (e.g. `statsd` or `opentelemetry`) what you generally want to do is to push as much work as possible to a background thread (possibly ractor in the future), but you also want this background thread to serialize and send packets in batch.

In such context, being able to pop `1..N` elements from the queue would be convenient. Currently with queue it's semi-doable by first doing a blocking pop, followed by a bounded number of non-blocking one.

----------------------------------------
Feature #21869: Add receive_all Method to Ractor API for Message Batching
https://bugs.ruby-lang.org/issues/21869#change-116631

* Author: synacker (Mikhail Milovidov)
* Status: Open
* Assignee: ractor
----------------------------------------
**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:

``` ruby
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



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-03-07  9:10 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-07 20:52 [ruby-core:124711] [Ruby Feature#21869] Add receive_all Method to Ractor API for Message Batching synacker (Mikhail Milovidov) via ruby-core
2026-02-07 22:41 ` [ruby-core:124712] " synacker (Mikhail Milovidov) via ruby-core
2026-02-10  7:15 ` [ruby-core:124752] " ko1 (Koichi Sasada) via ruby-core
2026-02-10 10:03 ` [ruby-core:124757] " Eregon (Benoit Daloze) via ruby-core
2026-02-10 12:11 ` [ruby-core:124759] " synacker (Mikhail Milovidov) via ruby-core
2026-02-10 13:14 ` [ruby-core:124760] " synacker (Mikhail Milovidov) via ruby-core
2026-03-07  9:09 ` [ruby-core:124947] " byroot (Jean Boussier) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).