Skip to content

THRIFT-6427: Avoid copying payload bytes in native Ruby Compact writes - #4015

Merged
kpumuk merged 1 commit into
apache:masterfrom
kpumuk:rb-compact-payload-storage
Oct 16, 2026
Merged

kpumuk merged 1 commit into
apache:masterfrom
kpumuk:rb-compact-payload-storage

Conversation

@kpumuk

@kpumuk kpumuk commented Oct 16, 2026

Copy link
Copy Markdown
Member

Native Ruby Compact writes copy every binary payload into a new String before calling the transport. Repeated large writes therefore allocate payload-sized temporary storage even when the transport immediately discards the bytes. write_string reaches the same path after encoding conversion.

Use rb_str_subseq to provide a separate mutable plain String while allowing Ruby to share its backing storage until either value changes. This preserves callback mutation isolation, retained snapshots, frozen inputs, and transport write overrides. If the length-prefix callback changes the source length, retain the existing copying path.

Behavioral coverage exercises ownership, encodings, empty payloads, callback exceptions, and MemoryBuffer/Framed round trips. Allocation thresholds remain outside the regression suite.

Benchmarks

Run the compact-payload.rb reproducer in THRIFT-6427 after building the native extension:

ruby -Ilib/rb/lib -Ilib/rb/ext compact-payload.rb

CRuby 4.0.7 on aarch64 Linux; 64 writes of one frozen 1 MiB payload to a discarding transport, with GC disabled during measurement. Three fresh processes per revision:

Temporary String storage Current master (5248fb28) Proposed Change
Median 64.005 MiB 0.005 MiB −99.99%

Every trial produced the corresponding value above. The Ruby sources on current master match the benchmark baseline. This measures temporary String storage rather than post-GC retained memory or throughput.

Shared backing storage changes retention costs: retaining a 4 KiB substring of an 8 MiB parent can keep approximately 8 MiB alive, whereas the previous copy retained approximately 4 KiB. Value isolation remains intact. Ruby implementations and sharing thresholds can produce different results.

  • Did you create an Apache Jira ticket?
  • If a ticket exists: Does your pull request title follow the pattern "THRIFT-NNNN: describe my issue"?
  • Did you squash your changes to a single commit? (not required, but preferred)
  • Did you do your best to avoid breaking changes? If one was needed, did you label the Jira ticket with "Breaking-Change"?

Copilot AI balanced review requested due to automatic review settings October 16, 2026 00:42
@mergeable mergeable Bot added the ruby Pull requests that update Ruby code label Oct 16, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Header callbacks can change the payload encoding, breaking the existing binary callback contract.

1 open finding
What changed in this PR

Reduces temporary payload allocations in native Ruby Compact writes by allowing shared String storage while preserving callback isolation.

Changes:

  • Uses rb_str_subseq when the source length remains unchanged.
  • Adds ownership, encoding, callback, and transport round-trip coverage.
File Description
lib/​rb/​spec/​compact_protocol_spec.rb Adds payload ownership and compatibility tests.
lib/​rb/​ext/​compact_protocol.c Shares payload storage with a copying fallback.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/rb/ext/compact_protocol.c Outdated
Client: rb

Use a separate mutable String whose storage Ruby can share with the input
until either value changes. Preserve transport write overrides, callback
mutation isolation, frozen inputs, and retained payload snapshots. Keep the
existing copying path when a header callback changes the source length.

Normalize the callback payload after header writes so a callback that
re-encodes the source cannot change the payload's binary encoding.

Add behavioral coverage for callback ownership, encodings, and transport
overrides. Allocation measurements remain outside the regression suite.

Co-Authored-By: OpenAI Codex (gpt-6.1-sol) <codex@openai.com>
@kpumuk
kpumuk force-pushed the rb-compact-payload-storage branch from 25d7182 to 94442bd Compare October 16, 2026 00:57
@kpumuk
kpumuk requested a balanced review from Copilot October 16, 2026 01:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No blocking issues were identified in the localized change and focused regression coverage; tests were not executed.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

@kpumuk
kpumuk merged commit ab0a540 into apache:master Oct 16, 2026
200 of 201 checks passed
@kpumuk
kpumuk deleted the rb-compact-payload-storage branch October 16, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ruby Pull requests that update Ruby code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants