From: David ‘Bombe’ Roden Date: Wed, 31 Jul 2019 05:13:49 +0000 (+0200) Subject: ✨ Add metrics to SoneInserter X-Git-Tag: v81^2~146 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=aa83028a17a61b8a2103475fae4624fbfe4c9ca5 ✨ Add metrics to SoneInserter --- diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 71df45b..c3af217 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -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) { diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 79a6259..af180a0 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -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. */ diff --git a/src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt index f7ac026..9eb123c 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt @@ -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() val eventBus = mock() val database = mock() - 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() val identity = mock() whenever(identity.id).thenReturn("sone-id") @@ -166,7 +168,8 @@ class CoreTest { val updateChecker = mock() val webOfTrustUpdater = mock() val database = mock() - 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) } } diff --git a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt index e541915..d2bb3b4 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt @@ -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() private val eventBus = mock() private val freenetInterface = mock() @@ -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() 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() - 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() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) whenever(freenetInterface.insertDirectory(eq(insertUri), 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() 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>(), eq("index.html"))).thenAnswer { soneInserter.stop() finalUri @@ -140,7 +143,7 @@ class SoneInserterTest { val insertUri = mock() createSone(insertUri) val soneModificationDetector = mock() - 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() 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>(), eq("index.html"))).thenAnswer { soneInserter.stop() @@ -181,7 +184,7 @@ class SoneInserterTest { @Test fun `sone inserter exits if sone is unknown`() { val soneModificationDetector = mock() - 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() - val soneInserter = SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) val stopInserterAndThrowException = Answer> { soneInserter.stop() throw NullPointerException() @@ -236,4 +239,38 @@ class SoneInserterTest { nullValue()) } + @Test + fun `successful insert updates metrics`() { + val insertUri = mock() + val finalUri = mock() + createSone(insertUri) + val soneModificationDetector = mock() + whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) + whenever(freenetInterface.insertDirectory(eq(insertUri), 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() + createSone(insertUri) + val soneModificationDetector = mock() + whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + whenever(freenetInterface.insertDirectory(eq(insertUri), any>(), eq("index.html"))).thenAnswer { + soneInserter.stop() + throw SoneException(Exception()) + } + soneInserter.serviceRun() + val histogram = metricRegistry.histogram("sone.insert.duration") + assertThat(histogram.count, equalTo(0L)) + } + }