Skip to content

Commit 07552f3

Browse files
committed
[CELEBORN-2344] Support to force set serving state via HTTP APIs
1 parent 69df893 commit 07552f3

4 files changed

Lines changed: 113 additions & 0 deletions

File tree

service/src/main/scala/org/apache/celeborn/server/common/HttpService.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ abstract class HttpService extends Service with Logging {
195195
def updateInterruptionNotice(workerInterruptionNotices: Map[String, Long]): HandleResponse =
196196
throw new UnsupportedOperationException()
197197

198+
def getServingState(): String = throw new UnsupportedOperationException()
199+
200+
def setServingState(state: String, timeoutMs: String): String =
201+
throw new UnsupportedOperationException()
202+
198203
def startHttpServer(): Unit = {
199204
httpServer = HttpServer(
200205
serviceName,

worker/src/main/java/org/apache/celeborn/service/deploy/worker/memory/MemoryManager.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public class MemoryManager {
7979
private long pausePushDataAndReplicateTime = 0L;
8080
private int trimCounter = 0;
8181
private volatile boolean isPaused = false;
82+
private volatile ServingState forcedServingState = null;
83+
private volatile long forcedServingStateExpireTime = -1L; // -1 means no expiry
8284
// For credit stream
8385
private final AtomicLong readBufferCounter = new AtomicLong(0);
8486
private long readBufferThreshold;
@@ -307,6 +309,15 @@ public boolean shouldEvict(boolean aggressiveMemoryFileEvictEnabled, double evic
307309
}
308310

309311
public ServingState currentServingState() {
312+
if (forcedServingState != null) {
313+
if (forcedServingStateExpireTime > 0
314+
&& System.currentTimeMillis() > forcedServingStateExpireTime) {
315+
this.clearForcedServingState();
316+
} else {
317+
return forcedServingState;
318+
}
319+
}
320+
310321
long memoryUsage = getMemoryUsage();
311322
// pause replicate threshold always greater than pause push data threshold
312323
// so when trigger pause replicate, pause both push and replicate
@@ -587,6 +598,30 @@ public void releaseMemoryFileStorage(int bytes) {
587598
memoryFileStorageCounter.add(-1 * bytes);
588599
}
589600

601+
public ServingState getServingState() {
602+
return servingState;
603+
}
604+
605+
public ServingState getForcedServingState() {
606+
return forcedServingState;
607+
}
608+
609+
public void forceServingState(ServingState state, Long timeoutMs) {
610+
this.forcedServingState = state;
611+
this.forcedServingStateExpireTime =
612+
timeoutMs > 0 ? System.currentTimeMillis() + timeoutMs : -1L;
613+
logger.info(
614+
"Serving state manually forced to {} with forcedServingStateExpireTime {}",
615+
state,
616+
timeoutMs);
617+
}
618+
619+
public void clearForcedServingState() {
620+
this.forcedServingState = null;
621+
this.forcedServingStateExpireTime = -1L;
622+
logger.info("Forced serving state override cleared");
623+
}
624+
590625
public void close() {
591626
checkService.shutdown();
592627
reportService.shutdown();

worker/src/main/scala/org/apache/celeborn/service/deploy/worker/Worker.scala

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,51 @@ private[celeborn] class Worker(
947947
sb.toString()
948948
}
949949

950+
override def getServingState(): String = {
951+
val sb = new StringBuilder
952+
sb.append("====================== Worker Serving State ==========================\n")
953+
val current = memoryManager.getServingState
954+
sb.append(s"Current state: $current.\n")
955+
956+
val forced = memoryManager.getForcedServingState
957+
if (forced != null) {
958+
sb.append(s"Manual override active.\n")
959+
}
960+
sb.toString()
961+
}
962+
963+
override def setServingState(state: String, timeoutStr: String): String = {
964+
val sb = new StringBuilder
965+
sb.append("====================== Set Serving State ============================\n")
966+
if (state.isEmpty) {
967+
memoryManager.clearForcedServingState()
968+
sb.append("Manual servingState override cleared.\n")
969+
return sb.toString()
970+
}
971+
val servingState =
972+
try {
973+
ServingState.valueOf(state.toUpperCase(Locale.ROOT))
974+
} catch {
975+
case _: IllegalArgumentException =>
976+
return s"Invalid state '$state'. " +
977+
s"Legal values: PUSH_AND_REPLICATE_PAUSED, PUSH_PAUSED, NONE_PAUSED\n"
978+
}
979+
val timeout =
980+
if (timeoutStr.isEmpty) 0L
981+
else
982+
try {
983+
JavaUtils.timeStringAsMs(timeoutStr)
984+
} catch {
985+
case e: NumberFormatException =>
986+
return s"Invalid timeout '$timeoutStr'. $e\n"
987+
}
988+
memoryManager.forceServingState(servingState, timeout)
989+
sb.append(s"Serving state forced to: $servingState\n")
990+
if (timeout > 0) sb.append(s"Override will auto-clear after $timeoutStr.\n")
991+
else sb.append("Override will persist until explicitly cleared.\n")
992+
sb.toString()
993+
}
994+
950995
override def exit(exitType: String): String = {
951996
exitType.toUpperCase(Locale.ROOT) match {
952997
case "DECOMMISSION" =>

worker/src/main/scala/org/apache/celeborn/service/deploy/worker/http/api/ApiWorkerResource.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,32 @@ class ApiWorkerResource extends ApiRequestContext {
8888
def exit(@FormParam("type") exitType: String): String = {
8989
httpService.exit(normalizeParam(exitType))
9090
}
91+
92+
@Path("/servingState")
93+
@ApiResponse(
94+
responseCode = "200",
95+
content = Array(new Content(
96+
mediaType = MediaType.TEXT_PLAIN)),
97+
description =
98+
"Show the current serving state and whether a manual override is active.")
99+
@GET
100+
def getServingState(): String = httpService.getServingState()
101+
102+
@Path("/servingState")
103+
@ApiResponse(
104+
responseCode = "200",
105+
content = Array(new Content(
106+
mediaType = MediaType.APPLICATION_FORM_URLENCODED)),
107+
description =
108+
"Force the worker serving state. " +
109+
"Legal values for 'state' are 'PUSH_AND_REPLICATE_PAUSED', 'PUSH_PAUSED' and 'NONE_PAUSED'," +
110+
" or empty to clear the override. " +
111+
"Optional 'timeoutMs' auto-clears the override after the given duration; omit to hold indefinitely.")
112+
@POST
113+
def setServingState(
114+
@FormParam("state") state: String,
115+
@FormParam("timeout") timeoutStr: String): String = {
116+
httpService.setServingState(normalizeParam(state), normalizeParam(timeoutStr))
117+
}
118+
91119
}

0 commit comments

Comments
 (0)