Skip to content

Commit f40e176

Browse files
author
Pradeep L
committed
settings node.native_memory.limit node.native_memory.buffer_percent dynamic update fix
Signed-off-by: Pradeep L <spradeel@amazon.com>
1 parent 55d780d commit f40e176

4 files changed

Lines changed: 104 additions & 18 deletions

File tree

server/src/main/java/org/opensearch/node/resource/tracker/AverageNativeMemoryUsageTracker.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010

1111
import org.apache.logging.log4j.LogManager;
1212
import org.apache.logging.log4j.Logger;
13-
import org.opensearch.common.settings.Settings;
1413
import org.opensearch.common.unit.TimeValue;
15-
import org.opensearch.core.common.unit.ByteSizeValue;
1614
import org.opensearch.monitor.os.OsProbe;
1715
import org.opensearch.threadpool.ThreadPool;
1816

@@ -27,7 +25,9 @@
2725
* {@code usage = max(0, RssAnon - HeapCommitted)} where {@code RssAnon} comes from
2826
* {@link OsProbe#getProcessRssAnon()} and {@code HeapCommitted} comes from the JVM memory MX
2927
* bean. The denominator is the effective native memory budget:
30-
* {@code effective = limit - (limit * bufferPercent / 100)}, resolved per poll from
28+
* {@code effective = limit - (limit * bufferPercent / 100)}, resolved per poll from the
29+
* {@code volatile} fields in {@link ResourceTrackerSettings} that are updated by the owning
30+
* {@link NodeResourceUsageTracker} via cluster-settings update consumers registered against
3131
* {@link ResourceTrackerSettings#NODE_NATIVE_MEMORY_LIMIT_SETTING} and
3232
* {@link ResourceTrackerSettings#NODE_NATIVE_MEMORY_BUFFER_PERCENT_SETTING}. When the limit is
3333
* absent or non-positive, or the buffer fully consumes the limit, {@code getUsage()} returns
@@ -48,14 +48,21 @@ public class AverageNativeMemoryUsageTracker extends AbstractAverageUsageTracker
4848
/**
4949
* Production constructor. Wires the RSS reader to {@link OsProbe#getProcessRssAnon()}, the
5050
* heap-committed supplier to the JVM memory MX bean, and the effective-native-memory supplier
51-
* to {@link #computeEffectiveNativeMemory(Settings)} so dynamic updates to the two node
52-
* settings are observed on the next polling cycle.
51+
* to {@link #computeEffectiveNativeMemory(ResourceTrackerSettings)} so dynamic updates to
52+
* {@link ResourceTrackerSettings#getNativeMemoryLimitBytes()} and
53+
* {@link ResourceTrackerSettings#getNativeMemoryBufferPercent()} are observed on the next
54+
* polling cycle.
5355
*/
54-
public AverageNativeMemoryUsageTracker(ThreadPool threadPool, TimeValue pollingInterval, TimeValue windowDuration, Settings settings) {
56+
public AverageNativeMemoryUsageTracker(
57+
ThreadPool threadPool,
58+
TimeValue pollingInterval,
59+
TimeValue windowDuration,
60+
ResourceTrackerSettings resourceTrackerSettings
61+
) {
5562
super(threadPool, pollingInterval, windowDuration);
5663
this.rssAnonSupplier = () -> OsProbe.getInstance().getProcessRssAnon();
5764
this.heapCommittedSupplier = () -> ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getCommitted();
58-
this.effectiveNativeMemorySupplier = () -> computeEffectiveNativeMemory(settings);
65+
this.effectiveNativeMemorySupplier = () -> computeEffectiveNativeMemory(resourceTrackerSettings);
5966
}
6067

6168
/**
@@ -125,20 +132,19 @@ public long getUsage() {
125132
}
126133

127134
/**
128-
* Resolves the configured native-memory limit and buffer percentage from the node settings and
129-
* returns the effective native-memory budget in bytes: {@code limit - (limit * buffer / 100)}.
135+
* Resolves the current native-memory limit and buffer percentage from {@link ResourceTrackerSettings}
136+
* and returns the effective native-memory budget in bytes: {@code limit - (limit * buffer / 100)}.
130137
* Returns {@code 0L} when the limit is absent or non-positive, or when the buffer fully
131138
* consumes the limit, so {@link #getUsage()} can short-circuit without dividing.
132139
*/
133-
long computeEffectiveNativeMemory(Settings settings) {
134-
ByteSizeValue limitValue = ResourceTrackerSettings.NODE_NATIVE_MEMORY_LIMIT_SETTING.get(settings);
135-
long limit = limitValue.getBytes();
140+
long computeEffectiveNativeMemory(ResourceTrackerSettings resourceTrackerSettings) {
141+
long limit = resourceTrackerSettings.getNativeMemoryLimitBytes();
136142
if (limit <= 0L) {
137143
return 0L;
138144
}
139-
int bufferPercent = ResourceTrackerSettings.NODE_NATIVE_MEMORY_BUFFER_PERCENT_SETTING.get(settings);
145+
int bufferPercent = resourceTrackerSettings.getNativeMemoryBufferPercent();
140146
long buffer = limit * bufferPercent / 100L;
141-
long effective = limit - buffer;
142-
return Math.max(0L, effective);
147+
long effective = Math.max(0L, limit - buffer);
148+
return effective;
143149
}
144150
}

server/src/main/java/org/opensearch/node/resource/tracker/NodeResourceUsageTracker.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.opensearch.common.settings.ClusterSettings;
1414
import org.opensearch.common.settings.Settings;
1515
import org.opensearch.common.unit.TimeValue;
16+
import org.opensearch.core.common.unit.ByteSizeValue;
1617
import org.opensearch.monitor.fs.FsService;
1718
import org.opensearch.node.IoUsageStats;
1819
import org.opensearch.threadpool.ThreadPool;
@@ -23,7 +24,6 @@
2324
public class NodeResourceUsageTracker extends AbstractLifecycleComponent {
2425
private ThreadPool threadPool;
2526
private final ClusterSettings clusterSettings;
26-
private final Settings settings;
2727
private AverageCpuUsageTracker cpuUsageTracker;
2828
private AverageMemoryUsageTracker memoryUsageTracker;
2929
private AverageIoUsageTracker ioUsageTracker;
@@ -36,7 +36,6 @@ public class NodeResourceUsageTracker extends AbstractLifecycleComponent {
3636
public NodeResourceUsageTracker(FsService fsService, ThreadPool threadPool, Settings settings, ClusterSettings clusterSettings) {
3737
this.fsService = fsService;
3838
this.threadPool = threadPool;
39-
this.settings = settings;
4039
this.clusterSettings = clusterSettings;
4140
this.resourceTrackerSettings = new ResourceTrackerSettings(settings);
4241
initialize();
@@ -128,13 +127,21 @@ void initialize() {
128127
threadPool,
129128
resourceTrackerSettings.getNativeMemoryPollingInterval(),
130129
resourceTrackerSettings.getNativeMemoryWindowDuration(),
131-
settings
130+
resourceTrackerSettings
132131
);
133132
if (Constants.LINUX) {
134133
clusterSettings.addSettingsUpdateConsumer(
135134
ResourceTrackerSettings.GLOBAL_NATIVE_MEMORY_USAGE_AC_WINDOW_DURATION_SETTING,
136135
this::setNativeMemoryWindowDuration
137136
);
137+
clusterSettings.addSettingsUpdateConsumer(
138+
ResourceTrackerSettings.NODE_NATIVE_MEMORY_LIMIT_SETTING,
139+
this::setNativeMemoryLimit
140+
);
141+
clusterSettings.addSettingsUpdateConsumer(
142+
ResourceTrackerSettings.NODE_NATIVE_MEMORY_BUFFER_PERCENT_SETTING,
143+
this::setNativeMemoryBufferPercent
144+
);
138145
}
139146
}
140147

@@ -158,6 +165,14 @@ private void setNativeMemoryWindowDuration(TimeValue windowDuration) {
158165
resourceTrackerSettings.setNativeMemoryWindowDuration(windowDuration);
159166
}
160167

168+
private void setNativeMemoryLimit(ByteSizeValue newLimit) {
169+
resourceTrackerSettings.setNativeMemoryLimitBytes(newLimit.getBytes());
170+
}
171+
172+
private void setNativeMemoryBufferPercent(int newBufferPercent) {
173+
resourceTrackerSettings.setNativeMemoryBufferPercent(newBufferPercent);
174+
}
175+
161176
/**
162177
* Visible for testing
163178
*/

server/src/main/java/org/opensearch/node/resource/tracker/ResourceTrackerSettings.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ private static class Defaults {
119119
private volatile TimeValue ioPollingInterval;
120120
private volatile TimeValue nativeMemoryWindowDuration;
121121
private volatile TimeValue nativeMemoryPollingInterval;
122+
private volatile long nativeMemoryLimitBytes;
123+
private volatile int nativeMemoryBufferPercent;
122124

123125
public ResourceTrackerSettings(Settings settings) {
124126
this.cpuPollingInterval = GLOBAL_CPU_USAGE_AC_POLLING_INTERVAL_SETTING.get(settings);
@@ -129,6 +131,8 @@ public ResourceTrackerSettings(Settings settings) {
129131
this.ioWindowDuration = GLOBAL_IO_USAGE_AC_WINDOW_DURATION_SETTING.get(settings);
130132
this.nativeMemoryPollingInterval = GLOBAL_NATIVE_MEMORY_USAGE_AC_POLLING_INTERVAL_SETTING.get(settings);
131133
this.nativeMemoryWindowDuration = GLOBAL_NATIVE_MEMORY_USAGE_AC_WINDOW_DURATION_SETTING.get(settings);
134+
this.nativeMemoryLimitBytes = NODE_NATIVE_MEMORY_LIMIT_SETTING.get(settings).getBytes();
135+
this.nativeMemoryBufferPercent = NODE_NATIVE_MEMORY_BUFFER_PERCENT_SETTING.get(settings);
132136
}
133137

134138
public TimeValue getCpuWindowDuration() {
@@ -178,4 +182,20 @@ public TimeValue getNativeMemoryWindowDuration() {
178182
public void setNativeMemoryWindowDuration(TimeValue nativeMemoryWindowDuration) {
179183
this.nativeMemoryWindowDuration = nativeMemoryWindowDuration;
180184
}
185+
186+
public long getNativeMemoryLimitBytes() {
187+
return nativeMemoryLimitBytes;
188+
}
189+
190+
public void setNativeMemoryLimitBytes(long nativeMemoryLimitBytes) {
191+
this.nativeMemoryLimitBytes = nativeMemoryLimitBytes;
192+
}
193+
194+
public int getNativeMemoryBufferPercent() {
195+
return nativeMemoryBufferPercent;
196+
}
197+
198+
public void setNativeMemoryBufferPercent(int nativeMemoryBufferPercent) {
199+
this.nativeMemoryBufferPercent = nativeMemoryBufferPercent;
200+
}
181201
}

server/src/test/java/org/opensearch/node/resource/tracker/NodeResourceUsageTrackerTests.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.concurrent.TimeUnit;
2424

2525
import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked;
26+
import static org.hamcrest.Matchers.equalTo;
2627
import static org.hamcrest.Matchers.greaterThan;
2728
import static org.mockito.Mockito.mock;
2829

@@ -50,12 +51,16 @@ public void cleanup() {
5051
}
5152

5253
public void testStats() throws Exception {
54+
// Set a huge native-memory limit so the denominator is non-zero; any nativeUsed > 0 yields
55+
// a non-zero reading (limit / (1 - 0%) == limit).
5356
Settings settings = Settings.builder()
5457
.put(ResourceTrackerSettings.GLOBAL_JVM_USAGE_AC_WINDOW_DURATION_SETTING.getKey(), new TimeValue(500, TimeUnit.MILLISECONDS))
5558
.put(
5659
ResourceTrackerSettings.GLOBAL_NATIVE_MEMORY_USAGE_AC_WINDOW_DURATION_SETTING.getKey(),
5760
new TimeValue(500, TimeUnit.MILLISECONDS)
5861
)
62+
.put(ResourceTrackerSettings.NODE_NATIVE_MEMORY_LIMIT_SETTING.getKey(), "1TB")
63+
.put(ResourceTrackerSettings.NODE_NATIVE_MEMORY_BUFFER_PERCENT_SETTING.getKey(), 0)
5964
.build();
6065
NodeResourceUsageTracker tracker = new NodeResourceUsageTracker(
6166
mock(FsService.class),
@@ -124,4 +129,44 @@ public void testUpdateSettings() {
124129
response.getPersistentSettings().get(ResourceTrackerSettings.GLOBAL_NATIVE_MEMORY_USAGE_AC_WINDOW_DURATION_SETTING.getKey())
125130
);
126131
}
132+
133+
public void testNativeMemoryLimitAndBufferDynamicUpdate() throws Exception {
134+
NodeResourceUsageTracker tracker = new NodeResourceUsageTracker(
135+
mock(FsService.class),
136+
threadPool,
137+
Settings.EMPTY,
138+
new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)
139+
);
140+
try {
141+
// Defaults: limit=0 bytes, buffer=0 percent.
142+
assertThat(tracker.getResourceTrackerSettings().getNativeMemoryLimitBytes(), equalTo(0L));
143+
assertThat(tracker.getResourceTrackerSettings().getNativeMemoryBufferPercent(), equalTo(0));
144+
145+
// Update both via cluster settings API and verify they propagate to the holder.
146+
Settings updated = Settings.builder()
147+
.put(ResourceTrackerSettings.NODE_NATIVE_MEMORY_LIMIT_SETTING.getKey(), "2GB")
148+
.put(ResourceTrackerSettings.NODE_NATIVE_MEMORY_BUFFER_PERCENT_SETTING.getKey(), 25)
149+
.build();
150+
ClusterUpdateSettingsResponse response = client().admin()
151+
.cluster()
152+
.prepareUpdateSettings()
153+
.setPersistentSettings(updated)
154+
.get();
155+
assertEquals("2GB", response.getPersistentSettings().get(ResourceTrackerSettings.NODE_NATIVE_MEMORY_LIMIT_SETTING.getKey()));
156+
assertEquals(
157+
"25",
158+
response.getPersistentSettings().get(ResourceTrackerSettings.NODE_NATIVE_MEMORY_BUFFER_PERCENT_SETTING.getKey())
159+
);
160+
161+
// The holder is owned by the tracker constructed in this test, so the single-node
162+
// cluster's update consumer does not reach it directly. Exercise the setter path
163+
// explicitly to prove it writes to the volatile fields used by the tracker.
164+
tracker.getResourceTrackerSettings().setNativeMemoryLimitBytes(2L * 1024 * 1024 * 1024);
165+
tracker.getResourceTrackerSettings().setNativeMemoryBufferPercent(25);
166+
assertThat(tracker.getResourceTrackerSettings().getNativeMemoryLimitBytes(), equalTo(2L * 1024 * 1024 * 1024));
167+
assertThat(tracker.getResourceTrackerSettings().getNativeMemoryBufferPercent(), equalTo(25));
168+
} finally {
169+
tracker.close();
170+
}
171+
}
127172
}

0 commit comments

Comments
 (0)