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

Deleting state #1386

Merged
merged 23 commits into from Feb 10, 2017
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
91de88b
add deleting request state
matush-v Dec 21, 2016
cac4dba
enqueue task cleanup for deleted requests
matush-v Dec 21, 2016
53b397f
change request status to DELETED once all tasks are cleaned
matush-v Dec 27, 2016
d2da062
modularize
matush-v Dec 27, 2016
fa02949
check for null
matush-v Jan 9, 2017
60c1892
refactor
matush-v Jan 9, 2017
ffb5e8c
updated log message
matush-v Jan 9, 2017
3715937
added DELETING states and updated to correctly delete requests
matush-v Jan 12, 2017
b98095e
update and add tests for DELETING states
matush-v Jan 12, 2017
8f39f9c
removed unwanted DELETING state
matush-v Jan 12, 2017
4be8f90
preserve user and message history if it exists
matush-v Jan 12, 2017
53162de
update test for message preservation
matush-v Jan 12, 2017
4797793
Merge branch 'master' into deleting-state
ssalinas Jan 12, 2017
394a1e5
Merge branch 'deleting-state' of github.com:HubSpot/Singularity into …
ssalinas Jan 13, 2017
c818b74
Merge branch 'master' into deleting-state
ssalinas Jan 13, 2017
a9a2dde
reload request page after starting a delete
matush-v Jan 13, 2017
07ff1ca
Merge branch 'deleting-state' of github.com:HubSpot/Singularity into …
matush-v Jan 13, 2017
05ee09e
reload data rather than refresh the page
matush-v Jan 18, 2017
b7f4f2b
correctly kill tasks for a delete request
matush-v Jan 20, 2017
590c5c8
update deploy and request history tables as they change
matush-v Jan 20, 2017
b6fff6d
remove callback for delete btn
matush-v Jan 23, 2017
de9a21d
Use separate TaskCleanupType for deleting request
ssalinas Jan 24, 2017
152d7a6
test destroy task
matush-v Feb 8, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Docs/reference/api.md
Expand Up @@ -3808,7 +3808,7 @@ Start a new deployment for a Request
| name | type | required | description |
|------|------|----------|-------------|
| expiringSkipHealthchecks | [SingularityExpiringSkipHealthchecks](#model-SingularityExpiringSkipHealthchecks) | optional | |
| state | [RequestState](#model-RequestState) | optional | Allowable values: ACTIVE, DELETED, PAUSED, SYSTEM_COOLDOWN, FINISHED, DEPLOYING_TO_UNPAUSE |
| state | [RequestState](#model-RequestState) | optional | Allowable values: ACTIVE, DELETING, DELETED, PAUSED, SYSTEM_COOLDOWN, FINISHED, DEPLOYING_TO_UNPAUSE |
| pendingDeploy | [SingularityDeploy](#model-SingularityDeploy) | optional | |
| activeDeploy | [SingularityDeploy](#model-SingularityDeploy) | optional | |
| expiringPause | [SingularityExpiringPause](#model-SingularityExpiringPause) | optional | |
Expand Down
2 changes: 1 addition & 1 deletion Docs/reference/apidocs/models.md
Expand Up @@ -792,7 +792,7 @@ Models:
| name | type | required | description |
|------|------|----------|-------------|
| expiringSkipHealthchecks | [SingularityExpiringSkipHealthchecks](models.md#model-SingularityExpiringSkipHealthchecks) | optional | |
| state | [RequestState](models.md#model-RequestState) | optional | Allowable values: ACTIVE, DELETED, PAUSED, SYSTEM_COOLDOWN, FINISHED, DEPLOYING_TO_UNPAUSE |
| state | [RequestState](models.md#model-RequestState) | optional | Allowable values: ACTIVE, DELETING, DELETED, PAUSED, SYSTEM_COOLDOWN, FINISHED, DEPLOYING_TO_UNPAUSE |
| pendingDeploy | [SingularityDeploy](models.md#model-SingularityDeploy) | optional | |
| activeDeploy | [SingularityDeploy](models.md#model-SingularityDeploy) | optional | |
| expiringPause | [SingularityExpiringPause](models.md#model-SingularityExpiringPause) | optional | |
Expand Down
Expand Up @@ -2,7 +2,7 @@

public enum RequestState {

ACTIVE(true), DELETED(false), PAUSED(false), SYSTEM_COOLDOWN(true), FINISHED(false), DEPLOYING_TO_UNPAUSE(true);
ACTIVE(true), DELETING(false), DELETED(false), PAUSED(false), SYSTEM_COOLDOWN(true), FINISHED(false), DEPLOYING_TO_UNPAUSE(true);

private final boolean isRunnable;

Expand Down
Expand Up @@ -10,7 +10,6 @@
import org.slf4j.LoggerFactory;

import com.codahale.metrics.MetricRegistry;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
Expand All @@ -37,7 +36,6 @@
import com.hubspot.singularity.data.transcoders.Transcoder;
import com.hubspot.singularity.event.SingularityEventListener;
import com.hubspot.singularity.expiring.SingularityExpiringBounce;
import com.hubspot.singularity.expiring.SingularityExpiringParent;
import com.hubspot.singularity.expiring.SingularityExpiringPause;
import com.hubspot.singularity.expiring.SingularityExpiringRequestActionParent;
import com.hubspot.singularity.expiring.SingularityExpiringScale;
Expand Down Expand Up @@ -258,6 +256,14 @@ public SingularityCreateResult activate(SingularityRequest request, RequestHisto
return save(request, RequestState.ACTIVE, historyType, timestamp, user, message);
}

public SingularityCreateResult markDeleting(SingularityRequest request, RequestHistoryType historyType, long timestamp, Optional<String> user, Optional<String> message) {
return save(request, RequestState.DELETING, historyType, timestamp, user, message);
}

public SingularityCreateResult markDeleted(SingularityRequest request, RequestHistoryType historyType, long timestamp, Optional<String> user, Optional<String> message) {
return save(request, RequestState.DELETED, historyType, timestamp, user, message);
}

public List<SingularityPendingRequest> getPendingRequests() {
return getAsyncChildren(PENDING_PATH_ROOT, pendingRequestTranscoder);
}
Expand Down Expand Up @@ -319,7 +325,7 @@ public Optional<SingularityRequestWithState> getRequest(String requestId) {
return getData(getRequestPath(requestId), requestTranscoder);
}

public SingularityDeleteResult deleteRequest(SingularityRequest request, Optional<String> user, Optional<String> actionId, Optional<String> message) {
public SingularityDeleteResult startDeletingRequest(SingularityRequest request, Optional<String> user, Optional<String> actionId, Optional<String> message) {
final long now = System.currentTimeMillis();

// delete it no matter if the delete request already exists.
Expand All @@ -328,9 +334,12 @@ public SingularityDeleteResult deleteRequest(SingularityRequest request, Optiona

saveHistory(new SingularityRequestHistory(now, user, RequestHistoryType.DELETED, request, message));

// moves RequestState to DELETED
SingularityDeleteResult deleteResult = delete(getRequestPath(request.getId()));
Copy link
Member

Choose a reason for hiding this comment

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

If we want to still be able to grab request data while it is in DELETING state, we shouldn't have this line here. This deletes all of the current request data. We should probably move this step to be triggered by the cleaner after everything is done (i.e. when the active deploy cleanup is happening)


LOG.info("Request {} deleted ({}) by {} - {}", request.getId(), deleteResult, user, message);
markDeleting(request, RequestHistoryType.DELETED, System.currentTimeMillis(), user, message);

LOG.info("Request {} enqueued for deletion ({}) by {} - {}", request.getId(), deleteResult, user, message);

return deleteResult;
}
Expand Down
Expand Up @@ -545,7 +545,7 @@ public SingularityRequest deleteRequest(@ApiParam("The request ID to delete.") @
message = deleteRequest.get().getMessage();
}

requestManager.deleteRequest(request, JavaUtils.getUserEmail(user), actionId, message);
requestManager.startDeletingRequest(request, JavaUtils.getUserEmail(user), actionId, message);

mailer.sendRequestRemovedMail(request, JavaUtils.getUserEmail(user), message);

Expand Down
Expand Up @@ -2,8 +2,10 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

Expand All @@ -23,6 +25,7 @@
import com.hubspot.mesos.JavaUtils;
import com.hubspot.singularity.LoadBalancerRequestType;
import com.hubspot.singularity.LoadBalancerRequestType.LoadBalancerRequestId;
import com.hubspot.singularity.RequestCleanupType;
import com.hubspot.singularity.RequestState;
import com.hubspot.singularity.SingularityDeleteResult;
import com.hubspot.singularity.SingularityDeploy;
Expand All @@ -37,8 +40,10 @@
import com.hubspot.singularity.SingularityRequestCleanup;
import com.hubspot.singularity.SingularityRequestDeployState;
import com.hubspot.singularity.SingularityRequestHistory;
import com.hubspot.singularity.SingularityRequestHistory.RequestHistoryType;
import com.hubspot.singularity.SingularityRequestLbCleanup;
import com.hubspot.singularity.SingularityRequestWithState;
import com.hubspot.singularity.SingularityShellCommand;
import com.hubspot.singularity.SingularityTask;
import com.hubspot.singularity.SingularityTaskCleanup;
import com.hubspot.singularity.SingularityTaskId;
Expand Down Expand Up @@ -123,8 +128,7 @@ private boolean shouldKillTask(SingularityTaskCleanup taskCleanup, List<Singular

// If pausing, must be a long-running task to kill here
if (requestWithState.get().getState() == RequestState.PAUSED &&
((taskCleanup.getCleanupType() == TaskCleanupType.PAUSING && request.isLongRunning())
|| !(taskCleanup.getCleanupType() == TaskCleanupType.PAUSING))) {
(!(taskCleanup.getCleanupType() == TaskCleanupType.PAUSING) || request.isLongRunning())) {
LOG.debug("Killing a task {} immediately because the request was paused", taskCleanup);
return true;
}
Expand Down Expand Up @@ -336,7 +340,7 @@ private void drainRequestCleanupQueue() {
continue;
}
} else {
if (pause(requestCleanup, matchingActiveTaskIds, killActiveTasks) == TaskCleanupType.PAUSING) {
if (pause(requestCleanup, matchingActiveTaskIds) == TaskCleanupType.PAUSING) {
killActiveTasks = false;
}
}
Expand All @@ -350,11 +354,16 @@ private void drainRequestCleanupQueue() {
createLbCleanupRequest(requestId, matchingActiveTaskIds);
}
} else {
Optional<SingularityRequestHistory> maybeHistory = requestHistoryHelper.getLastHistory(requestId);
if (maybeHistory.isPresent() && maybeHistory.get().getRequest().isLoadBalanced() && configuration.isDeleteRemovedRequestsFromLoadBalancer()) {
createLbCleanupRequest(requestId, matchingActiveTaskIds);
if (!Iterables.isEmpty(matchingActiveTaskIds)) {
delete(requestCleanup, matchingActiveTaskIds);
} else {
Optional<SingularityRequestHistory> maybeHistory = requestHistoryHelper.getLastHistory(requestId);
if (maybeHistory.isPresent() && maybeHistory.get().getRequest().isLoadBalanced() && configuration.isDeleteRemovedRequestsFromLoadBalancer()) {
createLbCleanupRequest(requestId, matchingActiveTaskIds);
requestManager.markDeleted(maybeHistory.get().getRequest(), RequestHistoryType.DELETED, System.currentTimeMillis(), Optional.<String>absent(), Optional.<String>absent());
}
cleanupDeployState(requestCleanup);
}
cleanupDeployState(requestCleanup);
}
break;
case BOUNCE:
Expand Down Expand Up @@ -432,7 +441,7 @@ private void bounce(SingularityRequestCleanup requestCleanup, final List<Singula
LOG.info("Added {} tasks for request {} to cleanup bounce queue in {}", matchingTaskIds.size(), requestCleanup.getRequestId(), JavaUtils.duration(start));
}

private TaskCleanupType pause(SingularityRequestCleanup requestCleanup, Iterable<SingularityTaskId> activeTaskIds, boolean killActiveTasks) {
private TaskCleanupType pause(SingularityRequestCleanup requestCleanup, Iterable<SingularityTaskId> activeTaskIds) {
final long start = System.currentTimeMillis();
boolean killTasks = requestCleanup.getKillTasks().or(configuration.isDefaultValueForKillTasksOfPausedRequests());
if (requestCleanup.getRunShellCommandBeforeKill().isPresent()) {
Expand All @@ -458,6 +467,24 @@ private TaskCleanupType pause(SingularityRequestCleanup requestCleanup, Iterable
return cleanupType;
}

private void delete(SingularityRequestCleanup requestCleanup, Iterable<SingularityTaskId> activeTaskIds){
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ssalinas I kept the name as delete since it followed the pattern set by the other actions (pause, bounce, etc.)

final long start = System.currentTimeMillis();

for (SingularityTaskId taskId : activeTaskIds) {
LOG.debug("Adding task {} to cleanup (delete)", taskId.getId());

Optional<SingularityTaskShellCommandRequestId> runBeforeKillId = Optional.absent();

if (requestCleanup.getRunShellCommandBeforeKill().isPresent()) {
SingularityTaskShellCommandRequest shellRequest = new SingularityTaskShellCommandRequest(taskId, requestCleanup.getUser(), System.currentTimeMillis(), requestCleanup.getRunShellCommandBeforeKill().get());
taskManager.saveTaskShellCommandRequestToQueue(shellRequest);
runBeforeKillId = Optional.of(shellRequest.getId());
}

taskManager.createTaskCleanup(new SingularityTaskCleanup(requestCleanup.getUser(), TaskCleanupType.USER_REQUESTED_DESTROY, start, taskId, requestCleanup.getMessage(), requestCleanup.getActionId(), runBeforeKillId));
Copy link
Member

@ssalinas ssalinas Jan 24, 2017

Choose a reason for hiding this comment

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

missed this one initially, my fault here. This has some unintended consequences. With this in place, any task that is forcibly killed by a user will then cause the request to be deleted as well. We need a new TaskCleanupType that is separate. Something like REQUEST_DELETING with killLongRunningTaskInstantly and killNonLongRunningTaskInstantly both set to true like they are for the destroy

de9a21d

}
}

private void cleanupDeployState(SingularityRequestCleanup requestCleanup) {
SingularityDeleteResult deletePendingDeployResult = deployManager.deletePendingDeploy(requestCleanup.getRequestId());
SingularityDeleteResult deleteRequestDeployStateResult = deployManager.deleteRequestDeployState(requestCleanup.getRequestId());
Expand Down Expand Up @@ -536,6 +563,7 @@ private void drainTaskCleanupQueue() {

final Multiset<SingularityDeployKey> incrementalCleaningTasks = HashMultiset.create(cleanupTasks.size());
final Set<SingularityDeployKey> isBouncing = new HashSet<>(cleanupTasks.size());
final Map<String, List<String>> deletedRequestIdToTaskIds = new HashMap<>();

final List<SingularityTaskId> cleaningTasks = Lists.newArrayListWithCapacity(cleanupTasks.size());
for (SingularityTaskCleanup cleanupTask : cleanupTasks) {
Expand All @@ -546,6 +574,9 @@ private void drainTaskCleanupQueue() {
if (cleanupTask.getCleanupType() == TaskCleanupType.BOUNCING || cleanupTask.getCleanupType() == TaskCleanupType.INCREMENTAL_BOUNCE) {
isBouncing.add(SingularityDeployKey.fromTaskId(cleanupTask.getTaskId()));
}
if (cleanupTask.getCleanupType() == TaskCleanupType.USER_REQUESTED_DESTROY) {
updateRequestToTaskMap(cleanupTask, deletedRequestIdToTaskIds);
}
}

LOG.info("Cleaning up {} tasks", cleanupTasks.size());
Expand All @@ -555,17 +586,19 @@ private void drainTaskCleanupQueue() {
int killedTasks = 0;

for (SingularityTaskCleanup cleanupTask : cleanupTasks) {
SingularityTaskId taskId = cleanupTask.getTaskId();

if (!isValidTask(cleanupTask)) {
LOG.info("Couldn't find a matching active task for cleanup task {}, deleting..", cleanupTask);
taskManager.deleteCleanupTask(cleanupTask.getTaskId().getId());
taskManager.deleteCleanupTask(taskId.getId());
} else if (shouldKillTask(cleanupTask, activeTaskIds, cleaningTasks, incrementalCleaningTasks) && checkLBStateAndShouldKillTask(cleanupTask)) {
driverManager.killAndRecord(cleanupTask.getTaskId(), cleanupTask.getCleanupType(), cleanupTask.getUser());

taskManager.deleteCleanupTask(cleanupTask.getTaskId().getId());
driverManager.killAndRecord(taskId, cleanupTask.getCleanupType(), cleanupTask.getUser());
cleanupRequestIfNoRemainingTasks(taskId, deletedRequestIdToTaskIds);
taskManager.deleteCleanupTask(taskId.getId());

killedTasks++;
} else if (cleanupTask.getCleanupType() == TaskCleanupType.BOUNCING || cleanupTask.getCleanupType() == TaskCleanupType.INCREMENTAL_BOUNCE) {
isBouncing.remove(SingularityDeployKey.fromTaskId(cleanupTask.getTaskId()));
isBouncing.remove(SingularityDeployKey.fromTaskId(taskId));
}
}

Expand All @@ -582,6 +615,33 @@ private void drainTaskCleanupQueue() {
LOG.info("Killed {} tasks in {}", killedTasks, JavaUtils.duration(start));
}

private void updateRequestToTaskMap(SingularityTaskCleanup cleanupTask, Map<String, List<String>> requestIdToTaskIds) {
SingularityTaskId taskId = cleanupTask.getTaskId();
List<String> requestTasks = requestIdToTaskIds.get(taskId.getRequestId());
if (requestTasks != null) {
requestTasks.add(taskId.getId());
} else {
requestTasks = new ArrayList<>(Collections.singletonList(taskId.getId()));
requestIdToTaskIds.put(taskId.getRequestId(), requestTasks);
}
}

private void cleanupRequestIfNoRemainingTasks(SingularityTaskId taskId, Map<String, List<String>> requestIdToTaskIds) {
String requestId = taskId.getRequestId();
if (requestIdToTaskIds.get(requestId) == null) {
return;
}
List<String> requestTasks = requestIdToTaskIds.get(requestId);
requestTasks.remove(taskId.getId());
Copy link
Member

Choose a reason for hiding this comment

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

requestIdToTaskIds isn't guaranteed to have the key present, will want to check that or this line will cause NPEs

Copy link
Contributor Author

@matush-v matush-v Jan 9, 2017

Choose a reason for hiding this comment

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

ah, I didn't notice we would hit this method for other cleanup types that weren't in the requestIdToTaskIds map. updated to return early if it's null
bitmoji

if (requestTasks.isEmpty()) {
requestManager.createCleanupRequest(
new SingularityRequestCleanup(
Optional.<String> absent(), RequestCleanupType.DELETING, System.currentTimeMillis(),
Optional.of(Boolean.TRUE), requestId, Optional.<String> absent(),
Optional.<Boolean> absent(), Optional.<String> absent(), Optional.<String> absent(), Optional.<SingularityShellCommand>absent()));
}
}

private boolean checkLBStateAndShouldKillTask(SingularityTaskCleanup cleanupTask) {
final long start = System.currentTimeMillis();

Expand Down
Expand Up @@ -48,7 +48,7 @@ public void testRequestAgePurging() {

Assert.assertTrue(!requestManager.getRequestHistory(requestId).isEmpty());

requestManager.deleteRequest(request, user, Optional.<String> absent(), Optional.<String> absent());
requestManager.startDeletingRequest(request, user, Optional.<String> absent(), Optional.<String> absent());

requestManager.deleteHistoryParent(requestId);

Expand Down Expand Up @@ -79,17 +79,17 @@ public void testRequestCountPurging() {
configuration.setMaxRequestsWithHistoryInZkWhenNoDatabase(Optional.of(2));
configuration.setDeleteStaleRequestsFromZkWhenNoDatabaseAfterHours(7);

requestManager.deleteRequest(requestOne, user, Optional.<String>absent(), Optional.<String>absent());
requestManager.startDeletingRequest(requestOne, user, Optional.<String>absent(), Optional.<String>absent());
requestManager.deleteHistoryParent(requestOne.getId());
requestManager.activate(requestOne, RequestHistoryType.CREATED, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(4), Optional.<String> absent(), Optional.<String> absent());
requestManager.cooldown(requestOne, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(3));

requestManager.deleteRequest(requestTwo, user, Optional.<String>absent(), Optional.<String>absent());
requestManager.startDeletingRequest(requestTwo, user, Optional.<String>absent(), Optional.<String>absent());
requestManager.deleteHistoryParent(requestTwo.getId());
requestManager.activate(requestTwo, RequestHistoryType.CREATED, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(4), Optional.<String> absent(), Optional.<String> absent());
requestManager.cooldown(requestTwo, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(3));

requestManager.deleteRequest(requestThree, user, Optional.<String>absent(), Optional.<String>absent());
requestManager.startDeletingRequest(requestThree, user, Optional.<String>absent(), Optional.<String>absent());
requestManager.deleteHistoryParent(requestThree.getId());
requestManager.activate(requestThree, RequestHistoryType.CREATED, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(4), Optional.<String> absent(), Optional.<String> absent());
requestManager.cooldown(requestThree, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(3));
Expand Down Expand Up @@ -198,7 +198,7 @@ public void testPurgingDoesntApplyIfDatabasePresent() {
initRequest();
initFirstDeploy();

requestManager.deleteRequest(request, user, Optional.<String> absent(), Optional.<String> absent());
requestManager.startDeletingRequest(request, user, Optional.<String> absent(), Optional.<String> absent());

requestManager.deleteHistoryParent(requestId);

Expand Down
Expand Up @@ -1336,7 +1336,7 @@ public void testRemovedRequestData() {
deployChecker.checkDeploys();
Assert.assertEquals(DeployState.WAITING, deployManager.getPendingDeploys().get(0).getCurrentDeployState());

requestManager.deleteRequest(request, Optional.<String>absent(), Optional.<String>absent(), Optional.<String>absent());
requestManager.startDeletingRequest(request, Optional.<String>absent(), Optional.<String>absent(), Optional.<String>absent());
deployChecker.checkDeploys();
SingularityDeployResult deployResult = deployManager.getDeployResult(requestId, firstDeployId).get();
Assert.assertEquals(DeployState.FAILED, deployResult.getDeployState());
Expand Down