Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ public class AlwaysHealthyMonitor extends ResourceMonitor {

/** always healthy. */
@Override
public boolean isHealthy() {
protected boolean isHealthyImpl() {
return true;
}

@Override
public ResourceStatus getStatus() {
return ResourceStatus.healthy(ResourceStatus.ResourceType.OTHER);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,43 @@ public abstract class ResourceMonitor {
* Is the resource healthy.
*
* @return true for healthy, otherwise false.
* @throws UnsupportedOperationException if the subclass doesn't override getStatus() or
* isHealthyImpl()
*/
public abstract boolean isHealthy();
public boolean isHealthy() {
return getStatus().isHealthy();
}

/**
* Get detailed resource status including context for error messages. Subclasses should override
* this method to provide rich status information.
*
* @return ResourceStatus with health state and detailed context
* @throws UnsupportedOperationException if the subclass doesn't override getStatus() or
* isHealthyImpl()
*/
public ResourceStatus getStatus() {
// Default implementation for backwards compatibility
// Subclasses should override this to provide detailed status
boolean healthy = isHealthyImpl();
return healthy
? ResourceStatus.healthy(ResourceStatus.ResourceType.OTHER)
: ResourceStatus.builder()
.healthy(false)
.type(ResourceStatus.ResourceType.OTHER)
.description("Resource monitor reports unhealthy status")
.build();
}

/**
* Internal implementation for health check. Subclasses that don't override getStatus() should
* override this instead.
*
* @return true for healthy, otherwise false.
*/
protected boolean isHealthyImpl() {
// Subclass must override either getStatus() or isHealthyImpl()
throw new UnsupportedOperationException(
"Subclass must override either getStatus() or isHealthyImpl()");
}
}
116 changes: 116 additions & 0 deletions core/src/main/java/org/opensearch/sql/monitor/ResourceStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.monitor;

import lombok.Builder;
import lombok.Getter;

/**
* Represents the health status of a resource with detailed context information. This wrapper allows
* error messages to include actionable details about resource exhaustion instead of just boolean
* health checks.
*/
@Getter
@Builder
public class ResourceStatus {
/** Type of resource being monitored. */
public enum ResourceType {
MEMORY,
CPU,
DISK,
OTHER
}

/** Whether the resource is healthy (within limits). */
private final boolean healthy;

/** Type of resource (memory, CPU, etc.). */
private final ResourceType type;

/** Human-readable description of resource state. */
private final String description;

/** Current resource usage value (optional, for metrics). */
private final Long currentUsage;

/** Maximum allowed resource value (optional, for metrics). */
private final Long maxLimit;

/** Additional contextual information (optional). */
private final String additionalContext;

/**
* Creates a healthy status with minimal information.
*
* @param type Resource type
* @return Healthy ResourceStatus
*/
public static ResourceStatus healthy(ResourceType type) {
return ResourceStatus.builder()
.healthy(true)
.type(type)
.description(type + " resources are healthy")
.build();
}

/**
* Creates an unhealthy status with detailed context.
*
* @param type Resource type
* @param currentUsage Current usage value
* @param maxLimit Maximum allowed limit
* @param description Human-readable description
* @return Unhealthy ResourceStatus with context
*/
public static ResourceStatus unhealthy(
ResourceType type, long currentUsage, long maxLimit, String description) {
return ResourceStatus.builder()
.healthy(false)
.type(type)
.currentUsage(currentUsage)
.maxLimit(maxLimit)
.description(description)
.build();
}

/**
* Gets a formatted description including usage metrics if available.
*
* @return Formatted description string
*/
public String getFormattedDescription() {
if (currentUsage != null && maxLimit != null) {
if (maxLimit <= 0) {
// Treat invalid limit as 0, don't compute percentage
return String.format(
"%s (current: %s, limit: %s)", description, formatBytes(currentUsage), formatBytes(0));
}
double percentage = (double) currentUsage / maxLimit * 100;
return String.format(
"%s (current: %s, limit: %s, usage: %.1f%%)",
description, formatBytes(currentUsage), formatBytes(maxLimit), percentage);
}
return description;
}

/**
* Formats byte values into human-readable format (KB, MB, GB).
*
* @param bytes Byte value
* @return Formatted string
*/
private String formatBytes(long bytes) {
if (bytes < 1024) {
return bytes + "B";
} else if (bytes < 1024 * 1024) {
return String.format("%.1fKB", bytes / 1024.0);
} else if (bytes < 1024 * 1024 * 1024) {
return String.format("%.1fMB", bytes / (1024.0 * 1024));
} else {
return String.format("%.1fGB", bytes / (1024.0 * 1024 * 1024));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.monitor;

import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

class ResourceMonitorTest {

@Test
void testDefaultImplementationThrowsException() {
// Create a minimal subclass that doesn't override getStatus() or isHealthyImpl()
ResourceMonitor monitor = new ResourceMonitor() {
// Intentionally empty - doesn't override anything
};

// Attempting to use the default path should throw UnsupportedOperationException
assertThrows(UnsupportedOperationException.class, monitor::isHealthy);
}
}
Loading
Loading