From a379daf78ec1da17ca6c81ef958083723c83b033 Mon Sep 17 00:00:00 2001 From: Tom George Date: Thu, 2 Jul 2020 20:26:42 -0500 Subject: [PATCH 1/8] Add workspace maximum time che property Signed-off-by: Tom George --- .../WEB-INF/classes/che/multiuser.properties | 4 ++ .../MultiUserWorkspaceActivityManager.java | 7 +- ...MultiUserWorkspaceActivityManagerTest.java | 4 +- .../InmemoryWorkspaceActivityDao.java | 7 +- .../activity/JpaWorkspaceActivityDao.java | 3 +- .../workspace/activity/WorkspaceActivity.java | 11 ++- .../activity/WorkspaceActivityChecker.java | 4 +- .../activity/WorkspaceActivityDao.java | 3 +- .../activity/WorkspaceActivityManager.java | 19 +++++- .../WorkspaceActivityCheckerTest.java | 11 ++- .../WorkspaceActivityManagerTest.java | 21 +++++- .../spi/tck/WorkspaceActivityDaoTest.java | 68 +++++++++++++++++-- .../che/core/db/jpa/CascadeRemovalTest.java | 5 ++ 13 files changed, 146 insertions(+), 21 deletions(-) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties index a976e6e080..aa64d180f9 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties @@ -40,6 +40,10 @@ che.limits.workspace.env.ram=16gb # counts toward idleness. che.limits.workspace.idle.timeout=1800000 +# The length of time that a workspace will run, regardless of activity, before +# the system will suspend it. +che.limits.workspace.run.timeout=0 + ### Users workspace limits # The total amount of RAM that a single user is allowed to allocate to running diff --git a/multiuser/api/che-multiuser-api-workspace-activity/src/main/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManager.java b/multiuser/api/che-multiuser-api-workspace-activity/src/main/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManager.java index d98fc45b9f..d60d526e1b 100644 --- a/multiuser/api/che-multiuser-api-workspace-activity/src/main/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManager.java +++ b/multiuser/api/che-multiuser-api-workspace-activity/src/main/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManager.java @@ -46,6 +46,7 @@ public class MultiUserWorkspaceActivityManager extends WorkspaceActivityManager private final AccountManager accountManager; private final ResourceManager resourceManager; private final long defaultTimeout; + private final long runTimeout; @Inject public MultiUserWorkspaceActivityManager( @@ -54,11 +55,13 @@ public class MultiUserWorkspaceActivityManager extends WorkspaceActivityManager EventService eventService, AccountManager accountManager, ResourceManager resourceManager, - @Named("che.limits.workspace.idle.timeout") long defaultTimeout) { - super(workspaceManager, activityDao, eventService, defaultTimeout); + @Named("che.limits.workspace.idle.timeout") long defaultTimeout, + @Named("che.limits.workspace.run.timeout") long runTimeout) { + super(workspaceManager, activityDao, eventService, defaultTimeout, runTimeout); this.accountManager = accountManager; this.resourceManager = resourceManager; this.defaultTimeout = defaultTimeout; + this.runTimeout = runTimeout; } @Override diff --git a/multiuser/api/che-multiuser-api-workspace-activity/src/test/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManagerTest.java b/multiuser/api/che-multiuser-api-workspace-activity/src/test/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManagerTest.java index 511c0915e0..1b39b95586 100644 --- a/multiuser/api/che-multiuser-api-workspace-activity/src/test/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManagerTest.java +++ b/multiuser/api/che-multiuser-api-workspace-activity/src/test/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManagerTest.java @@ -35,6 +35,7 @@ import org.testng.annotations.Test; public class MultiUserWorkspaceActivityManagerTest { private static final long DEFAULT_TIMEOUT = 60_000L; // 1 minute private static final long USER_LIMIT_TIMEOUT = 120_000L; // 2 minutes + private static final long DEFAULT_HARD_EXPIRATION_TIMEOUT = 60000 * 60 * 3; // 3 hours @Mock private AccountManager accountManager; @Mock private ResourceManager resourceManager; @@ -58,7 +59,8 @@ public class MultiUserWorkspaceActivityManagerTest { eventService, accountManager, resourceManager, - DEFAULT_TIMEOUT); + DEFAULT_TIMEOUT, + DEFAULT_HARD_EXPIRATION_TIMEOUT); when(account.getId()).thenReturn("account123"); when(accountManager.getByName(anyString())).thenReturn(account); diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java index 22ff994daa..bf5fca19bd 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java @@ -43,11 +43,14 @@ public class InmemoryWorkspaceActivityDao implements WorkspaceActivityDao { } @Override - public List findExpired(long timestamp) { + public List findExpired(long timestamp, long runTimeout) { return workspaceActivities .values() .stream() - .filter(a -> a.getExpiration() != null && a.getExpiration() < timestamp) + .filter( + a -> + (a.getExpiration() != null && a.getExpiration() < timestamp) + || (runTimeout > 0 && a.getLastRunning() - a.getLastStarting() > runTimeout)) .map(WorkspaceActivity::getWorkspaceId) .collect(toList()); } diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java index 7d8b827327..d02c2f0b1a 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java @@ -52,12 +52,13 @@ public class JpaWorkspaceActivityDao implements WorkspaceActivityDao { @Override @Transactional(rollbackOn = ServerException.class) - public List findExpired(long timestamp) throws ServerException { + public List findExpired(long timestamp, long runTimeout) throws ServerException { try { return managerProvider .get() .createNamedQuery("WorkspaceActivity.getExpired", WorkspaceActivity.class) .setParameter("expiration", timestamp) + .setParameter("runTimeout", runTimeout) .getResultList() .stream() .map(WorkspaceActivity::getWorkspaceId) diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java index d83b3101f1..a9240bb94e 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java @@ -27,7 +27,11 @@ import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; @NamedQueries({ @NamedQuery( name = "WorkspaceActivity.getExpired", - query = "SELECT a FROM WorkspaceActivity a WHERE a.expiration < :expiration"), + query = + "SELECT a FROM WorkspaceActivity a WHERE a.expiration < :expiration OR " + + "(a.status = org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING AND " + + ":runTimeout > 0 AND " + + ":expiration - a.lastRunning > :runTimeout)"), @NamedQuery( name = "WorkspaceActivity.getStoppedSince", query = @@ -165,6 +169,10 @@ public class WorkspaceActivity { this.expiration = expiration; } + public Long getRunTimeout() { + return this.lastRunning - this.lastStarting; + } + public WorkspaceStatus getStatus() { return status; } @@ -181,6 +189,7 @@ public class WorkspaceActivity { if (o == null || getClass() != o.getClass()) { return false; } + WorkspaceActivity activity = (WorkspaceActivity) o; return Objects.equals(workspaceId, activity.workspaceId) && Objects.equals(created, activity.created) diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java index 80923e2d67..0df9604921 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java @@ -101,7 +101,9 @@ public class WorkspaceActivityChecker { private void stopAllExpired() { try { - activityDao.findExpired(clock.millis()).forEach(this::stopExpiredQuietly); + activityDao + .findExpired(clock.millis(), workspaceActivityManager.getRunTimeout()) + .forEach(this::stopExpiredQuietly); } catch (ServerException e) { LOG.error("Failed to list all expired to perform stop. Cause: {}", e.getMessage(), e); } diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityDao.java index d7cda269a1..0c1d3e297b 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityDao.java @@ -46,10 +46,11 @@ public interface WorkspaceActivityDao { * Finds workspaces which are passed given expiration time and must be stopped. * * @param timestamp expiration time + * @param runTimeout time after which the workspace will be stopped regardless of activity * @return list of workspaces which expiration time is older than given timestamp * @throws ServerException when operation failed */ - List findExpired(long timestamp) throws ServerException; + List findExpired(long timestamp, long runTimeout) throws ServerException; /** * Removes the activity record of the provided workspace. diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManager.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManager.java index 7a12b328ad..aa48140d6f 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManager.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManager.java @@ -54,6 +54,7 @@ public class WorkspaceActivityManager { private static final Logger LOG = LoggerFactory.getLogger(WorkspaceActivityManager.class); private final long defaultTimeout; + private final long runTimeout; private final WorkspaceActivityDao activityDao; private final EventService eventService; private final EventSubscriber updateStatusChangedTimestampSubscriber; @@ -69,9 +70,16 @@ public class WorkspaceActivityManager { WorkspaceManager workspaceManager, WorkspaceActivityDao activityDao, EventService eventService, - @Named("che.limits.workspace.idle.timeout") long timeout) { + @Named("che.limits.workspace.idle.timeout") long timeout, + @Named("che.limits.workspace.run.timeout") long runTimeout) { - this(workspaceManager, activityDao, eventService, timeout, Clock.systemDefaultZone()); + this( + workspaceManager, + activityDao, + eventService, + timeout, + runTimeout, + Clock.systemDefaultZone()); } @VisibleForTesting @@ -80,11 +88,13 @@ public class WorkspaceActivityManager { WorkspaceActivityDao activityDao, EventService eventService, long timeout, + long runTimeout, Clock clock) { this.workspaceManager = workspaceManager; this.eventService = eventService; this.activityDao = activityDao; this.defaultTimeout = timeout; + this.runTimeout = runTimeout; this.clock = clock; if (timeout > 0 && timeout < MINIMAL_TIMEOUT) { LOG.warn( @@ -116,7 +126,6 @@ public class WorkspaceActivityManager { activityDao.removeActivity(event.getWorkspace().getId()); } }; - this.updateStatusChangedTimestampSubscriber = new UpdateStatusChangedTimestampSubscriber(); } @@ -170,6 +179,10 @@ public class WorkspaceActivityManager { return defaultTimeout; } + protected long getRunTimeout() { + return runTimeout; + } + private class UpdateStatusChangedTimestampSubscriber implements EventSubscriber { @Override diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java index fce62a3fd2..a5d51a1fe1 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java @@ -55,6 +55,8 @@ import org.testng.annotations.Test; @Listeners(value = MockitoTestNGListener.class) public class WorkspaceActivityCheckerTest { private static final long DEFAULT_TIMEOUT = 60_000L; // 1 minute + private static final long DEFAULT_RUN_TIMEOUT = 0; // No default run timeout + private static final long ACTIVE_RUN_TIMEOUT = 60000 * 60 * 3; // 3 hours private ManualClock clock; private WorkspaceActivityChecker checker; @@ -69,7 +71,12 @@ public class WorkspaceActivityCheckerTest { WorkspaceActivityManager activityManager = new WorkspaceActivityManager( - workspaceManager, workspaceActivityDao, eventService, DEFAULT_TIMEOUT, clock); + workspaceManager, + workspaceActivityDao, + eventService, + DEFAULT_TIMEOUT, + DEFAULT_RUN_TIMEOUT, + clock); lenient() .when(workspaceActivityDao.getAll(anyInt(), anyLong())) @@ -88,7 +95,7 @@ public class WorkspaceActivityCheckerTest { @Test public void shouldStopAllExpiredWorkspaces() throws Exception { - when(workspaceActivityDao.findExpired(anyLong())).thenReturn(asList("1", "2", "3")); + when(workspaceActivityDao.findExpired(anyLong(), anyLong())).thenReturn(asList("1", "2", "3")); checker.expire(); diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManagerTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManagerTest.java index 21298b42e5..f9b8461177 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManagerTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManagerTest.java @@ -49,6 +49,8 @@ import org.testng.annotations.Test; public class WorkspaceActivityManagerTest { private static final long DEFAULT_TIMEOUT = 60_000L; // 1 minute + private static final long DEFAULT_RUN_TIMEOUT = 0; // No run timeout + private static final long ACTIVE_RUN_TIMEOUT = 60000 * 60 * 3; // 3 hours @Mock private WorkspaceManager workspaceManager; @@ -68,7 +70,11 @@ public class WorkspaceActivityManagerTest { private void setUp() throws Exception { activityManager = new WorkspaceActivityManager( - workspaceManager, workspaceActivityDao, eventService, DEFAULT_TIMEOUT); + workspaceManager, + workspaceActivityDao, + eventService, + DEFAULT_TIMEOUT, + DEFAULT_RUN_TIMEOUT); lenient().when(account.getName()).thenReturn("accountName"); lenient().when(account.getId()).thenReturn("account123"); @@ -102,6 +108,19 @@ public class WorkspaceActivityManagerTest { assertEquals(wsIdCaptor.getValue(), wsId); } + @Test + public void shouldRemoveRunTimeoutWhenWorkspaceStopped() throws Exception { + final String wsId = "testWsId"; + final EventSubscriber subscriber = subscribeAndGetStatusEventSubscriber(); + + subscriber.onEvent( + DtoFactory.newDto(WorkspaceStatusEvent.class) + .withStatus(WorkspaceStatus.STOPPED) + .withWorkspaceId(wsId)); + ArgumentCaptor wsIdCaptor = ArgumentCaptor.forClass(String.class); + verify(workspaceActivityDao, times(1)).removeExpiration(wsIdCaptor.capture()); + } + @Test public void shouldCeaseToTrackTheWorkspaceActivityAfterStopping() throws Exception { final String wsId = "testWsId"; diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java index ac2b6cfad1..4b7b47c7f9 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java @@ -12,8 +12,7 @@ package org.eclipse.che.api.workspace.activity.spi.tck; import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; +import static java.util.Collections.*; import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STARTING; import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STOPPED; import static org.testng.Assert.assertEquals; @@ -65,6 +64,8 @@ public class WorkspaceActivityDaoTest { private static final int COUNT = 3; + private static final long DEFAULT_RUN_TIMEOUT = 0L; + @Inject private WorkspaceActivityDao workspaceActivityDao; private AccountImpl[] accounts = new AccountImpl[COUNT]; @@ -95,6 +96,7 @@ public class WorkspaceActivityDaoTest { a.setExpiration(base * 1_000_000); activities[i] = a; + System.out.println("activity is " + a); } accountTckRepository.createAll(asList(accounts)); @@ -112,7 +114,7 @@ public class WorkspaceActivityDaoTest { @Test public void shouldFindExpirationsByTimestamp() throws Exception { List expected = asList(activities[0].getWorkspaceId(), activities[1].getWorkspaceId()); - List found = workspaceActivityDao.findExpired(2_500_000); + List found = workspaceActivityDao.findExpired(2_500_000, DEFAULT_RUN_TIMEOUT); assertEquals(found, expected); } @@ -123,7 +125,22 @@ public class WorkspaceActivityDaoTest { workspaceActivityDao.removeExpiration(activities[0].getWorkspaceId()); - List found = workspaceActivityDao.findExpired(2_500_000); + List found = workspaceActivityDao.findExpired(2_500_000, DEFAULT_RUN_TIMEOUT); + assertEquals(found, expected); + } + + @Test(dependsOnMethods = "shouldFindExpirationsByTimestamp") + public void shouldExpireWorkspaceThatExceedsRunTimeout() throws Exception { + List expected = singletonList(activities[0].getWorkspaceId()); + + // Need more accurate activities for this test + workspaceActivityDao.removeActivity("ws0"); + workspaceActivityDao.removeActivity("ws1"); + workspaceActivityDao.removeActivity("ws2"); + + activityTckRepository.createAll(createWorkspaceActivitiesWithStatuses()); + + List found = workspaceActivityDao.findExpired(8_000_000, 1_000_000); assertEquals(found, expected); } @@ -137,7 +154,7 @@ public class WorkspaceActivityDaoTest { workspaceActivityDao.setExpirationTime(activities[2].getWorkspaceId(), 1_750_000); - List found = workspaceActivityDao.findExpired(2_500_000); + List found = workspaceActivityDao.findExpired(2_500_000, DEFAULT_RUN_TIMEOUT); assertEquals(found, expected); } @@ -160,7 +177,7 @@ public class WorkspaceActivityDaoTest { // create new again workspaceActivityDao.setExpirationTime(activities[1].getWorkspaceId(), 1_250_000); - List found = workspaceActivityDao.findExpired(1_500_000); + List found = workspaceActivityDao.findExpired(1_500_000, DEFAULT_RUN_TIMEOUT); assertEquals(found, expected); } @@ -286,6 +303,45 @@ public class WorkspaceActivityDaoTest { .toArray(Object[][]::new); } + /** + * Helper function that creates workspaces that are in the RUNNING and STOPPED state for + * shouldExpireWorkspaceThatExceedsRunTimeout + * + * @return A list of WorkspaceActivity objects + */ + private List createWorkspaceActivitiesWithStatuses() { + WorkspaceActivity[] a = new WorkspaceActivity[3]; + a[0] = new WorkspaceActivity(); + a[0].setWorkspaceId("ws0"); + a[0].setStatus(WorkspaceStatus.RUNNING); + a[0].setCreated(1_000_000); + a[0].setLastStarting(1_000_000); + a[0].setLastRunning(1_000_100); + a[0].setLastStopped(0); + a[0].setLastStopping(0); + a[0].setExpiration(1_100_000L); + + a[1] = new WorkspaceActivity(); + a[1].setWorkspaceId("ws1"); + a[1].setStatus(WorkspaceStatus.RUNNING); + a[1].setCreated(7_000_000); + a[1].setLastStarting(7_000_000); + a[1].setLastRunning(7_100_000); + a[1].setLastStopped(0); + a[1].setLastStopping(0); + a[1].setExpiration(8_000_000L); + + a[2] = new WorkspaceActivity(); + a[2].setWorkspaceId("ws2"); + a[2].setStatus(WorkspaceStatus.STOPPED); + a[2].setCreated(1_000_200); + a[2].setLastStarting(1_000_200); + a[2].setLastRunning(1_000_300); + a[2].setLastStopped(1_000_400); + a[2].setLastStopping(1_000_350); + return asList(a); + } + private static WorkspaceConfigImpl createWorkspaceConfig(String name) { // Project Sources configuration final SourceStorageImpl source1 = new SourceStorageImpl(); diff --git a/wsmaster/integration-tests/cascade-removal/src/test/java/org/eclipse/che/core/db/jpa/CascadeRemovalTest.java b/wsmaster/integration-tests/cascade-removal/src/test/java/org/eclipse/che/core/db/jpa/CascadeRemovalTest.java index bb9faa46ee..a542b617fc 100644 --- a/wsmaster/integration-tests/cascade-removal/src/test/java/org/eclipse/che/core/db/jpa/CascadeRemovalTest.java +++ b/wsmaster/integration-tests/cascade-removal/src/test/java/org/eclipse/che/core/db/jpa/CascadeRemovalTest.java @@ -239,6 +239,11 @@ public class CascadeRemovalTest { bind(Long.class) .annotatedWith(Names.named("che.limits.workspace.idle.timeout")) .toInstance(100000L); + + bind(Long.class) + .annotatedWith(Names.named("che.limits.workspace.run.timeout")) + .toInstance(0L); + bind(UserManager.class); bind(AccountManager.class); From 97faa8d1b46b5ee9b8fc976b8ab027e938a70bdf Mon Sep 17 00:00:00 2001 From: Tom George Date: Tue, 7 Jul 2020 18:05:54 -0500 Subject: [PATCH 2/8] Fix name Signed-off-by: Tom George --- .../MultiUserWorkspaceActivityManagerTest.java | 4 ++-- .../activity/InmemoryWorkspaceActivityDao.java | 2 +- .../api/workspace/activity/WorkspaceActivity.java | 5 ----- .../activity/WorkspaceActivityCheckerTest.java | 1 - .../activity/WorkspaceActivityManagerTest.java | 14 -------------- .../activity/spi/tck/WorkspaceActivityDaoTest.java | 1 - 6 files changed, 3 insertions(+), 24 deletions(-) diff --git a/multiuser/api/che-multiuser-api-workspace-activity/src/test/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManagerTest.java b/multiuser/api/che-multiuser-api-workspace-activity/src/test/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManagerTest.java index 1b39b95586..af1e08006d 100644 --- a/multiuser/api/che-multiuser-api-workspace-activity/src/test/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManagerTest.java +++ b/multiuser/api/che-multiuser-api-workspace-activity/src/test/java/org/eclipse/che/multiuser/api/workspace/activity/MultiUserWorkspaceActivityManagerTest.java @@ -35,7 +35,7 @@ import org.testng.annotations.Test; public class MultiUserWorkspaceActivityManagerTest { private static final long DEFAULT_TIMEOUT = 60_000L; // 1 minute private static final long USER_LIMIT_TIMEOUT = 120_000L; // 2 minutes - private static final long DEFAULT_HARD_EXPIRATION_TIMEOUT = 60000 * 60 * 3; // 3 hours + private static final long DEFAULT_RUN_TIMEOUT = 0; // No default run timeout @Mock private AccountManager accountManager; @Mock private ResourceManager resourceManager; @@ -60,7 +60,7 @@ public class MultiUserWorkspaceActivityManagerTest { accountManager, resourceManager, DEFAULT_TIMEOUT, - DEFAULT_HARD_EXPIRATION_TIMEOUT); + DEFAULT_RUN_TIMEOUT); when(account.getId()).thenReturn("account123"); when(accountManager.getByName(anyString())).thenReturn(account); diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java index bf5fca19bd..af8e6c8fa2 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java @@ -50,7 +50,7 @@ public class InmemoryWorkspaceActivityDao implements WorkspaceActivityDao { .filter( a -> (a.getExpiration() != null && a.getExpiration() < timestamp) - || (runTimeout > 0 && a.getLastRunning() - a.getLastStarting() > runTimeout)) + || (runTimeout > 0 && timestamp - a.getLastRunning() > runTimeout)) .map(WorkspaceActivity::getWorkspaceId) .collect(toList()); } diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java index a9240bb94e..278b69566f 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java @@ -169,10 +169,6 @@ public class WorkspaceActivity { this.expiration = expiration; } - public Long getRunTimeout() { - return this.lastRunning - this.lastStarting; - } - public WorkspaceStatus getStatus() { return status; } @@ -189,7 +185,6 @@ public class WorkspaceActivity { if (o == null || getClass() != o.getClass()) { return false; } - WorkspaceActivity activity = (WorkspaceActivity) o; return Objects.equals(workspaceId, activity.workspaceId) && Objects.equals(created, activity.created) diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java index a5d51a1fe1..5163df2d44 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java @@ -56,7 +56,6 @@ import org.testng.annotations.Test; public class WorkspaceActivityCheckerTest { private static final long DEFAULT_TIMEOUT = 60_000L; // 1 minute private static final long DEFAULT_RUN_TIMEOUT = 0; // No default run timeout - private static final long ACTIVE_RUN_TIMEOUT = 60000 * 60 * 3; // 3 hours private ManualClock clock; private WorkspaceActivityChecker checker; diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManagerTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManagerTest.java index f9b8461177..6a1055be63 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManagerTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityManagerTest.java @@ -50,7 +50,6 @@ public class WorkspaceActivityManagerTest { private static final long DEFAULT_TIMEOUT = 60_000L; // 1 minute private static final long DEFAULT_RUN_TIMEOUT = 0; // No run timeout - private static final long ACTIVE_RUN_TIMEOUT = 60000 * 60 * 3; // 3 hours @Mock private WorkspaceManager workspaceManager; @@ -108,19 +107,6 @@ public class WorkspaceActivityManagerTest { assertEquals(wsIdCaptor.getValue(), wsId); } - @Test - public void shouldRemoveRunTimeoutWhenWorkspaceStopped() throws Exception { - final String wsId = "testWsId"; - final EventSubscriber subscriber = subscribeAndGetStatusEventSubscriber(); - - subscriber.onEvent( - DtoFactory.newDto(WorkspaceStatusEvent.class) - .withStatus(WorkspaceStatus.STOPPED) - .withWorkspaceId(wsId)); - ArgumentCaptor wsIdCaptor = ArgumentCaptor.forClass(String.class); - verify(workspaceActivityDao, times(1)).removeExpiration(wsIdCaptor.capture()); - } - @Test public void shouldCeaseToTrackTheWorkspaceActivityAfterStopping() throws Exception { final String wsId = "testWsId"; diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java index 4b7b47c7f9..d282d5ddaa 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java @@ -96,7 +96,6 @@ public class WorkspaceActivityDaoTest { a.setExpiration(base * 1_000_000); activities[i] = a; - System.out.println("activity is " + a); } accountTckRepository.createAll(asList(accounts)); From 1384122ed54e4e2f810a29439b0718f1168ba7ed Mon Sep 17 00:00:00 2001 From: Tom George Date: Wed, 8 Jul 2020 09:25:55 -0500 Subject: [PATCH 3/8] Update run timeout documentation information Signed-off-by: Tom George --- .../src/main/webapp/WEB-INF/classes/che/multiuser.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties index aa64d180f9..edfb68d85c 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties @@ -41,7 +41,9 @@ che.limits.workspace.env.ram=16gb che.limits.workspace.idle.timeout=1800000 # The length of time that a workspace will run, regardless of activity, before -# the system will suspend it. +# the system will suspend it. Set this property if you want to automatically stop +# workspaces after a period of time. The default is zero, meaning that there is no +# run timeout. che.limits.workspace.run.timeout=0 ### Users workspace limits From b507dc2478dc810dec39c5a74ebfa69ee8122f50 Mon Sep 17 00:00:00 2001 From: Tom George Date: Wed, 8 Jul 2020 09:26:38 -0500 Subject: [PATCH 4/8] Clarify time unit of run timeout Signed-off-by: Tom George --- .../src/main/webapp/WEB-INF/classes/che/multiuser.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties index edfb68d85c..3a848b65f2 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties @@ -40,7 +40,7 @@ che.limits.workspace.env.ram=16gb # counts toward idleness. che.limits.workspace.idle.timeout=1800000 -# The length of time that a workspace will run, regardless of activity, before +# The length of time in milliseconds that a workspace will run, regardless of activity, before # the system will suspend it. Set this property if you want to automatically stop # workspaces after a period of time. The default is zero, meaning that there is no # run timeout. From 13c83831b941734d86fde862c0616087578776ed Mon Sep 17 00:00:00 2001 From: Tom George Date: Thu, 9 Jul 2020 09:31:46 -0500 Subject: [PATCH 5/8] Add javadoc and clarify expired query Signed-off-by: Tom George --- .../activity/InmemoryWorkspaceActivityDao.java | 14 +++++++++++++- .../activity/JpaWorkspaceActivityDao.java | 10 ++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java index af8e6c8fa2..79955c2418 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java @@ -42,6 +42,16 @@ public class InmemoryWorkspaceActivityDao implements WorkspaceActivityDao { findActivity(workspaceId).setExpiration(null); } + /** + * Finds any workspaces that have expired. + * + *

