✨ Add metrics to SoneInserter
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 31 Jul 2019 05:13:49 +0000 (07:13 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 31 Jul 2019 05:13:49 +0000 (07:13 +0200)
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/SoneInserter.java
src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt
src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt

index 71df45b..c3af217 100644 (file)
@@ -44,6 +44,7 @@ import java.util.logging.Logger;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import com.codahale.metrics.*;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidAlbumFound;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidImageFound;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidParentAlbumFound;
@@ -174,6 +175,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        /** The time the configuration was last touched. */
        private volatile long lastConfigurationUpdate;
 
+       private final MetricRegistry metricRegistry;
+
        /**
         * Creates a new core.
         *
@@ -191,7 +194,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         *            The database
         */
        @Inject
-       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) {
+       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database, MetricRegistry metricRegistry) {
                super("Sone Core");
                this.configuration = configuration;
                this.freenetInterface = freenetInterface;
@@ -202,6 +205,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                this.webOfTrustUpdater = webOfTrustUpdater;
                this.eventBus = eventBus;
                this.database = database;
+               this.metricRegistry = metricRegistry;
                preferences = new Preferences(eventBus);
        }
 
@@ -618,7 +622,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                sone.setLatestEdition(fromNullable(tryParse(property)).or(0L));
                sone.setClient(new Client("Sone", SonePlugin.getPluginVersion()));
                sone.setKnown(true);
