Browse Source

SONAR-12038 Add pendingTime to api/ce/activity_status and ComputeEngineTasks JMX metrics

tags/7.8
Duarte Meneses 5 years ago
parent
commit
36d13bae20
18 changed files with 208 additions and 47 deletions
  1. 14
    8
      server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CEQueueStatus.java
  2. 12
    1
      server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CEQueueStatusImpl.java
  3. 11
    5
      server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeTasksMBean.java
  4. 7
    0
      server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeTasksMBeanImpl.java
  5. 3
    2
      server/sonar-ce/src/main/java/org/sonar/ce/monitoring/DistributedCEQueueStatusImpl.java
  6. 2
    1
      server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CEQueueStatusImplConcurrentTest.java
  7. 2
    1
      server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CEQueueStatusImplTest.java
  8. 10
    1
      server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CeTasksMBeanImplTest.java
  9. 2
    1
      server/sonar-ce/src/test/java/org/sonar/ce/monitoring/DistributedCEQueueStatusImplTest.java
  10. 1
    1
      server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java
  11. 4
    1
      server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDao.java
  12. 3
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueMapper.java
  13. 12
    0
      server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml
  14. 61
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java
  15. 27
    19
      server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityStatusAction.java
  16. 2
    1
      server/sonar-server/src/main/resources/org/sonar/server/ce/ws/activity_status-example.json
  17. 34
    4
      server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityStatusActionTest.java
  18. 1
    0
      sonar-ws/src/main/protobuf/ws-ce.proto

+ 14
- 8
server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CEQueueStatus.java View File

*/ */
package org.sonar.ce.monitoring; package org.sonar.ce.monitoring;


import java.util.Optional;
import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.ce.ComputeEngineSide;


@ComputeEngineSide @ComputeEngineSide
long addSuccess(long processingTime); long addSuccess(long processingTime);


/** /**
* Adds 1 to the count of batch reports which processing ended with an error and removes 1 from the count of batch
* reports under processing. Adds the specified time to the processing time counter.
* Adds 1 to the count of tasks which processing ended with an error and removes 1 from the count of tasks
* under processing. Adds the specified time to the processing time counter.
* *
* @param processingTime duration of processing in ms * @param processingTime duration of processing in ms
* *
* @return the new count of batch reports which processing ended with an error
* @return the new count of tasks which processing ended with an error
* *
* @see #getErrorCount() * @see #getErrorCount()
* @see #getInProgressCount() * @see #getInProgressCount()
long addError(long processingTime); long addError(long processingTime);


/** /**
* Count of batch reports waiting for processing since startup, including reports received before instance startup.
* Number of pending tasks, including tasks received before instance startup.
*/ */
long getPendingCount(); long getPendingCount();


/** /**
* Count of batch reports under processing.
* The age, in ms, of the oldest pending task.
*/
Optional<Long> getLongestTimePending();

/**
* Count of tasks under processing.
*/ */
long getInProgressCount(); long getInProgressCount();


/** /**
* Count of batch reports which processing ended with an error since instance startup.
* Count of tasks which processing ended with an error since instance startup.
*/ */
long getErrorCount(); long getErrorCount();


/** /**
* Count of batch reports which processing ended successfully since instance startup.
* Count of tasks which processing ended successfully since instance startup.
*/ */
long getSuccessCount(); long getSuccessCount();


/** /**
* Time spent processing batch reports since startup, in milliseconds.
* Time spent processing tasks since startup, in milliseconds.
*/ */
long getProcessingTime(); long getProcessingTime();



+ 12
- 1
server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CEQueueStatusImpl.java View File



