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

apupier pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 668c9299d846 CAMEL-24927: camel-core - Sample EIP should let the first 
exchange through whatever the value of System.nanoTime() is
668c9299d846 is described below

commit 668c9299d84648c7e6e1ff39f5402160216256dc
Author: smjain <[email protected]>
AuthorDate: Wed Sep 23 17:02:16 2026 +0530

    CAMEL-24927: camel-core - Sample EIP should let the first exchange through 
whatever the value of System.nanoTime() is
    
    Since CAMEL-20267 the SamplingThrottler measures time with 
System.nanoTime(),
    but it still starts with timeOfLastExchange = 0 and lets an exchange through
    when now >= timeOfLastExchange + period. The origin of System.nanoTime() is
    arbitrary, and on Linux and macOS it is about the time since the machine was
    started. So as long as the machine has been up for less than the sample
    period, every exchange was dropped, for example all of them for up to a day
    with sample(Duration.ofDays(1)) on a machine started in the morning.
    
    The first exchange is now always let through, and the period is measured
    from there.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../org/apache/camel/processor/SamplingThrottler.java    |  5 ++++-
 .../apache/camel/processor/SamplingThrottlerTest.java    | 16 ++++++++++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SamplingThrottler.java
 
b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SamplingThrottler.java
index de903397e0fc..c46d7ee12464 100644
--- 
a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SamplingThrottler.java
+++ 
b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SamplingThrottler.java
@@ -53,6 +53,7 @@ public class SamplingThrottler extends BaseProcessorSupport 
implements Traceable
     private long periodInMillis;
     private TimeUnit units;
     private long timeOfLastExchange;
+    private boolean sampledOnce;
     private final StopProcessor stopper = new StopProcessor();
     private final Lock calculationLock = new ReentrantLock();
     private final SampleStats sampled = new SampleStats();
@@ -145,7 +146,9 @@ public class SamplingThrottler extends BaseProcessorSupport 
implements Traceable
                 }
             } else {
                 long now = Duration.ofNanos(System.nanoTime()).toMillis();
-                if (now >= timeOfLastExchange + periodInMillis) {
+                // System.nanoTime() has an arbitrary origin, so the first 
exchange cannot be compared with a time
+                if (!sampledOnce || now >= timeOfLastExchange + 
periodInMillis) {
+                    sampledOnce = true;
                     doSend = true;
                     if (LOG.isTraceEnabled()) {
                         LOG.trace(sampled.sample());
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/SamplingThrottlerTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/SamplingThrottlerTest.java
index b98e5e9f9e68..c3851ff94773 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/processor/SamplingThrottlerTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/SamplingThrottlerTest.java
@@ -54,6 +54,20 @@ public class SamplingThrottlerTest extends 
ContextTestSupport {
         validateDroppedExchanges(sentExchanges, mock.getReceivedCounter());
     }
 
+    @Test
+    public void testFirstExchangeIsSampledWithLongPeriod() throws Exception {
+        // the first exchange is sampled whatever the period is, also when it 
is longer than the time since the
+        // (arbitrary) origin of System.nanoTime(), which is usually the time 
since the machine was started
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("1");
+
+        for (int i = 1; i <= 5; i++) {
+            template.sendBody("direct:sample-long-period", String.valueOf(i));
+        }
+
+        mock.assertIsSatisfied();
+    }
+
     @Test
     public void testBurstySampling() throws Exception {
         NotifyBuilder notify = new NotifyBuilder(context).whenDone(5).create();
@@ -190,6 +204,8 @@ public class SamplingThrottlerTest extends 
ContextTestSupport {
 
                 
from("direct:sample-messageFrequency-via-dsl").sample().sampleMessageFrequency(5).to("mock:result");
 
+                
from("direct:sample-long-period").sample(Duration.ofDays(36500)).to("mock:result");
+
                 
from("direct:sample-placeholder").sample("{{sample.period}}").to("mock:result");
 
                 // END SNIPPET: e1

Reply via email to