-               SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, ownIdentity.getId());
+               SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, metricRegistry, ownIdentity.getId());
                soneInserter.insertionDelayChanged(new InsertionDelayChangedEvent(preferences.getInsertionDelay()));
                eventBus.register(soneInserter);
                synchronized (soneInserters) {
index 79a6259..af180a0 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.core;
 
 import static java.lang.String.format;
 import static java.lang.System.currentTimeMillis;
+import static java.util.concurrent.TimeUnit.*;
 import static java.util.logging.Logger.getLogger;
 import static net.pterodactylus.sone.data.Album.NOT_EMPTY;
 
@@ -31,10 +32,13 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.codahale.metrics.*;
+import com.google.common.base.*;
 import net.pterodactylus.sone.core.SoneModificationDetector.LockableFingerprintProvider;
 import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
 import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent;
@@ -58,7 +62,6 @@ import net.pterodactylus.util.template.TemplateParser;
 import net.pterodactylus.util.template.XmlFilter;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Ordering;
 import com.google.common.eventbus.EventBus;
@@ -105,6 +108,7 @@ public class SoneInserter extends AbstractService {
        private final SoneModificationDetector soneModificationDetector;
        private final long delay;
        private final String soneId;
+       private final Histogram soneInsertDurationHistogram;
 
        /**
         * Creates a new Sone inserter.
@@ -118,8 +122,8 @@ public class SoneInserter extends AbstractService {
         * @param soneId
         *            The ID of the Sone to insert
         */
-       public SoneInserter(final Core core, EventBus eventBus, FreenetInterface freenetInterface, final String soneId) {
-               this(core, eventBus, freenetInterface, soneId, new SoneModificationDetector(new LockableFingerprintProvider() {
+       public SoneInserter(final Core core, EventBus eventBus, FreenetInterface freenetInterface, MetricRegistry metricRegistry, final String soneId) {
+               this(core, eventBus, freenetInterface, metricRegistry, soneId, new SoneModificationDetector(new LockableFingerprintProvider() {
                        @Override
                        public boolean isLocked() {
                                Sone sone = core.getSone(soneId);
@@ -141,11 +145,12 @@ public class SoneInserter extends AbstractService {
        }
 
        @VisibleForTesting
-       SoneInserter(Core core, EventBus eventBus, FreenetInterface freenetInterface, String soneId, SoneModificationDetector soneModificationDetector, long delay) {
+       SoneInserter(Core core, EventBus eventBus, FreenetInterface freenetInterface, MetricRegistry metricRegistry, String soneId, SoneModificationDetector soneModificationDetector, long delay) {
                super("Sone Inserter for “" + soneId + "”", false);
                this.core = core;
                this.eventBus = eventBus;
                this.freenetInterface = freenetInterface;
+               this.soneInsertDurationHistogram = metricRegistry.histogram("sone.insert.duration");
                this.soneId = soneId;
                this.soneModificationDetector = soneModificationDetector;
                this.delay = delay;
@@ -229,8 +234,11 @@ public class SoneInserter extends AbstractService {
                                                sone.setStatus(SoneStatus.inserting);
                                                long insertTime = currentTimeMillis();
                                                eventBus.post(new SoneInsertingEvent(sone));
+                                               Stopwatch stopwatch = Stopwatch.createStarted();
                                                FreenetURI finalUri = freenetInterface.insertDirectory(sone.getInsertUri(), insertInformation.generateManifestEntries(), "index.html");
-                                               eventBus.post(new SoneInsertedEvent(sone, currentTimeMillis() - insertTime, insertInformation.getFingerprint()));
+                                               stopwatch.stop();
+                                               soneInsertDurationHistogram.update(stopwatch.elapsed(MICROSECONDS));
+                                               eventBus.post(new SoneInsertedEvent(sone, stopwatch.elapsed(MILLISECONDS), insertInformation.getFingerprint()));
                                                /* at this point we might already be stopped. */
                                                if (shouldStop()) {
                                                        /* if so, bail out, don’t change anything. */
index f7ac026..9eb123c 100644 (file)
@@ -1,5 +1,6 @@
 package net.pterodactylus.sone.core
 
+import com.codahale.metrics.*
 import com.google.common.collect.*
 import com.google.common.eventbus.*
 import net.pterodactylus.sone.core.event.*
@@ -50,7 +51,8 @@ class CoreTest {
                val webOfTrustUpdater = mock<WebOfTrustUpdater>()
                val eventBus = mock<EventBus>()
                val database = mock<Database>()
-               val core = Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database)
+               val metricRegistry = MetricRegistry()
+               val core = Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry)
                val ownIdentity = mock<OwnIdentity>()
                val identity = mock<Identity>()
                whenever(identity.id).thenReturn("sone-id")
@@ -166,7 +168,8 @@ class CoreTest {
                val updateChecker = mock<UpdateChecker>()
                val webOfTrustUpdater = mock<WebOfTrustUpdater>()
                val database = mock<Database>()
-               return Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database)
+               val metricRegistry = MetricRegistry()
+               return Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry)
        }
 
 }
index e541915..d2bb3b4 100644 (file)
@@ -1,5 +1,6 @@
 package net.pterodactylus.sone.core
 
+import com.codahale.metrics.*
 import com.google.common.base.*
 import com.google.common.base.Optional
 import com.google.common.eventbus.*
@@ -26,12 +27,14 @@ import org.mockito.hamcrest.MockitoHamcrest.*
 import org.mockito.stubbing.*
 import java.lang.System.*
 import java.util.*
+import kotlin.test.Test
 
 /**
  * Unit test for [SoneInserter] and its subclasses.
  */
 class SoneInserterTest {
 
+       private val metricRegistry = MetricRegistry()
        private val core = mock<Core>()
        private val eventBus = mock<EventBus>()
        private val freenetInterface = mock<FreenetInterface>()
@@ -46,7 +49,7 @@ class SoneInserterTest {
        @Test
        fun `insertion delay is forwarded to sone inserter`() {
                val eventBus = AsyncEventBus(directExecutor())
-               eventBus.register(SoneInserter(core, eventBus, freenetInterface, "SoneId"))
+               eventBus.register(SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId"))
                eventBus.post(InsertionDelayChangedEvent(15))
                assertThat(SoneInserter.getInsertionDelay().get(), equalTo(15))
        }
@@ -64,27 +67,27 @@ class SoneInserterTest {
        fun `isModified is true if modification detector says so`() {
                val soneModificationDetector = mock<SoneModificationDetector>()
                whenever(soneModificationDetector.isModified).thenReturn(true)
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
                assertThat(soneInserter.isModified, equalTo(true))
        }
 
        @Test
        fun `isModified is false if modification detector says so`() {
                val soneModificationDetector = mock<SoneModificationDetector>()
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
                assertThat(soneInserter.isModified, equalTo(false))
        }
 
        @Test
        fun `last fingerprint is stored correctly`() {
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId")
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId")
                soneInserter.lastInsertFingerprint = "last-fingerprint"
                assertThat(soneInserter.lastInsertFingerprint, equalTo("last-fingerprint"))
        }
 
        @Test
        fun `sone inserter stops when it should`() {
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId")
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId")
                soneInserter.stop()
                soneInserter.serviceRun()
        }
@@ -97,7 +100,7 @@ class SoneInserterTest {
                val soneModificationDetector = mock<SoneModificationDetector>()
                whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true)
                whenever(freenetInterface.insertDirectory(eq(insertUri), any<HashMap<String, Any>>(), eq("index.html"))).thenReturn(finalUri)
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
                doAnswer {
                        soneInserter.stop()
                        null
@@ -119,7 +122,7 @@ class SoneInserterTest {
                val sone = createSone(insertUri)
                val soneModificationDetector = mock<SoneModificationDetector>()
                whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true)
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
                whenever(freenetInterface.insertDirectory(eq(insertUri), any<HashMap<String, Any>>(), eq("index.html"))).thenAnswer {
                        soneInserter.stop()
                        finalUri
@@ -140,7 +143,7 @@ class SoneInserterTest {
                val insertUri = mock<FreenetURI>()
                createSone(insertUri)
                val soneModificationDetector = mock<SoneModificationDetector>()
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
                Thread(Runnable {
                        try {
                                Thread.sleep(500)
@@ -161,7 +164,7 @@ class SoneInserterTest {
                val sone = createSone(insertUri)
                val soneModificationDetector = mock<SoneModificationDetector>()
                whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true)
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
                val soneException = SoneException(Exception())
                whenever(freenetInterface.insertDirectory(eq(insertUri), any<HashMap<String, Any>>(), eq("index.html"))).thenAnswer {
                        soneInserter.stop()
@@ -181,7 +184,7 @@ class SoneInserterTest {
        @Test
        fun `sone inserter exits if sone is unknown`() {
                val soneModificationDetector = mock<SoneModificationDetector>()
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
                whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true)
                whenever(core.getSone("SoneId")).thenReturn(null)
                soneInserter.serviceRun()
@@ -190,7 +193,7 @@ class SoneInserterTest {
        @Test
        fun `sone inserter catches exception and continues`() {
                val soneModificationDetector = mock<SoneModificationDetector>()
-               val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
                val stopInserterAndThrowException = Answer<Optional<Sone>> {
                        soneInserter.stop()
                        throw NullPointerException()
@@ -236,4 +239,38 @@ class SoneInserterTest {
                                nullValue())
        }
 
+       @Test
+       fun `successful insert updates metrics`() {
+               val insertUri = mock<FreenetURI>()
+               val finalUri = mock<FreenetURI>()
+               createSone(insertUri)
+               val soneModificationDetector = mock<SoneModificationDetector>()
+               whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true)
+               whenever(freenetInterface.insertDirectory(eq(insertUri), any<HashMap<String, Any>>(), eq("index.html"))).thenReturn(finalUri)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry,"SoneId", soneModificationDetector, 1)
+               doAnswer {
+                       soneInserter.stop()
+                       null
+               }.`when`(core).touchConfiguration()
+               soneInserter.serviceRun()
+               val histogram = metricRegistry.histogram("sone.insert.duration")
+               assertThat(histogram.count, equalTo(1L))
+       }
+
+       @Test
+       fun `unsuccessful insert does not update metrics`() {
+               val insertUri = mock<FreenetURI>()
+               createSone(insertUri)
+               val soneModificationDetector = mock<SoneModificationDetector>()
+               whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true)
+               val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1)
+               whenever(freenetInterface.insertDirectory(eq(insertUri), any<HashMap<String, Any>>(), eq("index.html"))).thenAnswer {
+                       soneInserter.stop()
+                       throw SoneException(Exception())
+               }
+               soneInserter.serviceRun()
+               val histogram = metricRegistry.histogram("sone.insert.duration")
+               assertThat(histogram.count, equalTo(0L))
+       }
+
 }