-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Workspace termination time metrics (#13635)
add workspace termination time metrics Signed-off-by: Michal Vala <mvala@redhat.com>
- Loading branch information
1 parent
b2dff13
commit cd891d7
Showing
5 changed files
with
296 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
...pi-metrics/src/main/java/org/eclipse/che/api/metrics/WorkspaceStopTrackerMeterBinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* Copyright (c) 2012-2018 Red Hat, Inc. | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat, Inc. - initial API and implementation | ||
*/ | ||
package org.eclipse.che.api.metrics; | ||
|
||
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.*; | ||
import static org.eclipse.che.api.metrics.WorkspaceBinders.withStandardTags; | ||
import static org.eclipse.che.api.metrics.WorkspaceBinders.workspaceMetric; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.inject.Inject; | ||
import io.micrometer.core.instrument.MeterRegistry; | ||
import io.micrometer.core.instrument.Timer; | ||
import io.micrometer.core.instrument.binder.MeterBinder; | ||
import java.time.Duration; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import javax.inject.Singleton; | ||
import org.eclipse.che.api.core.notification.EventService; | ||
import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* {@link MeterBinder} that is providing metrics about workspace stop time. | ||
* | ||
* <p>We're measuring these state transitions: | ||
* | ||
* <pre> | ||
* RUNNING -> STOPPING -> STOPPED | ||
* STARTING -> STOPPING -> STOPPED | ||
* </pre> | ||
*/ | ||
@Singleton | ||
public class WorkspaceStopTrackerMeterBinder implements MeterBinder { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(WorkspaceStopTrackerMeterBinder.class); | ||
|
||
private final EventService eventService; | ||
|
||
private final Map<String, Long> workspaceStopTime; | ||
private Timer stopTimer; | ||
|
||
@Inject | ||
public WorkspaceStopTrackerMeterBinder(EventService eventService) { | ||
this(eventService, new ConcurrentHashMap<>()); | ||
} | ||
|
||
@VisibleForTesting | ||
WorkspaceStopTrackerMeterBinder(EventService eventService, Map<String, Long> workspaceStopTime) { | ||
this.eventService = eventService; | ||
this.workspaceStopTime = workspaceStopTime; | ||
} | ||
|
||
@Override | ||
public void bindTo(MeterRegistry registry) { | ||
|
||
stopTimer = | ||
Timer.builder(workspaceMetric("stop.time")) | ||
.description("The time of workspace stop") | ||
.tags(withStandardTags("result", "success")) | ||
.register(registry); | ||
|
||
// only subscribe to the event once we have the counters ready | ||
eventService.subscribe(this::handleWorkspaceStatusChange, WorkspaceStatusEvent.class); | ||
} | ||
|
||
private void handleWorkspaceStatusChange(WorkspaceStatusEvent event) { | ||
if ((event.getPrevStatus() == RUNNING || event.getPrevStatus() == STARTING) | ||
&& event.getStatus() == STOPPING) { | ||
workspaceStopTime.put(event.getWorkspaceId(), System.currentTimeMillis()); | ||
} else if (event.getPrevStatus() == STOPPING) { | ||
Long stopTime = workspaceStopTime.remove(event.getWorkspaceId()); | ||
if (stopTime == null) { | ||
LOG.warn("No workspace stop time recorded for workspace {}", event.getWorkspaceId()); | ||
return; | ||
} | ||
|
||
if (event.getStatus() == STOPPED) { | ||
stopTimer.record(Duration.ofMillis(System.currentTimeMillis() - stopTime)); | ||
} else { | ||
LOG.error("Unexpected change of status from STOPPING to {}", event.getStatus()); | ||
return; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
111 changes: 111 additions & 0 deletions
111
...etrics/src/test/java/org/eclipse/che/api/metrics/WorkspaceStopTrackerMeterBinderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* | ||
* Copyright (c) 2012-2018 Red Hat, Inc. | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat, Inc. - initial API and implementation | ||
*/ | ||
package org.eclipse.che.api.metrics; | ||
|
||
import static java.util.Arrays.asList; | ||
|
||
import io.micrometer.core.instrument.MeterRegistry; | ||
import io.micrometer.core.instrument.Timer; | ||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.TimeUnit; | ||
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; | ||
import org.eclipse.che.api.core.notification.EventService; | ||
import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; | ||
import org.eclipse.che.dto.server.DtoFactory; | ||
import org.testng.Assert; | ||
import org.testng.annotations.BeforeMethod; | ||
import org.testng.annotations.DataProvider; | ||
import org.testng.annotations.Test; | ||
|
||
public class WorkspaceStopTrackerMeterBinderTest { | ||
|
||
private EventService eventService; | ||
private MeterRegistry registry; | ||
private WorkspaceStopTrackerMeterBinder meterBinder; | ||
private Map<String, Long> workspaceStopTime; | ||
|
||
@BeforeMethod | ||
public void setUp() { | ||
eventService = new EventService(); | ||
registry = new SimpleMeterRegistry(); | ||
workspaceStopTime = new ConcurrentHashMap<>(); | ||
meterBinder = new WorkspaceStopTrackerMeterBinder(eventService, workspaceStopTime); | ||
meterBinder.bindTo(registry); | ||
} | ||
|
||
@Test | ||
public void shouldRecordWorkspaceStopTime() { | ||
// given | ||
// when | ||
eventService.publish( | ||
DtoFactory.newDto(WorkspaceStatusEvent.class) | ||
.withPrevStatus(WorkspaceStatus.RUNNING) | ||
.withStatus(WorkspaceStatus.STOPPING) | ||
.withWorkspaceId("id1")); | ||
// then | ||
Assert.assertTrue(workspaceStopTime.containsKey("id1")); | ||
} | ||
|
||
@Test(dataProvider = "allStatusTransitionsWithoutStarting") | ||
public void shouldNotRecordWorkspaceStopTimeForNonStoppingStatuses( | ||
WorkspaceStatus from, WorkspaceStatus to) { | ||
// given | ||
// when | ||
eventService.publish( | ||
DtoFactory.newDto(WorkspaceStatusEvent.class) | ||
.withPrevStatus(from) | ||
.withStatus(to) | ||
.withWorkspaceId("id1")); | ||
// then | ||
Assert.assertTrue(workspaceStopTime.isEmpty()); | ||
} | ||
|
||
@Test | ||
public void shouldCountSuccessfulStart() { | ||
// given | ||
workspaceStopTime.put("id1", System.currentTimeMillis() - 60 * 1000); | ||
// when | ||
eventService.publish( | ||
DtoFactory.newDto(WorkspaceStatusEvent.class) | ||
.withPrevStatus(WorkspaceStatus.STOPPING) | ||
.withStatus(WorkspaceStatus.STOPPED) | ||
.withWorkspaceId("id1")); | ||
|
||
// then | ||
|
||
Timer t = registry.find("che.workspace.stop.time").tag("result", "success").timer(); | ||
Assert.assertEquals(t.count(), 1); | ||
Assert.assertTrue(t.totalTime(TimeUnit.MILLISECONDS) >= 60 * 1000); | ||
} | ||
|
||
@DataProvider | ||
public Object[][] allStatusTransitionsWithoutStarting() { | ||
List<List<WorkspaceStatus>> transitions = new ArrayList<>(9); | ||
|
||
for (WorkspaceStatus from : WorkspaceStatus.values()) { | ||
for (WorkspaceStatus to : WorkspaceStatus.values()) { | ||
if ((from == WorkspaceStatus.RUNNING || from == WorkspaceStatus.STARTING) | ||
&& to == WorkspaceStatus.STOPPING) { | ||
continue; | ||
} | ||
|
||
transitions.add(asList(from, to)); | ||
} | ||
} | ||
|
||
return transitions.stream().map(List::toArray).toArray(Object[][]::new); | ||
} | ||
} |