From 6d6359057ee0e992780f51b9e6b42faa76075c6a Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Mon, 21 Aug 2017 14:37:12 +0200 Subject: [PATCH] SONAR-9721 Send telemetry data once a week --- .../server/telemetry/TelemetryDaemon.java | 28 ++++++++-- .../server/telemetry/TelemetryDaemonTest.java | 53 +++++++++++++++++-- .../java/org/sonar/api/utils/DateUtils.java | 4 ++ .../org/sonar/api/utils/DateUtilsTest.java | 2 + .../tests/telemetry/TelemetryTest.java | 1 + 5 files changed, 79 insertions(+), 9 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java index 7ec293d7e19..d32b773bcd6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java +++ b/server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDaemon.java @@ -21,6 +21,7 @@ package org.sonar.server.telemetry; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.io.StringWriter; +import java.util.Optional; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -28,25 +29,33 @@ import org.picocontainer.Startable; import org.sonar.api.config.Configuration; import org.sonar.api.platform.Server; import org.sonar.api.server.ServerSide; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.System2; import org.sonar.api.utils.text.JsonWriter; +import org.sonar.server.property.InternalProperties; + +import static org.sonar.api.utils.DateUtils.formatDate; +import static org.sonar.api.utils.DateUtils.parseDate; @ServerSide public class TelemetryDaemon implements Startable { private static final String THREAD_NAME_PREFIX = "sq-telemetry-service-"; - private static final Logger LOG = Loggers.get(TelemetryDaemon.class); + private static final int SEVEN_DAYS = 7 * 24 * 60 * 60 * 1_000; + private static final String I_PROP_LAST_PING = "sonar.telemetry.lastPing"; private final TelemetryClient telemetryClient; + private final InternalProperties internalProperties; private final Server server; + private final System2 system2; private final TelemetryFrequency frequencyInSeconds; private ScheduledExecutorService executorService; - public TelemetryDaemon(TelemetryClient telemetryClient, Server server, Configuration config) { + public TelemetryDaemon(TelemetryClient telemetryClient, InternalProperties internalProperties, Server server, System2 system2, Configuration config) { this.telemetryClient = telemetryClient; + this.internalProperties = internalProperties; this.server = server; this.frequencyInSeconds = new TelemetryFrequency(config); + this.system2 = system2; } @Override @@ -58,6 +67,12 @@ public class TelemetryDaemon implements Startable { .build()); executorService.scheduleWithFixedDelay(() -> { try { + Optional lastPing = internalProperties.read(I_PROP_LAST_PING).map(Long::valueOf); + long now = system2.now(); + if (lastPing.isPresent() && now - lastPing.get() < SEVEN_DAYS) { + return; + } + StringWriter json = new StringWriter(); try (JsonWriter writer = JsonWriter.of(json)) { writer.beginObject(); @@ -65,6 +80,7 @@ public class TelemetryDaemon implements Startable { writer.endObject(); } telemetryClient.send(json.toString()); + internalProperties.write(I_PROP_LAST_PING, String.valueOf(startOfDay(now))); } catch (Exception e) { // fail silently } @@ -81,4 +97,8 @@ public class TelemetryDaemon implements Startable { Thread.currentThread().interrupt(); } } + + private static long startOfDay(long now) { + return parseDate(formatDate(now)).getTime(); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java b/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java index 1f523fd723f..62011777a39 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java @@ -24,18 +24,28 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.internal.TestSystem2; +import org.sonar.server.property.InternalProperties; +import org.sonar.server.property.MapInternalProperties; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.contains; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; +import static org.sonar.api.utils.DateUtils.parseDate; public class TelemetryDaemonTest { + private static final long ONE_HOUR = 60 * 60 * 1_000L; + private static final long ONE_DAY = 24 * ONE_HOUR; + private TelemetryClient client = mock(TelemetryClient.class); + private InternalProperties internalProperties = new MapInternalProperties(); private FakeServer server = new FakeServer(); + private TestSystem2 system2 = new TestSystem2(); private MapSettings settings; private TelemetryDaemon underTest; @@ -43,8 +53,9 @@ public class TelemetryDaemonTest { @Before public void setUp() throws Exception { settings = new MapSettings(new PropertyDefinitions(TelemetryProperties.class)); + system2.setNow(System.currentTimeMillis()); - underTest = new TelemetryDaemon(client, server, settings.asConfig()); + underTest = new TelemetryDaemon(client, internalProperties, server, system2, settings.asConfig()); } @Test @@ -56,13 +67,18 @@ public class TelemetryDaemonTest { } @Test - public void send_data_periodically() { + public void check_if_should_send_data_periodically() { + long now = system2.now(); + long sixDaysAgo = now - (ONE_DAY * 6L); + long sevenDaysAgo = now - (ONE_DAY * 7L); + internalProperties.write("sonar.telemetry.lastPing", String.valueOf(sixDaysAgo)); settings.setProperty("sonar.telemetry.frequency", "1"); - underTest = new TelemetryDaemon(client, server, settings.asConfig()); - + underTest = new TelemetryDaemon(client, internalProperties, server, system2, settings.asConfig()); underTest.start(); + verify(client, timeout(1_000).never()).send(anyString()); + internalProperties.write("sonar.telemetry.lastPing", String.valueOf(sevenDaysAgo)); - verify(client, timeout(3_000).atLeast(2)).send(anyString()); + verify(client, timeout(1_000).atLeastOnce()).send(anyString()); } @Test @@ -74,4 +90,31 @@ public class TelemetryDaemonTest { verify(client, timeout(2_000).atLeastOnce()).send(contains(id)); } + + @Test + public void do_not_send_data_if_last_ping_earlier_than_one_week_ago() { + settings.setProperty("sonar.telemetry.frequency", "1"); + long now = system2.now(); + long sixDaysAgo = now - (ONE_DAY * 6L); + + internalProperties.write("sonar.telemetry.lastPing", String.valueOf(sixDaysAgo)); + underTest.start(); + + verify(client, timeout(2_000).never()).send(anyString()); + } + + @Test + public void send_data_if_last_ping_is_one_week_ago() { + settings.setProperty("sonar.telemetry.frequency", "1"); + long today = parseDate("2017-08-01").getTime(); + system2.setNow(today + 15 * ONE_HOUR); + long now = system2.now(); + long sevenDaysAgo = now - (ONE_DAY * 7L); + internalProperties.write("sonar.telemetry.lastPing", String.valueOf(sevenDaysAgo)); + + underTest.start(); + + verify(client, timeout(2_000)).send(anyString()); + assertThat(internalProperties.read("sonar.telemetry.lastPing").get()).isEqualTo(String.valueOf(today)); + } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java index d226fe6270a..aa326dce1d0 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java @@ -54,6 +54,10 @@ public final class DateUtils { return THREAD_SAFE_DATE_FORMAT.format(d); } + public static String formatDate(long ms) { + return THREAD_SAFE_DATE_FORMAT.format(new Date(ms)); + } + public static String formatDateTime(Date d) { return THREAD_SAFE_DATETIME_FORMAT.format(d); } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java index cda37e7996f..0dd1ed07292 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java @@ -103,7 +103,9 @@ public class DateUtilsTest { @Test public void shouldFormatDate() { assertThat(DateUtils.formatDate(new Date())).startsWith("20"); + assertThat(DateUtils.formatDate(new Date().getTime())).startsWith("20"); assertThat(DateUtils.formatDate(new Date()).length()).isEqualTo(10); + assertThat(DateUtils.formatDate(new Date().getTime()).length()).isEqualTo(10); } @Test diff --git a/tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryTest.java b/tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryTest.java index 48797c7a7d5..c6b20b58fb7 100644 --- a/tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryTest.java +++ b/tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryTest.java @@ -57,6 +57,7 @@ public class TelemetryTest { orchestrator = Orchestrator.builderEnv() .addPlugin(xooPlugin()) .setServerProperty("sonar.telemetry.url", url) + .setServerProperty("sonar.telemetry.frequency", "1") .setServerProperty("sonar.core.id", serverId) .build(); orchestrator.start(); -- 2.39.5