You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/06/19 11:03:48 UTC

[james-project] branch master updated (60d08c5 -> 7f08147)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.


    from 60d08c5  JAMES-3213 Source ReplyTo in ICALToJsonAttribute
     new 46e6f64  JAMES-3170: Add metric latency for BackEnd in CachedBlobStore
     new 7492fda  JAMES-3170: Add Grafana board for BackEndLatencyMetric
     new 93e61a8  JAMES-3170 Rename MetricFactory run methods
     new 520cdac  JAMES-3170 Fix metric measurement upon reactor publisher replay
     new 4c44a9b  JAMES:3170: add test about timerShouldAllowRecordingIndividualAndTotalTiming
     new 7f08147  JAMES-3208 Add SizeGreaterThan integration test

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../CacheBlobStore-15911761170000-dashboard.json   | 163 +++++++++++++++++++--
 .../mailbox/events/delivery/InVmEventDelivery.java |   2 +-
 .../mailbox/events/MailboxListenerExecutor.java    |   2 +-
 .../vault/blob/BlobStoreDeletedMessageVault.java   |  10 +-
 .../james/mailbox/store/PreDeletionHooks.java      |   2 +-
 .../james/mailbox/tika/TikaTextExtractor.java      |   9 +-
 .../apache/james/metrics/api/MetricFactory.java    |  10 +-
 .../dropwizard/DropWizardMetricFactory.java        |  19 +--
 .../metrics/dropwizard/DropWizardTimeMetric.java   |  11 ++
 .../dropwizard/DropWizardMetricFactoryTest.java    |  65 ++++++++
 .../james/metrics/logger/DefaultMetricFactory.java |  16 +-
 metrics/metrics-tests/pom.xml                      |   5 +
 .../metrics/tests/RecordingMetricFactory.java      |  16 +-
 .../metrics/tests/RecordingMetricFactoryTest.java  |  34 ++++-
 .../apache/james/blob/api/MetricableBlobStore.java |  12 +-
 .../blob/cassandra/cache/CachedBlobStore.java      |  37 +++--
 .../blob/cassandra/cache/CachedBlobStoreTest.java  | 156 ++++++++++++++++----
 ...st.java => SizeGreaterThanIntegrationTest.java} |  63 +++++---
 .../james/jmap/draft/methods/GetFilterMethod.java  |   2 +-
 .../jmap/draft/methods/GetMailboxesMethod.java     |   2 +-
 .../jmap/draft/methods/GetMessageListMethod.java   |   2 +-
 .../jmap/draft/methods/GetMessagesMethod.java      |   2 +-
 .../draft/methods/GetVacationResponseMethod.java   |   2 +-
 .../james/jmap/draft/methods/SendMDNProcessor.java |   2 +-
 .../james/jmap/draft/methods/SetFilterMethod.java  |   2 +-
 .../jmap/draft/methods/SetMailboxesMethod.java     |   2 +-
 .../methods/SetMessagesDestructionProcessor.java   |   2 +-
 .../jmap/draft/methods/SetMessagesMethod.java      |   2 +-
 .../draft/methods/SetVacationResponseMethod.java   |   2 +-
 .../james/jmap/http/AuthenticationRoutes.java      |   2 +-
 .../jmap/http/DefaultMailboxesProvisioner.java     |   2 +-
 .../org/apache/james/jmap/http/DownloadRoutes.java |   4 +-
 .../org/apache/james/jmap/http/JMAPApiRoutes.java  |   2 +-
 .../org/apache/james/jmap/http/UploadRoutes.java   |   2 +-
 .../james/jmap/method/MailboxGetMethod.scala       |   4 +-
 .../org/apache/james/jmap/http/Authenticator.java  |   2 +-
 .../james/spamassassin/SpamAssassinInvoker.java    |   8 +-
 37 files changed, 533 insertions(+), 147 deletions(-)
 copy server/mailet/integration-testing/src/test/java/org/apache/james/mailets/{CommonMailetConfigurationTest.java => SizeGreaterThanIntegrationTest.java} (54%)


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 05/06: JAMES:3170: add test about timerShouldAllowRecordingIndividualAndTotalTiming

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 4c44a9ba96b8accb66fe2dc82efbdb840899c718
Author: duc91 <du...@gmail.com>
AuthorDate: Thu Jun 18 17:23:31 2020 +0700

    JAMES:3170: add test about timerShouldAllowRecordingIndividualAndTotalTiming
---
 .../dropwizard/DropWizardMetricFactoryTest.java    | 35 ++++++++++++++++++++++
 metrics/metrics-tests/pom.xml                      |  5 ++++
 .../metrics/tests/RecordingMetricFactoryTest.java  | 18 +++++++----
 3 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/metrics/metrics-dropwizard/src/test/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactoryTest.java b/metrics/metrics-dropwizard/src/test/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactoryTest.java
index 368075c..968095e 100644
--- a/metrics/metrics-dropwizard/src/test/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactoryTest.java
+++ b/metrics/metrics-dropwizard/src/test/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactoryTest.java
@@ -26,11 +26,13 @@ import java.time.temporal.ChronoUnit;
 
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.metrics.api.MetricFactoryContract;
+import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import com.codahale.metrics.MetricRegistry;
 
+import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 class DropWizardMetricFactoryTest implements MetricFactoryContract {
@@ -68,4 +70,37 @@ class DropWizardMetricFactoryTest implements MetricFactoryContract {
         assertThat(testee.timer("any").getTimer().getSnapshot().get99thPercentile())
             .isLessThan(duration.get(ChronoUnit.NANOS) * 2);
     }
+
+    @Test
+    void timerShouldAllowRecordingIndividualAndTotalTiming() {
+        Duration duration = Duration.ofMillis(100);
+        Flux.from(testee.decoratePublisherWithTimerMetric("anyTotal",
+            Flux.from(testee.decoratePublisherWithTimerMetric("any", Mono.delay(duration)))
+            .repeat(5)))
+            .blockLast();
+
+        SoftAssertions.assertSoftly(softly -> {
+            softly.assertThat(testee.timer("any").getTimer().getCount())
+                .isEqualTo(6);
+            softly.assertThat(testee.timer("anyTotal").getTimer().getCount())
+                .isEqualTo(1);
+        });
+    }
+
+     @Test
+    void timerShouldAllowRecordingIndividualAndTotalTimingWithP99() {
+        Duration duration = Duration.ofMillis(100);
+        Flux.from(testee.decoratePublisherWithTimerMetric("anyTotal",
+            Flux.from(testee.decoratePublisherWithTimerMetric("any", Mono.delay(duration)))
+            .repeat(5)))
+            .blockLast();
+         SoftAssertions.assertSoftly(softly -> {
+             softly.assertThat(testee.timer("any").getTimer().getSnapshot().get99thPercentile())
+                 .isLessThan(duration.get(ChronoUnit.NANOS) * 2)
+                 .isGreaterThan(duration.get(ChronoUnit.NANOS));
+             softly.assertThat(testee.timer("anyTotal").getTimer().getSnapshot().get99thPercentile())
+                 .isLessThan(duration.get(ChronoUnit.NANOS) * 7)
+                 .isGreaterThan(duration.get(ChronoUnit.NANOS) * 6);
+         });
+    }
 }
\ No newline at end of file
diff --git a/metrics/metrics-tests/pom.xml b/metrics/metrics-tests/pom.xml
index c8971d6..de47e55 100644
--- a/metrics/metrics-tests/pom.xml
+++ b/metrics/metrics-tests/pom.xml
@@ -62,5 +62,10 @@
             <groupId>io.projectreactor</groupId>
             <artifactId>reactor-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java b/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
