Browse Source

SONAR-9040 make CE_QUEUE.EXECUTION_COUNT not nullable

tags/6.4-RC1
Sébastien Lesaint 7 years ago
parent
commit
667c7453da

+ 1
- 1
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl View File

@@ -563,7 +563,7 @@ CREATE TABLE "CE_QUEUE" (
"STATUS" VARCHAR(15) NOT NULL,
"SUBMITTER_LOGIN" VARCHAR(255) NULL,
"WORKER_UUID" VARCHAR(40) NULL,
"EXECUTION_COUNT" INTEGER NULL,
"EXECUTION_COUNT" INTEGER NOT NULL,
"STARTED_AT" BIGINT NULL,
"CREATED_AT" BIGINT NOT NULL,
"UPDATED_AT" BIGINT NOT NULL

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

@@ -81,8 +81,9 @@ public class CeQueueDao implements Dao {

public CeQueueDto insert(DbSession session, CeQueueDto dto) {
if (dto.getCreatedAt() == 0L || dto.getUpdatedAt() == 0L) {
dto.setCreatedAt(system2.now());
dto.setUpdatedAt(system2.now());
long now = system2.now();
dto.setCreatedAt(now);
dto.setUpdatedAt(now);
}

mapper(session).insert(dto);

+ 40
- 11
server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeQueueDto.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.db.ce;

import com.google.common.base.MoreObjects;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

@@ -36,6 +35,14 @@ public class CeQueueDto {
private String componentUuid;
private Status status;
private String submitterLogin;
/**
* UUID of the worker that is executing, or of the last worker that executed, the current task.
*/
private String workerUuid;
/**
* This counter is incremented by 1 each time the tasks switches to status {@link Status#IN_PROGRESS IN_PROGRESS}.
*/
private int executionCount = 0;
private Long startedAt;
private long createdAt;
private long updatedAt;
@@ -91,6 +98,26 @@ public class CeQueueDto {
return this;
}

public String getWorkerUuid() {
return workerUuid;
}

public CeQueueDto setWorkerUuid(@Nullable String workerUuid) {
checkArgument(workerUuid == null || workerUuid.length() <= 40, "worker uuid is too long: %s", workerUuid);
this.workerUuid = workerUuid;
return this;
}

public int getExecutionCount() {
return executionCount;
}

public CeQueueDto setExecutionCount(int executionCount) {
checkArgument(executionCount >= 0, "execution count can't be < 0");
this.executionCount = executionCount;
return this;
}

@CheckForNull
public Long getStartedAt() {
return startedAt;
@@ -121,16 +148,18 @@ public class CeQueueDto {

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("uuid", uuid)
.add("taskType", taskType)
.add("componentUuid", componentUuid)
.add("status", status)
.add("submitterLogin", submitterLogin)
.add("startedAt", startedAt)
.add("createdAt", createdAt)
.add("updatedAt", updatedAt)
.toString();
return "CeQueueDto{" +
"uuid='" + uuid + '\'' +
", taskType='" + taskType + '\'' +
", componentUuid='" + componentUuid + '\'' +
", status=" + status +
", submitterLogin='" + submitterLogin + '\'' +
", workerUuid='" + workerUuid + '\'' +
", executionCount=" + executionCount +
", startedAt=" + startedAt +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}

@Override

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

@@ -9,6 +9,8 @@
cq.component_uuid as componentUuid,
cq.status as status,
cq.submitter_login as submitterLogin,
cq.worker_uuid as workerUuid,
cq.execution_count as executionCount,
cq.started_at as startedAt,
cq.created_at as createdAt,
cq.updated_at as updatedAt
@@ -137,6 +139,8 @@
component_uuid,
status,
submitter_login,
worker_uuid,
execution_count,
started_at,
created_at,
updated_at
@@ -147,6 +151,8 @@
#{componentUuid,jdbcType=VARCHAR},
#{status,jdbcType=VARCHAR},
#{submitterLogin,jdbcType=VARCHAR},
#{workerUuid,jdbcType=VARCHAR},
#{executionCount,jdbcType=INTEGER},
#{startedAt,jdbcType=BIGINT},
#{createdAt,jdbcType=BIGINT},
#{updatedAt,jdbcType=BIGINT}

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

@@ -26,9 +26,12 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.TestSystem2;
import org.sonar.db.DbTester;

@@ -36,6 +39,8 @@ import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.singletonList;
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.CeQueueDto.Status.IN_PROGRESS;
import static org.sonar.db.ce.CeQueueDto.Status.PENDING;
import static org.sonar.db.ce.CeQueueTesting.newCeQueueDto;
@@ -46,7 +51,11 @@ public class CeQueueDaoTest {
private static final String TASK_UUID_2 = "TASK_2";
private static final String COMPONENT_UUID_1 = "PROJECT_1";
private static final String COMPONENT_UUID_2 = "PROJECT_2";
public static final String TASK_UUID_3 = "TASK_3";
private static final String TASK_UUID_3 = "TASK_3";
private static final String SELECT_QUEUE_UUID_AND_STATUS_QUERY = "select uuid,status from ce_queue";
private static final String SUBMITTER_LOGIN = "henri";
private static final String WORKER_UUID = "worker uuid";
private static final int EXECUTION_COUNT = 42;

private TestSystem2 system2 = new TestSystem2().setNow(INIT_TIME);

@@ -54,14 +63,61 @@ public class CeQueueDaoTest {
public DbTester db = DbTester.create(system2);

private CeQueueDao underTest = new CeQueueDao(system2);
private static final String SELECT_QUEUE_UUID_AND_STATUS_QUERY = "select uuid,status from ce_queue";

@Test
public void test_insert() {
insert(TASK_UUID_1, COMPONENT_UUID_1, PENDING);
public void insert_populates_createdAt_and_updateAt_from_System2_with_same_value_if_any_is_not_set() {
System2 system2 = mock(System2.class);
CeQueueDao ceQueueDao = new CeQueueDao(system2);
long now = 1_334_333L;
CeQueueDto dto = new CeQueueDto()
.setTaskType(CeTaskTypes.REPORT)
.setComponentUuid(COMPONENT_UUID_1)
.setStatus(PENDING)
.setSubmitterLogin(SUBMITTER_LOGIN)
.setWorkerUuid(WORKER_UUID)
.setExecutionCount(EXECUTION_COUNT);

mockSystem2ForSingleCall(system2, now);
ceQueueDao.insert(db.getSession(), dto.setUuid(TASK_UUID_1));
mockSystem2ForSingleCall(system2, now);
ceQueueDao.insert(db.getSession(), dto.setUuid(TASK_UUID_2).setCreatedAt(8_000_999L).setUpdatedAt(0));
mockSystem2ForSingleCall(system2, now);
ceQueueDao.insert(db.getSession(), dto.setUuid(TASK_UUID_3).setCreatedAt(0).setUpdatedAt(8_000_999L));
mockSystem2ForSingleCall(system2, now);
String uuid4 = "uuid 4";
ceQueueDao.insert(db.getSession(), dto.setUuid(uuid4).setCreatedAt(6_888_777L).setUpdatedAt(8_000_999L));
db.getSession().commit();

Stream.of(TASK_UUID_1, TASK_UUID_2, TASK_UUID_3)
.forEach(uuid -> {
CeQueueDto saved = underTest.selectByUuid(db.getSession(), uuid).get();
assertThat(saved.getUuid()).isEqualTo(uuid);
assertThat(saved.getTaskType()).isEqualTo(CeTaskTypes.REPORT);
assertThat(saved.getComponentUuid()).isEqualTo(COMPONENT_UUID_1);
assertThat(saved.getStatus()).isEqualTo(PENDING);
assertThat(saved.getSubmitterLogin()).isEqualTo(SUBMITTER_LOGIN);
assertThat(saved.getWorkerUuid()).isEqualTo(WORKER_UUID);
assertThat(saved.getExecutionCount()).isEqualTo(EXECUTION_COUNT);
assertThat(saved.getCreatedAt()).isEqualTo(now);
assertThat(saved.getUpdatedAt()).isEqualTo(now);
assertThat(saved.getStartedAt()).isNull();
});
CeQueueDto saved = underTest.selectByUuid(db.getSession(), uuid4).get();
assertThat(saved.getUuid()).isEqualTo(uuid4);
assertThat(saved.getTaskType()).isEqualTo(CeTaskTypes.REPORT);
assertThat(saved.getComponentUuid()).isEqualTo(COMPONENT_UUID_1);
assertThat(saved.getStatus()).isEqualTo(PENDING);
assertThat(saved.getSubmitterLogin()).isEqualTo(SUBMITTER_LOGIN);
assertThat(saved.getWorkerUuid()).isEqualTo(WORKER_UUID);
assertThat(saved.getExecutionCount()).isEqualTo(EXECUTION_COUNT);
assertThat(saved.getCreatedAt()).isEqualTo(6_888_777L);
assertThat(saved.getUpdatedAt()).isEqualTo(8_000_999L);
assertThat(saved.getStartedAt()).isNull();
}

Optional<CeQueueDto> saved = underTest.selectByUuid(db.getSession(), TASK_UUID_1);
assertThat(saved.isPresent()).isTrue();
private void mockSystem2ForSingleCall(System2 system2, long now) {
Mockito.reset(system2);
when(system2.now()).thenReturn(now).thenThrow(new IllegalStateException("now should be called only once"));
}

@Test
@@ -75,6 +131,8 @@ public class CeQueueDaoTest {
assertThat(saved.getComponentUuid()).isEqualTo(COMPONENT_UUID_1);
assertThat(saved.getStatus()).isEqualTo(PENDING);
assertThat(saved.getSubmitterLogin()).isEqualTo("henri");
assertThat(saved.getWorkerUuid()).isNull();
assertThat(saved.getExecutionCount()).isEqualTo(0);
assertThat(saved.getCreatedAt()).isEqualTo(INIT_TIME);
assertThat(saved.getUpdatedAt()).isEqualTo(INIT_TIME);
assertThat(saved.getStartedAt()).isNull();
@@ -357,6 +415,6 @@ public class CeQueueDaoTest {
}

private static Map<String, Object> rowMap(String uuid, CeQueueDto.Status status) {
return ImmutableMap.<String, Object>of("UUID", uuid, "STATUS", status.name());
return ImmutableMap.of("UUID", uuid, "STATUS", status.name());
}
}

+ 125
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/ce/CeQueueDtoTest.java View File

@@ -0,0 +1,125 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.ce;

import java.util.Random;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class CeQueueDtoTest {
private static final String STR_15_CHARS = "012345678901234";
private static final String STR_40_CHARS = "0123456789012345678901234567890123456789";
private static final String STR_255_CHARS = STR_40_CHARS + STR_40_CHARS + STR_40_CHARS + STR_40_CHARS
+ STR_40_CHARS + STR_40_CHARS + STR_15_CHARS;

@Rule
public ExpectedException expectedException = ExpectedException.none();

private CeQueueDto underTest = new CeQueueDto();

@Test
public void setComponentUuid_accepts_null_empty_and_string_40_chars_or_less() {
underTest.setComponentUuid(null);
underTest.setComponentUuid("");
underTest.setComponentUuid("bar");
underTest.setComponentUuid(STR_40_CHARS);
}

@Test
public void setComponentUuid_throws_IAE_if_value_is_41_chars() {
String str_41_chars = STR_40_CHARS + "a";

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Value of component UUID is too long: " + str_41_chars);

underTest.setComponentUuid(str_41_chars);
}

@Test
public void setTaskType_throws_NPE_if_argument_is_null() {
expectedException.expect(NullPointerException.class);

underTest.setTaskType(null);
}

@Test
public void setTaskType_accepts_empty_and_string_15_chars_or_less() {
underTest.setTaskType("");
underTest.setTaskType("bar");
underTest.setTaskType(STR_15_CHARS);
}

@Test
public void setTaskType_throws_IAE_if_value_is_41_chars() {
String str_16_chars = STR_15_CHARS + "a";

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Value of task type is too long: " + str_16_chars);

underTest.setTaskType(str_16_chars);
}

@Test
public void setSubmitterLogin_accepts_null_empty_and_string_255_chars_or_less() {
underTest.setSubmitterLogin(null);
underTest.setSubmitterLogin("");
underTest.setSubmitterLogin("bar");
underTest.setSubmitterLogin(STR_255_CHARS);
}

@Test
public void setSubmitterLogin_throws_IAE_if_value_is_41_chars() {
String str_256_chars = STR_255_CHARS + "a";

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Value of submitter login is too long: " + str_256_chars);

underTest.setSubmitterLogin(str_256_chars);
}

@Test
public void setWorkerUuid_accepts_null_empty_and_string_40_chars_or_less() {
underTest.setWorkerUuid(null);
underTest.setWorkerUuid("");
underTest.setWorkerUuid("bar");
underTest.setWorkerUuid(STR_40_CHARS);
}

@Test
public void setWorkerUuid_throws_IAE_if_value_is_41_chars() {
String str_41_chars = STR_40_CHARS + "a";

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("worker uuid is too long: " + str_41_chars);

underTest.setWorkerUuid(str_41_chars);
}

@Test
public void setExecutionCount_throws_IAE_if_value_is_less_than_0() {
int lessThanZero = -1-(Math.abs(new Random().nextInt()));

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("execution count can't be < 0");

underTest.setExecutionCount(lessThanZero);
}
}

+ 3
- 2
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/AddCeQueueWorkerUuidAndExecutionCount.java View File

@@ -21,11 +21,12 @@ package org.sonar.server.platform.db.migration.version.v64;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.def.IntegerColumnDef;
import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

import static org.sonar.server.platform.db.migration.def.IntegerColumnDef.newIntegerColumnDefBuilder;

public class AddCeQueueWorkerUuidAndExecutionCount extends DdlChange {

private static final String TABLE_CE_QUEUE = "ce_queue";
@@ -42,7 +43,7 @@ public class AddCeQueueWorkerUuidAndExecutionCount extends DdlChange {
.setLimit(VarcharColumnDef.UUID_SIZE)
.setIsNullable(true)
.build())
.addColumn(IntegerColumnDef.newIntegerColumnDefBuilder()
.addColumn(newIntegerColumnDefBuilder()
.setColumnName("execution_count")
.setIsNullable(true)
.build())

+ 1
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64.java View File

@@ -59,5 +59,6 @@ public class DbVersion64 implements DbVersion {
.add(1627, "Delete permission templates linked to removed users", DeletePermissionTemplatesLinkedToRemovedUsers.class)
;
.add(1628, "Add columns CE_QUEUE.WORKER_UUID and EXECUTION_COUNT", AddCeQueueWorkerUuidAndExecutionCount.class);
.add(1629, "Make CE_QUEUE.EXECUTION_COUNT not nullable", MakeCeQueueExecutionCountNotNullable.class);
}
}

+ 48
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/MakeCeQueueExecutionCountNotNullable.java View File

@@ -0,0 +1,48 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v64;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

import static org.sonar.server.platform.db.migration.def.IntegerColumnDef.newIntegerColumnDefBuilder;

public class MakeCeQueueExecutionCountNotNullable extends DdlChange {

private static final String TABLE_CE_QUEUE = "ce_queue";

public MakeCeQueueExecutionCountNotNullable(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
context.execute("update ce_queue set execution_count = 0 where execution_count is null");

context.execute(new AlterColumnsBuilder(getDialect(), TABLE_CE_QUEUE)
.updateColumn(newIntegerColumnDefBuilder()
.setColumnName("execution_count")
.setIsNullable(false)
.build())
.build());
}
}

+ 2
- 2
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64Test.java View File

@@ -35,7 +35,7 @@ public class DbVersion64Test {

@Test
public void verify_migration_count() {
verifyMigrationCount(underTest, 28);
verifyMigrationCount(underTest, 29);
}

}


+ 88
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/MakeCeQueueExecutionCountNotNullableTest.java View File

@@ -0,0 +1,88 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v64;

import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.CoreDbTester;

import static org.assertj.core.api.Assertions.assertThat;

public class MakeCeQueueExecutionCountNotNullableTest {

private static final String TABLE_CE_QUEUE = "ce_queue";

@Rule
public CoreDbTester db = CoreDbTester.createForSchema(MakeCeQueueExecutionCountNotNullableTest.class, "ce_queue.sql");
@Rule
public ExpectedException expectedException = ExpectedException.none();

private MakeCeQueueExecutionCountNotNullable underTest = new MakeCeQueueExecutionCountNotNullable(db.database());

@Test
public void execute_makes_column_execution_count_not_nullable_when_table_is_empty() throws SQLException {
underTest.execute();

verifyColumnDefinition();
}

@Test
public void execute_set_column_execution_count_to_0_and_not_nullable_no_matter_status_of_the_task() throws SQLException {
insertCeQueue("u1", Status.IN_PROGRESS);
insertCeQueue("u2", Status.PENDING);

underTest.execute();

verifyColumnDefinition();
assertThat(getUuidsForExecutionCount(0)).containsOnly("u1", "u2");
assertThat(getUuidsForExecutionCount(1)).isEmpty();
}

private List<Object> getUuidsForExecutionCount(int executionCount) {
return db.select("select uuid as \"UUID\" from ce_queue where execution_count=" + executionCount)
.stream()
.flatMap(row -> Stream.of(row.get("UUID")))
.collect(MoreCollectors.toList());
}

private void verifyColumnDefinition() {
db.assertColumnDefinition(TABLE_CE_QUEUE, "execution_count", Types.INTEGER, null, false);
}

private void insertCeQueue(String uuid, Status status) {
db.executeInsert(TABLE_CE_QUEUE,
"UUID", uuid,
"TASK_TYPE", uuid + "_type",
"STATUS", status.name(),
"CREATED_AT", new Random().nextLong() + "",
"UPDATED_AT", new Random().nextLong() + "");
}

public enum Status {
PENDING, IN_PROGRESS
}
}

+ 16
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/MakeCeQueueExecutionCountNotNullableTest/ce_queue.sql View File

@@ -0,0 +1,16 @@
CREATE TABLE "CE_QUEUE" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"UUID" VARCHAR(40) NOT NULL,
"TASK_TYPE" VARCHAR(15) NOT NULL,
"COMPONENT_UUID" VARCHAR(40) NULL,
"STATUS" VARCHAR(15) NOT NULL,
"SUBMITTER_LOGIN" VARCHAR(255) NULL,
"WORKER_UUID" VARCHAR(40) NULL,
"EXECUTION_COUNT" INTEGER NULL,
"STARTED_AT" BIGINT NULL,
"CREATED_AT" BIGINT NOT NULL,
"UPDATED_AT" BIGINT NOT NULL
);
CREATE UNIQUE INDEX "CE_QUEUE_UUID" ON "CE_QUEUE" ("UUID");
CREATE INDEX "CE_QUEUE_COMPONENT_UUID" ON "CE_QUEUE" ("COMPONENT_UUID");
CREATE INDEX "CE_QUEUE_STATUS" ON "CE_QUEUE" ("STATUS");

Loading…
Cancel
Save