import java.util.Optional; import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.ce.CeQueueDto; import org.sonar.db.ce.CeQueueDto;
public class CEQueueStatusImpl implements CEQueueStatus { public class CEQueueStatusImpl implements CEQueueStatus {


private final DbClient dbClient; private final DbClient dbClient;
private final System2 system;
private final AtomicLong inProgress = new AtomicLong(0); private final AtomicLong inProgress = new AtomicLong(0);
private final AtomicLong error = new AtomicLong(0); private final AtomicLong error = new AtomicLong(0);
private final AtomicLong success = new AtomicLong(0); private final AtomicLong success = new AtomicLong(0);
private final AtomicLong processingTime = new AtomicLong(0); private final AtomicLong processingTime = new AtomicLong(0);


public CEQueueStatusImpl(DbClient dbClient) {
public CEQueueStatusImpl(DbClient dbClient, System2 system) {
this.dbClient = dbClient; this.dbClient = dbClient;
this.system = system;
} }


@Override @Override
} }
} }


@Override
public Optional<Long> getLongestTimePending() {
try (DbSession dbSession = dbClient.openSession(false)) {
return dbClient.ceQueueDao().selectCreationDateOfOldestPendingByMainComponentUuid(dbSession, null)
.map(creationDate -> system.now() - creationDate);
}
}

