Mathias Roeppischer created IGNITE-28980:
--------------------------------------------

             Summary: BinaryMemoryAllocator$ThreadLocalAllocator$Chunk does not 
reset maxMsgSize after shrink check
                 Key: IGNITE-28980
                 URL: https://issues.apache.org/jira/browse/IGNITE-28980
             Project: Ignite
          Issue Type: Bug
          Components: general
    Affects Versions: 2.18
         Environment: * Apache Ignite 2.18.0
* {{BinaryMemoryAllocator$ThreadLocalAllocator$Chunk}}
* Java 25.0.3+9
* Linux 6.8.0-100-generic amd64
            Reporter: Mathias Roeppischer
            Assignee: Mathias Roeppischer
             Fix For: 2.19


{{BinaryMemoryAllocator$ThreadLocalAllocator$Chunk}} keeps track of the maximum 
message size detected between checks in {{maxMsgSize}}.

The value is updated as follows:

{code:java}
if (maxMsgSize > this.maxMsgSize)
    this.maxMsgSize = maxMsgSize;
{code}

The shrink logic periodically checks this value based on {{CHECK_FREQ}}:

{code:java}
if (CommonUtils.nanosToMillis(nowNanos - lastCheckNanos) >= CHECK_FREQ) {
    int halfSize = data.length >> 1;

    if (this.maxMsgSize < halfSize)
        this.data = new byte[halfSize];

    lastCheckNanos = nowNanos;
}
{code}

However, {{this.maxMsgSize}} is never reset after the check.

As a result, the value effectively represents the maximum message size seen 
during the entire lifetime of the {{Chunk}}, rather than the maximum message 
size detected between checks as documented.

h3. Impact

A single large serialization can permanently prevent the chunk from shrinking 
again.

For example, if a chunk grows to 128 MB due to a large message and 
{{maxMsgSize}} reaches 100 MB, subsequent small messages cannot lower 
{{maxMsgSize}} below 100 MB. Consequently, the shrink condition

{code:java}
this.maxMsgSize < halfSize
{code}

may never become true, even if the chunk is subsequently used only for much 
smaller messages.

This can cause large byte arrays to remain retained by thread-local allocators. 
In applications using thread pools, a number of worker threads can therefore 
retain large buffers after processing occasional large messages, resulting in 
unnecessarily high heap usage.

h3. Expected behavior

{{maxMsgSize}} should represent the maximum message size observed during the 
current check interval.

After the shrink check has been performed, the recorded maximum should be reset 
so that the next interval starts with a fresh measurement.

h3. Proposed fix

Reset {{maxMsgSize}} after each {{CHECK_FREQ}} check:

{code:java}
if (CommonUtils.nanosToMillis(nowNanos - lastCheckNanos) >= CHECK_FREQ) {
    int halfSize = data.length >> 1;

    if (this.maxMsgSize < halfSize)
        this.data = new byte[halfSize];

    this.maxMsgSize = 0;
    lastCheckNanos = nowNanos;
}
{code}

This preserves the existing design of collecting the maximum message size 
between checks while allowing the chunk to shrink when subsequent serialization 
workloads are smaller.

h3. Additional Information

Discussion on the Ignite User Mailing list:

https://lists.apache.org/thread/cr2rvdpyh1njn2wghvg1mq7gr66wgjls



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to