Skip to content

Optimize Mapper v2 binary conversions - #7401

Merged
alextwoods merged 1 commit into
feature/master/DDB-mapperv2from
rvaknin/mapper-v2-binary-optimizations
Sep 29, 2026
Merged

alextwoods merged 1 commit into
feature/master/DDB-mapperv2from
rvaknin/mapper-v2-binary-optimizations

Conversation

@RanVaknin

@RanVaknin RanVaknin commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Context

The DynamoDB low-level client uses SdkBytes for binary values in SDK v2, while Mapper v1 exposes binary POJO fields as ByteBuffer. To preserve Mapper v1 compatibility, Mapper v2 converts SdkBytes into a writable ByteBuffer defensive copy by default. That conversion adds allocation and copying overhead for binary-heavy payloads.

This PR provides two independent opt-in alternatives. Customers can use SdkBytes directly in their POJOs to avoid conversion on reads and writes, or keep their existing ByteBuffer fields and enable read-only mode to avoid defensive copies on reads.

Native SdkBytes fields

Mapper v2 now supports POJO fields declared as SdkBytes or Set<SdkBytes>. Customers opt in by changing their binary POJO fields and accessors. This removes binary conversion on both reads and writes. Existing ByteBuffer POJOs remain unchanged. The benchmark compares equivalent ByteBuffer and SdkBytes schemas.

Operation Payload Baseline ops/s Candidate ops/s Delta Baseline B/op Candidate B/op Allocation delta
GET TINY 2,075,273 2,112,647 +1.80% 640 640 0.00%
GET SMALL 1,237,377 1,277,202 +3.22% 896 800 -10.71%
GET HUGE 143,805 181,626 +26.30% 9,968 4,424 -55.62%
GET HUGE_FLAT 173,506 176,924 +1.97% 2,624 2,624 0.00%
PUT TINY 1,881,203 1,888,171 +0.37% 1,144 1,144 0.00%
PUT SMALL 1,112,741 1,151,281 +3.46% 1,920 1,836 -4.37%
PUT HUGE 91,911 111,816 +21.66% 17,944 15,360 -14.40%
PUT HUGE_FLAT 87,847 92,490 +5.28% 17,600 17,536 -0.36%
UPDATE TINY 1,341,765 1,358,899 +1.28% 1,448 1,432 -1.10%
UPDATE SMALL 670,211 693,493 +3.47% 2,576 2,480 -3.73%
UPDATE HUGE 85,167 90,073 +5.76% 18,768 16,200 -13.68%
UPDATE HUGE_FLAT 59,903 60,927 +1.71% 20,616 20,616 0.00%
DELETE TINY 3,744,490 3,586,324 -4.22% 592 592 0.00%
DELETE SMALL 3,795,484 3,757,726 -0.99% 592 592 0.00%
DELETE HUGE 3,859,940 3,558,134 -7.82% 592 592 0.00%
DELETE HUGE_FLAT 3,742,580 3,738,040 -0.12% 592 592 0.00%
QUERY TINY 716,238 721,096 +0.68% 2,192 2,192 0.00%
QUERY SMALL 412,799 426,852 +3.40% 2,904 2,648 -8.82%
QUERY HUGE 47,111 58,437 +24.04% 30,056 13,424 -55.34%
QUERY HUGE_FLAT 55,487 55,732 +0.44% 7,432 7,432 0.00%
SCAN TINY 1,177,353 1,078,748 -8.38% 1,120 1,068 -4.64%
SCAN SMALL 533,285 551,104 +3.34% 1,736 1,472 -15.21%
SCAN HUGE 49,126 61,906 +26.02% 28,880 12,248 -57.59%
SCAN HUGE_FLAT 64,152 63,221 -1.45% 6,224 6,224 0.00%

Read-only ByteBuffer mode

Customers retaining ByteBuffer fields can enable ByteBufferReadBehavior.READ_ONLY. The mapper then returns a read-only ByteBuffer without creating a writable defensive copy. This remains opt-in because Mapper v1 returns writable buffers; applications that modify loaded buffers or call methods such as compact() or array() could otherwise break. MUTABLE_COPY remains the default. The benchmark uses the same ByteBuffer schema for both configurations.

Operation Payload Baseline ops/s Candidate ops/s Delta Baseline B/op Candidate B/op Allocation delta
GET TINY 2,075,273 2,134,247 +2.84% 640 640 0.00%
GET SMALL 1,237,377 1,252,780 +1.24% 896 864 -3.57%
GET HUGE 143,805 156,332 +8.71% 9,968 7,952 -20.22%
GET HUGE_FLAT 173,506 180,504 +4.03% 2,624 2,624 0.00%
PUT TINY 1,881,203 1,880,551 -0.03% 1,144 1,144 0.00%
PUT SMALL 1,112,741 1,033,524 -7.12% 1,920 1,940 +1.04%
PUT HUGE 91,911 91,956 +0.05% 17,944 17,936 -0.04%
PUT HUGE_FLAT 87,847 92,953 +5.81% 17,600 17,608 +0.05%
UPDATE TINY 1,341,765 1,370,520 +2.14% 1,448 1,424 -1.66%
UPDATE SMALL 670,211 684,557 +2.14% 2,576 2,576 0.00%
UPDATE HUGE 85,167 85,595 +0.50% 18,768 18,784 +0.09%
UPDATE HUGE_FLAT 59,903 60,648 +1.24% 20,616 20,616 0.00%
DELETE TINY 3,744,490 3,608,754 -3.62% 592 592 0.00%
DELETE SMALL 3,795,484 3,921,761 +3.33% 592 592 0.00%
DELETE HUGE 3,859,940 3,937,867 +2.02% 592 592 0.00%
DELETE HUGE_FLAT 3,742,580 3,909,428 +4.46% 592 592 0.00%
QUERY TINY 716,238 727,825 +1.62% 2,192 2,192 0.00%
QUERY SMALL 412,799 418,440 +1.37% 2,904 2,816 -3.03%
QUERY HUGE 47,111 50,490 +7.17% 30,056 24,008 -20.12%
QUERY HUGE_FLAT 55,487 56,526 +1.87% 7,432 7,432 0.00%
SCAN TINY 1,177,353 1,193,862 +1.40% 1,120 1,120 0.00%
SCAN SMALL 533,285 537,009 +0.70% 1,736 1,640 -5.53%
SCAN HUGE 49,126 52,726 +7.33% 28,880 22,832 -20.94%
SCAN HUGE_FLAT 64,152 59,818 -6.76% 6,224 6,224 0.00%

Testing

BinaryConversionOptimizationsTest verifies writable defensive copies by default, read only buffers when enabled, separate table model caches, binary set behavior, and native scalar and set SdkBytes identity. DynamoDBMapperConfigTest verifies the default and merged configuration behavior.

@RanVaknin
RanVaknin requested a review from a team as a code owner September 24, 2026 18:15
@RanVaknin RanVaknin added the perf-improvement Label for PRs that contain performance improvement changes. label Sep 24, 2026
@alextwoods
alextwoods merged commit af01ac3 into feature/master/DDB-mapperv2 Sep 29, 2026
14 of 15 checks passed
@github-actions

Copy link
Copy Markdown

This pull request has been closed and the conversation has been locked. Comments on closed PRs are hard for our team to see. If you need more assistance, please open a new issue that references this one.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Sep 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

perf-improvement Label for PRs that contain performance improvement changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants