yandrey321 opened a new pull request, #11884:
URL: https://github.com/apache/ozone/pull/11884

   ## What changes were proposed in this pull request?
   
   **Problem.** Every conversion to `ECReplicationConfig` creates a new object, 
although only a few EC configs are used
   in practice. `ReplicationConfig.fromProto` is called for every key, 
container and pipeline read from a proto, so OM,
   SCM and clients keep many equal copies alive, one per `OmKeyInfo`, 
`ContainerInfo` or `Pipeline`.
   `RatisReplicationConfig` and `StandaloneReplicationConfig` already return 
shared instances from `getInstance`.
   
   **Change.**
   - `ECReplicationConfig` creates one shared instance, at class loading, for 
each config that
     `ozone.replication.allowed-configs` allows by default 
(`EC/(3-2|6-3|10-4)-(512|1024|2048|4096)k`), with each codec
     (RS and XOR): 24 instances.
   - New `ECReplicationConfig.getInstance(String)` and 
`getInstance(HddsProtos.ECReplicationConfig)` return the shared
     instance for those configs. Any other config is created as before.
   - For protos, the lookup reads the fields directly and matches the codec 
with `equalsIgnoreCase`, so a predefined
     config is returned without creating any object. The proto constructor 
upper-cases the codec name and looks it up
     with `EcCodec.valueOf`, which creates and hashes a new string on every 
call; other configs are now built from the
     matched codec, which skips that too.
   - For strings, a new private helper `parse(String)` extracts the codec, 
data, parity and chunk size. It first looks
     the string up in an immutable map (`ImmutableMap`, built once at class 
loading) from the 24 predefined configs'
     usual form (`rs-6-3-1024k`, as `getReplication()` prints them) to the 
shared instance, and returns that instance
     without running the regex. Other strings go through the regex as before,
     with the same checks and error messages, and are shared too if they turn 
out to be a predefined config (for
     example `RS-6-3-1024K` or `rs-6-3-1048576`). The public String constructor 
uses the same helper and copies the
     fields, so it skips the regex for the usual forms as well, and still 
returns a new object.
   - Callers that convert from a string or proto use `getInstance`:
     - `ReplicationConfig.fromProto` and `parseWithoutFallback`;
     - `DefaultReplicationConfig.fromProto`;
     - SCM's `StorageContainerLocationProtocolServerSideTranslatorPB`;
     - `ReconstructECContainersCommand.getFromProtobuf`;
     - the S3 Gateway's `S3Utils`.
   
   **Key decisions.**
   - **A fixed set, not a cache.** The protos come from clients, and the server 
doesn't validate them against
     `ozone.replication.allowed-configs` yet (HDDS-6048), so a cache filled 
from them would have to be bounded and could
     be filled with unused configs. The supported configs are known in advance, 
so they are created up front; nothing is
     added at runtime, so there is no growth, no eviction, no lock and no race.
   - **The default allowed configs, not the configured ones.** 
`allowed-configs` can be changed per configuration, but
     the shared instances live in a static table that doesn't depend on a 
configuration. A cluster that allows other
     configs still works; those configs are just not shared. Validation is 
unchanged.
   - **Both codecs.** The default `allowed-configs` pattern doesn't restrict 
the codec, so XOR configs are included.
   - **Constructors are kept** and are not deprecated: they are public API, and 
more than 250 test call sites use them.
     `S3StorageType`'s `new ECReplicationConfig(3, 2)` is an enum constant 
created once, so it is unchanged.
   
   Generated-by: Claude Code (Claude Opus 5.5)
   
   ## What is the link to the Apache JIRA
   
   https://issues.apache.org/jira/browse/HDDS-17570
   
   ## How was this patch tested?
   
   Unit + integration tests.
   
   - **Benchmark:** new `ECReplicationConfigDecodeBenchmark` (tagged 
`benchmark`, so not part of normal runs). It uses
     only public API, so the same class ran on master and on this branch, 2 
runs each, JDK 21, with
     `-Djacoco.skip=true` (the JaCoCo agent that surefire attaches by default 
slows instrumented code down, most of all
     with several threads). It decodes protos and strings with 3 common EC 