index bbdcf92..b8d001f 100644
--- a/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
+++ b/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
@@ -22,6 +22,7 @@ package org.apache.james.metrics.tests;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.time.Duration;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.james.metrics.api.Metric;
@@ -29,6 +30,8 @@ import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.metrics.api.MetricFactoryContract;
 import org.apache.james.metrics.api.TimeMetric;
 import org.apache.james.util.concurrency.ConcurrentTestRunner;
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionFactory;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -40,6 +43,7 @@ class RecordingMetricFactoryTest implements MetricFactoryContract {
     private static final String METRIC_NAME = "metric";
     private static final java.time.Duration ONE_SECOND = java.time.Duration.ofSeconds(1);
     private static final java.time.Duration FIVE_SECONDS = java.time.Duration.ofSeconds(5);
+    private static final ConditionFactory WAIT_AT_MOST = Awaitility.waitAtMost(5, TimeUnit.SECONDS);
 
     private RecordingMetricFactory testee;
 
@@ -128,9 +132,10 @@ class RecordingMetricFactoryTest implements MetricFactoryContract {
             .repeat(5)
             .blockLast();
 
-        assertThat(testee.executionTimesFor("any"))
-            .hasSize(6)
-            .allSatisfy(timing -> assertThat(timing).isLessThan(duration.multipliedBy(2)));
+        WAIT_AT_MOST.untilAsserted(() ->
+            assertThat(testee.executionTimesFor("any"))
+                .hasSize(6)
+                .allSatisfy(timing -> assertThat(timing).isLessThan(duration.multipliedBy(2))));
     }
 
     @Test
@@ -140,8 +145,9 @@ class RecordingMetricFactoryTest implements MetricFactoryContract {
             .repeat(5)
             .blockLast();
 
-        assertThat(testee.executionTimesFor("any"))
-            .hasSize(6)
-            .allSatisfy(timing -> assertThat(timing).isLessThan(duration.multipliedBy(2)));
+        WAIT_AT_MOST.untilAsserted(() ->
+            assertThat(testee.executionTimesFor("any"))
+                .hasSize(6)
+                .allSatisfy(timing -> assertThat(timing).isLessThan(duration.multipliedBy(2))));
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 06/06: JAMES-3208 Add SizeGreaterThan integration test

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 7f08147d5f0e0af5d6fbe22460a5de4c75778a52
Author: RĂ©mi KOWALSKI <rk...@linagora.com>
AuthorDate: Tue Jun 9 17:40:12 2020 +0200

    JAMES-3208 Add SizeGreaterThan integration test
---
 .../mailets/SizeGreaterThanIntegrationTest.java    | 113 +++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SizeGreaterThanIntegrationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SizeGreaterThanIntegrationTest.java
new file mode 100644
index 0000000..30715ef
--- /dev/null
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SizeGreaterThanIntegrationTest.java
@@ -0,0 +1,113 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailets;
+
+import static org.apache.james.mailets.configuration.CommonProcessors.ERROR_REPOSITORY;
+import static org.apache.james.mailets.configuration.Constants.DEFAULT_DOMAIN;
+import static org.apache.james.mailets.configuration.Constants.LOCALHOST_IP;
+import static org.apache.james.mailets.configuration.Constants.PASSWORD;
+import static org.apache.james.mailets.configuration.Constants.RECIPIENT;
+import static org.apache.james.mailets.configuration.Constants.awaitAtMostOneMinute;
+
+import org.apache.james.MemoryJamesServerMain;
+import org.apache.james.mailets.configuration.CommonProcessors;
+import org.apache.james.mailets.configuration.MailetConfiguration;
+import org.apache.james.mailets.configuration.MailetContainer;
+import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.modules.protocols.ImapGuiceProbe;
+import org.apache.james.modules.protocols.SmtpGuiceProbe;
+import org.apache.james.transport.mailets.ToRepository;
+import org.apache.james.transport.matchers.SizeGreaterThan;
+import org.apache.james.utils.DataProbeImpl;
+import org.apache.james.utils.MailRepositoryProbeImpl;
+import org.apache.james.utils.SMTPMessageSender;
+import org.apache.james.utils.TestIMAPClient;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class SizeGreaterThanIntegrationTest {
+    public static final String POSTMASTER = "postmaster@" + DEFAULT_DOMAIN;
+    public static final String SENDER = "sender@" + DEFAULT_DOMAIN;
+
+    @Rule
+    public TemporaryFolder temporaryFolder = new TemporaryFolder();
+    @Rule
+    public TestIMAPClient testIMAPClient = new TestIMAPClient();
+    @Rule
+    public SMTPMessageSender messageSender = new SMTPMessageSender(DEFAULT_DOMAIN);
+
+    private TemporaryJamesServer jamesServer;
+
+    @Before
+    public void setup() throws Exception {
+        jamesServer = TemporaryJamesServer.builder()
+            .withBase(MemoryJamesServerMain.SMTP_AND_IMAP_MODULE)
+            .withMailetContainer(
+                generateMailetContainerConfiguration())
+            .build(temporaryFolder.newFolder());
+
+        jamesServer.getProbe(DataProbeImpl.class)
+            .fluent()
+            .addDomain(DEFAULT_DOMAIN)
+            .addUser(RECIPIENT, PASSWORD)
+            .addUser(SENDER, PASSWORD);
+    }
+
+    @After
+    public void tearDown() {
+        jamesServer.shutdown();
+    }
+
+    @Test
+    public void mailShouldGoToErrorRepositoryWhenSizeExceeded() throws Exception {
+
+        messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .sendMessageWithHeaders(SENDER, RECIPIENT, "01234567\r\n".repeat(1025));
+
+        awaitAtMostOneMinute.until(() -> jamesServer.getProbe(MailRepositoryProbeImpl.class).getRepositoryMailCount(ERROR_REPOSITORY) == 1);
+    }
+
+    @Test
+    public void mailShouldBeDeliveredWhenSizeWithinLimit() throws Exception {
+        messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .sendMessageWithHeaders(SENDER, RECIPIENT, "01234567\r\n".repeat(1000));
+
+        testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD)
+            .select(TestIMAPClient.INBOX)
+            .awaitMessage(awaitAtMostOneMinute);
+    }
+
+    private MailetContainer.Builder generateMailetContainerConfiguration() {
+        return TemporaryJamesServer.DEFAULT_MAILET_CONTAINER_CONFIGURATION
+                .postmaster(POSTMASTER)
+                .putProcessor(ProcessorConfiguration.transport()
+                        .addMailet(MailetConfiguration.builder()
+                                .matcher(SizeGreaterThan.class)
+                                .matcherCondition("10k")
+                                .mailet(ToRepository.class)
+                                .addProperty("repositoryPath", ERROR_REPOSITORY.asString())
+                                .addProperty("passThrough", "false"))
+                        .addMailetsFrom(CommonProcessors.deliverOnlyTransport()));
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 03/06: JAMES-3170 Rename MetricFactory run methods

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 93e61a856deb7e9e3bef76eded8049699fe274f9
Author: duc91 <du...@gmail.com>
AuthorDate: Thu Jun 11 09:50:51 2020 +0700

    JAMES-3170 Rename MetricFactory run methods
    
    It allows disambiguating differences between suppliers and publishers
    as passing a Supplier<Publisher> has in non obvious results
---
 .../mailbox/events/delivery/InVmEventDelivery.java |  2 +-
 .../mailbox/events/MailboxListenerExecutor.java    |  2 +-
 .../vault/blob/BlobStoreDeletedMessageVault.java   | 10 +--
 .../james/mailbox/store/PreDeletionHooks.java      |  2 +-
 .../james/mailbox/tika/TikaTextExtractor.java      |  9 +-
 .../apache/james/metrics/api/MetricFactory.java    | 10 +--
 .../dropwizard/DropWizardMetricFactory.java        |  4 +-
 .../james/metrics/logger/DefaultMetricFactory.java |  4 +-
 .../metrics/tests/RecordingMetricFactory.java      |  4 +-
 .../metrics/tests/RecordingMetricFactoryTest.java  |  2 +-
 .../apache/james/blob/api/MetricableBlobStore.java | 12 +--
 .../blob/cassandra/cache/CachedBlobStore.java      | 35 +++++---
 .../blob/cassandra/cache/CachedBlobStoreTest.java  | 96 ++++++++++++++++------
 .../james/jmap/draft/methods/GetFilterMethod.java  |  2 +-
 .../jmap/draft/methods/GetMailboxesMethod.java     |  2 +-
 .../jmap/draft/methods/GetMessageListMethod.java   |  2 +-
 .../jmap/draft/methods/GetMessagesMethod.java      |  2 +-
 .../draft/methods/GetVacationResponseMethod.java   |  2 +-
 .../james/jmap/draft/methods/SendMDNProcessor.java |  2 +-
 .../james/jmap/draft/methods/SetFilterMethod.java  |  2 +-
 .../jmap/draft/methods/SetMailboxesMethod.java     |  2 +-
 .../methods/SetMessagesDestructionProcessor.java   |  2 +-
 .../jmap/draft/methods/SetMessagesMethod.java      |  2 +-
 .../draft/methods/SetVacationResponseMethod.java   |  2 +-
 .../james/jmap/http/AuthenticationRoutes.java      |  2 +-
 .../jmap/http/DefaultMailboxesProvisioner.java     |  2 +-
 .../org/apache/james/jmap/http/DownloadRoutes.java |  4 +-
 .../org/apache/james/jmap/http/JMAPApiRoutes.java  |  2 +-
 .../org/apache/james/jmap/http/UploadRoutes.java   |  2 +-
 .../james/jmap/method/MailboxGetMethod.scala       |  4 +-
 .../org/apache/james/jmap/http/Authenticator.java  |  2 +-
 .../james/spamassassin/SpamAssassinInvoker.java    |  8 +-
 32 files changed, 144 insertions(+), 96 deletions(-)

diff --git a/mailbox/event/event-memory/src/main/java/org/apache/james/mailbox/events/delivery/InVmEventDelivery.java b/mailbox/event/event-memory/src/main/java/org/apache/james/mailbox/events/delivery/InVmEventDelivery.java
index 369fe3f..f4f7036 100644
--- a/mailbox/event/event-memory/src/main/java/org/apache/james/mailbox/events/delivery/InVmEventDelivery.java
+++ b/mailbox/event/event-memory/src/main/java/org/apache/james/mailbox/events/delivery/InVmEventDelivery.java
@@ -79,7 +79,7 @@ public class InVmEventDelivery implements EventDelivery {
 
     private Mono<Void> doDeliverToListener(MailboxListener.ReactiveMailboxListener mailboxListener, Event event) {
         if (mailboxListener.isHandling(event)) {
-            return Mono.defer(() -> Mono.from(metricFactory.runPublishingTimerMetric(timerName(mailboxListener),
+            return Mono.defer(() -> Mono.from(metricFactory.decoratePublisherWithTimerMetric(timerName(mailboxListener),
                     mailboxListener.reactiveEvent(event))))
                 .subscriberContext(context("deliver", buildMDC(mailboxListener, event)));
         }
diff --git a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/MailboxListenerExecutor.java b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/MailboxListenerExecutor.java
index 32276ef..deb0098 100644
--- a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/MailboxListenerExecutor.java
+++ b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/MailboxListenerExecutor.java
@@ -36,7 +36,7 @@ class MailboxListenerExecutor {
 
     Mono<Void> execute(MailboxListener.ReactiveMailboxListener listener, MDCBuilder mdcBuilder, Event event) {
         if (listener.isHandling(event)) {
-            return Mono.from(metricFactory.runPublishingTimerMetric(timerName(listener),
+            return Mono.from(metricFactory.decoratePublisherWithTimerMetric(timerName(listener),
                 Mono.from(listener.reactiveEvent(event))
                     .subscriberContext(ReactorUtils.context("MailboxListenerExecutor", mdc(listener, mdcBuilder, event)))));
         }
diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
index a7bf391..8febe09 100644
--- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
@@ -92,7 +92,7 @@ public class BlobStoreDeletedMessageVault implements DeletedMessageVault {
         Preconditions.checkNotNull(mimeMessage);
         BucketName bucketName = nameGenerator.currentBucket();
 
-        return metricFactory.runPublishingTimerMetric(
+        return metricFactory.decoratePublisherWithTimerMetric(
             APPEND_METRIC_NAME,
             appendMessage(deletedMessage, mimeMessage, bucketName));
     }
@@ -112,7 +112,7 @@ public class BlobStoreDeletedMessageVault implements DeletedMessageVault {
         Preconditions.checkNotNull(username);
         Preconditions.checkNotNull(messageId);
 
-        return metricFactory.runPublishingTimerMetric(
+        return metricFactory.decoratePublisherWithTimerMetric(
             LOAD_MIME_MESSAGE_METRIC_NAME,
             Mono.from(messageMetadataVault.retrieveStorageInformation(username, messageId))
                 .flatMap(storageInformation -> loadMimeMessage(storageInformation, username, messageId)));
@@ -130,7 +130,7 @@ public class BlobStoreDeletedMessageVault implements DeletedMessageVault {
         Preconditions.checkNotNull(username);
         Preconditions.checkNotNull(query);
 
-        return metricFactory.runPublishingTimerMetric(
+        return metricFactory.decoratePublisherWithTimerMetric(
             SEARCH_METRIC_NAME,
             searchOn(username, query));
     }
@@ -147,7 +147,7 @@ public class BlobStoreDeletedMessageVault implements DeletedMessageVault {
         Preconditions.checkNotNull(username);
         Preconditions.checkNotNull(messageId);
 
-        return metricFactory.runPublishingTimerMetric(
+        return metricFactory.decoratePublisherWithTimerMetric(
             DELETE_METRIC_NAME,
             deleteMessage(username, messageId));
     }
@@ -168,7 +168,7 @@ public class BlobStoreDeletedMessageVault implements DeletedMessageVault {
 
     Flux<BucketName> deleteExpiredMessages(ZonedDateTime beginningOfRetentionPeriod) {
         return Flux.from(
-            metricFactory.runPublishingTimerMetric(
+            metricFactory.decoratePublisherWithTimerMetric(
                 DELETE_EXPIRED_MESSAGES_METRIC_NAME,
                 retentionQualifiedBuckets(beginningOfRetentionPeriod)
                     .flatMap(bucketName -> deleteBucketData(bucketName).then(Mono.just(bucketName)))));
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/PreDeletionHooks.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/PreDeletionHooks.java
index b53e039..c56c87a 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/PreDeletionHooks.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/PreDeletionHooks.java
@@ -63,6 +63,6 @@ public class PreDeletionHooks {
 
     private Mono<Void> publishMetric(PreDeletionHook.DeleteOperation deleteOperation, PreDeletionHook hook, MetricFactory factory) {
         return Mono.from(
-            factory.runPublishingTimerMetric(PRE_DELETION_HOOK_METRIC_NAME, hook.notifyDelete(deleteOperation)));
+            factory.decoratePublisherWithTimerMetric(PRE_DELETION_HOOK_METRIC_NAME, hook.notifyDelete(deleteOperation)));
     }
 }
diff --git a/mailbox/tika/src/main/java/org/apache/james/mailbox/tika/TikaTextExtractor.java b/mailbox/tika/src/main/java/org/apache/james/mailbox/tika/TikaTextExtractor.java
index b20da09..78b0eed 100644
--- a/mailbox/tika/src/main/java/org/apache/james/mailbox/tika/TikaTextExtractor.java
+++ b/mailbox/tika/src/main/java/org/apache/james/mailbox/tika/TikaTextExtractor.java
@@ -37,13 +37,10 @@ import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.store.extractor.JsoupTextExtractor;
 import org.apache.james.metrics.api.MetricFactory;
 
-import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.TreeNode;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.module.SimpleModule;
@@ -85,7 +82,7 @@ public class TikaTextExtractor implements TextExtractor {
         if (contentType.mediaType().equals(TEXT)) {
             return jsoupTextExtractor.extractContent(inputStream, contentType);
         }
-        return metricFactory.runPublishingTimerMetric("tikaTextExtraction", Throwing.supplier(
+        return metricFactory.decorateSupplierWithTimerMetric("tikaTextExtraction", Throwing.supplier(
             () -> performContentExtraction(inputStream, contentType))
             .sneakyThrow());
     }
@@ -95,7 +92,7 @@ public class TikaTextExtractor implements TextExtractor {
         return new ParsedContent(contentAndMetadata.getContent(), contentAndMetadata.getMetadata());
     }
 
-    private ContentAndMetadata convert(Optional<InputStream> maybeInputStream) throws IOException, JsonParseException, JsonMappingException {
+    private ContentAndMetadata convert(Optional<InputStream> maybeInputStream) throws IOException {
         return maybeInputStream
                 .map(Throwing.function(inputStream -> objectMapper.readValue(inputStream, ContentAndMetadata.class)))
                 .orElse(ContentAndMetadata.empty());
@@ -105,7 +102,7 @@ public class TikaTextExtractor implements TextExtractor {
     static class ContentAndMetadataDeserializer extends JsonDeserializer<ContentAndMetadata> {
 
         @Override
-        public ContentAndMetadata deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
+        public ContentAndMetadata deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
             TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
             Preconditions.checkState(treeNode.isArray() && treeNode.size() >= 1, "The response should be an array with at least one element");
             Preconditions.checkState(treeNode.get(0).isObject(), "The element should be a Json object");
diff --git a/metrics/metrics-api/src/main/java/org/apache/james/metrics/api/MetricFactory.java b/metrics/metrics-api/src/main/java/org/apache/james/metrics/api/MetricFactory.java
index 2c0b464..adb64a1 100644
--- a/metrics/metrics-api/src/main/java/org/apache/james/metrics/api/MetricFactory.java
+++ b/metrics/metrics-api/src/main/java/org/apache/james/metrics/api/MetricFactory.java
@@ -31,7 +31,7 @@ public interface MetricFactory {
 
     TimeMetric timer(String name);
 
-    default <T> T runPublishingTimerMetric(String name, Supplier<T> operation) {
+    default <T> T decorateSupplierWithTimerMetric(String name, Supplier<T> operation) {
         TimeMetric timer = timer(name);
         try {
             return operation.get();
@@ -40,7 +40,7 @@ public interface MetricFactory {
         }
     }
 
-    default <T> T runPublishingTimerMetricLogP99(String name, Supplier<T> operation) {
+    default <T> T decorateSupplierWithTimerMetricLogP99(String name, Supplier<T> operation) {
         TimeMetric timer = timer(name);
         try {
             return operation.get();
@@ -49,12 +49,12 @@ public interface MetricFactory {
         }
     }
 
-    <T> Publisher<T> runPublishingTimerMetric(String name, Publisher<T> publisher);
+    <T> Publisher<T> decoratePublisherWithTimerMetric(String name, Publisher<T> publisher);
 
-    <T> Publisher<T> runPublishingTimerMetricLogP99(String name, Publisher<T> publisher);
+    <T> Publisher<T> decoratePublisherWithTimerMetricLogP99(String name, Publisher<T> publisher);
 
     default void runPublishingTimerMetric(String name, Runnable runnable) {
-        runPublishingTimerMetric(name, () -> {
+        decorateSupplierWithTimerMetric(name, () -> {
             runnable.run();
             return null;
         });
diff --git a/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactory.java b/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactory.java
index e65fbb7..2b0081c 100644
--- a/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactory.java
+++ b/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactory.java
@@ -59,13 +59,13 @@ public class DropWizardMetricFactory implements MetricFactory, Startable {
     }
 
     @Override
-    public <T> Publisher<T> runPublishingTimerMetric(String name, Publisher<T> publisher) {
+    public <T> Publisher<T> decoratePublisherWithTimerMetric(String name, Publisher<T> publisher) {
         TimeMetric timer = timer(name);
         return Flux.from(publisher).doOnComplete(timer::stopAndPublish);
     }
 
     @Override
-    public <T> Publisher<T> runPublishingTimerMetricLogP99(String name, Publisher<T> publisher) {
+    public <T> Publisher<T> decoratePublisherWithTimerMetricLogP99(String name, Publisher<T> publisher) {
         TimeMetric timer = timer(name);
         return Flux.from(publisher)
             .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD));
diff --git a/metrics/metrics-logger/src/main/java/org/apache/james/metrics/logger/DefaultMetricFactory.java b/metrics/metrics-logger/src/main/java/org/apache/james/metrics/logger/DefaultMetricFactory.java
index 373d813..919363c 100644
--- a/metrics/metrics-logger/src/main/java/org/apache/james/metrics/logger/DefaultMetricFactory.java
+++ b/metrics/metrics-logger/src/main/java/org/apache/james/metrics/logger/DefaultMetricFactory.java
@@ -44,13 +44,13 @@ public class DefaultMetricFactory implements MetricFactory {
     }
 
     @Override
-    public <T> Publisher<T> runPublishingTimerMetric(String name, Publisher<T> publisher) {
+    public <T> Publisher<T> decoratePublisherWithTimerMetric(String name, Publisher<T> publisher) {
         TimeMetric timer = timer(name);
         return Flux.from(publisher).doOnComplete(timer::stopAndPublish);
     }
 
     @Override
-    public <T> Publisher<T> runPublishingTimerMetricLogP99(String name, Publisher<T> publisher) {
+    public <T> Publisher<T> decoratePublisherWithTimerMetricLogP99(String name, Publisher<T> publisher) {
         TimeMetric timer = timer(name);
         return Flux.from(publisher)
             .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD));
diff --git a/metrics/metrics-tests/src/main/java/org/apache/james/metrics/tests/RecordingMetricFactory.java b/metrics/metrics-tests/src/main/java/org/apache/james/metrics/tests/RecordingMetricFactory.java
index 99d293f..63a8922 100644
--- a/metrics/metrics-tests/src/main/java/org/apache/james/metrics/tests/RecordingMetricFactory.java
+++ b/metrics/metrics-tests/src/main/java/org/apache/james/metrics/tests/RecordingMetricFactory.java
@@ -62,13 +62,13 @@ public class RecordingMetricFactory implements MetricFactory {
     }
 
     @Override
-    public <T> Publisher<T> runPublishingTimerMetric(String name, Publisher<T> publisher) {
+    public <T> Publisher<T> decoratePublisherWithTimerMetric(String name, Publisher<T> publisher) {
         TimeMetric timer = timer(name);
         return Flux.from(publisher).doOnComplete(timer::stopAndPublish);
     }
 
     @Override
-    public <T> Publisher<T> runPublishingTimerMetricLogP99(String name, Publisher<T> publisher) {
+    public <T> Publisher<T> decoratePublisherWithTimerMetricLogP99(String name, Publisher<T> publisher) {
         TimeMetric timer = timer(name);
         return Flux.from(publisher)
             .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD));
diff --git a/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java b/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
index eb39585..c125ea3 100644
--- a/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
+++ b/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
@@ -78,7 +78,7 @@ class RecordingMetricFactoryTest implements MetricFactoryContract {
         AtomicInteger count = new AtomicInteger();
 
         ConcurrentTestRunner.builder()
-            .operation((threadNumber, step) -> testee.runPublishingTimerMetric(TIME_METRIC_NAME, count::incrementAndGet))
+            .operation((threadNumber, step) -> testee.decorateSupplierWithTimerMetric(TIME_METRIC_NAME, count::incrementAndGet))
             .threadCount(10)
             .operationCount(200)
             .runSuccessfullyWithin(Duration.ofSeconds(10));
diff --git a/server/blob/blob-api/src/main/java/org/apache/james/blob/api/MetricableBlobStore.java b/server/blob/blob-api/src/main/java/org/apache/james/blob/api/MetricableBlobStore.java
index 0e4e383..309a20e 100644
--- a/server/blob/blob-api/src/main/java/org/apache/james/blob/api/MetricableBlobStore.java
+++ b/server/blob/blob-api/src/main/java/org/apache/james/blob/api/MetricableBlobStore.java
@@ -50,28 +50,28 @@ public class MetricableBlobStore implements BlobStore {
 
     @Override
     public Publisher<BlobId> save(BucketName bucketName, byte[] data, StoragePolicy storagePolicy) {
-        return metricFactory.runPublishingTimerMetric(SAVE_BYTES_TIMER_NAME, blobStoreImpl.save(bucketName, data, storagePolicy));
+        return metricFactory.decoratePublisherWithTimerMetric(SAVE_BYTES_TIMER_NAME, blobStoreImpl.save(bucketName, data, storagePolicy));
     }
 
     @Override
     public Publisher<BlobId> save(BucketName bucketName, InputStream data, StoragePolicy storagePolicy) {
-        return metricFactory.runPublishingTimerMetric(SAVE_INPUT_STREAM_TIMER_NAME, blobStoreImpl.save(bucketName, data, storagePolicy));
+        return metricFactory.decoratePublisherWithTimerMetric(SAVE_INPUT_STREAM_TIMER_NAME, blobStoreImpl.save(bucketName, data, storagePolicy));
     }
 
     @Override
     public Publisher<byte[]> readBytes(BucketName bucketName, BlobId blobId) {
-        return metricFactory.runPublishingTimerMetric(READ_BYTES_TIMER_NAME, blobStoreImpl.readBytes(bucketName, blobId));
+        return metricFactory.decoratePublisherWithTimerMetric(READ_BYTES_TIMER_NAME, blobStoreImpl.readBytes(bucketName, blobId));
     }
 
     @Override
     public InputStream read(BucketName bucketName, BlobId blobId) {
         return metricFactory
-            .runPublishingTimerMetric(READ_TIMER_NAME, () -> blobStoreImpl.read(bucketName, blobId));
+            .decorateSupplierWithTimerMetric(READ_TIMER_NAME, () -> blobStoreImpl.read(bucketName, blobId));
     }
 
     @Override
     public Publisher<Void> deleteBucket(BucketName bucketName) {
-        return metricFactory.runPublishingTimerMetric(DELETE_BUCKET_TIMER_NAME, blobStoreImpl.deleteBucket(bucketName));
+        return metricFactory.decoratePublisherWithTimerMetric(DELETE_BUCKET_TIMER_NAME, blobStoreImpl.deleteBucket(bucketName));
     }
 
     @Override
@@ -81,7 +81,7 @@ public class MetricableBlobStore implements BlobStore {
 
     @Override
     public Publisher<Void> delete(BucketName bucketName, BlobId blobId) {
-        return metricFactory.runPublishingTimerMetric(DELETE_TIMER_NAME, blobStoreImpl.delete(bucketName, blobId));
+        return metricFactory.decoratePublisherWithTimerMetric(DELETE_TIMER_NAME, blobStoreImpl.delete(bucketName, blobId));
     }
 
 }
diff --git a/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java b/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
index 1a14773..48a8cfd 100644
--- a/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
+++ b/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
@@ -38,6 +38,7 @@ import org.apache.james.blob.api.ObjectNotFoundException;
 import org.apache.james.blob.api.ObjectStoreIOException;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.metrics.api.MetricFactory;
+import org.apache.james.metrics.api.TimeMetric;
 import org.reactivestreams.Publisher;
 
 import com.google.common.base.Preconditions;
@@ -153,18 +154,23 @@ public class CachedBlobStore implements BlobStore {
 
     @Override
     public Mono<byte[]> readBytes(BucketName bucketName, BlobId blobId) {
-        return Mono.just(bucketName)
-            .filter(getDefaultBucketName()::equals)
-            .flatMap(deleteBucket -> readBytesInDefaultBucket(bucketName, blobId))
-            .switchIfEmpty(Mono.defer(() -> readBytesFromBackend(bucketName, blobId)));
+        if (getDefaultBucketName().equals(bucketName)) {
+            return readBytesInDefaultBucket(bucketName, blobId);
+        }
+        return readBytesFromBackend(bucketName, blobId);
     }
 
     private Mono<byte[]> readBytesInDefaultBucket(BucketName bucketName, BlobId blobId) {
-        return readFromCache(blobId)
-            .switchIfEmpty(Mono.defer(() -> readBytesFromBackend(bucketName, blobId))
-                .filter(this::isAbleToCache)
-                .doOnNext(any -> metricRetrieveMissCount.increment())
-                .flatMap(bytes -> saveInCache(blobId, bytes).then(Mono.just(bytes))));
+        return readFromCache(blobId).switchIfEmpty(
+            readBytesFromBackend(bucketName, blobId)
+                .flatMap(bytes -> {
+                    if (isAbleToCache(bytes)) {
+                        metricRetrieveMissCount.increment();
+                        return saveInCache(blobId, bytes)
+                            .then(Mono.just(bytes));
+                    }
+                    return Mono.just(bytes);
+                }));
     }
 
     @Override
@@ -261,17 +267,20 @@ public class CachedBlobStore implements BlobStore {
     }
 
     private Mono<byte[]> readFromCache(BlobId blobId) {
-        return Mono.from(metricFactory.runPublishingTimerMetric(BLOBSTORE_CACHED_LATENCY_METRIC_NAME, cache.read(blobId)))
+        return Mono.from(metricFactory.decoratePublisherWithTimerMetric(BLOBSTORE_CACHED_LATENCY_METRIC_NAME, cache.read(blobId)))
             .doOnNext(any -> metricRetrieveHitCount.increment());
     }
 
     private Mono<InputStream> readFromBackend(BucketName bucketName, BlobId blobId) {
-        return Mono.from(metricFactory.runPublishingTimerMetric(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME,
+        return Mono.from(metricFactory.decoratePublisherWithTimerMetric(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME,
             Mono.fromCallable(() -> backend.read(bucketName, blobId))));
     }
 
     private Mono<byte[]> readBytesFromBackend(BucketName bucketName, BlobId blobId) {
-        return Mono.from(metricFactory.runPublishingTimerMetric(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME,
-            () -> backend.readBytes(bucketName, blobId)));
+        TimeMetric timer = metricFactory.timer(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME);
+
+        return Mono.from(backend.readBytes(bucketName, blobId))
+            .doOnSuccess(any -> timer.stopAndPublish())
+            .doOnError(ObjectNotFoundException.class, any -> timer.stopAndPublish());
     }
 }
diff --git a/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java b/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
index a903d1c..5caff3f 100644
--- a/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
+++ b/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
@@ -272,7 +272,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
 
     @Test
     public void shouldNotCacheWhenReadWithBigStream() {
-        BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, new ByteArrayInputStream(TWELVE_MEGABYTES), SIZE_BASED)).block();
+        BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, new ByteArrayInputStream(TWELVE_MEGABYTES), SIZE_BASED)).block();
 
         SoftAssertions.assertSoftly(soflty -> {
             soflty.assertThat(Mono.from(cache.read(blobId)).blockOptional()).isEmpty();
@@ -285,30 +285,40 @@ public class CachedBlobStoreTest implements BlobStoreContract {
     @Nested
     class MetricsTest {
         @Test
-        public void readBlobStoreCacheWithNoneDefaultBucketNameShouldNotImpact() {
+        void readBlobStoreCacheWithNoneDefaultBucketNameShouldNotImpact() {
             BlobId blobId = Mono.from(testee.save(TEST_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             testee.read(TEST_BUCKETNAME, blobId);
             testee.read(TEST_BUCKETNAME, blobId);
 
             SoftAssertions.assertSoftly(soflty -> {
-                assertThat(metricFactory.countFor(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME))
+                soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME)
                     .isEqualTo(0);
                 soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME)
                     .isEqualTo(0);
-                assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
                     .hasSize(0);
-                assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
-                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
-                    .hasSize(2);
             });
         }
 
         @Test
-        public void readBytesWithNoneDefaultBucketNameShouldNotImpact() {
+        void readBlobStoreWithNoneDefaultBucketNameShouldRecordByBackendLatency() {
+            BlobId blobId = Mono.from(testee.save(TEST_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
+
+            testee.read(TEST_BUCKETNAME, blobId);
+            testee.read(TEST_BUCKETNAME, blobId);
+
+            SoftAssertions.assertSoftly(soflty ->
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(2));
+        }
+
+        @Test
+        void readBytesWithNoneDefaultBucketNameShouldNotImpact() {
             BlobId blobId = Mono.from(testee.save(TEST_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             Mono.from(testee.readBytes(TEST_BUCKETNAME, blobId)).block();
@@ -322,17 +332,30 @@ public class CachedBlobStoreTest implements BlobStoreContract {
                 soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME)
                     .isEqualTo(0);
-                assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
                     .hasSize(0);
-                assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
                     .hasSize(2);
             });
         }
 
         @Test
-        public void readBlobStoreCacheShouldPublishTimerMetrics() {
+        void readBytesWithNoneDefaultBucketNameShouldPublishBackendTimerMetrics() {
+            BlobId blobId = Mono.from(testee.save(TEST_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
+
+            Mono.from(testee.readBytes(TEST_BUCKETNAME, blobId)).block();
+            Mono.from(testee.readBytes(TEST_BUCKETNAME, blobId)).block();
+
+            SoftAssertions.assertSoftly(soflty ->
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(2));
+        }
+
+        @Test
+        void readBlobStoreCacheShouldPublishTimerMetrics() {
             BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             testee.read(DEFAULT_BUCKETNAME, blobId);
@@ -342,14 +365,11 @@ public class CachedBlobStoreTest implements BlobStoreContract {
                 soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
                     .hasSize(2);
-                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
-                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
-                    .hasSize(0);
             });
         }
 
         @Test
-        public void readBytesCacheShouldPublishTimerMetrics() {
+        void readBytesCacheShouldPublishTimerMetrics() {
             BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
@@ -362,14 +382,37 @@ public class CachedBlobStoreTest implements BlobStoreContract {
                 soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME)
                     .isEqualTo(2);
+            });
+        }
+
+        @Test
+        void readBytesShouldPublishBackendTimerMetricsForBigBlobs() {
+            BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, ELEVEN_KILOBYTES, SIZE_BASED)).block();
+
+            Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
+            Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
+
+            SoftAssertions.assertSoftly(soflty ->
                 soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
-                    .hasSize(0);
-            });
+                    .hasSize(2));
         }
 
         @Test
-        public void readBytesShouldNotIncreaseCacheCounterForBigBlobs() {
+        void readInputStreamShouldPublishBackendTimerForBigBlobs() {
+            BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, ELEVEN_KILOBYTES, SIZE_BASED)).block();
+
+            testee.read(DEFAULT_BUCKETNAME, blobId);
+            testee.read(DEFAULT_BUCKETNAME, blobId);
+
+            SoftAssertions.assertSoftly(soflty ->
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(2));
+        }
+
+        @Test
+        void readBytesShouldNotIncreaseCacheCounterForBigBlobs() {
             BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, ELEVEN_KILOBYTES, SIZE_BASED)).block();
 
             Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
@@ -386,7 +429,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
         }
 
         @Test
-        public void readInputStreamShouldNotIncreaseCacheCounterForBigBlobs() {
+        void readInputStreamShouldNotIncreaseCacheCounterForBigBlobs() {
             BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, ELEVEN_KILOBYTES, SIZE_BASED)).block();
 
             testee.read(DEFAULT_BUCKETNAME, blobId);
@@ -402,9 +445,8 @@ public class CachedBlobStoreTest implements BlobStoreContract {
             });
         }
 
-
         @Test
-        public void readBlobStoreCacheShouldCountWhenHit() {
+        void readBlobStoreCacheShouldCountWhenHit() {
             BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             testee.read(DEFAULT_BUCKETNAME, blobId);
@@ -414,7 +456,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
         }
 
         @Test
-        public void readBytesCacheShouldCountWhenHit() {
+        void readBytesCacheShouldCountWhenHit() {
             BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
@@ -425,7 +467,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
 
 
         @Test
-        public void readBlobStoreCacheShouldCountWhenMissed() {
+        void readBlobStoreCacheShouldCountWhenMissed() {
             BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             Mono.from(cache.remove(blobId)).block();
@@ -435,8 +477,8 @@ public class CachedBlobStoreTest implements BlobStoreContract {
         }
 
         @Test
-        public void readBytesCacheShouldCountWhenMissed() {
-            BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
+        void readBytesCacheShouldCountWhenMissed() {
+            BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             Mono.from(cache.remove(blobId)).block();
             Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
@@ -445,7 +487,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
         }
 
         @Test
-        public void metricsShouldNotWorkExceptLatencyWhenReadNonExistingBlob() {
+        void metricsShouldNotWorkExceptLatencyWhenReadNonExistingBlob() {
             SoftAssertions.assertSoftly(soflty -> {
                 soflty.assertThatThrownBy(() -> testee.read(DEFAULT_BUCKETNAME, new TestBlobId.Factory().randomId()))
                     .isInstanceOf(ObjectNotFoundException.class);
@@ -466,7 +508,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
         }
 
         @Test
-        public void metricsShouldNotWorkExceptLatencyWhenReadNonExistingBlobAsBytes() {
+        void metricsShouldNotWorkExceptLatencyWhenReadNonExistingBlobAsBytes() {
             SoftAssertions.assertSoftly(soflty -> {
                 soflty.assertThatThrownBy(() -> Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, new TestBlobId.Factory().randomId())).blockOptional())
                     .isInstanceOf(ObjectNotFoundException.class);
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetFilterMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetFilterMethod.java
index 6c1ca63..ebf9b53 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetFilterMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetFilterMethod.java
@@ -75,7 +75,7 @@ public class GetFilterMethod implements Method {
 
         GetFilterRequest filterRequest = (GetFilterRequest) request;
 
-        return Flux.from(metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+        return Flux.from(metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
             () -> process(methodCallId, mailboxSession, filterRequest)
                 .subscriberContext(context("GET_FILTER", MDCBuilder.of(MDCBuilder.ACTION, "GET_FILTER")))));
     }
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java
index f5936a9..43396a7 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java
@@ -95,7 +95,7 @@ public class GetMailboxesMethod implements Method {
         Preconditions.checkArgument(request instanceof GetMailboxesRequest);
         GetMailboxesRequest mailboxesRequest = (GetMailboxesRequest) request;
 
-        return metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+        return metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
             () -> process(methodCallId, mailboxSession, mailboxesRequest)
             .subscriberContext(context(ACTION, mdc(mailboxesRequest))));
     }
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
index 39dc573..a14069c 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
@@ -101,7 +101,7 @@ public class GetMessageListMethod implements Method {
 
         GetMessageListRequest messageListRequest = (GetMessageListRequest) request;
 
-        return metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+        return metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
             () -> process(methodCallId, mailboxSession, messageListRequest)
                 .subscriberContext(context("GET_MESSAGE_LIST", mdc(messageListRequest))))
             .subscribeOn(Schedulers.elastic());
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessagesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessagesMethod.java
index 59f2731..1532525 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessagesMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessagesMethod.java
@@ -83,7 +83,7 @@ public class GetMessagesMethod implements Method {
         GetMessagesRequest getMessagesRequest = (GetMessagesRequest) request;
         MessageProperties outputProperties = getMessagesRequest.getProperties().toOutputProperties();
 
-        return metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+        return metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
             () -> Flux.from(getMessagesResponse(mailboxSession, getMessagesRequest)
                 .map(response -> JmapResponse.builder().methodCallId(methodCallId)
                     .response(response)
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethod.java
index da9f326..e47af51 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetVacationResponseMethod.java
@@ -72,7 +72,7 @@ public class GetVacationResponseMethod implements Method {
         Preconditions.checkNotNull(mailboxSession);
         Preconditions.checkArgument(request instanceof GetVacationRequest);
 
-        return Flux.from(metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+        return Flux.from(metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
             process(mailboxSession)
                 .map(response -> JmapResponse.builder()
                     .methodCallId(methodCallId)
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SendMDNProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SendMDNProcessor.java
index 98f6675..0fb3bea 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SendMDNProcessor.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SendMDNProcessor.java
@@ -77,7 +77,7 @@ public class SendMDNProcessor implements SetMessagesProcessor {
 
     @Override
     public SetMessagesResponse process(SetMessagesRequest request, MailboxSession mailboxSession) {
-        return metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + "SendMDN",
+        return metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + "SendMDN",
             () -> handleMDNCreation(request, mailboxSession));
     }
 
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetFilterMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetFilterMethod.java
index f0ac91a..154aefa 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetFilterMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetFilterMethod.java
@@ -118,7 +118,7 @@ public class SetFilterMethod implements Method {
 
         SetFilterRequest setFilterRequest = (SetFilterRequest) request;
 
-        return Flux.from(metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+        return Flux.from(metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
             () -> process(methodCallId, mailboxSession, setFilterRequest)
                 .subscriberContext(jmapAction("SET_FILTER"))
                 .subscriberContext(context("SET_FILTER", MDCBuilder.of("update", setFilterRequest.getSingleton())))));
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesMethod.java
index 32291fe..9a0ffbf 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMailboxesMethod.java
@@ -74,7 +74,7 @@ public class SetMailboxesMethod implements Method {
             .addContext("update", setMailboxesRequest.getUpdate())
             .addContext("destroy", setMailboxesRequest.getDestroy())
             .wrapArround(
-                () -> metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+                () -> metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
                     () -> Stream.of(
                         JmapResponse.builder().methodCallId(methodCallId)
                             .response(setMailboxesResponse(setMailboxesRequest, mailboxSession))
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesDestructionProcessor.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesDestructionProcessor.java
index c6ee87c..a961a73 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesDestructionProcessor.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesDestructionProcessor.java
@@ -56,7 +56,7 @@ public class SetMessagesDestructionProcessor implements SetMessagesProcessor {
 
     @Override
     public SetMessagesResponse process(SetMessagesRequest request, MailboxSession mailboxSession) {
-        return metricFactory.runPublishingTimerMetric(JMAP_PREFIX + "SetMessageDestructionProcessor",
+        return metricFactory.decorateSupplierWithTimerMetric(JMAP_PREFIX + "SetMessageDestructionProcessor",
             () -> delete(request.getDestroy(), mailboxSession)
                 .reduce(SetMessagesResponse.builder(),
                     SetMessagesResponse.Builder::accumulator,
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesMethod.java
index dd7b385..05ecc44 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetMessagesMethod.java
@@ -71,7 +71,7 @@ public class SetMessagesMethod implements Method {
             .addContext("destroy", setMessagesRequest.getDestroy())
             .addContext("ifInState", setMessagesRequest.getIfInState())
             .wrapArround(
-                () -> metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+                () -> metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
                     () ->  Stream.of(
                         JmapResponse.builder().methodCallId(methodCallId)
                             .response(setMessagesResponse(setMessagesRequest, mailboxSession))
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethod.java
index 4da3a22..e8f2839 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/SetVacationResponseMethod.java
@@ -80,7 +80,7 @@ public class SetVacationResponseMethod implements Method {
         Preconditions.checkArgument(request instanceof SetVacationRequest);
         SetVacationRequest setVacationRequest = (SetVacationRequest) request;
 
-        return metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+        return metricFactory.decorateSupplierWithTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
             () -> process(methodCallId, mailboxSession, setVacationRequest)
                 .subscriberContext(jmapAction("SET_VACATION"))
                 .subscriberContext(context("set-vacation", MDCBuilder.of("update", setVacationRequest.getUpdate()))));
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java
index 7b4a2c9..987ed7a 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java
@@ -123,7 +123,7 @@ public class AuthenticationRoutes implements JMAPRoutes {
     }
 
     private Mono<Void> post(HttpServerRequest request, HttpServerResponse response) {
-        return Mono.from(metricFactory.runPublishingTimerMetricLogP99("JMAP-authentication-post",
+        return Mono.from(metricFactory.decoratePublisherWithTimerMetricLogP99("JMAP-authentication-post",
             Mono.just(request)
                 .map(this::assertJsonContentType)
                 .map(this::assertAcceptJsonOnly)
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesProvisioner.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesProvisioner.java
index 3252e95..6449ff0 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesProvisioner.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesProvisioner.java
@@ -61,7 +61,7 @@ class DefaultMailboxesProvisioner {
     }
 
     Mono<Void> createMailboxesIfNeeded(MailboxSession session) {
-        return metricFactory.runPublishingTimerMetric("JMAP-mailboxes-provisioning",
+        return metricFactory.decorateSupplierWithTimerMetric("JMAP-mailboxes-provisioning",
             () -> {
                 Username username = session.getUser();
                 return createDefaultMailboxes(username);
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
index 4291758..160beae 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
@@ -139,7 +139,7 @@ public class DownloadRoutes implements JMAPRoutes {
 
     private Mono<Void> post(HttpServerRequest request, HttpServerResponse response, DownloadPath downloadPath) {
         return authenticator.authenticate(request)
-            .flatMap(session -> Mono.from(metricFactory.runPublishingTimerMetric("JMAP-download-post",
+            .flatMap(session -> Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-download-post",
                     respondAttachmentAccessToken(session, downloadPath, response)))
                 .subscriberContext(jmapAuthContext(session)))
             .onErrorResume(UnauthorizedException.class, e -> handleAuthenticationFailure(response, LOGGER, e))
@@ -169,7 +169,7 @@ public class DownloadRoutes implements JMAPRoutes {
 
     private Mono<Void> get(HttpServerRequest request, HttpServerResponse response, DownloadPath downloadPath) {
         return authenticator.authenticate(request)
-            .flatMap(session -> Mono.from(metricFactory.runPublishingTimerMetric("JMAP-download-get",
+            .flatMap(session -> Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-download-get",
                     download(session, downloadPath, response)))
                 .subscriberContext(jmapAuthContext(session)))
             .onErrorResume(UnauthorizedException.class, e -> handleAuthenticationFailure(response, LOGGER, e))
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java
index f723f5d..c33c0f3 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java
@@ -99,7 +99,7 @@ public class JMAPApiRoutes implements JMAPRoutes {
             .flatMap(session -> Flux.merge(
                 userProvisioner.provisionUser(session),
                 defaultMailboxesProvisioner.createMailboxesIfNeeded(session))
-                .then(Mono.from(metricFactory.runPublishingTimerMetric("JMAP-request",
+                .then(Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-request",
                     post(request, response, session))))
                 .subscriberContext(jmapAuthContext(session)))
             .onErrorResume(BadRequestException.class, e -> handleBadRequest(response, LOGGER, e))
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java
index 918ef92..9f7c235 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java
@@ -115,7 +115,7 @@ public class UploadRoutes implements JMAPRoutes {
 
     private Mono<Void> post(HttpServerRequest request, HttpServerResponse response, ContentType contentType, MailboxSession session) {
         InputStream content = ReactorUtils.toInputStream(request.receive().asByteBuffer().subscribeOn(Schedulers.elastic()));
-        return Mono.from(metricFactory.runPublishingTimerMetric("JMAP-upload-post",
+        return Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-upload-post",
             handle(contentType, content, session, response)));
     }
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index 5fb7d15..5b72d8f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -32,7 +32,7 @@ import org.apache.james.mailbox.model.search.MailboxQuery
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
 import org.apache.james.metrics.api.MetricFactory
 import org.reactivestreams.Publisher
-import play.api.libs.json.{JsError, JsObject, JsSuccess, Json}
+import play.api.libs.json.{JsError, JsObject, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
 
@@ -44,7 +44,7 @@ class MailboxGetMethod @Inject() (serializer: Serializer,
   override val methodName: MethodName = MethodName("Mailbox/get")
 
   override def process(invocation: Invocation, mailboxSession: MailboxSession): Publisher[Invocation] = {
-    metricFactory.runPublishingTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
+    metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + methodName.value,
       asMailboxGetRequest(invocation.arguments)
         .flatMap(mailboxGetRequest => getMailboxes(mailboxGetRequest, mailboxSession)
           .collectSeq()
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/Authenticator.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/Authenticator.java
index e3b47ad..49fb6f5 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/Authenticator.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/Authenticator.java
@@ -50,7 +50,7 @@ public class Authenticator {
     }
 
     public Mono<MailboxSession> authenticate(HttpServerRequest request) {
-        return Mono.from(metricFactory.runPublishingTimerMetric("JMAP-authentication-filter",
+        return Mono.from(metricFactory.decoratePublisherWithTimerMetric("JMAP-authentication-filter",
             Flux.fromIterable(authMethods)
                 .concatMap(auth -> auth.createMailboxSession(request))
                 .onErrorContinue((throwable, nothing) -> LOGGER.error("Error while trying to authenticate with JMAP", throwable))
diff --git a/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinInvoker.java b/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinInvoker.java
index bf3d4b7..12e82d7 100644
--- a/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinInvoker.java
+++ b/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinInvoker.java
@@ -94,7 +94,7 @@ public class SpamAssassinInvoker {
      *             if an error on scanning is detected
      */
     public SpamAssassinResult scanMail(MimeMessage message, Username username) throws MessagingException {
-        return metricFactory.runPublishingTimerMetric(
+        return metricFactory.decorateSupplierWithTimerMetric(
             "spamAssassin-check",
             Throwing.supplier(
                 () -> scanMailWithAdditionalHeaders(message,
@@ -103,7 +103,7 @@ public class SpamAssassinInvoker {
     }
 
     public SpamAssassinResult scanMail(MimeMessage message) throws MessagingException {
-        return metricFactory.runPublishingTimerMetric(
+        return metricFactory.decorateSupplierWithTimerMetric(
             "spamAssassin-check",
             Throwing.supplier(
                 () -> scanMailWithoutAdditionalHeaders(message))
@@ -194,7 +194,7 @@ public class SpamAssassinInvoker {
      *             if an error occured during learning.
      */
     public boolean learnAsSpam(InputStream message, Username username) throws MessagingException {
-        return metricFactory.runPublishingTimerMetric(
+        return metricFactory.decorateSupplierWithTimerMetric(
             "spamAssassin-spam-report",
             Throwing.supplier(
                 () -> reportMessageAs(message, username, MessageClass.SPAM))
@@ -210,7 +210,7 @@ public class SpamAssassinInvoker {
      *             if an error occured during learning.
      */
     public boolean learnAsHam(InputStream message, Username username) throws MessagingException {
-        return metricFactory.runPublishingTimerMetric(
+        return metricFactory.decorateSupplierWithTimerMetric(
             "spamAssassin-ham-report",
             Throwing.supplier(
                 () -> reportMessageAs(message, username, MessageClass.HAM))


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 01/06: JAMES-3170: Add metric latency for BackEnd in CachedBlobStore

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 46e6f64a0f73a4616eab2c1b566b49182120cd4c
Author: duc91 <du...@gmail.com>
AuthorDate: Wed Jun 10 15:40:54 2020 +0700

    JAMES-3170: Add metric latency for BackEnd in CachedBlobStore
---
 .../blob/cassandra/cache/CachedBlobStore.java      | 14 +++---
 .../blob/cassandra/cache/CachedBlobStoreTest.java  | 50 +++++++++++++++++-----
 2 files changed, 49 insertions(+), 15 deletions(-)

diff --git a/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java b/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
index 9acbbed..1a14773 100644
--- a/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
+++ b/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
@@ -103,8 +103,10 @@ public class CachedBlobStore implements BlobStore {
 
     public static final String BACKEND = "blobStoreBackend";
 
-    public static final String BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME = "blobStoreCacheMisses";
     public static final String BLOBSTORE_CACHED_LATENCY_METRIC_NAME = "blobStoreCacheLatency";
+    public static final String BLOBSTORE_BACKEND_LATENCY_METRIC_NAME = "blobStoreBackEndLatency";
+
+    public static final String BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME = "blobStoreCacheMisses";
     public static final String BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME = "blobStoreCacheHits";
 
     private final MetricFactory metricFactory;
@@ -154,12 +156,12 @@ public class CachedBlobStore implements BlobStore {
         return Mono.just(bucketName)
             .filter(getDefaultBucketName()::equals)
             .flatMap(deleteBucket -> readBytesInDefaultBucket(bucketName, blobId))
-            .switchIfEmpty(readBytesFromBackend(bucketName, blobId));
+            .switchIfEmpty(Mono.defer(() -> readBytesFromBackend(bucketName, blobId)));
     }
 
     private Mono<byte[]> readBytesInDefaultBucket(BucketName bucketName, BlobId blobId) {
         return readFromCache(blobId)
-            .switchIfEmpty(readBytesFromBackend(bucketName, blobId)
+            .switchIfEmpty(Mono.defer(() -> readBytesFromBackend(bucketName, blobId))
                 .filter(this::isAbleToCache)
                 .doOnNext(any -> metricRetrieveMissCount.increment())
                 .flatMap(bytes -> saveInCache(blobId, bytes).then(Mono.just(bytes))));
@@ -264,10 +266,12 @@ public class CachedBlobStore implements BlobStore {
     }
 
     private Mono<InputStream> readFromBackend(BucketName bucketName, BlobId blobId) {
-        return Mono.fromCallable(() -> backend.read(bucketName, blobId));
+        return Mono.from(metricFactory.runPublishingTimerMetric(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME,
+            Mono.fromCallable(() -> backend.read(bucketName, blobId))));
     }
 
     private Mono<byte[]> readBytesFromBackend(BucketName bucketName, BlobId blobId) {
-        return Mono.from(backend.readBytes(bucketName, blobId));
+        return Mono.from(metricFactory.runPublishingTimerMetric(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME,
+            () -> backend.readBytes(bucketName, blobId)));
     }
 }
diff --git a/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java b/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
index ad6c3d3..a903d1c 100644
--- a/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
+++ b/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
@@ -23,6 +23,7 @@ import static org.apache.james.blob.api.BlobStore.StoragePolicy.LOW_COST;
 import static org.apache.james.blob.api.BlobStore.StoragePolicy.SIZE_BASED;
 import static org.apache.james.blob.api.BucketName.DEFAULT;
 import static org.apache.james.blob.cassandra.cache.BlobStoreCacheContract.EIGHT_KILOBYTES;
+import static org.apache.james.blob.cassandra.cache.CachedBlobStore.BLOBSTORE_BACKEND_LATENCY_METRIC_NAME;
 import static org.apache.james.blob.cassandra.cache.CachedBlobStore.BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME;
 import static org.apache.james.blob.cassandra.cache.CachedBlobStore.BLOBSTORE_CACHED_LATENCY_METRIC_NAME;
 import static org.apache.james.blob.cassandra.cache.CachedBlobStore.BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME;
@@ -285,7 +286,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
     class MetricsTest {
         @Test
         public void readBlobStoreCacheWithNoneDefaultBucketNameShouldNotImpact() {
-            BlobId blobId = Mono.from(backend.save(TEST_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
+            BlobId blobId = Mono.from(testee.save(TEST_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             testee.read(TEST_BUCKETNAME, blobId);
             testee.read(TEST_BUCKETNAME, blobId);
@@ -300,12 +301,15 @@ public class CachedBlobStoreTest implements BlobStoreContract {
                 assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
                     .hasSize(0);
+                assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(2);
             });
         }
 
         @Test
         public void readBytesWithNoneDefaultBucketNameShouldNotImpact() {
-            BlobId blobId = Mono.from(backend.save(TEST_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
+            BlobId blobId = Mono.from(testee.save(TEST_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             Mono.from(testee.readBytes(TEST_BUCKETNAME, blobId)).block();
             Mono.from(testee.readBytes(TEST_BUCKETNAME, blobId)).block();
@@ -321,27 +325,47 @@ public class CachedBlobStoreTest implements BlobStoreContract {
                 assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
                     .hasSize(0);
+                assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(2);
             });
         }
 
         @Test
         public void readBlobStoreCacheShouldPublishTimerMetrics() {
-            BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
+            BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             testee.read(DEFAULT_BUCKETNAME, blobId);
             testee.read(DEFAULT_BUCKETNAME, blobId);
 
-            assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)).hasSize(2);
+            SoftAssertions.assertSoftly(soflty -> {
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
+                    .hasSize(2);
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(0);
+            });
         }
 
         @Test
         public void readBytesCacheShouldPublishTimerMetrics() {
-            BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
+            BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 
             Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
             Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
 
-            assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)).hasSize(2);
+            SoftAssertions.assertSoftly(soflty -> {
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
+                    .hasSize(2);
+                soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME))
+                    .describedAs(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME)
+                    .isEqualTo(2);
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(0);
+            });
         }
 
         @Test
@@ -352,7 +376,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
             Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
 
             SoftAssertions.assertSoftly(soflty -> {
-                assertThat(metricFactory.countFor(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME))
+                soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME)
                     .isEqualTo(0);
                 soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME))
@@ -369,7 +393,7 @@ public class CachedBlobStoreTest implements BlobStoreContract {
             testee.read(DEFAULT_BUCKETNAME, blobId);
 
             SoftAssertions.assertSoftly(soflty -> {
-                assertThat(metricFactory.countFor(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME))
+                soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME)
                     .isEqualTo(0);
                 soflty.assertThat(metricFactory.countFor(BLOBSTORE_CACHED_HIT_COUNT_METRIC_NAME))
@@ -403,8 +427,8 @@ public class CachedBlobStoreTest implements BlobStoreContract {
         @Test
         public void readBlobStoreCacheShouldCountWhenMissed() {
             BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
-            Mono.from(cache.remove(blobId)).block();
 
+            Mono.from(cache.remove(blobId)).block();
             testee.read(DEFAULT_BUCKETNAME, blobId);
 
             assertThat(metricFactory.countFor(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME)).isEqualTo(1);
@@ -413,8 +437,8 @@ public class CachedBlobStoreTest implements BlobStoreContract {
         @Test
         public void readBytesCacheShouldCountWhenMissed() {
             BlobId blobId = Mono.from(backend.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
-            Mono.from(cache.remove(blobId)).block();
 
+            Mono.from(cache.remove(blobId)).block();
             Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId)).block();
 
             assertThat(metricFactory.countFor(BLOBSTORE_CACHED_MISS_COUNT_METRIC_NAME)).isEqualTo(1);
@@ -435,6 +459,9 @@ public class CachedBlobStoreTest implements BlobStoreContract {
                 soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
                     .hasSize(1);
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(1);
             });
         }
 
@@ -453,6 +480,9 @@ public class CachedBlobStoreTest implements BlobStoreContract {
                 soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
                     .describedAs(BLOBSTORE_CACHED_LATENCY_METRIC_NAME)
                     .hasSize(1);
+                soflty.assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                    .describedAs(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME)
+                    .hasSize(1);
             });
         }
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 04/06: JAMES-3170 Fix metric measurement upon reactor publisher replay

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 520cdac2f54674f96ea7edaca913a42778e8d4d1
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Jun 12 10:36:48 2020 +0700

    JAMES-3170 Fix metric measurement upon reactor publisher replay
    
    A single metric was used for all the retries leading to an inaccurate
    measurement.
---
 .../dropwizard/DropWizardMetricFactory.java        | 15 ++++++-----
 .../metrics/dropwizard/DropWizardTimeMetric.java   | 11 ++++++++
 .../dropwizard/DropWizardMetricFactoryTest.java    | 30 ++++++++++++++++++++++
 .../james/metrics/logger/DefaultMetricFactory.java | 12 +++++----
 .../metrics/tests/RecordingMetricFactory.java      | 12 +++++----
 .../metrics/tests/RecordingMetricFactoryTest.java  | 26 +++++++++++++++++++
 .../blob/cassandra/cache/CachedBlobStore.java      | 10 +++-----
 .../blob/cassandra/cache/CachedBlobStoreTest.java  | 30 ++++++++++++++++++++++
 8 files changed, 123 insertions(+), 23 deletions(-)

diff --git a/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactory.java b/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactory.java
index 2b0081c..b18a04e 100644
--- a/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactory.java
+++ b/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactory.java
@@ -28,13 +28,13 @@ import javax.inject.Inject;
 import org.apache.james.lifecycle.api.Startable;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.metrics.api.TimeMetric;
 import org.reactivestreams.Publisher;
 
 import com.codahale.metrics.MetricRegistry;
 import com.codahale.metrics.jmx.JmxReporter;
 
 import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
 
 public class DropWizardMetricFactory implements MetricFactory, Startable {
 
@@ -54,21 +54,22 @@ public class DropWizardMetricFactory implements MetricFactory, Startable {
     }
 
     @Override
-    public TimeMetric timer(String name) {
+    public DropWizardTimeMetric timer(String name) {
         return new DropWizardTimeMetric(name, metricRegistry.timer(name));
     }
 
     @Override
     public <T> Publisher<T> decoratePublisherWithTimerMetric(String name, Publisher<T> publisher) {
-        TimeMetric timer = timer(name);
-        return Flux.from(publisher).doOnComplete(timer::stopAndPublish);
+        return Mono.fromCallable(() -> timer(name))
+            .flatMapMany(timer ->  Flux.from(publisher)
+                .doOnComplete(timer::stopAndPublish));
     }
 
     @Override
     public <T> Publisher<T> decoratePublisherWithTimerMetricLogP99(String name, Publisher<T> publisher) {
-        TimeMetric timer = timer(name);
-        return Flux.from(publisher)
-            .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD));
+        return Mono.fromCallable(() -> timer(name))
+            .flatMapMany(timer ->  Flux.from(publisher)
+                .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD)));
     }
 
     @PostConstruct
diff --git a/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardTimeMetric.java b/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardTimeMetric.java
index adf2c7a..834da92 100644
--- a/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardTimeMetric.java
+++ b/metrics/metrics-dropwizard/src/main/java/org/apache/james/metrics/dropwizard/DropWizardTimeMetric.java
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.codahale.metrics.Timer;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
 public class DropWizardTimeMetric implements TimeMetric {
@@ -73,6 +74,16 @@ public class DropWizardTimeMetric implements TimeMetric {
         this.context = this.timer.time();
     }
 
+    @VisibleForTesting
+    Timer.Context getContext() {
+        return context;
+    }
+
+    @VisibleForTesting
+    Timer getTimer() {
+        return timer;
+    }
+
     @Override
     public String name() {
         return name;
diff --git a/metrics/metrics-dropwizard/src/test/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactoryTest.java b/metrics/metrics-dropwizard/src/test/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactoryTest.java
index 75aada0..368075c 100644
--- a/metrics/metrics-dropwizard/src/test/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactoryTest.java
+++ b/metrics/metrics-dropwizard/src/test/java/org/apache/james/metrics/dropwizard/DropWizardMetricFactoryTest.java
@@ -19,12 +19,20 @@
 
 package org.apache.james.metrics.dropwizard;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.metrics.api.MetricFactoryContract;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import com.codahale.metrics.MetricRegistry;
 
+import reactor.core.publisher.Mono;
+
 class DropWizardMetricFactoryTest implements MetricFactoryContract {
 
     private DropWizardMetricFactory testee;
@@ -38,4 +46,26 @@ class DropWizardMetricFactoryTest implements MetricFactoryContract {
     public MetricFactory testee() {
         return testee;
     }
+
+    @Test
+    void decoratePublisherWithTimerMetricShouldRecordANewValueForEachRetry() {
+        Duration duration = Duration.ofMillis(100);
+        Mono.from(testee.decoratePublisherWithTimerMetric("any", Mono.delay(duration)))
+            .repeat(5)
+            .blockLast();
+
+        assertThat(testee.timer("any").getTimer().getSnapshot().get99thPercentile())
+            .isLessThan(duration.get(ChronoUnit.NANOS) * 2);
+    }
+
+    @Test
+    void decoratePublisherWithTimerMetricLogP99ShouldRecordANewValueForEachRetry() {
+        Duration duration = Duration.ofMillis(100);
+        Mono.from(testee.decoratePublisherWithTimerMetricLogP99("any", Mono.delay(duration)))
+            .repeat(5)
+            .blockLast();
+
+        assertThat(testee.timer("any").getTimer().getSnapshot().get99thPercentile())
+            .isLessThan(duration.get(ChronoUnit.NANOS) * 2);
+    }
 }
\ No newline at end of file
diff --git a/metrics/metrics-logger/src/main/java/org/apache/james/metrics/logger/DefaultMetricFactory.java b/metrics/metrics-logger/src/main/java/org/apache/james/metrics/logger/DefaultMetricFactory.java
index 919363c..e0aa75f 100644
--- a/metrics/metrics-logger/src/main/java/org/apache/james/metrics/logger/DefaultMetricFactory.java
+++ b/metrics/metrics-logger/src/main/java/org/apache/james/metrics/logger/DefaultMetricFactory.java
@@ -28,6 +28,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
 
 public class DefaultMetricFactory implements MetricFactory {
 
@@ -45,14 +46,15 @@ public class DefaultMetricFactory implements MetricFactory {
 
     @Override
     public <T> Publisher<T> decoratePublisherWithTimerMetric(String name, Publisher<T> publisher) {
-        TimeMetric timer = timer(name);
-        return Flux.from(publisher).doOnComplete(timer::stopAndPublish);
+        return Mono.fromCallable(() -> timer(name))
+            .flatMapMany(timer ->  Flux.from(publisher)
+                .doOnComplete(timer::stopAndPublish));
     }
 
     @Override
     public <T> Publisher<T> decoratePublisherWithTimerMetricLogP99(String name, Publisher<T> publisher) {
-        TimeMetric timer = timer(name);
-        return Flux.from(publisher)
-            .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD));
+        return Mono.fromCallable(() -> timer(name))
+            .flatMapMany(timer ->  Flux.from(publisher)
+                .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD)));
     }
 }
diff --git a/metrics/metrics-tests/src/main/java/org/apache/james/metrics/tests/RecordingMetricFactory.java b/metrics/metrics-tests/src/main/java/org/apache/james/metrics/tests/RecordingMetricFactory.java
index 63a8922..78aeecd 100644
--- a/metrics/metrics-tests/src/main/java/org/apache/james/metrics/tests/RecordingMetricFactory.java
+++ b/metrics/metrics-tests/src/main/java/org/apache/james/metrics/tests/RecordingMetricFactory.java
@@ -38,6 +38,7 @@ import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
 
 import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
 
 public class RecordingMetricFactory implements MetricFactory {
     private final Multimap<String, Duration> executionTimes = Multimaps.synchronizedListMultimap(ArrayListMultimap.create());
@@ -63,15 +64,16 @@ public class RecordingMetricFactory implements MetricFactory {
 
     @Override
     public <T> Publisher<T> decoratePublisherWithTimerMetric(String name, Publisher<T> publisher) {
-        TimeMetric timer = timer(name);
-        return Flux.from(publisher).doOnComplete(timer::stopAndPublish);
+        return Mono.fromCallable(() -> timer(name))
+            .flatMapMany(timer ->  Flux.from(publisher)
+                .doOnComplete(timer::stopAndPublish));
     }
 
     @Override
     public <T> Publisher<T> decoratePublisherWithTimerMetricLogP99(String name, Publisher<T> publisher) {
-        TimeMetric timer = timer(name);
-        return Flux.from(publisher)
-            .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD));
+        return Mono.fromCallable(() -> timer(name))
+            .flatMapMany(timer ->  Flux.from(publisher)
+                .doOnComplete(() -> timer.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD)));
     }
 
     public Collection<Duration> executionTimesFor(String name) {
diff --git a/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java b/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
index c125ea3..bbdcf92 100644
--- a/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
+++ b/metrics/metrics-tests/src/test/java/org/apache/james/metrics/tests/RecordingMetricFactoryTest.java
@@ -32,6 +32,8 @@ import org.apache.james.util.concurrency.ConcurrentTestRunner;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import reactor.core.publisher.Mono;
+
 class RecordingMetricFactoryTest implements MetricFactoryContract {
 
     private static final String TIME_METRIC_NAME = "timerMetric";
@@ -118,4 +120,28 @@ class RecordingMetricFactoryTest implements MetricFactoryContract {
         assertThat(testee.countFor(METRIC_NAME))
             .isEqualTo(5);
     }
+
+    @Test
+    void decoratePublisherWithTimerMetricShouldRecordANewValueForEachRetry() {
+        Duration duration = Duration.ofMillis(100);
+        Mono.from(testee.decoratePublisherWithTimerMetric("any", Mono.delay(duration)))
+            .repeat(5)
+            .blockLast();
+
+        assertThat(testee.executionTimesFor("any"))
+            .hasSize(6)
+            .allSatisfy(timing -> assertThat(timing).isLessThan(duration.multipliedBy(2)));
+    }
+
+    @Test
+    void decoratePublisherWithTimerMetricLogP99ShouldRecordANewValueForEachRetry() {
+        Duration duration = Duration.ofMillis(100);
+        Mono.from(testee.decoratePublisherWithTimerMetricLogP99("any", Mono.delay(duration)))
+            .repeat(5)
+            .blockLast();
+
+        assertThat(testee.executionTimesFor("any"))
+            .hasSize(6)
+            .allSatisfy(timing -> assertThat(timing).isLessThan(duration.multipliedBy(2)));
+    }
 }
diff --git a/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java b/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
index 48a8cfd..0e998c0 100644
--- a/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
+++ b/server/blob/blob-cassandra/src/main/java/org/apache/james/blob/cassandra/cache/CachedBlobStore.java
@@ -38,7 +38,6 @@ import org.apache.james.blob.api.ObjectNotFoundException;
 import org.apache.james.blob.api.ObjectStoreIOException;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.metrics.api.MetricFactory;
-import org.apache.james.metrics.api.TimeMetric;
 import org.reactivestreams.Publisher;
 
 import com.google.common.base.Preconditions;
@@ -277,10 +276,9 @@ public class CachedBlobStore implements BlobStore {
     }
 
     private Mono<byte[]> readBytesFromBackend(BucketName bucketName, BlobId blobId) {
-        TimeMetric timer = metricFactory.timer(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME);
-
-        return Mono.from(backend.readBytes(bucketName, blobId))
-            .doOnSuccess(any -> timer.stopAndPublish())
-            .doOnError(ObjectNotFoundException.class, any -> timer.stopAndPublish());
+        return Mono.fromCallable(() -> metricFactory.timer(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+            .flatMap(timer -> Mono.from(backend.readBytes(bucketName, blobId))
+                .doOnSuccess(any -> timer.stopAndPublish())
+                .doOnError(ObjectNotFoundException.class, any -> timer.stopAndPublish()));
     }
 }
diff --git a/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java b/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
index 5caff3f..1d2abcd 100644
--- a/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
+++ b/server/blob/blob-cassandra/src/test/java/org/apache/james/blob/cassandra/cache/CachedBlobStoreTest.java
@@ -446,6 +446,36 @@ public class CachedBlobStoreTest implements BlobStoreContract {
         }
 
         @Test
+        void readBytesShouldRecordDistinctTimingsWhenRepeatAndBackendRead() {
+            BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, ELEVEN_KILOBYTES, SIZE_BASED)).block();
+
+            Duration delay = Duration.ofMillis(500);
+            Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId))
+                .then(Mono.delay(delay))
+                .repeat(2)
+                .blockLast();
+
+            assertThat(metricFactory.executionTimesFor(BLOBSTORE_BACKEND_LATENCY_METRIC_NAME))
+                .hasSize(3)
+                .allSatisfy(timing -> assertThat(timing).isLessThan(delay));
+        }
+
+        @Test
+        void readBytesShouldRecordDistinctTimingsWhenRepeat() {
+            BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
+
+            Duration delay = Duration.ofMillis(500);
+            Mono.from(testee.readBytes(DEFAULT_BUCKETNAME, blobId))
+                .then(Mono.delay(delay))
+                .repeat(2)
+                .blockLast();
+
+            assertThat(metricFactory.executionTimesFor(BLOBSTORE_CACHED_LATENCY_METRIC_NAME))
+                .hasSize(3)
+                .allSatisfy(timing -> assertThat(timing).isLessThan(delay));
+        }
+
+        @Test
         void readBlobStoreCacheShouldCountWhenHit() {
             BlobId blobId = Mono.from(testee.save(DEFAULT_BUCKETNAME, APPROXIMATELY_FIVE_KILOBYTES, SIZE_BASED)).block();
 


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[james-project] 02/06: JAMES-3170: Add Grafana board for BackEndLatencyMetric

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 7492fda96aedb273504e00f9019a23bb3823bfdd
Author: duc91 <du...@gmail.com>
AuthorDate: Wed Jun 10 15:51:23 2020 +0700

    JAMES-3170: Add Grafana board for BackEndLatencyMetric
---
 .../CacheBlobStore-15911761170000-dashboard.json   | 163 +++++++++++++++++++--
 1 file changed, 150 insertions(+), 13 deletions(-)

diff --git a/grafana-reporting/CacheBlobStore-15911761170000-dashboard.json b/grafana-reporting/CacheBlobStore-15911761170000-dashboard.json
index ed58953..d7c3493 100644
--- a/grafana-reporting/CacheBlobStore-15911761170000-dashboard.json
+++ b/grafana-reporting/CacheBlobStore-15911761170000-dashboard.json
@@ -38,7 +38,7 @@
       "bars": false,
       "dashLength": 10,
       "dashes": false,
-      "datasource": null,
+      "datasource": "Elasticsearch",
       "fieldConfig": {
         "defaults": {
           "custom": {}
@@ -131,7 +131,7 @@
       "timeFrom": null,
       "timeRegions": [],
       "timeShift": null,
-      "title": "Latency",
+      "title": "Cache_Latency",
       "tooltip": {
         "shared": true,
         "sort": 0,
@@ -151,7 +151,7 @@
           "label": null,
           "logBase": 1,
           "max": null,
-          "min": null,
+          "min": "0",
           "show": true
         },
         {
@@ -159,7 +159,7 @@
           "label": null,
           "logBase": 1,
           "max": null,
-          "min": null,
+          "min": "0",
           "show": true
         }
       ],
@@ -216,10 +216,14 @@
       "seriesOverrides": [
         {
           "alias": "Derivative hit",
+          "color": "#B877D9",
+          "dashes": true,
           "yaxis": 2
         },
         {
           "alias": "Derivative miss",
+          "color": "#F2495C",
+          "dashes": true,
           "yaxis": 2
         }
       ],
@@ -257,7 +261,9 @@
               "id": "4",
               "meta": {},
               "pipelineAgg": "3",
-              "settings": {},
+              "settings": {
+                "unit": "minute"
+              },
               "type": "derivative"
             }
           ],
@@ -311,12 +317,6 @@
         "sort": 1,
         "value_type": "individual"
       },
-      "transformations": [
-        {
-          "id": "filterByRefId",
-          "options": {}
-        }
-      ],
       "type": "graph",
       "xaxis": {
         "buckets": null,
@@ -349,9 +349,146 @@
         "align": false,
         "alignLevel": null
       }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "Elasticsearch",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {}
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 9
+      },
+      "hiddenSeries": false,
+      "id": 10,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "nullPointMode": "connected",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 2,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "bucketAggs": [
+            {
+              "field": "@timestamp",
+              "id": "2",
+              "settings": {
+                "interval": "auto",
+                "min_doc_count": 0,
+                "trimEdges": 0
+              },
+              "type": "date_histogram"
+            }
+          ],
+          "metrics": [
+            {
+              "field": "p50",
+              "id": "3",
+              "meta": {},
+              "settings": {},
+              "type": "avg"
+            },
+            {
+              "field": "p75",
+              "id": "4",
+              "meta": {},
+              "settings": {},
+              "type": "avg"
+            },
+            {
+              "field": "p95",
+              "id": "5",
+              "meta": {},
+              "settings": {},
+              "type": "avg"
+            },
+            {
+              "field": "p99",
+              "id": "6",
+              "meta": {},
+              "settings": {},
+              "type": "avg"
+            }
+          ],
+          "query": "name:\"blobStoreBackEndLatency\"",
+          "refId": "A",
+          "timeField": "@timestamp"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeRegions": [],
+      "timeShift": null,
+      "title": "Backend_Latency",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "decimals": null,
+          "format": "ms",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        },
+        {
+          "decimals": 0,
+          "format": "ms",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": "0",
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
     }
   ],
-  "refresh": "30s",
+  "refresh": "10s",
   "schemaVersion": 25,
   "style": "dark",
   "tags": [],
@@ -359,7 +496,7 @@
     "list": []
   },
   "time": {
-    "from": "now-12h",
+    "from": "now-30m",
     "to": "now"
   },
   "timepicker": {


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org