A workspace is expired when the expiration value is less than the current time or when the + * difference between the current time and the last running time is greater than the run timeout + * + * @param timestamp expiration time + * @param runTimeout time after which the workspace will be stopped regardless of activity + * @return + */ @Override public List findExpired(long timestamp, long runTimeout) { return workspaceActivities @@ -50,7 +60,9 @@ public class InmemoryWorkspaceActivityDao implements WorkspaceActivityDao { .filter( a -> (a.getExpiration() != null && a.getExpiration() < timestamp) - || (runTimeout > 0 && timestamp - a.getLastRunning() > runTimeout)) + || (runTimeout > 0 + && a.getStatus() == WorkspaceStatus.RUNNING + && timestamp - a.getLastRunning() > runTimeout)) .map(WorkspaceActivity::getWorkspaceId) .collect(toList()); } diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java index d02c2f0b1a..42fb4a4eac 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java @@ -50,6 +50,16 @@ public class JpaWorkspaceActivityDao implements WorkspaceActivityDao { doUpdateOptionally(workspaceId, a -> a.setExpiration(null)); } + /** + * Finds any workspaces that have expired. + * + *

A workspace is expired when the expiration value is less than the current time or when the + * difference between the current time and the last running time is greater than the run timeout + * + * @param timestamp expiration time + * @param runTimeout time after which the workspace will be stopped regardless of activity + * @return + */ @Override @Transactional(rollbackOn = ServerException.class) public List findExpired(long timestamp, long runTimeout) throws ServerException { From 9ead3afc024302baccd87f806565acdf706abdb9 Mon Sep 17 00:00:00 2001 From: Tom George Date: Fri, 10 Jul 2020 14:11:08 -0500 Subject: [PATCH 6/8] Split into two named queries, and find expired run timeout workspaces when runTimeout > 0 Signed-off-by: Tom George --- .../InmemoryWorkspaceActivityDao.java | 12 +++++++++- .../activity/JpaWorkspaceActivityDao.java | 24 ++++++++++++++++--- .../workspace/activity/WorkspaceActivity.java | 9 ++++--- .../activity/WorkspaceActivityChecker.java | 17 +++++++++---- .../activity/WorkspaceActivityDao.java | 14 ++++++++--- .../WorkspaceActivityCheckerTest.java | 2 +- .../spi/tck/WorkspaceActivityDaoTest.java | 10 ++++---- 7 files changed, 67 insertions(+), 21 deletions(-) diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java index 79955c2418..d0227dbe91 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/InmemoryWorkspaceActivityDao.java @@ -53,7 +53,7 @@ public class InmemoryWorkspaceActivityDao implements WorkspaceActivityDao { * @return */ @Override - public List findExpired(long timestamp, long runTimeout) { + public List findExpiredRunTimeout(long timestamp, long runTimeout) { return workspaceActivities .values() .stream() @@ -67,6 +67,16 @@ public class InmemoryWorkspaceActivityDao implements WorkspaceActivityDao { .collect(toList()); } + @Override + public List findExpiredIdle(long timestamp) { + return workspaceActivities + .values() + .stream() + .filter(a -> a.getExpiration() != null && a.getExpiration() < timestamp) + .map(WorkspaceActivity::getWorkspaceId) + .collect(toList()); + } + @Override public void setCreatedTime(String workspaceId, long createdTimestamp) { WorkspaceActivity activity = findActivity(workspaceId); diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java index 42fb4a4eac..7afe2989eb 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java @@ -62,12 +62,13 @@ public class JpaWorkspaceActivityDao implements WorkspaceActivityDao { */ @Override @Transactional(rollbackOn = ServerException.class) - public List findExpired(long timestamp, long runTimeout) throws ServerException { + public List findExpiredRunTimeout(long timestamp, long runTimeout) + throws ServerException { try { return managerProvider .get() - .createNamedQuery("WorkspaceActivity.getExpired", WorkspaceActivity.class) - .setParameter("expiration", timestamp) + .createNamedQuery("WorkspaceActivity.getExpiredRunTimeout", WorkspaceActivity.class) + .setParameter("timestamp", timestamp) .setParameter("runTimeout", runTimeout) .getResultList() .stream() @@ -78,6 +79,23 @@ public class JpaWorkspaceActivityDao implements WorkspaceActivityDao { } } + @Override + @Transactional(rollbackOn = ServerException.class) + public List findExpiredIdle(long timestamp) throws ServerException { + try { + return managerProvider + .get() + .createNamedQuery("WorkspaceActivity.getExpiredIdle", WorkspaceActivity.class) + .setParameter("expiration", timestamp) + .getResultList() + .stream() + .map(WorkspaceActivity::getWorkspaceId) + .collect(Collectors.toList()); + } catch (RuntimeException x) { + throw new ServerException(x.getLocalizedMessage(), x); + } + } + @Override public void removeActivity(String workspaceId) throws ServerException { try { diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java index 278b69566f..59033dc0e8 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java @@ -26,12 +26,15 @@ import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; @Table(name = "che_workspace_activity") @NamedQueries({ @NamedQuery( - name = "WorkspaceActivity.getExpired", + name = "WorkspaceActivity.getExpiredIdle", + query = "SELECT a FROM WorkspaceActivity a WHERE a.expiration < :expiration"), + @NamedQuery( + name = "WorkspaceActivity.getExpiredRunTimeout", query = - "SELECT a FROM WorkspaceActivity a WHERE a.expiration < :expiration OR " + "SELECT a FROM WorkspaceActivity a WHERE " + "(a.status = org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING AND " + ":runTimeout > 0 AND " - + ":expiration - a.lastRunning > :runTimeout)"), + + ":timestamp - :runTimeout > a.lastRunning)"), @NamedQuery( name = "WorkspaceActivity.getStoppedSince", query = diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java index 0df9604921..9b34998609 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java @@ -46,6 +46,9 @@ public class WorkspaceActivityChecker { private static final Logger LOG = LoggerFactory.getLogger(WorkspaceActivityChecker.class); private static final String ACTIVITY_CHECKER = "activity-checker"; + private final String WORKSPACE_IDLE_TIMEOUT_EXCEEDED = "Workspace idle timeout exceeded"; + private final String WORKSPACE_RUN_TIMEOUT_EXCEEDED = "Workspace run timeout exceeded"; + private final WorkspaceActivityDao activityDao; private final WorkspaceManager workspaceManager; private final WorkspaceRuntimes workspaceRuntimes; @@ -102,20 +105,24 @@ public class WorkspaceActivityChecker { private void stopAllExpired() { try { activityDao - .findExpired(clock.millis(), workspaceActivityManager.getRunTimeout()) - .forEach(this::stopExpiredQuietly); + .findExpiredIdle(clock.millis()) + .forEach(wsId -> stopExpiredQuietly(wsId, WORKSPACE_IDLE_TIMEOUT_EXCEEDED)); + if (workspaceActivityManager.getRunTimeout() > 0) { + activityDao + .findExpiredRunTimeout(clock.millis(), workspaceActivityManager.getRunTimeout()) + .forEach(wsId -> stopExpiredQuietly(wsId, WORKSPACE_RUN_TIMEOUT_EXCEEDED)); + } } catch (ServerException e) { LOG.error("Failed to list all expired to perform stop. Cause: {}", e.getMessage(), e); } } - private void stopExpiredQuietly(String workspaceId) { + private void stopExpiredQuietly(String workspaceId, String reason) { try { Workspace workspace = workspaceManager.getWorkspace(workspaceId); workspace.getAttributes().put(WORKSPACE_STOPPED_BY, ACTIVITY_CHECKER); workspaceManager.updateWorkspace(workspaceId, workspace); - workspaceManager.stopWorkspace( - workspaceId, singletonMap(WORKSPACE_STOP_REASON, "Workspace idle timeout exceeded")); + workspaceManager.stopWorkspace(workspaceId, singletonMap(WORKSPACE_STOP_REASON, reason)); } catch (NotFoundException ignored) { // workspace no longer exists, no need to do anything } catch (ConflictException e) { diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityDao.java index 0c1d3e297b..373e04d13c 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityDao.java @@ -43,14 +43,22 @@ public interface WorkspaceActivityDao { void removeExpiration(String workspaceId) throws ServerException; /** - * Finds workspaces which are passed given expiration time and must be stopped. + * Finds workspaces which are passed given run timeout and must be stopped. * - * @param timestamp expiration time * @param runTimeout time after which the workspace will be stopped regardless of activity * @return list of workspaces which expiration time is older than given timestamp * @throws ServerException when operation failed */ - List findExpired(long timestamp, long runTimeout) throws ServerException; + List findExpiredRunTimeout(long timestamp, long runTimeout) throws ServerException; + + /** + * Finds workspaces which are passed given expiration time and must be stopped. + * + * @param timestamp expiration time + * @return list of workspaces which expiration time is older than given timestamp + * @throws ServerException when operation failed + */ + List findExpiredIdle(long timestamp) throws ServerException; /** * Removes the activity record of the provided workspace. diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java index 5163df2d44..dab40b457e 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityCheckerTest.java @@ -94,7 +94,7 @@ public class WorkspaceActivityCheckerTest { @Test public void shouldStopAllExpiredWorkspaces() throws Exception { - when(workspaceActivityDao.findExpired(anyLong(), anyLong())).thenReturn(asList("1", "2", "3")); + when(workspaceActivityDao.findExpiredIdle(anyLong())).thenReturn(asList("1", "2", "3")); checker.expire(); diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java index d282d5ddaa..2e82217928 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java @@ -113,7 +113,7 @@ public class WorkspaceActivityDaoTest { @Test public void shouldFindExpirationsByTimestamp() throws Exception { List expected = asList(activities[0].getWorkspaceId(), activities[1].getWorkspaceId()); - List found = workspaceActivityDao.findExpired(2_500_000, DEFAULT_RUN_TIMEOUT); + List found = workspaceActivityDao.findExpiredIdle(2_500_000); assertEquals(found, expected); } @@ -124,7 +124,7 @@ public class WorkspaceActivityDaoTest { workspaceActivityDao.removeExpiration(activities[0].getWorkspaceId()); - List found = workspaceActivityDao.findExpired(2_500_000, DEFAULT_RUN_TIMEOUT); + List found = workspaceActivityDao.findExpiredIdle(2_500_000); assertEquals(found, expected); } @@ -139,7 +139,7 @@ public class WorkspaceActivityDaoTest { activityTckRepository.createAll(createWorkspaceActivitiesWithStatuses()); - List found = workspaceActivityDao.findExpired(8_000_000, 1_000_000); + List found = workspaceActivityDao.findExpiredIdle(8_000_000); assertEquals(found, expected); } @@ -153,7 +153,7 @@ public class WorkspaceActivityDaoTest { workspaceActivityDao.setExpirationTime(activities[2].getWorkspaceId(), 1_750_000); - List found = workspaceActivityDao.findExpired(2_500_000, DEFAULT_RUN_TIMEOUT); + List found = workspaceActivityDao.findExpiredIdle(2_500_000); assertEquals(found, expected); } @@ -176,7 +176,7 @@ public class WorkspaceActivityDaoTest { // create new again workspaceActivityDao.setExpirationTime(activities[1].getWorkspaceId(), 1_250_000); - List found = workspaceActivityDao.findExpired(1_500_000, DEFAULT_RUN_TIMEOUT); + List found = workspaceActivityDao.findExpiredIdle(1_500_000); assertEquals(found, expected); } From e3eb0d9b8759f49289f379c36b9a30e65f4eb50a Mon Sep 17 00:00:00 2001 From: Tom George Date: Mon, 13 Jul 2020 09:32:46 -0500 Subject: [PATCH 7/8] Fix query, add log message for workspace run timeout exceeded Signed-off-by: Tom George --- .../che/api/workspace/activity/WorkspaceActivity.java | 3 +-- .../api/workspace/activity/WorkspaceActivityChecker.java | 6 +++++- .../activity/spi/tck/WorkspaceActivityDaoTest.java | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java index 59033dc0e8..48f64e420e 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java @@ -33,8 +33,7 @@ import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; query = "SELECT a FROM WorkspaceActivity a WHERE " + "(a.status = org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING AND " - + ":runTimeout > 0 AND " - + ":timestamp - :runTimeout > a.lastRunning)"), + + "a.lastRunning < :timestamp - :runTimeout)"), @NamedQuery( name = "WorkspaceActivity.getStoppedSince", query = diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java index 9b34998609..08d1cdd636 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivityChecker.java @@ -110,7 +110,11 @@ public class WorkspaceActivityChecker { if (workspaceActivityManager.getRunTimeout() > 0) { activityDao .findExpiredRunTimeout(clock.millis(), workspaceActivityManager.getRunTimeout()) - .forEach(wsId -> stopExpiredQuietly(wsId, WORKSPACE_RUN_TIMEOUT_EXCEEDED)); + .forEach( + wsId -> { + LOG.info("{} for workspace {}", WORKSPACE_RUN_TIMEOUT_EXCEEDED, wsId); + stopExpiredQuietly(wsId, WORKSPACE_RUN_TIMEOUT_EXCEEDED); + }); } } catch (ServerException e) { LOG.error("Failed to list all expired to perform stop. Cause: {}", e.getMessage(), e); diff --git a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java index 2e82217928..82cb178dea 100644 --- a/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java +++ b/wsmaster/che-core-api-workspace-activity/src/test/java/org/eclipse/che/api/workspace/activity/spi/tck/WorkspaceActivityDaoTest.java @@ -12,7 +12,8 @@ package org.eclipse.che.api.workspace.activity.spi.tck; import static java.util.Arrays.asList; -import static java.util.Collections.*; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STARTING; import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.STOPPED; import static org.testng.Assert.assertEquals; From c32cb855b5b769e2796c881f4d6430d64b4f29fd Mon Sep 17 00:00:00 2001 From: Tom George Date: Tue, 14 Jul 2020 08:32:56 -0500 Subject: [PATCH 8/8] getExpired queries return list of String instead of WorkspaceActivity Signed-off-by: Tom George --- .../api/workspace/activity/JpaWorkspaceActivityDao.java | 9 +++------ .../che/api/workspace/activity/WorkspaceActivity.java | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java index 7afe2989eb..68dd3b7061 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/JpaWorkspaceActivityDao.java @@ -67,12 +67,10 @@ public class JpaWorkspaceActivityDao implements WorkspaceActivityDao { try { return managerProvider .get() - .createNamedQuery("WorkspaceActivity.getExpiredRunTimeout", WorkspaceActivity.class) - .setParameter("timestamp", timestamp) - .setParameter("runTimeout", runTimeout) + .createNamedQuery("WorkspaceActivity.getExpiredRunTimeout", String.class) + .setParameter("timeDifference", timestamp - runTimeout) .getResultList() .stream() - .map(WorkspaceActivity::getWorkspaceId) .collect(Collectors.toList()); } catch (RuntimeException x) { throw new ServerException(x.getLocalizedMessage(), x); @@ -85,11 +83,10 @@ public class JpaWorkspaceActivityDao implements WorkspaceActivityDao { try { return managerProvider .get() - .createNamedQuery("WorkspaceActivity.getExpiredIdle", WorkspaceActivity.class) + .createNamedQuery("WorkspaceActivity.getExpiredIdle", String.class) .setParameter("expiration", timestamp) .getResultList() .stream() - .map(WorkspaceActivity::getWorkspaceId) .collect(Collectors.toList()); } catch (RuntimeException x) { throw new ServerException(x.getLocalizedMessage(), x); diff --git a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java index 48f64e420e..944448c17f 100644 --- a/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java +++ b/wsmaster/che-core-api-workspace-activity/src/main/java/org/eclipse/che/api/workspace/activity/WorkspaceActivity.java @@ -27,13 +27,13 @@ import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; @NamedQueries({ @NamedQuery( name = "WorkspaceActivity.getExpiredIdle", - query = "SELECT a FROM WorkspaceActivity a WHERE a.expiration < :expiration"), + query = "SELECT a.workspaceId FROM WorkspaceActivity a WHERE a.expiration < :expiration"), @NamedQuery( name = "WorkspaceActivity.getExpiredRunTimeout", query = - "SELECT a FROM WorkspaceActivity a WHERE " + "SELECT a.workspaceId FROM WorkspaceActivity a WHERE " + "(a.status = org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING AND " - + "a.lastRunning < :timestamp - :runTimeout)"), + + "a.lastRunning < :timeDifference)"), @NamedQuery( name = "WorkspaceActivity.getStoppedSince", query =