configs (rs-3-2, rs-6-3, rs-10-4 at
     1024k), and with 3 configs outside the predefined set (rs-4-2-1024k, 
rs-6-3-8192k, rs-12-4-1024k):
     - retained heap: 1,000,000 decoded objects are kept and the heap they 
retain is measured after GC;
     - decode: time and allocation per call on one thread, best of 5 rounds of 
1 s;
     - throughput: total calls/ms with 1, 4, 8 and 16 threads, best of 5 rounds 
of 1 s.
   
     | common configs | master | this PR | |
     |---|---|---|---|
     | distinct `ECReplicationConfig` instances held by 1M decoded objects | 
1,000,000 | 3 | |
     | heap retained by 1M `ReplicationConfig.fromProto` results | 30.5 MB | 0 
MB | 32 bytes per object saved |
     | heap retained by 1M `ContainerInfo.fromProtobuf` results | 145.8 MB | 
115.0–115.2 MB | −21% |
     | `ReplicationConfig.fromProto`, ns/call | 11.1 | 6.8–6.9 | 1.6x faster |
     | `ReplicationConfig.fromProto`, bytes/call | 80 | 0 | |
     | `ContainerInfo.fromProtobuf`, ns/call | 53.8–54.0 | 47.6–48.2 | −11% |
     | `ContainerInfo.fromProtobuf`, bytes/call | 304 | 224 | −26% |
     | `new ECReplicationConfig(String)`, ns/call | 161.3–162.5 | 6.0–7.0 | 
23–27x faster |
     | `new ECReplicationConfig(String)`, bytes/call | 528 | 32 | |
     | `fromProto` calls/ms, 1 / 4 / 8 / 16 threads | 89k / 340k / 662k / 
820–835k | 146–150k / 570–581k / 1.11–1.13M / 1.41–1.48M | 1.65–1.75x |
     | `ContainerInfo` calls/ms, 1 / 4 / 8 / 16 threads | 18.5k / 29–30k / 24k 
/ 11k | 21–22k / 30k / 23–24k / 12–16k | 1.15x at 1 thread, same after |
   
     | other configs | master | this PR |
     |---|---|---|
     | `ReplicationConfig.fromProto`, ns/call | 10.7 | 5.9–6.5 |
     | `ReplicationConfig.fromProto`, bytes/call | 80 | 32 |
     | `ContainerInfo.fromProtobuf`, ns/call | 54.6–54.9 | 47.2–49.4 |
     | `new ECReplicationConfig(String)`, ns/call | 161.5–161.9 | 164.1–174.0 |
     | `new ECReplicationConfig(String)`, bytes/call | 528 | 528–560 |
     | heap retained by 1M `ContainerInfo.fromProtobuf` results | 145.1 MB | 
145.2 MB |
   
     - **Memory:** each decoded config used to keep its own 32-byte object. SCM 
saves about 30 MB of heap per million
       EC containers, and every other object that holds an EC config decoded 
from a proto (pipelines, keys) saves 32
       bytes as well.
     - **Time:** decoding a common config no longer allocates, so `fromProto` 
is 1.6x faster and its throughput is
       1.65–1.75x higher at every thread count. Configs outside the set are not 
shared, but are cheaper to decode than
       before because the codec name is no longer upper-cased and hashed.
     - **Strings:** the usual form of a predefined config no longer runs the 
regex, so parsing it is 22x faster; the 32
       bytes left are the new object that the public constructor has to return. 
Other strings cost 1.5–7.5% more, for
       the failed map lookup and the predefined check before the regex result 
is returned; in one of the two runs they
       also allocated 32 more bytes, the object the String constructor copies 
its fields from, which the JIT removed in
       the other run.
     - **`ContainerInfo` throughput** is the same in both arms from 4 threads 
on, and stops scaling after 4 threads. That is `ContainerID.valueOf`'s
       shared `WeakValueCache`, which this benchmark hits with only 3 container 
IDs; it is unrelated to this change.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to