Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request Priority API #2245

Merged
merged 12 commits into from
Dec 1, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public enum SingularityAction {
BOUNCE_REQUEST(true),
SCALE_REQUEST(true),
PRIORITIZE_REQUEST(true),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quick note, there is an equivalent list in SingularityUI code that mirrors this enum

REMOVE_REQUEST(true),
CREATE_REQUEST(true),
UPDATE_REQUEST(true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ public enum RequestHistoryType {
DEPLOYED_TO_UNPAUSE,
BOUNCED,
SCALED,
SCALE_REVERTED
SCALE_REVERTED,
PRIORITIZED,
PRIORITY_REVERTED
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we able to maybe fold these into UPDATED instead somehow? these fields come through on things that a lot of other systems pull in and the roll out can be a pain with the deployer/etc having to know about all the new fields first. If the purpose is just to indicate the action more easily, we can always have the messge text reflect that, which will also show in the UI

}

@JsonCreator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum SingularityUserFacingAction {
BOUNCE,
PAUSE,
SCALE,
PRIORITIZE,
KILL_TASK,
DELETE_SCHEDULED_TASK,
RUN_SHELL_COMMAND
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
SingularityMachineChangeRequest.class,
SingularityExpiringParent.class,
SingularityScaleRequest.class,
SingularityPriorityRequest.class,
SingularityBounceRequest.class,
SingularityExpiringSkipHealthchecks.class
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.hubspot.singularity.api;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.hubspot.singularity.SingularityRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Optional;

@Schema(description = "Describes the new priority (0 to 1) for a request")
public class SingularityPriorityRequest extends SingularityExpiringRequestParent {
/** see {@link SingularityRequest#getTaskPriorityLevel()} */
private final Optional<Double> priority;

public SingularityPriorityRequest(
@JsonProperty("durationMillis") Optional<Long> durationMillis,
@JsonProperty("actionId") Optional<String> actionId,
@JsonProperty("message") Optional<String> message,
@JsonProperty("priority") Optional<Double> priority,
@JsonProperty("skipEmailNotification") Optional<Boolean> skipEmailNotification
) {
super(durationMillis, actionId, message);
this.priority = priority;
}

public Optional<Double> getPriority() {
return priority;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.hubspot.singularity.expiring;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.hubspot.singularity.api.SingularityPriorityRequest;
import com.hubspot.singularity.api.SingularityScaleRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Optional;

@Schema(description = "Details about a scale action that will eventually revert")
public class SingularityExpiringPriority
extends SingularityExpiringRequestActionParent<SingularityPriorityRequest> {
private final Optional<Double> revertToPriority;

public SingularityExpiringPriority(
@JsonProperty("requestId") String requestId,
@JsonProperty("user") Optional<String> user,
@JsonProperty("startMillis") long startMillis,
@JsonProperty("expiringAPIRequestObject") SingularityPriorityRequest request,
@JsonProperty("revertToPriority") Optional<Double> revertToPriority,
@JsonProperty("actionId") String actionId
) {
super(request, user, startMillis, actionId, requestId);
this.revertToPriority = revertToPriority;
}

@Schema(
description = "The scheduling priority to update to when time has elapsed",
nullable = true
)
public Optional<Double> getRevertToPriority() {
return revertToPriority;
}

@Override
public String toString() {
return "SingularityExpiringPriority{" + "revertToPriority=" + revertToPriority + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.hubspot.singularity.event.SingularityEventListener;
import com.hubspot.singularity.expiring.SingularityExpiringBounce;
import com.hubspot.singularity.expiring.SingularityExpiringPause;
import com.hubspot.singularity.expiring.SingularityExpiringPriority;
import com.hubspot.singularity.expiring.SingularityExpiringRequestActionParent;
import com.hubspot.singularity.expiring.SingularityExpiringScale;
import com.hubspot.singularity.expiring.SingularityExpiringSkipHealthchecks;
Expand Down Expand Up @@ -79,6 +80,8 @@ public class RequestManager extends CuratorAsyncManager {
EXPIRING_ACTION_PATH_ROOT + "/pause";
private static final String EXPIRING_SCALE_PATH_ROOT =
EXPIRING_ACTION_PATH_ROOT + "/scale";
private static final String EXPIRING_PRIORITY_PATH_ROOT =
EXPIRING_ACTION_PATH_ROOT + "/priority";
private static final String EXPIRING_SKIP_HC_PATH_ROOT =
EXPIRING_ACTION_PATH_ROOT + "/skipHc";

Expand All @@ -90,7 +93,9 @@ public class RequestManager extends CuratorAsyncManager {
SingularityExpiringScale.class,
EXPIRING_SCALE_PATH_ROOT,
SingularityExpiringSkipHealthchecks.class,
EXPIRING_SKIP_HC_PATH_ROOT
EXPIRING_SKIP_HC_PATH_ROOT,
SingularityExpiringPriority.class,
EXPIRING_PRIORITY_PATH_ROOT
);

private final Map<Class<? extends SingularityExpiringRequestActionParent<? extends SingularityExpiringRequestParent>>, Transcoder<? extends SingularityExpiringRequestActionParent<? extends SingularityExpiringRequestParent>>> expiringTranscoderMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.hubspot.singularity.api.SingularityDeleteRequestRequest;
import com.hubspot.singularity.api.SingularityExitCooldownRequest;
import com.hubspot.singularity.api.SingularityPauseRequest;
import com.hubspot.singularity.api.SingularityPriorityRequest;
import com.hubspot.singularity.api.SingularityRunNowRequest;
import com.hubspot.singularity.api.SingularityScaleRequest;
import com.hubspot.singularity.api.SingularitySkipHealthchecksRequest;
Expand All @@ -61,6 +62,7 @@
import com.hubspot.singularity.data.TaskManager;
import com.hubspot.singularity.expiring.SingularityExpiringBounce;
import com.hubspot.singularity.expiring.SingularityExpiringPause;
import com.hubspot.singularity.expiring.SingularityExpiringPriority;
import com.hubspot.singularity.expiring.SingularityExpiringRequestActionParent;
import com.hubspot.singularity.expiring.SingularityExpiringScale;
import com.hubspot.singularity.expiring.SingularityExpiringSkipHealthchecks;
Expand Down Expand Up @@ -1648,6 +1650,97 @@ public SingularityRequest deleteRequest(
return request;
}

@PUT
@Path("/request/{requestId}/priority")
@Consumes({ MediaType.APPLICATION_JSON })
@Operation(
summary = "Set the task scheduling priority for the request",
responses = {
@ApiResponse(responseCode = "404", description = "No Request with that ID")
}
)
public SingularityRequestParent setPriority(
@Parameter(hidden = true) @Auth SingularityUser user,
@Parameter(
required = true,
description = "The Request ID to modify priority for"
) @PathParam("requestId") String requestId,
@Context HttpServletRequest requestContext,
@RequestBody(
description = "Request priority (0.0 to 1.0)"
) SingularityPriorityRequest priorityRequest
) {
return maybeProxyToLeader(
requestContext,
SingularityRequestParent.class,
priorityRequest,
() -> setPriority(requestId, priorityRequest, user)
);
}

public SingularityRequestParent setPriority(
String requestId,
SingularityPriorityRequest priorityRequest,
SingularityUser user
) {
SingularityRequestWithState oldRequestWithState = fetchRequestWithState(
requestId,
user
);

SingularityRequest oldRequest = oldRequestWithState.getRequest();
authorizationHelper.checkForAuthorization(
oldRequest,
user,
SingularityAuthorizationScope.WRITE,
SingularityUserFacingAction.PRIORITIZE
);
validator.checkActionEnabled(SingularityAction.PRIORITIZE_REQUEST);

SingularityRequest newRequest = oldRequest
.toBuilder()
.setTaskPriorityLevel(priorityRequest.getPriority())
.build();
String message = String.format(
"Scaling from %d -> %d",
oldRequest.getTaskPriorityLevel(),
newRequest.getTaskPriorityLevel()
);
if (priorityRequest.getMessage().isPresent()) {
message = String.format("%s -- %s", priorityRequest.getMessage().get(), message);
} else {
message = String.format("%s", message);
}

submitRequest(
newRequest,
Optional.of(oldRequestWithState),
Optional.of(RequestHistoryType.PRIORITIZED),
Optional.of(false),
Optional.of(message),
Optional.empty(),
user
);

if (priorityRequest.getDurationMillis().isPresent()) {
requestManager.saveExpiringObject(
new SingularityExpiringPriority(
requestId,
user.getEmail(),
System.currentTimeMillis(),
priorityRequest,
oldRequest.getTaskPriorityLevel(),
priorityRequest.getActionId().orElse(UUID.randomUUID().toString())
)
);
} else {
requestManager.deleteExpiringObject(SingularityExpiringPriority.class, requestId);
}

// TODO - email
return fillEntireRequest(fetchRequestWithState(requestId, user));
}

@PUT
@Path("/request/{requestId}/scale")
@Consumes({ MediaType.APPLICATION_JSON })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.hubspot.singularity.expiring.SingularityExpiringMachineState;
import com.hubspot.singularity.expiring.SingularityExpiringParent;
import com.hubspot.singularity.expiring.SingularityExpiringPause;
import com.hubspot.singularity.expiring.SingularityExpiringPriority;
import com.hubspot.singularity.expiring.SingularityExpiringRequestActionParent;
import com.hubspot.singularity.expiring.SingularityExpiringScale;
import com.hubspot.singularity.expiring.SingularityExpiringSkipHealthchecks;
Expand Down Expand Up @@ -95,6 +96,7 @@ public class SingularityExpiringUserActionPoller extends SingularityLeaderOnlyPo
new SingularityExpiringBounceHandler(),
new SingularityExpiringPauseHandler(),
new SingularityExpiringScaleHandler(),
new SingularityExpiringPriorityHandler(),
new SingularityExpiringSkipHealthchecksHandler(),
new SingularityExpiringSlaveStateHandler(),
new SingularityExpiringRackStateHandler()
Expand Down Expand Up @@ -207,6 +209,53 @@ protected void checkExpiringObjects() {
}
}

private class SingularityExpiringPriorityHandler
extends SingularityExpiringRequestActionHandler<SingularityExpiringPriority> {

private SingularityExpiringPriorityHandler() {
super(SingularityExpiringPriority.class);
}

@Override
protected String getActionName() {
return "Priority";
}

@Override
protected void handleExpiringObject(
SingularityExpiringPriority expiringObject,
SingularityRequestWithState requestWithState,
String message
) {
final SingularityRequest oldRequest = requestWithState.getRequest();
final SingularityRequest newRequest = oldRequest
.toBuilder()
.setTaskPriorityLevel(expiringObject.getRevertToPriority())
.build();

try {
requestHelper.updateRequest(
newRequest,
Optional.of(oldRequest),
requestWithState.getState(),
Optional.of(RequestHistoryType.PRIORITY_REVERTED),
expiringObject.getUser(),
Optional.empty(),
Optional.of(message),
Optional.empty()
);
// TODO - email
} catch (WebApplicationException wae) {
LOG.error(
"While trying to apply {} for {}",
expiringObject,
expiringObject.getRequestId(),
wae
);
}
}
}

private class SingularityExpiringBounceHandler
extends SingularityExpiringRequestActionHandler<SingularityExpiringBounce> {

Expand Down