@Override @Override
public boolean areWorkersPaused() { public boolean areWorkersPaused() {
try (DbSession dbSession = dbClient.openSession(false)) { try (DbSession dbSession = dbClient.openSession(false)) {

+ 11
- 5
server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeTasksMBean.java View File

package org.sonar.ce.monitoring; package org.sonar.ce.monitoring;


import java.util.List; import java.util.List;
import java.util.Optional;


public interface CeTasksMBean { public interface CeTasksMBean {


String OBJECT_NAME = "SonarQube:name=ComputeEngineTasks"; String OBJECT_NAME = "SonarQube:name=ComputeEngineTasks";


/** /**
* Count of batch reports waiting for processing since startup, including reports received before instance startup.
* Number of pending tasks, including tasks received before instance startup.
*/ */
long getPendingCount(); long getPendingCount();


/** /**
* Count of batch reports under processing.
* The age, in ms, of the oldest pending task.
*/
Optional<Long> getLongestTimePending();

/**
* Count of tasks under processing.
*/ */
long getInProgressCount(); long getInProgressCount();


/** /**
* Count of batch reports which processing ended with an error since instance startup.
* Count of tasks which processing ended with an error since instance startup.
*/ */
long getErrorCount(); long getErrorCount();


/** /**
* Count of batch reports which processing ended successfully since instance startup.
* Count of tasks which processing ended successfully since instance startup.
*/ */
long getSuccessCount(); long getSuccessCount();


/** /**
* Time spent processing reports since startup, in milliseconds.
* Time spent processing tasks since startup, in milliseconds.
*/ */
long getProcessingTime(); long getProcessingTime();



+ 7
- 0
server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeTasksMBeanImpl.java View File

package org.sonar.ce.monitoring; package org.sonar.ce.monitoring;


import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.picocontainer.Startable; import org.picocontainer.Startable;
return queueStatus.getPendingCount(); return queueStatus.getPendingCount();
} }


@Override
public Optional<Long> getLongestTimePending() {
return queueStatus.getLongestTimePending();
}

@Override @Override
public long getInProgressCount() { public long getInProgressCount() {
return queueStatus.getInProgressCount(); return queueStatus.getInProgressCount();
ProtobufSystemInfo.Section.Builder builder = ProtobufSystemInfo.Section.newBuilder(); ProtobufSystemInfo.Section.Builder builder = ProtobufSystemInfo.Section.newBuilder();
builder.setName("Compute Engine Tasks"); builder.setName("Compute Engine Tasks");
builder.addAttributesBuilder().setKey("Pending").setLongValue(getPendingCount()).build(); builder.addAttributesBuilder().setKey("Pending").setLongValue(getPendingCount()).build();
builder.addAttributesBuilder().setKey("Longest Time Pending (ms)").setLongValue(getLongestTimePending().orElse(0L)).build();
builder.addAttributesBuilder().setKey("In Progress").setLongValue(getInProgressCount()).build(); builder.addAttributesBuilder().setKey("In Progress").setLongValue(getInProgressCount()).build();
builder.addAttributesBuilder().setKey("Processed With Error").setLongValue(getErrorCount()).build(); builder.addAttributesBuilder().setKey("Processed With Error").setLongValue(getErrorCount()).build();
builder.addAttributesBuilder().setKey("Processed With Success").setLongValue(getSuccessCount()).build(); builder.addAttributesBuilder().setKey("Processed With Success").setLongValue(getSuccessCount()).build();

+ 3
- 2
server/sonar-ce/src/main/java/org/sonar/ce/monitoring/DistributedCEQueueStatusImpl.java View File

*/ */
package org.sonar.ce.monitoring; package org.sonar.ce.monitoring;


import org.sonar.api.utils.System2;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;


public class DistributedCEQueueStatusImpl extends CEQueueStatusImpl { public class DistributedCEQueueStatusImpl extends CEQueueStatusImpl {
public DistributedCEQueueStatusImpl(DbClient dbClient) {
super(dbClient);
public DistributedCEQueueStatusImpl(DbClient dbClient, System2 system2) {
super(dbClient, system2);
} }


@Override @Override

+ 2
- 1
server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CEQueueStatusImplConcurrentTest.java View File

import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;


import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
return new Thread(r, CEQueueStatusImplConcurrentTest.class.getSimpleName() + cnt++); return new Thread(r, CEQueueStatusImplConcurrentTest.class.getSimpleName() + cnt++);
} }
}); });
private CEQueueStatusImpl underTest = new CEQueueStatusImpl(mock(DbClient.class));
private CEQueueStatusImpl underTest = new CEQueueStatusImpl(mock(DbClient.class), mock(System2.class));


@After @After
public void tearDown() { public void tearDown() {

+ 2
- 1
server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CEQueueStatusImplTest.java View File



import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.ce.CeQueueDto; import org.sonar.db.ce.CeQueueDto;


import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;


public class CEQueueStatusImplTest extends CommonCEQueueStatusImplTest { public class CEQueueStatusImplTest extends CommonCEQueueStatusImplTest {
private CEQueueStatusImpl underTest = new CEQueueStatusImpl(getDbClient());
private CEQueueStatusImpl underTest = new CEQueueStatusImpl(getDbClient(), mock(System2.class));


public CEQueueStatusImplTest() { public CEQueueStatusImplTest() {
super(mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS)); super(mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS));

+ 10
- 1
server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CeTasksMBeanImplTest.java View File

import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;


public class CeTasksMBeanImplTest { public class CeTasksMBeanImplTest {
private static final long PENDING_COUNT = 2; private static final long PENDING_COUNT = 2;
private static final Optional<Long> PENDING_TIME = Optional.of(10_000L);
private static final long IN_PROGRESS_COUNT = 5; private static final long IN_PROGRESS_COUNT = 5;
private static final long ERROR_COUNT = 10; private static final long ERROR_COUNT = 10;
private static final long SUCCESS_COUNT = 13; private static final long SUCCESS_COUNT = 13;
@Test @Test
public void get_methods_delegate_to_the_CEQueueStatus_instance() { public void get_methods_delegate_to_the_CEQueueStatus_instance() {
assertThat(underTest.getPendingCount()).isEqualTo(PENDING_COUNT); assertThat(underTest.getPendingCount()).isEqualTo(PENDING_COUNT);
assertThat(underTest.getLongestTimePending()).isEqualTo(PENDING_TIME);
assertThat(underTest.getInProgressCount()).isEqualTo(IN_PROGRESS_COUNT); assertThat(underTest.getInProgressCount()).isEqualTo(IN_PROGRESS_COUNT);
assertThat(underTest.getErrorCount()).isEqualTo(ERROR_COUNT); assertThat(underTest.getErrorCount()).isEqualTo(ERROR_COUNT);
assertThat(underTest.getSuccessCount()).isEqualTo(SUCCESS_COUNT); assertThat(underTest.getSuccessCount()).isEqualTo(SUCCESS_COUNT);
public void export_system_info() { public void export_system_info() {
ProtobufSystemInfo.Section section = underTest.toProtobuf(); ProtobufSystemInfo.Section section = underTest.toProtobuf();
assertThat(section.getName()).isEqualTo("Compute Engine Tasks"); assertThat(section.getName()).isEqualTo("Compute Engine Tasks");
assertThat(section.getAttributesCount()).isEqualTo(8);
assertThat(section.getAttributesCount()).isEqualTo(9);
} }


private static class DumbCEQueueStatus implements CEQueueStatus { private static class DumbCEQueueStatus implements CEQueueStatus {
return PENDING_COUNT; return PENDING_COUNT;
} }


@Override
public Optional<Long> getLongestTimePending() {
return PENDING_TIME;
}

@Override @Override
public long addInProgress() { public long addInProgress() {
return methodNotImplemented(); return methodNotImplemented();
} }


} }

private static class DumbCeConfiguration implements CeConfiguration { private static class DumbCeConfiguration implements CeConfiguration {


@Override @Override

+ 2
- 1
server/sonar-ce/src/test/java/org/sonar/ce/monitoring/DistributedCEQueueStatusImplTest.java View File



import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;


import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;


public class DistributedCEQueueStatusImplTest extends CommonCEQueueStatusImplTest { public class DistributedCEQueueStatusImplTest extends CommonCEQueueStatusImplTest {
private DistributedCEQueueStatusImpl underTest = new DistributedCEQueueStatusImpl(getDbClient());
private DistributedCEQueueStatusImpl underTest = new DistributedCEQueueStatusImpl(getDbClient(), mock(System2.class));


public DistributedCEQueueStatusImplTest() { public DistributedCEQueueStatusImplTest() {
super(mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS)); super(mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS));

+ 1
- 1
server/sonar-ce/src/test/java/org/sonar/ce/queue/InternalCeQueueImplTest.java View File

private DbSession session = db.getSession(); private DbSession session = db.getSession();


private UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE; private UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE;
private CEQueueStatus queueStatus = new CEQueueStatusImpl(db.getDbClient());
private CEQueueStatus queueStatus = new CEQueueStatusImpl(db.getDbClient(), mock(System2.class));
private DefaultOrganizationProvider defaultOrganizationProvider = mock(DefaultOrganizationProvider.class); private DefaultOrganizationProvider defaultOrganizationProvider = mock(DefaultOrganizationProvider.class);
private ComputeEngineStatus computeEngineStatus = mock(ComputeEngineStatus.class); private ComputeEngineStatus computeEngineStatus = mock(ComputeEngineStatus.class);
private InternalCeQueue underTest = new InternalCeQueueImpl(system2, db.getDbClient(), uuidFactory, queueStatus, defaultOrganizationProvider, computeEngineStatus); private InternalCeQueue underTest = new InternalCeQueueImpl(system2, db.getDbClient(), uuidFactory, queueStatus, defaultOrganizationProvider, computeEngineStatus);

+ 4
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDao.java View File

return mapper(dbSession).countByStatusAndMainComponentUuid(status, mainComponentUuid); return mapper(dbSession).countByStatusAndMainComponentUuid(status, mainComponentUuid);
} }


public Optional<Long> selectCreationDateOfOldestPendingByMainComponentUuid(DbSession dbSession, @Nullable String mainComponentUuid) {
return Optional.ofNullable(mapper(dbSession).selectCreationDateOfOldestPendingByMainComponentUuid(mainComponentUuid));
}

/** /**
* Counts entries in the queue with the specified status for each specified main component uuid. * Counts entries in the queue with the specified status for each specified main component uuid.
*
* The returned map doesn't contain any entry for main component uuids for which there is no entry in the queue (ie. * The returned map doesn't contain any entry for main component uuids for which there is no entry in the queue (ie.
* all entries have a value >= 0). * all entries have a value >= 0).
*/ */

+ 3
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueMapper.java View File



int countByStatusAndMainComponentUuid(@Param("status") CeQueueDto.Status status, @Nullable @Param("mainComponentUuid") String mainComponentUuid); int countByStatusAndMainComponentUuid(@Param("status") CeQueueDto.Status status, @Nullable @Param("mainComponentUuid") String mainComponentUuid);


@CheckForNull
Long selectCreationDateOfOldestPendingByMainComponentUuid(@Nullable @Param("mainComponentUuid") String mainComponentUuid);

List<QueueCount> countByStatusAndMainComponentUuids(@Param("status") CeQueueDto.Status status, @Param("mainComponentUuids") List<String> mainComponentUuids); List<QueueCount> countByStatusAndMainComponentUuids(@Param("status") CeQueueDto.Status status, @Param("mainComponentUuids") List<String> mainComponentUuids);


void insert(CeQueueDto dto); void insert(CeQueueDto dto);

+ 12
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml View File

</if> </if>
</select> </select>


<select id="selectCreationDateOfOldestPendingByMainComponentUuid" parameterType="map" resultType="Long">
select
min(created_at)
from
ce_queue
where
status='PENDING'
<if test="mainComponentUuid!=null">
and main_component_uuid=#{mainComponentUuid,jdbcType=VARCHAR}
</if>
</select>

<select id="countByStatusAndMainComponentUuids" resultType="org.sonar.db.ce.QueueCount"> <select id="countByStatusAndMainComponentUuids" resultType="org.sonar.db.ce.QueueCount">
select select
main_component_uuid as mainComponentUuid, main_component_uuid as mainComponentUuid,

+ 61
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java View File

import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.function.Consumer;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.junit.Rule; import org.junit.Rule;
.containsOnly("p1", "p2", "p3"); .containsOnly("p1", "p2", "p3");
} }


@Test
public void selectCreationDateOfOldestPendingByMainComponentUuid_on_any_component_returns_date() {
long time = alwaysIncreasingSystem2.now() + 10_000;
insertPending("p1", dto -> {
dto.setCreatedAt(time);
dto.setUpdatedAt(time+500);
dto.setMainComponentUuid("c1");
});
insertPending("p2", dto -> {
dto.setCreatedAt(time + 1000);
dto.setUpdatedAt(time + 2000);
dto.setMainComponentUuid("c2");
});

makeInProgress("w1", alwaysIncreasingSystem2.now(), insertPending("i1", dto -> dto.setMainComponentUuid("c3")));

assertThat(underTest.selectCreationDateOfOldestPendingByMainComponentUuid(db.getSession(), null))
.isEqualTo(Optional.of(time));
}

@Test
public void selectCreationDateOfOldestPendingByMainComponentUuid_on_specific_component_returns_date() {
long time = alwaysIncreasingSystem2.now() + 10_000;
insertPending("p1", dto -> {
dto.setCreatedAt(time);
dto.setUpdatedAt(time+500);
dto.setMainComponentUuid("c2");
});
insertPending("p2", dto -> {
dto.setCreatedAt(time + 2000);
dto.setUpdatedAt(time + 3000);
dto.setMainComponentUuid("c1");
});
insertPending("p3", dto -> {
dto.setCreatedAt(time + 4000);
dto.setUpdatedAt(time + 5000);
dto.setMainComponentUuid("c1");
});

makeInProgress("w1", alwaysIncreasingSystem2.now(), insertPending("i1", dto -> dto.setMainComponentUuid("c1")));

assertThat(underTest.selectCreationDateOfOldestPendingByMainComponentUuid(db.getSession(), "c1"))
.isEqualTo(Optional.of(time+2000));
}

@Test
public void selectCreationDateOfOldestPendingByMainComponentUuid_returns_empty_when_no_pending_tasks() {
makeInProgress("w1", alwaysIncreasingSystem2.now(), insertPending("i1"));
assertThat(underTest.selectCreationDateOfOldestPendingByMainComponentUuid(db.getSession(), null))
.isEmpty();
}

@Test @Test
public void selectWornout_returns_task_pending_with_a_non_null_startedAt() { public void selectWornout_returns_task_pending_with_a_non_null_startedAt() {
insertPending("p1"); insertPending("p1");
} }


private CeQueueDto insertPending(String uuid) { private CeQueueDto insertPending(String uuid) {
return insertPending(uuid, (Consumer<CeQueueDto>) null);
}

private CeQueueDto insertPending(String uuid, @Nullable Consumer<CeQueueDto> dtoConsumer) {
CeQueueDto dto = new CeQueueDto(); CeQueueDto dto = new CeQueueDto();
dto.setUuid(uuid); dto.setUuid(uuid);
dto.setTaskType(CeTaskTypes.REPORT); dto.setTaskType(CeTaskTypes.REPORT);
dto.setStatus(PENDING); dto.setStatus(PENDING);
dto.setSubmitterUuid("henri"); dto.setSubmitterUuid("henri");
if (dtoConsumer != null) {
dtoConsumer.accept(dto);
}
underTestAlwaysIncreasingSystem2.insert(db.getSession(), dto); underTestAlwaysIncreasingSystem2.insert(db.getSession(), dto);
db.getSession().commit(); db.getSession().commit();
return dto; return dto;
} }


private void verifyCeQueueStatuses(String taskUuid1, CeQueueDto.Status taskStatus1, String taskUuid2, CeQueueDto.Status taskStatus2) { private void verifyCeQueueStatuses(String taskUuid1, CeQueueDto.Status taskStatus1, String taskUuid2, CeQueueDto.Status taskStatus2) {
verifyCeQueueStatuses(new String[]{taskUuid1, taskUuid2}, new CeQueueDto.Status[]{taskStatus1, taskStatus2});
verifyCeQueueStatuses(new String[] {taskUuid1, taskUuid2}, new CeQueueDto.Status[] {taskStatus1, taskStatus2});
} }


private void verifyCeQueueStatuses(String[] taskUuids, CeQueueDto.Status[] statuses) { private void verifyCeQueueStatuses(String[] taskUuids, CeQueueDto.Status[] statuses) {

+ 27
- 19
server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityStatusAction.java View File

*/ */
package org.sonar.server.ce.ws; package org.sonar.server.ce.ws;


import com.google.common.base.Optional;
import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole; import org.sonar.api.web.UserRole;
import org.sonar.core.util.Uuids; import org.sonar.core.util.Uuids;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
private final UserSession userSession; private final UserSession userSession;
private final DbClient dbClient; private final DbClient dbClient;
private final ComponentFinder componentFinder; private final ComponentFinder componentFinder;
private final System2 system2;


public ActivityStatusAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder) {
public ActivityStatusAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder, System2 system2) {
this.userSession = userSession; this.userSession = userSession;
this.dbClient = dbClient; this.dbClient = dbClient;
this.componentFinder = componentFinder; this.componentFinder = componentFinder;
this.system2 = system2;
} }


@Override @Override
public void define(WebService.NewController controller) { public void define(WebService.NewController controller) {
WebService.NewAction action = controller WebService.NewAction action = controller
.createAction("activity_status") .createAction("activity_status")
.setDescription("Return CE activity related metrics.<br>" +
.setDescription("Returns CE activity related metrics.<br>" +
"Requires 'Administer System' permission or 'Administer' rights on the specified project.") "Requires 'Administer System' permission or 'Administer' rights on the specified project.")
.setSince("5.5") .setSince("5.5")
.setResponseExample(getClass().getResource("activity_status-example.json")) .setResponseExample(getClass().getResource("activity_status-example.json"))
.setExampleValue(KeyExamples.KEY_PROJECT_EXAMPLE_001); .setExampleValue(KeyExamples.KEY_PROJECT_EXAMPLE_001);


action.setChangelog(new Change("6.6", "New field 'inProgress' in response")); action.setChangelog(new Change("6.6", "New field 'inProgress' in response"));
action.setChangelog(new Change("7.8", "New field 'pendingTime' in response, only included when there are pending tasks"));
} }


@Override @Override
private ActivityStatusWsResponse doHandle(Request request) { private ActivityStatusWsResponse doHandle(Request request) {
try (DbSession dbSession = dbClient.openSession(false)) { try (DbSession dbSession = dbClient.openSession(false)) {
Optional<ComponentDto> component = searchComponent(dbSession, request); Optional<ComponentDto> component = searchComponent(dbSession, request);
String componentUuid = component.isPresent() ? component.get().uuid() : null;
String componentUuid = component.map(ComponentDto::uuid).orElse(null);
checkPermissions(component); checkPermissions(component);
int pendingCount = dbClient.ceQueueDao().countByStatusAndMainComponentUuid(dbSession, CeQueueDto.Status.PENDING, componentUuid); int pendingCount = dbClient.ceQueueDao().countByStatusAndMainComponentUuid(dbSession, CeQueueDto.Status.PENDING, componentUuid);
int inProgressCount = dbClient.ceQueueDao().countByStatusAndMainComponentUuid(dbSession, CeQueueDto.Status.IN_PROGRESS, componentUuid); int inProgressCount = dbClient.ceQueueDao().countByStatusAndMainComponentUuid(dbSession, CeQueueDto.Status.IN_PROGRESS, componentUuid);
int failingCount = dbClient.ceActivityDao().countLastByStatusAndMainComponentUuid(dbSession, CeActivityDto.Status.FAILED, componentUuid); int failingCount = dbClient.ceActivityDao().countLastByStatusAndMainComponentUuid(dbSession, CeActivityDto.Status.FAILED, componentUuid);


return ActivityStatusWsResponse.newBuilder()
Optional<Long> creationDate = dbClient.ceQueueDao().selectCreationDateOfOldestPendingByMainComponentUuid(dbSession, componentUuid);

ActivityStatusWsResponse.Builder builder = ActivityStatusWsResponse.newBuilder()
.setPending(pendingCount) .setPending(pendingCount)
.setInProgress(inProgressCount) .setInProgress(inProgressCount)
.setFailing(failingCount)
.build();
.setFailing(failingCount);

creationDate.ifPresent(d -> {
long ageOfOldestPendingTime = system2.now() - d;
builder.setPendingTime(ageOfOldestPendingTime);
});

return builder.build();
} }
} }


if (hasComponentInRequest(request)) { if (hasComponentInRequest(request)) {
component = componentFinder.getByUuidOrKey(dbSession, request.getComponentId(), request.getComponentKey(), COMPONENT_ID_AND_KEY); component = componentFinder.getByUuidOrKey(dbSession, request.getComponentId(), request.getComponentKey(), COMPONENT_ID_AND_KEY);
} }
return Optional.fromNullable(component);
return Optional.ofNullable(component);
} }


private void checkPermissions(Optional<ComponentDto> component) { private void checkPermissions(Optional<ComponentDto> component) {
} }


private static Request toWsRequest(org.sonar.api.server.ws.Request request) { private static Request toWsRequest(org.sonar.api.server.ws.Request request) {
return new Request()
.setComponentId(request.param(PARAM_COMPONENT_ID))
.setComponentKey(request.param(DEPRECATED_PARAM_COMPONENT_KEY));
return new Request(request.param(PARAM_COMPONENT_ID), request.param(DEPRECATED_PARAM_COMPONENT_KEY));
} }


private static class Request { private static class Request {

private String componentId; private String componentId;
private String componentKey; private String componentKey;


public Request setComponentId(String componentId) {
Request(@Nullable String componentId, @Nullable String componentKey) {
this.componentId = componentId; this.componentId = componentId;
return this;
this.componentKey = componentKey;
} }


@CheckForNull
public String getComponentId() { public String getComponentId() {
return componentId; return componentId;
} }


public Request setComponentKey(String componentKey) {
this.componentKey = componentKey;
return this;
}

@CheckForNull
public String getComponentKey() { public String getComponentKey() {
return componentKey; return componentKey;
} }

+ 2
- 1
server/sonar-server/src/main/resources/org/sonar/server/ce/ws/activity_status-example.json View File

{ {
"pending": 2, "pending": 2,
"inProgress": 1, "inProgress": 1,
"failing": 5
"failing": 5,
"pendingTime": 100123
} }

+ 34
- 4
server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityStatusActionTest.java View File

import org.sonarqube.ws.Ce; import org.sonarqube.ws.Ce;


import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.db.ce.CeQueueTesting.newCeQueueDto; import static org.sonar.db.ce.CeQueueTesting.newCeQueueDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.server.ce.ws.CeWsParameters.DEPRECATED_PARAM_COMPONENT_KEY; import static org.sonar.server.ce.ws.CeWsParameters.DEPRECATED_PARAM_COMPONENT_KEY;
@Rule @Rule
public DbTester db = DbTester.create(System2.INSTANCE); public DbTester db = DbTester.create(System2.INSTANCE);


private System2 system2 = mock(System2.class);

private DbClient dbClient = db.getDbClient(); private DbClient dbClient = db.getDbClient();
private DbSession dbSession = db.getSession(); private DbSession dbSession = db.getSession();
private WsActionTester ws = new WsActionTester(new ActivityStatusAction(userSession, dbClient, TestComponentFinder.from(db)));
private WsActionTester ws = new WsActionTester(new ActivityStatusAction(userSession, dbClient, TestComponentFinder.from(db), system2));


@Test @Test
public void test_definition() { public void test_definition() {


@Test @Test
public void json_example() { public void json_example() {
dbClient.ceQueueDao().insert(dbSession, newCeQueueDto("ce-queue-uuid-1").setStatus(CeQueueDto.Status.PENDING));
when(system2.now()).thenReturn(200123L);
dbClient.ceQueueDao().insert(dbSession, newCeQueueDto("ce-queue-uuid-1").setStatus(CeQueueDto.Status.PENDING).setCreatedAt(100000));
dbClient.ceQueueDao().insert(dbSession, newCeQueueDto("ce-queue-uuid-2").setStatus(CeQueueDto.Status.PENDING)); dbClient.ceQueueDao().insert(dbSession, newCeQueueDto("ce-queue-uuid-2").setStatus(CeQueueDto.Status.PENDING));
dbClient.ceQueueDao().insert(dbSession, newCeQueueDto("ce-queue-uuid-3").setStatus(CeQueueDto.Status.IN_PROGRESS)); dbClient.ceQueueDao().insert(dbSession, newCeQueueDto("ce-queue-uuid-3").setStatus(CeQueueDto.Status.IN_PROGRESS));
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
assertThat(result.getFailing()).isEqualTo(1); assertThat(result.getFailing()).isEqualTo(1);
} }


@Test
public void add_pending_time() {
String projectUuid = "project-uuid";
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto project = newPrivateProjectDto(organizationDto, projectUuid);
db.components().insertComponent(project);

userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
when(system2.now()).thenReturn(2000L);
insertInQueue(CeQueueDto.Status.PENDING, project, 1000L);
Ce.ActivityStatusWsResponse result = call(projectUuid);

assertThat(result).extracting(Ce.ActivityStatusWsResponse::getPending, Ce.ActivityStatusWsResponse::getFailing,
Ce.ActivityStatusWsResponse::getInProgress, Ce.ActivityStatusWsResponse::getPendingTime)
.containsOnly(1, 0, 0, 1000L);
}

@Test @Test
public void empty_status() { public void empty_status() {
Ce.ActivityStatusWsResponse result = call(); Ce.ActivityStatusWsResponse result = call();
} }


private void insertInQueue(CeQueueDto.Status status, @Nullable ComponentDto componentDto) { private void insertInQueue(CeQueueDto.Status status, @Nullable ComponentDto componentDto) {
dbClient.ceQueueDao().insert(dbSession, newCeQueueDto(Uuids.createFast())
insertInQueue(status, componentDto, null);
}

private void insertInQueue(CeQueueDto.Status status, @Nullable ComponentDto componentDto, @Nullable Long createdAt) {
CeQueueDto ceQueueDto = newCeQueueDto(Uuids.createFast())
.setStatus(status) .setStatus(status)
.setComponent(componentDto));
.setComponent(componentDto);
if (createdAt != null) {
ceQueueDto.setCreatedAt(createdAt);
}
dbClient.ceQueueDao().insert(dbSession, ceQueueDto);
db.commit(); db.commit();
} }



+ 1
- 0
sonar-ws/src/main/protobuf/ws-ce.proto View File

optional int32 pending = 1; optional int32 pending = 1;
optional int32 failing = 2; optional int32 failing = 2;
optional int32 inProgress = 3; optional int32 inProgress = 3;
optional int64 pendingTime = 4;
} }


// GET api/ce/analysis_status // GET api/ce/analysis_status

Loading…
Cancel
Save