Skip to content

Commit

Permalink
Add check and handle negative SearchRequestStats
Browse files Browse the repository at this point in the history
Signed-off-by: David Zane <davizane@amazon.com>
  • Loading branch information
dzane17 committed Nov 7, 2024
1 parent 5909e1a commit b72b74d
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Add Setting to adjust the primary constraint weights ([#16471](https://github.com/opensearch-project/OpenSearch/pull/16471))
- Switch from `buildSrc/version.properties` to Gradle version catalog (`gradle/libs.versions.toml`) to enable dependabot to perform automated upgrades on common libs ([#16284](https://github.com/opensearch-project/OpenSearch/pull/16284))
- Add dynamic setting allowing size > 0 requests to be cached in the request cache ([#16483](https://github.com/opensearch-project/OpenSearch/pull/16483))
- Add check and handle negative SearchRequestStats ([#16569](https://github.com/opensearch-project/OpenSearch/pull/16569))
- Make IndexStoreListener a pluggable interface ([#16583](https://github.com/opensearch-project/OpenSearch/pull/16583))

### Dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

package org.opensearch.index.search.stats;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.Version;
import org.opensearch.action.search.SearchPhaseName;
import org.opensearch.action.search.SearchRequestStats;
Expand Down Expand Up @@ -67,7 +69,7 @@ public class SearchStats implements Writeable, ToXContentFragment {
*/
@PublicApi(since = "1.0.0")
public static class PhaseStatsLongHolder implements Writeable {

private static final Logger logger = LogManager.getLogger(PhaseStatsLongHolder.class);
long current;
long total;
long timeInMillis;
Expand All @@ -86,9 +88,19 @@ public long getTimeInMillis() {

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVLong(current);
if (current < 0) {
logger.warn("current is negative: {}", current);
out.writeVLong(0);
} else {
out.writeVLong(current);
}
out.writeVLong(total);
out.writeVLong(timeInMillis);
if (timeInMillis < 0) {
logger.warn("timeInMillis is negative: {}", timeInMillis);
out.writeVLong(0);

Check warning on line 100 in server/src/main/java/org/opensearch/index/search/stats/SearchStats.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/index/search/stats/SearchStats.java#L99-L100

Added lines #L99 - L100 were not covered by tests
} else {
out.writeVLong(timeInMillis);
}
}

PhaseStatsLongHolder() {
Expand Down Expand Up @@ -173,7 +185,7 @@ public RequestStatsLongHolder getRequestStatsLongHolder() {
return requestStatsLongHolder;
}

private Stats() {
Stats() {
// for internal use, initializes all counts to 0
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.opensearch.action.search.SearchPhaseName;
import org.opensearch.action.search.SearchRequestOperationsListenerSupport;
import org.opensearch.action.search.SearchRequestStats;
import org.opensearch.common.io.stream.BytesStreamOutput;
import org.opensearch.common.settings.ClusterSettings;
import org.opensearch.common.settings.Settings;
import org.opensearch.index.search.stats.SearchStats.Stats;
Expand All @@ -52,6 +53,46 @@

public class SearchStatsTests extends OpenSearchTestCase implements SearchRequestOperationsListenerSupport {

public void testNegativeRequestStats() throws Exception {
SearchStats searchStats = new SearchStats(new Stats(), 0, new HashMap<>());

long paramValue = randomIntBetween(2, 50);

// Testing for request stats
ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
SearchRequestStats testRequestStats = new SearchRequestStats(clusterSettings);
SearchPhaseContext ctx = mock(SearchPhaseContext.class);
for (SearchPhaseName searchPhaseName : SearchPhaseName.values()) {
SearchPhase mockSearchPhase = mock(SearchPhase.class);
when(ctx.getCurrentPhase()).thenReturn(mockSearchPhase);
when(mockSearchPhase.getStartTimeInNanos()).thenReturn(System.nanoTime() - TimeUnit.SECONDS.toNanos(paramValue));
when(mockSearchPhase.getSearchPhaseName()).thenReturn(searchPhaseName);
for (int iterator = 0; iterator < paramValue; iterator++) {
onPhaseStart(testRequestStats, ctx);
onPhaseEnd(testRequestStats, ctx);
onPhaseEnd(testRequestStats, ctx); // call onPhaseEnd() twice to make 'current' negative
}
}
searchStats.setSearchRequestStats(testRequestStats);
for (SearchPhaseName searchPhaseName : SearchPhaseName.values()) {
assertEquals(
-1 * paramValue, // current is negative, equals -1 * paramValue (num loop iterations)
searchStats.getTotal().getRequestStatsLongHolder().getRequestStatsHolder().get(searchPhaseName.getName()).current
);
assertEquals(
2 * paramValue,
searchStats.getTotal().getRequestStatsLongHolder().getRequestStatsHolder().get(searchPhaseName.getName()).total
);
assertThat(
searchStats.getTotal().getRequestStatsLongHolder().getRequestStatsHolder().get(searchPhaseName.getName()).timeInMillis,
greaterThanOrEqualTo(paramValue)
);
}

// Ensure writeTo() does not throw error with negative 'current'
searchStats.writeTo(new BytesStreamOutput(10));
}

// https://github.com/elastic/elasticsearch/issues/7644
public void testShardLevelSearchGroupStats() throws Exception {
// let's create two dummy search stats with groups
Expand Down

0 comments on commit b72b74d

Please sign in to comment.