diff options
author | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2016-12-06 12:19:29 +0100 |
---|---|---|
committer | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2016-12-06 12:19:29 +0100 |
commit | f37be4d76f5615f249a11d8c88aa0482bb84f21a (patch) | |
tree | 7aa220e885a72b34f5feb58b7cea72bf03e2ac88 | |
parent | 79a8e50ccd27c442e35a757bca181d783e99b1ba (diff) | |
parent | aea44b9d8f223e201b5a4a2f0d85206880e76de5 (diff) | |
download | sonarqube-f37be4d76f5615f249a11d8c88aa0482bb84f21a.tar.gz sonarqube-f37be4d76f5615f249a11d8c88aa0482bb84f21a.zip |
Merge branch 'branch-6.2'
5 files changed, 76 insertions, 36 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/util/UuidGeneratorImpl.java b/sonar-core/src/main/java/org/sonar/core/util/UuidGeneratorImpl.java index f76adf7e0cc..2b3b13d7f92 100644 --- a/sonar-core/src/main/java/org/sonar/core/util/UuidGeneratorImpl.java +++ b/sonar-core/src/main/java/org/sonar/core/util/UuidGeneratorImpl.java @@ -38,7 +38,6 @@ public final class UuidGeneratorImpl implements UuidGenerator { } private static class UuidGeneratorBase { - private final byte[] buffer = new byte[15]; // We only use bottom 3 bytes for the sequence number. Paranoia: init with random int so that if JVM/OS/machine goes down, clock slips // backwards, and JVM comes back up, we are less likely to be on the same sequenceNumber at the same time: private final AtomicInteger sequenceNumber = new AtomicInteger(new SecureRandom().nextInt()); @@ -46,7 +45,7 @@ public final class UuidGeneratorImpl implements UuidGenerator { // Used to ensure clock moves forward private long lastTimestamp = 0L; - void initBase(int sequenceId) { + void initBase(byte[] buffer, int sequenceId) { long timestamp = System.currentTimeMillis(); synchronized (this) { @@ -70,7 +69,7 @@ public final class UuidGeneratorImpl implements UuidGenerator { System.arraycopy(secureMungedAddress, 0, buffer, 6, secureMungedAddress.length); } - protected byte[] generate(int increment) { + protected byte[] generate(byte[] buffer, int increment) { // Sequence number adds 3 bytes putLong(buffer, increment, 12, 3); @@ -93,21 +92,26 @@ public final class UuidGeneratorImpl implements UuidGenerator { @Override public byte[] get() { + byte[] buffer = new byte[15]; int sequenceId = getSequenceId(); - initBase(sequenceId); - return super.generate(sequenceId); + initBase(buffer, sequenceId); + return super.generate(buffer, sequenceId); } } private static class FixedBasedUuidGenerator extends UuidGeneratorBase implements WithFixedBase { + private final byte[] base = new byte[15]; + FixedBasedUuidGenerator() { int sequenceId = getSequenceId(); - initBase(sequenceId); + initBase(base, sequenceId); } @Override public byte[] generate(int increment) { - return super.generate(increment); + byte[] buffer = new byte[15]; + System.arraycopy(base, 0, buffer, 0, buffer.length); + return super.generate(buffer, increment); } } } diff --git a/sonar-core/src/test/java/org/sonar/core/util/UuidGeneratorImplTest.java b/sonar-core/src/test/java/org/sonar/core/util/UuidGeneratorImplTest.java index 21200b0aa40..175aec720c6 100644 --- a/sonar-core/src/test/java/org/sonar/core/util/UuidGeneratorImplTest.java +++ b/sonar-core/src/test/java/org/sonar/core/util/UuidGeneratorImplTest.java @@ -19,10 +19,13 @@ */ package org.sonar.core.util; +import java.util.ArrayList; import java.util.Base64; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -31,7 +34,7 @@ public class UuidGeneratorImplTest { private UuidGeneratorImpl underTest = new UuidGeneratorImpl(); @Test - public void generate_returns_unique_values_without_common_initial_letter_given_more_than_one_milisecond_between_generate_calls() throws InterruptedException { + public void generate_returns_unique_values_without_common_initial_letter_given_more_than_one_millisecond_between_generate_calls() throws InterruptedException { Base64.Encoder encoder = Base64.getEncoder(); int count = 30; Set<String> uuids = new HashSet<>(count); @@ -50,6 +53,33 @@ public class UuidGeneratorImplTest { } @Test + public void generate_concurrent_test() throws InterruptedException { + int rounds = 500; + List<byte[]> uuids1 = new ArrayList<>(rounds); + List<byte[]> uuids2 = new ArrayList<>(rounds); + Thread t1 = new Thread(() -> { + for (int i = 0; i < rounds; i++) { + uuids1.add(underTest.generate()); + } + }); + Thread t2 = new Thread(() -> { + for (int i = 0; i < rounds; i++) { + uuids2.add(underTest.generate()); + } + }); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + Base64.Encoder encoder = Base64.getEncoder(); + Set<String> uuids = new HashSet<>(rounds * 2); + uuids1.forEach(bytes -> uuids.add(encoder.encodeToString(bytes))); + uuids2.forEach(bytes -> uuids.add(encoder.encodeToString(bytes))); + assertThat(uuids).hasSize(rounds * 2); + } + + @Test public void generate_from_FixedBase_returns_unique_values_where_only_last_4_later_letter_change() { Base64.Encoder encoder = Base64.getEncoder(); int count = 100_000; @@ -68,4 +98,33 @@ public class UuidGeneratorImplTest { assertThat(iterator.next()).startsWith(base); } } + + @Test + public void generate_from_FixedBase_concurrent_test() throws InterruptedException { + UuidGenerator.WithFixedBase withFixedBase = underTest.withFixedBase(); + int rounds = 500; + List<byte[]> uuids1 = new ArrayList<>(rounds); + List<byte[]> uuids2 = new ArrayList<>(rounds); + AtomicInteger cnt = new AtomicInteger(); + Thread t1 = new Thread(() -> { + for (int i = 0; i < rounds; i++) { + uuids1.add(withFixedBase.generate(cnt.getAndIncrement())); + } + }); + Thread t2 = new Thread(() -> { + for (int i = 0; i < rounds; i++) { + uuids2.add(withFixedBase.generate(cnt.getAndIncrement())); + } + }); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + + Base64.Encoder encoder = Base64.getEncoder(); + Set<String> uuids = new HashSet<>(rounds * 2); + uuids1.forEach(bytes -> uuids.add(encoder.encodeToString(bytes))); + uuids2.forEach(bytes -> uuids.add(encoder.encodeToString(bytes))); + assertThat(uuids).hasSize(rounds * 2); + } } diff --git a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java index 5a7cada22da..ec07a732ff8 100644 --- a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java +++ b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java @@ -24,7 +24,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; import javax.annotation.Nullable; -import org.apache.ibatis.session.RowBounds; import org.sonar.api.utils.System2; import org.sonar.db.Dao; import org.sonar.db.DbSession; @@ -46,15 +45,13 @@ public class CeActivityDao implements Dao { public void insert(DbSession dbSession, CeActivityDto dto) { dto.setCreatedAt(system2.now()); dto.setUpdatedAt(system2.now()); - dto.setIsLast(false); - mapper(dbSession).insert(dto); + dto.setIsLast(dto.getStatus() != CeActivityDto.Status.CANCELED); - List<String> uuids = mapper(dbSession).selectUuidsOfRecentlyCreatedByIsLastKey(dto.getIsLastKey(), new RowBounds(0, 1)); - // should never be empty, as a row was just inserted! - if (!uuids.isEmpty()) { - mapper(dbSession).updateIsLastToFalseForLastKey(dto.getIsLastKey(), dto.getUpdatedAt()); - mapper(dbSession).updateIsLastToTrueForUuid(uuids.get(0), dto.getUpdatedAt()); + CeActivityMapper ceActivityMapper = mapper(dbSession); + if (dto.getIsLast()) { + ceActivityMapper.updateIsLastToFalseForLastKey(dto.getIsLastKey(), dto.getUpdatedAt()); } + ceActivityMapper.insert(dto); } public List<CeActivityDto> selectOlderThan(DbSession dbSession, long beforeDate) { diff --git a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityMapper.java b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityMapper.java index ea5d9e9c885..07015f5335f 100644 --- a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityMapper.java @@ -23,12 +23,9 @@ import java.util.List; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.session.RowBounds; public interface CeActivityMapper { - List<String> selectUuidsOfRecentlyCreatedByIsLastKey(@Param("isLastKey") String isLastKey, RowBounds rowBounds); - @CheckForNull CeActivityDto selectByUuid(@Param("uuid") String uuid); @@ -44,7 +41,5 @@ public interface CeActivityMapper { void updateIsLastToFalseForLastKey(@Param("isLastKey") String isLastKey, @Param("updatedAt") long updatedAt); - void updateIsLastToTrueForUuid(@Param("uuid") String uuid, @Param("updatedAt") long updatedAt); - void deleteByUuids(@Param("uuids") List<String> uuids); } diff --git a/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml b/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml index c0281f1b9d9..c4226b8efb2 100644 --- a/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml @@ -44,14 +44,6 @@ where ca.uuid=#{uuid} </select> - <select id="selectUuidsOfRecentlyCreatedByIsLastKey" parameterType="String" resultType="String"> - select uuid - from ce_activity - where is_last_key=#{isLastKey} - and status <> 'CANCELED' - order by id desc - </select> - <select id="selectByQuery" parameterType="map" resultType="org.sonar.db.ce.CeActivityDto"> SELECT <include refid="columns"/> @@ -181,13 +173,6 @@ where is_last=${_true} and is_last_key=#{isLastKey} </update> - <update id="updateIsLastToTrueForUuid" parameterType="map"> - update ce_activity - set is_last=${_true}, - updated_at=#{updatedAt,jdbcType=BIGINT} - where uuid=#{uuid} - </update> - <delete id="deleteByUuids" parameterType="string"> delete from ce_activity |