You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@camel.apache.org by Shashank Jain <al...@gmail.com> on 2026/10/19 07:14:39 UTC
[DISCUSS] An internal time source in camel-util (for deterministic simulation tests)
Hi all,
camel-test-dst (CAMEL-25738, PR https://github.com/apache/camel/pull/28067)
runs the real consumer code of a
component under one seeded scheduler with virtual time, so a test can try
thousands of interleavings and faults in
seconds and replay a failing seed exactly. Worlds written with it found or
confirmed several timing bugs, for
example CAMEL-25683, CAMEL-25737, CAMEL-25741, CAMEL-25756 and CAMEL-25758.
The harness controls Camel's thread pools through the existing
ThreadPoolFactory SPI. Time is the gap: core reads
System directly, so today a test either rewrites the byte code of core
classes or lives with real time and real
sleeps in core. Claus suggested a small time source in camel-util, and I
would like to discuss that here before
anything goes into core.
Where core reads the clock or sleeps today (src/main/java on main,
2026-10-17):
camel-util 4 currentTimeMillis, 5 nanoTime, 3 Thread.sleep
camel-support 24 currentTimeMillis, 6 nanoTime, 6 Thread.sleep, 5
Instant.now
camel-base 43 currentTimeMillis (nearly all event timestamps)
camel-base-engine 9 currentTimeMillis, 2 Thread.sleep, 1 Instant.now,
1 new Date()
camel-core-processor 6 currentTimeMillis, 7 nanoTime, 5 Thread.sleep
camel-management 9 currentTimeMillis, 1 Thread.sleep, 2 new Date()
others (api, languages, console, health, main): a handful
About 30 of these change behaviour, and only those need to be controlled in
a test:
- waits: RedeliveryPolicy.sleep and the sleep loop of
RedeliveryErrorHandler, ForegroundTask, the wait loop of
DefaultShutdownStrategy, DelayProcessorSupport, MulticastProcessor,
OptimisticLockRetryPolicy,
DefaultStartupConditionStrategy;
- task timing: StopWatch, MonotonicClock, AbstractTask, BackgroundTask,
TimeBoundedBudget, BackOffTimerTask,
DefaultTimeoutMap;
- throttling: TotalRequestsThrottler, ConcurrentRequestsThrottler,
SamplingThrottler,
ThrottlingExceptionRoutePolicy.
Event timestamps, the debugger, the tracer, management and the consoles can
stay on System.
Options I see:
1. Byte code rewriting only (what stage 1 does). No core change, but
runtime agent attach is being phased out in the
JDK, it only covers the calls it is told about, and rewritten core
classes stay rewritten for the whole test JVM.
2. A time source for all of core: about 120 call sites, many on hot paths,
for no gain in tests.
3. In between, which is what I would propose:
- a small static, internal TimeSource in camel-util:
currentTimeMillis(), nanoTime(), instant(), sleep(long).
The default delegates to System and Thread.sleep; a test harness can
install another provider. Static rather
than per CamelContext, because StopWatch and MonotonicClock have no
context;
- move only the ~30 call sites above;
- keep byte code rewriting for the component under test, pointed at
TimeSource, with the agent loaded by
-javaagent instead of attached at runtime;
- later, components use it when they are touched anyway, and a
forbidden-apis rule keeps new
Instant.now() / Thread.sleep() out of consumers.
Rough shape:
public final class TimeSource {
public interface Provider {
long currentTimeMillis();
long nanoTime();
void sleep(long millis) throws InterruptedException;
}
private static volatile Provider provider = SYSTEM;
public static long nanoTime() { return provider.nanoTime(); }
...
public static Provider install(Provider p) { ... } // test harnesses
only
}
Open points: package and name; a volatile field or a plain static field set
once before the context starts; and
whether sleep belongs there or in a separate wait helper.
On cost: I will run a JMH benchmark (direct System.nanoTime vs. TimeSource
with a volatile and a plain field,
StopWatch, and a direct -> log route with statistics on) and post the
numbers in this thread. My expectation is
that the default path inlines to the same call plus one ordinary load, so
it should be within noise.
Thoughts?
Thanks,
Shashank
Re: [DISCUSS] An internal time source in camel-util (for deterministic simulation tests)
Posted by Shashank Jain <al...@gmail.com>.
Hi JB,
Thanks for the +1 and good to hear from you :-)
On the follow-ups:
1. Forbidden calls: you're right, there's no check in the build yet,
and Camel doesn't use the forbidden-apis plugin today. To start small,
I'd add a test in camel-util that scans the sources of the moved
classes and fails on System.nanoTime(), System.currentTimeMillis() and
Thread.sleep(), with an allow-list for the TimeSource default itself.
If people would rather have the forbidden-apis plugin for this, I can
do that instead.
2. API or not: I'd keep it internal, as the javadoc says. Components
get virtual time through the core classes they already use (the task
classes, the delayer, the throttlers, the redelivery), and camel-chaos
stays the one exception. My proposal said components would adopt it
when touched; I'll correct that, so we don't end up with a public API
in org.apache.camel.util by accident. If a component later needs it
directly, we can discuss that case on its own.
3. Static provider: agreed. I'll log a line at INFO when a non-default
provider is installed, and when the default one is put back, naming
the provider class, so it shows up if it ever leaks out of a test
harness in a shared runtime like Karaf.
4. camel-performance-tests: the numbers I posted came from a local JMH
harness; the benchmark isn't in camel-performance-tests yet. I'll add
it there, so anyone can rerun it on x86 or a new JDK.
I'll open a JIRA for 1-3 and the PRs over the next days. The next
worlds (IMAP, NATS JetStream, Azure Service Bus and Storage Queue,
JMS) will come with their components' fixes.
Good to hear about Karaf and Camel Karaf. Is there a JIRA or a branch
I can look at? If they get a similar time source, it would be nice to
line them up, so a test can drive one virtual clock across Karaf,
Camel Karaf and Camel. Happy to help with that.
Thanks,
Shashank
On Sun, 8 Nov 2026 at 16:21, Jean-Baptiste Onofré <jb...@nanthrax.net> wrote:
> Hi Shashank
>
> Sorry for the late reply. A late +1 from me as well. Thanks for the clear
> write-up and posting the numbers.
>
> For anyone reading the archive later, this has landed:
> - CAMEL-25911 (#28382): TimeSource in camel-util, plus the call sites
> listed in this thread (redelivery, delayer, throttlers, the task classes,
> the shutdown wait loop, StopWatch, MonotonicClock).
> - CAMEL-26103 (#28782): the agent is now loaded with -javaagent instead of
> a runtime attach.
> - The send and step hooks (CAMEL-26331, CAMEL-26338) and camel-chaos also
> read time through TimeSource, so chaos delays run in virtual time too.
>
> I have a few follow-ups now that it's in:
>
> 1. Your proposal included a forbidden-apis rule, but I don't see one in the
> build yet. Without it, someone adding a System.nanoTime() or Thread.sleep()
> to one of these classes would take that wait out of virtual time, and
> nothing would catch it. A check limited to the classes that were moved
> would be enough to start with.
> 2. The javadoc says TimeSource is not an API for components (camel-chaos
> excepted). The proposal said components would adopt it when they are
> touched anyway. We should decide which one we mean. If components are
> expected to use it, it becomes a public API in org.apache.camel.util in
> practice, and we should treat its compatibility that way.
> 3. The provider is static, which is fine for tests. In a shared runtime
> like Karaf, though, one camel-util serves every CamelContext, so an
> installed provider affects all of them. Logging a line when a non-default
> provider is installed would make it easy to spot if this ever leaks outside
> a test harness.
> 4. +1 to putting it in camel-performance-tests, if that hasn't happened
> yet. Then anyone can rerun it on x86 or on a new JDK.
>
> Looking forward to the next worlds (IMAP, NATS JetStream, Azure Service Bus
> and Storage Queue, JMS).
>
> FYI, there's something similar in Karaf and Camel Karaf coming.
>
> Regards
> JB
>
> On Tue, Oct 20, 2026 at 3:08 AM Shashank Jain <al...@gmail.com>
> wrote:
>
> > Hi Claus,
> >
> > Thanks for the +1.
> >
> > Unless someone has concerns, I will leave the thread open a few more days
> > and then open a JIRA for it as stage 2 of
> > CAMEL-25738: the TimeSource in camel-util, the ~30 call sites in core
> that
> > change behaviour, and camel-test-dst
> > installing it, so the rewriting is only needed for the component under
> > test.
> >
> > On more fake worlds: I have worlds for IMAP, NATS JetStream, Azure
> Service
> > Bus, Azure Storage Queue and JMS that
> > found or confirmed bugs this week, and I plan to add them to their
> > components together with the fixes.
> >
> > Looking forward to hearing your other ideas for controlling time once DST
> > and chaos are in place.
> >
> > Thanks,
> > Shashank
> >
> > On Mon, 19 Oct 2026 at 22:53, Claus Ibsen <cl...@gmail.com> wrote:
> >
> > > Hi Shashank
> > >
> > > Thanks for doing these benchmarks so we can see what the affect would
> be.
> > >
> > > Surely the feature of allowing to "control the time" to make
> > deterministic
> > > testing in all the complex camel components possible,
> > > to ensure that we can have a really really well robust component that
> > > handles all kind of edge cases is worth this feature in the core.
> > >
> > > So its a +1 from me.
> > >
> > > PS: I have other ideas where this "control the time" can be useful, but
> > > lets get the DST and the Chaos feature built first. And more fake
> worlds
> > in
> > > various components so we can make Camel very roboust.
> > >
> > >
> > >
> > > On Mon, Oct 19, 2026 at 1:43 PM Shashank Jain <
> allthings.wasm@gmail.com>
> > > wrote:
> > >
> > > > Hi all,
> > > >
> > > > Here are the JMH numbers I promised. JMH 1.37, JDK 21.0.11, Apple M4
> > Pro
> > > > laptop (8P+4E cores), camel 4.24.0-SNAPSHOT;
> > > > 3 forks, 5 x 1 s warmup, 10 x 1 s measurement, average time in
> ns/op. A
> > > > laptop result, so indicative only.
> > > >
> > > > 1 thread 8 threads
> > > > System.nanoTime() 8.92 +- 0.01 356.1 +- 3.3
> > > > TimeSource.nanoTime(), volatile 9.10 +- 0.06 354.9 +- 3.2
> > > > TimeSource.nanoTime(), plain 9.15 +- 0.08 358.0 +- 3.3
> > > > TimeSource.nanoTime(), bimorphic 9.22 +- 0.13 355.7 +- 2.8
> > > > System.currentTimeMillis() 13.59 +- 0.09 186.5 +- 7.8
> > > > TimeSource.currentTimeMillis() 13.62 +- 0.02 185.2 +- 9.5
> > > > new StopWatch().taken(), today 19.96 +- 0.04 709.6 +- 7.6
> > > > the same with TimeSource 20.52 +- 0.12 710.8 +- 7.8
> > > >
> > > > direct:a -> log:x?level=OFF, route statistics on (today's core, for
> > > > scale):
> > > > 2.43M msg/s 10.6M msg/s
> (8
> > > > threads)
> > > >
> > > > "bimorphic" means a second provider class was installed and used once
> > in
> > > > setup, then SYSTEM was put back, so the
> > > > call site has seen two receiver types. With 8 threads the clock calls
> > are
> > > > much slower on this machine for every
> > > > variant, System.nanoTime() included; I have not dug into why, but the
> > > > variants are the same there.
> > > >
> > > > What it means: on one thread the indirection costs about 0.2 ns per
> > > > nanoTime() call and about 0.5 ns per StopWatch,
> > > > nothing measurable for currentTimeMillis(), and nothing measurable
> > with 8
> > > > threads. The volatile field is not
> > > > measurably slower than a plain static field here (9.10 vs 9.15 ns),
> so
> > I
> > > > would keep volatile, which also keeps
> > > > install() safe without extra rules. Against about 410 ns per message
> > for
> > > > the simplest direct -> log route, a few
> > > > tenths of a nanosecond per clock read is well below noise. On x86 a
> > > > volatile read is also a plain load, but
> > > > I have only measured on AArch64; numbers from an x86 box would be
> > > welcome.
> > > >
> > > > I can put the benchmark in camel-performance-tests if useful.
> > > >
> > > > Thanks,
> > > > Shashank
> > > >
> > > > On Mon, 19 Oct 2026 at 12:44, Shashank Jain <
> allthings.wasm@gmail.com>
> > > > wrote:
> > > >
> > > > >
> > > > > Hi all,
> > > > >
> > > > > camel-test-dst (CAMEL-25738, PR
> > > > https://github.com/apache/camel/pull/28067)
> > > > > runs the real consumer code of a
> > > > > component under one seeded scheduler with virtual time, so a test
> can
> > > try
> > > > > thousands of interleavings and faults in
> > > > > seconds and replay a failing seed exactly. Worlds written with it
> > found
> > > > or
> > > > > confirmed several timing bugs, for
> > > > > example CAMEL-25683, CAMEL-25737, CAMEL-25741, CAMEL-25756 and
> > > > CAMEL-25758.
> > > > >
> > > > > The harness controls Camel's thread pools through the existing
> > > > > ThreadPoolFactory SPI. Time is the gap: core reads
> > > > > System directly, so today a test either rewrites the byte code of
> > core
> > > > > classes or lives with real time and real
> > > > > sleeps in core. Claus suggested a small time source in camel-util,
> > and
> > > I
> > > > > would like to discuss that here before
> > > > > anything goes into core.
> > > > >
> > > > > Where core reads the clock or sleeps today (src/main/java on main,
> > > > > 2026-10-17):
> > > > >
> > > > > camel-util 4 currentTimeMillis, 5 nanoTime, 3
> > Thread.sleep
> > > > > camel-support 24 currentTimeMillis, 6 nanoTime, 6
> > > Thread.sleep,
> > > > 5
> > > > > Instant.now
> > > > > camel-base 43 currentTimeMillis (nearly all event
> > > timestamps)
> > > > > camel-base-engine 9 currentTimeMillis, 2 Thread.sleep, 1
> > > > > Instant.now, 1 new Date()
> > > > > camel-core-processor 6 currentTimeMillis, 7 nanoTime, 5
> > Thread.sleep
> > > > > camel-management 9 currentTimeMillis, 1 Thread.sleep, 2 new
> > > Date()
> > > > > others (api, languages, console, health, main): a handful
> > > > >
> > > > > About 30 of these change behaviour, and only those need to be
> > > controlled
> > > > > in a test:
> > > > > - waits: RedeliveryPolicy.sleep and the sleep loop of
> > > > > RedeliveryErrorHandler, ForegroundTask, the wait loop of
> > > > > DefaultShutdownStrategy, DelayProcessorSupport,
> MulticastProcessor,
> > > > > OptimisticLockRetryPolicy,
> > > > > DefaultStartupConditionStrategy;
> > > > > - task timing: StopWatch, MonotonicClock, AbstractTask,
> > BackgroundTask,
> > > > > TimeBoundedBudget, BackOffTimerTask,
> > > > > DefaultTimeoutMap;
> > > > > - throttling: TotalRequestsThrottler, ConcurrentRequestsThrottler,
> > > > > SamplingThrottler,
> > > > > ThrottlingExceptionRoutePolicy.
> > > > >
> > > > > Event timestamps, the debugger, the tracer, management and the
> > consoles
> > > > > can stay on System.
> > > > >
> > > > > Options I see:
> > > > >
> > > > > 1. Byte code rewriting only (what stage 1 does). No core change,
> but
> > > > > runtime agent attach is being phased out in the
> > > > > JDK, it only covers the calls it is told about, and rewritten
> core
> > > > > classes stay rewritten for the whole test JVM.
> > > > >
> > > > > 2. A time source for all of core: about 120 call sites, many on hot
> > > > paths,
> > > > > for no gain in tests.
> > > > >
> > > > > 3. In between, which is what I would propose:
> > > > > - a small static, internal TimeSource in camel-util:
> > > > > currentTimeMillis(), nanoTime(), instant(), sleep(long).
> > > > > The default delegates to System and Thread.sleep; a test
> harness
> > > can
> > > > > install another provider. Static rather
> > > > > than per CamelContext, because StopWatch and MonotonicClock
> have
> > > no
> > > > > context;
> > > > > - move only the ~30 call sites above;
> > > > > - keep byte code rewriting for the component under test, pointed
> > at
> > > > > TimeSource, with the agent loaded by
> > > > > -javaagent instead of attached at runtime;
> > > > > - later, components use it when they are touched anyway, and a
> > > > > forbidden-apis rule keeps new
> > > > > Instant.now() / Thread.sleep() out of consumers.
> > > > >
> > > > > Rough shape:
> > > > >
> > > > > public final class TimeSource {
> > > > > public interface Provider {
> > > > > long currentTimeMillis();
> > > > > long nanoTime();
> > > > > void sleep(long millis) throws InterruptedException;
> > > > > }
> > > > > private static volatile Provider provider = SYSTEM;
> > > > > public static long nanoTime() { return provider.nanoTime(); }
> > > > > ...
> > > > > public static Provider install(Provider p) { ... } // test
> > > > > harnesses only
> > > > > }
> > > > >
> > > > > Open points: package and name; a volatile field or a plain static
> > field
> > > > > set once before the context starts; and
> > > > > whether sleep belongs there or in a separate wait helper.
> > > > >
> > > > > On cost: I will run a JMH benchmark (direct System.nanoTime vs.
> > > > TimeSource
> > > > > with a volatile and a plain field,
> > > > > StopWatch, and a direct -> log route with statistics on) and post
> the
> > > > > numbers in this thread. My expectation is
> > > > > that the default path inlines to the same call plus one ordinary
> > load,
> > > so
> > > > > it should be within noise.
> > > > >
> > > > > Thoughts?
> > > > >
> > > > > Thanks,
> > > > > Shashank
> > > > >
> > > >
> > >
> > >
> > > --
> > > Claus Ibsen
> > >
> >
>
Re: [DISCUSS] An internal time source in camel-util (for deterministic simulation tests)
Posted by Jean-Baptiste Onofré <jb...@nanthrax.net>.
Hi Shashank
Sorry for the late reply. A late +1 from me as well. Thanks for the clear
write-up and posting the numbers.
For anyone reading the archive later, this has landed:
- CAMEL-25911 (#28382): TimeSource in camel-util, plus the call sites
listed in this thread (redelivery, delayer, throttlers, the task classes,
the shutdown wait loop, StopWatch, MonotonicClock).
- CAMEL-26103 (#28782): the agent is now loaded with -javaagent instead of
a runtime attach.
- The send and step hooks (CAMEL-26331, CAMEL-26338) and camel-chaos also
read time through TimeSource, so chaos delays run in virtual time too.
I have a few follow-ups now that it's in:
1. Your proposal included a forbidden-apis rule, but I don't see one in the
build yet. Without it, someone adding a System.nanoTime() or Thread.sleep()
to one of these classes would take that wait out of virtual time, and
nothing would catch it. A check limited to the classes that were moved
would be enough to start with.
2. The javadoc says TimeSource is not an API for components (camel-chaos
excepted). The proposal said components would adopt it when they are
touched anyway. We should decide which one we mean. If components are
expected to use it, it becomes a public API in org.apache.camel.util in
practice, and we should treat its compatibility that way.
3. The provider is static, which is fine for tests. In a shared runtime
like Karaf, though, one camel-util serves every CamelContext, so an
installed provider affects all of them. Logging a line when a non-default
provider is installed would make it easy to spot if this ever leaks outside
a test harness.
4. +1 to putting it in camel-performance-tests, if that hasn't happened
yet. Then anyone can rerun it on x86 or on a new JDK.
Looking forward to the next worlds (IMAP, NATS JetStream, Azure Service Bus
and Storage Queue, JMS).
FYI, there's something similar in Karaf and Camel Karaf coming.
Regards
JB
On Tue, Oct 20, 2026 at 3:08 AM Shashank Jain <al...@gmail.com>
wrote:
> Hi Claus,
>
> Thanks for the +1.
>
> Unless someone has concerns, I will leave the thread open a few more days
> and then open a JIRA for it as stage 2 of
> CAMEL-25738: the TimeSource in camel-util, the ~30 call sites in core that
> change behaviour, and camel-test-dst
> installing it, so the rewriting is only needed for the component under
> test.
>
> On more fake worlds: I have worlds for IMAP, NATS JetStream, Azure Service
> Bus, Azure Storage Queue and JMS that
> found or confirmed bugs this week, and I plan to add them to their
> components together with the fixes.
>
> Looking forward to hearing your other ideas for controlling time once DST
> and chaos are in place.
>
> Thanks,
> Shashank
>
> On Mon, 19 Oct 2026 at 22:53, Claus Ibsen <cl...@gmail.com> wrote:
>
> > Hi Shashank
> >
> > Thanks for doing these benchmarks so we can see what the affect would be.
> >
> > Surely the feature of allowing to "control the time" to make
> deterministic
> > testing in all the complex camel components possible,
> > to ensure that we can have a really really well robust component that
> > handles all kind of edge cases is worth this feature in the core.
> >
> > So its a +1 from me.
> >
> > PS: I have other ideas where this "control the time" can be useful, but
> > lets get the DST and the Chaos feature built first. And more fake worlds
> in
> > various components so we can make Camel very roboust.
> >
> >
> >
> > On Mon, Oct 19, 2026 at 1:43 PM Shashank Jain <al...@gmail.com>
> > wrote:
> >
> > > Hi all,
> > >
> > > Here are the JMH numbers I promised. JMH 1.37, JDK 21.0.11, Apple M4
> Pro
> > > laptop (8P+4E cores), camel 4.24.0-SNAPSHOT;
> > > 3 forks, 5 x 1 s warmup, 10 x 1 s measurement, average time in ns/op. A
> > > laptop result, so indicative only.
> > >
> > > 1 thread 8 threads
> > > System.nanoTime() 8.92 +- 0.01 356.1 +- 3.3
> > > TimeSource.nanoTime(), volatile 9.10 +- 0.06 354.9 +- 3.2
> > > TimeSource.nanoTime(), plain 9.15 +- 0.08 358.0 +- 3.3
> > > TimeSource.nanoTime(), bimorphic 9.22 +- 0.13 355.7 +- 2.8
> > > System.currentTimeMillis() 13.59 +- 0.09 186.5 +- 7.8
> > > TimeSource.currentTimeMillis() 13.62 +- 0.02 185.2 +- 9.5
> > > new StopWatch().taken(), today 19.96 +- 0.04 709.6 +- 7.6
> > > the same with TimeSource 20.52 +- 0.12 710.8 +- 7.8
> > >
> > > direct:a -> log:x?level=OFF, route statistics on (today's core, for
> > > scale):
> > > 2.43M msg/s 10.6M msg/s (8
> > > threads)
> > >
> > > "bimorphic" means a second provider class was installed and used once
> in
> > > setup, then SYSTEM was put back, so the
> > > call site has seen two receiver types. With 8 threads the clock calls
> are
> > > much slower on this machine for every
> > > variant, System.nanoTime() included; I have not dug into why, but the
> > > variants are the same there.
> > >
> > > What it means: on one thread the indirection costs about 0.2 ns per
> > > nanoTime() call and about 0.5 ns per StopWatch,
> > > nothing measurable for currentTimeMillis(), and nothing measurable
> with 8
> > > threads. The volatile field is not
> > > measurably slower than a plain static field here (9.10 vs 9.15 ns), so
> I
> > > would keep volatile, which also keeps
> > > install() safe without extra rules. Against about 410 ns per message
> for
> > > the simplest direct -> log route, a few
> > > tenths of a nanosecond per clock read is well below noise. On x86 a
> > > volatile read is also a plain load, but
> > > I have only measured on AArch64; numbers from an x86 box would be
> > welcome.
> > >
> > > I can put the benchmark in camel-performance-tests if useful.
> > >
> > > Thanks,
> > > Shashank
> > >
> > > On Mon, 19 Oct 2026 at 12:44, Shashank Jain <al...@gmail.com>
> > > wrote:
> > >
> > > >
> > > > Hi all,
> > > >
> > > > camel-test-dst (CAMEL-25738, PR
> > > https://github.com/apache/camel/pull/28067)
> > > > runs the real consumer code of a
> > > > component under one seeded scheduler with virtual time, so a test can
> > try
> > > > thousands of interleavings and faults in
> > > > seconds and replay a failing seed exactly. Worlds written with it
> found
> > > or
> > > > confirmed several timing bugs, for
> > > > example CAMEL-25683, CAMEL-25737, CAMEL-25741, CAMEL-25756 and
> > > CAMEL-25758.
> > > >
> > > > The harness controls Camel's thread pools through the existing
> > > > ThreadPoolFactory SPI. Time is the gap: core reads
> > > > System directly, so today a test either rewrites the byte code of
> core
> > > > classes or lives with real time and real
> > > > sleeps in core. Claus suggested a small time source in camel-util,
> and
> > I
> > > > would like to discuss that here before
> > > > anything goes into core.
> > > >
> > > > Where core reads the clock or sleeps today (src/main/java on main,
> > > > 2026-10-17):
> > > >
> > > > camel-util 4 currentTimeMillis, 5 nanoTime, 3
> Thread.sleep
> > > > camel-support 24 currentTimeMillis, 6 nanoTime, 6
> > Thread.sleep,
> > > 5
> > > > Instant.now
> > > > camel-base 43 currentTimeMillis (nearly all event
> > timestamps)
> > > > camel-base-engine 9 currentTimeMillis, 2 Thread.sleep, 1
> > > > Instant.now, 1 new Date()
> > > > camel-core-processor 6 currentTimeMillis, 7 nanoTime, 5
> Thread.sleep
> > > > camel-management 9 currentTimeMillis, 1 Thread.sleep, 2 new
> > Date()
> > > > others (api, languages, console, health, main): a handful
> > > >
> > > > About 30 of these change behaviour, and only those need to be
> > controlled
> > > > in a test:
> > > > - waits: RedeliveryPolicy.sleep and the sleep loop of
> > > > RedeliveryErrorHandler, ForegroundTask, the wait loop of
> > > > DefaultShutdownStrategy, DelayProcessorSupport, MulticastProcessor,
> > > > OptimisticLockRetryPolicy,
> > > > DefaultStartupConditionStrategy;
> > > > - task timing: StopWatch, MonotonicClock, AbstractTask,
> BackgroundTask,
> > > > TimeBoundedBudget, BackOffTimerTask,
> > > > DefaultTimeoutMap;
> > > > - throttling: TotalRequestsThrottler, ConcurrentRequestsThrottler,
> > > > SamplingThrottler,
> > > > ThrottlingExceptionRoutePolicy.
> > > >
> > > > Event timestamps, the debugger, the tracer, management and the
> consoles
> > > > can stay on System.
> > > >
> > > > Options I see:
> > > >
> > > > 1. Byte code rewriting only (what stage 1 does). No core change, but
> > > > runtime agent attach is being phased out in the
> > > > JDK, it only covers the calls it is told about, and rewritten core
> > > > classes stay rewritten for the whole test JVM.
> > > >
> > > > 2. A time source for all of core: about 120 call sites, many on hot
> > > paths,
> > > > for no gain in tests.
> > > >
> > > > 3. In between, which is what I would propose:
> > > > - a small static, internal TimeSource in camel-util:
> > > > currentTimeMillis(), nanoTime(), instant(), sleep(long).
> > > > The default delegates to System and Thread.sleep; a test harness
> > can
> > > > install another provider. Static rather
> > > > than per CamelContext, because StopWatch and MonotonicClock have
> > no
> > > > context;
> > > > - move only the ~30 call sites above;
> > > > - keep byte code rewriting for the component under test, pointed
> at
> > > > TimeSource, with the agent loaded by
> > > > -javaagent instead of attached at runtime;
> > > > - later, components use it when they are touched anyway, and a
> > > > forbidden-apis rule keeps new
> > > > Instant.now() / Thread.sleep() out of consumers.
> > > >
> > > > Rough shape:
> > > >
> > > > public final class TimeSource {
> > > > public interface Provider {
> > > > long currentTimeMillis();
> > > > long nanoTime();
> > > > void sleep(long millis) throws InterruptedException;
> > > > }
> > > > private static volatile Provider provider = SYSTEM;
> > > > public static long nanoTime() { return provider.nanoTime(); }
> > > > ...
> > > > public static Provider install(Provider p) { ... } // test
> > > > harnesses only
> > > > }
> > > >
> > > > Open points: package and name; a volatile field or a plain static
> field
> > > > set once before the context starts; and
> > > > whether sleep belongs there or in a separate wait helper.
> > > >
> > > > On cost: I will run a JMH benchmark (direct System.nanoTime vs.
> > > TimeSource
> > > > with a volatile and a plain field,
> > > > StopWatch, and a direct -> log route with statistics on) and post the
> > > > numbers in this thread. My expectation is
> > > > that the default path inlines to the same call plus one ordinary
> load,
> > so
> > > > it should be within noise.
> > > >
> > > > Thoughts?
> > > >
> > > > Thanks,
> > > > Shashank
> > > >
> > >
> >
> >
> > --
> > Claus Ibsen
> >
>
Re: [DISCUSS] An internal time source in camel-util (for deterministic simulation tests)
Posted by Shashank Jain <al...@gmail.com>.
Hi Claus,
Thanks for the +1.
Unless someone has concerns, I will leave the thread open a few more days
and then open a JIRA for it as stage 2 of
CAMEL-25738: the TimeSource in camel-util, the ~30 call sites in core that
change behaviour, and camel-test-dst
installing it, so the rewriting is only needed for the component under test.
On more fake worlds: I have worlds for IMAP, NATS JetStream, Azure Service
Bus, Azure Storage Queue and JMS that
found or confirmed bugs this week, and I plan to add them to their
components together with the fixes.
Looking forward to hearing your other ideas for controlling time once DST
and chaos are in place.
Thanks,
Shashank
On Mon, 19 Oct 2026 at 22:53, Claus Ibsen <cl...@gmail.com> wrote:
> Hi Shashank
>
> Thanks for doing these benchmarks so we can see what the affect would be.
>
> Surely the feature of allowing to "control the time" to make deterministic
> testing in all the complex camel components possible,
> to ensure that we can have a really really well robust component that
> handles all kind of edge cases is worth this feature in the core.
>
> So its a +1 from me.
>
> PS: I have other ideas where this "control the time" can be useful, but
> lets get the DST and the Chaos feature built first. And more fake worlds in
> various components so we can make Camel very roboust.
>
>
>
> On Mon, Oct 19, 2026 at 1:43 PM Shashank Jain <al...@gmail.com>
> wrote:
>
> > Hi all,
> >
> > Here are the JMH numbers I promised. JMH 1.37, JDK 21.0.11, Apple M4 Pro
> > laptop (8P+4E cores), camel 4.24.0-SNAPSHOT;
> > 3 forks, 5 x 1 s warmup, 10 x 1 s measurement, average time in ns/op. A
> > laptop result, so indicative only.
> >
> > 1 thread 8 threads
> > System.nanoTime() 8.92 +- 0.01 356.1 +- 3.3
> > TimeSource.nanoTime(), volatile 9.10 +- 0.06 354.9 +- 3.2
> > TimeSource.nanoTime(), plain 9.15 +- 0.08 358.0 +- 3.3
> > TimeSource.nanoTime(), bimorphic 9.22 +- 0.13 355.7 +- 2.8
> > System.currentTimeMillis() 13.59 +- 0.09 186.5 +- 7.8
> > TimeSource.currentTimeMillis() 13.62 +- 0.02 185.2 +- 9.5
> > new StopWatch().taken(), today 19.96 +- 0.04 709.6 +- 7.6
> > the same with TimeSource 20.52 +- 0.12 710.8 +- 7.8
> >
> > direct:a -> log:x?level=OFF, route statistics on (today's core, for
> > scale):
> > 2.43M msg/s 10.6M msg/s (8
> > threads)
> >
> > "bimorphic" means a second provider class was installed and used once in
> > setup, then SYSTEM was put back, so the
> > call site has seen two receiver types. With 8 threads the clock calls are
> > much slower on this machine for every
> > variant, System.nanoTime() included; I have not dug into why, but the
> > variants are the same there.
> >
> > What it means: on one thread the indirection costs about 0.2 ns per
> > nanoTime() call and about 0.5 ns per StopWatch,
> > nothing measurable for currentTimeMillis(), and nothing measurable with 8
> > threads. The volatile field is not
> > measurably slower than a plain static field here (9.10 vs 9.15 ns), so I
> > would keep volatile, which also keeps
> > install() safe without extra rules. Against about 410 ns per message for
> > the simplest direct -> log route, a few
> > tenths of a nanosecond per clock read is well below noise. On x86 a
> > volatile read is also a plain load, but
> > I have only measured on AArch64; numbers from an x86 box would be
> welcome.
> >
> > I can put the benchmark in camel-performance-tests if useful.
> >
> > Thanks,
> > Shashank
> >
> > On Mon, 19 Oct 2026 at 12:44, Shashank Jain <al...@gmail.com>
> > wrote:
> >
> > >
> > > Hi all,
> > >
> > > camel-test-dst (CAMEL-25738, PR
> > https://github.com/apache/camel/pull/28067)
> > > runs the real consumer code of a
> > > component under one seeded scheduler with virtual time, so a test can
> try
> > > thousands of interleavings and faults in
> > > seconds and replay a failing seed exactly. Worlds written with it found
> > or
> > > confirmed several timing bugs, for
> > > example CAMEL-25683, CAMEL-25737, CAMEL-25741, CAMEL-25756 and
> > CAMEL-25758.
> > >
> > > The harness controls Camel's thread pools through the existing
> > > ThreadPoolFactory SPI. Time is the gap: core reads
> > > System directly, so today a test either rewrites the byte code of core
> > > classes or lives with real time and real
> > > sleeps in core. Claus suggested a small time source in camel-util, and
> I
> > > would like to discuss that here before
> > > anything goes into core.
> > >
> > > Where core reads the clock or sleeps today (src/main/java on main,
> > > 2026-10-17):
> > >
> > > camel-util 4 currentTimeMillis, 5 nanoTime, 3 Thread.sleep
> > > camel-support 24 currentTimeMillis, 6 nanoTime, 6
> Thread.sleep,
> > 5
> > > Instant.now
> > > camel-base 43 currentTimeMillis (nearly all event
> timestamps)
> > > camel-base-engine 9 currentTimeMillis, 2 Thread.sleep, 1
> > > Instant.now, 1 new Date()
> > > camel-core-processor 6 currentTimeMillis, 7 nanoTime, 5 Thread.sleep
> > > camel-management 9 currentTimeMillis, 1 Thread.sleep, 2 new
> Date()
> > > others (api, languages, console, health, main): a handful
> > >
> > > About 30 of these change behaviour, and only those need to be
> controlled
> > > in a test:
> > > - waits: RedeliveryPolicy.sleep and the sleep loop of
> > > RedeliveryErrorHandler, ForegroundTask, the wait loop of
> > > DefaultShutdownStrategy, DelayProcessorSupport, MulticastProcessor,
> > > OptimisticLockRetryPolicy,
> > > DefaultStartupConditionStrategy;
> > > - task timing: StopWatch, MonotonicClock, AbstractTask, BackgroundTask,
> > > TimeBoundedBudget, BackOffTimerTask,
> > > DefaultTimeoutMap;
> > > - throttling: TotalRequestsThrottler, ConcurrentRequestsThrottler,
> > > SamplingThrottler,
> > > ThrottlingExceptionRoutePolicy.
> > >
> > > Event timestamps, the debugger, the tracer, management and the consoles
> > > can stay on System.
> > >
> > > Options I see:
> > >
> > > 1. Byte code rewriting only (what stage 1 does). No core change, but
> > > runtime agent attach is being phased out in the
> > > JDK, it only covers the calls it is told about, and rewritten core
> > > classes stay rewritten for the whole test JVM.
> > >
> > > 2. A time source for all of core: about 120 call sites, many on hot
> > paths,
> > > for no gain in tests.
> > >
> > > 3. In between, which is what I would propose:
> > > - a small static, internal TimeSource in camel-util:
> > > currentTimeMillis(), nanoTime(), instant(), sleep(long).
> > > The default delegates to System and Thread.sleep; a test harness
> can
> > > install another provider. Static rather
> > > than per CamelContext, because StopWatch and MonotonicClock have
> no
> > > context;
> > > - move only the ~30 call sites above;
> > > - keep byte code rewriting for the component under test, pointed at
> > > TimeSource, with the agent loaded by
> > > -javaagent instead of attached at runtime;
> > > - later, components use it when they are touched anyway, and a
> > > forbidden-apis rule keeps new
> > > Instant.now() / Thread.sleep() out of consumers.
> > >
> > > Rough shape:
> > >
> > > public final class TimeSource {
> > > public interface Provider {
> > > long currentTimeMillis();
> > > long nanoTime();
> > > void sleep(long millis) throws InterruptedException;
> > > }
> > > private static volatile Provider provider = SYSTEM;
> > > public static long nanoTime() { return provider.nanoTime(); }
> > > ...
> > > public static Provider install(Provider p) { ... } // test
> > > harnesses only
> > > }
> > >
> > > Open points: package and name; a volatile field or a plain static field
> > > set once before the context starts; and
> > > whether sleep belongs there or in a separate wait helper.
> > >
> > > On cost: I will run a JMH benchmark (direct System.nanoTime vs.
> > TimeSource
> > > with a volatile and a plain field,
> > > StopWatch, and a direct -> log route with statistics on) and post the
> > > numbers in this thread. My expectation is
> > > that the default path inlines to the same call plus one ordinary load,
> so
> > > it should be within noise.
> > >
> > > Thoughts?
> > >
> > > Thanks,
> > > Shashank
> > >
> >
>
>
> --
> Claus Ibsen
>
Re: [DISCUSS] An internal time source in camel-util (for deterministic simulation tests)
Posted by Claus Ibsen <cl...@gmail.com>.
Hi Shashank
Thanks for doing these benchmarks so we can see what the affect would be.
Surely the feature of allowing to "control the time" to make deterministic
testing in all the complex camel components possible,
to ensure that we can have a really really well robust component that
handles all kind of edge cases is worth this feature in the core.
So its a +1 from me.
PS: I have other ideas where this "control the time" can be useful, but
lets get the DST and the Chaos feature built first. And more fake worlds in
various components so we can make Camel very roboust.
On Mon, Oct 19, 2026 at 1:43 PM Shashank Jain <al...@gmail.com>
wrote:
> Hi all,
>
> Here are the JMH numbers I promised. JMH 1.37, JDK 21.0.11, Apple M4 Pro
> laptop (8P+4E cores), camel 4.24.0-SNAPSHOT;
> 3 forks, 5 x 1 s warmup, 10 x 1 s measurement, average time in ns/op. A
> laptop result, so indicative only.
>
> 1 thread 8 threads
> System.nanoTime() 8.92 +- 0.01 356.1 +- 3.3
> TimeSource.nanoTime(), volatile 9.10 +- 0.06 354.9 +- 3.2
> TimeSource.nanoTime(), plain 9.15 +- 0.08 358.0 +- 3.3
> TimeSource.nanoTime(), bimorphic 9.22 +- 0.13 355.7 +- 2.8
> System.currentTimeMillis() 13.59 +- 0.09 186.5 +- 7.8
> TimeSource.currentTimeMillis() 13.62 +- 0.02 185.2 +- 9.5
> new StopWatch().taken(), today 19.96 +- 0.04 709.6 +- 7.6
> the same with TimeSource 20.52 +- 0.12 710.8 +- 7.8
>
> direct:a -> log:x?level=OFF, route statistics on (today's core, for
> scale):
> 2.43M msg/s 10.6M msg/s (8
> threads)
>
> "bimorphic" means a second provider class was installed and used once in
> setup, then SYSTEM was put back, so the
> call site has seen two receiver types. With 8 threads the clock calls are
> much slower on this machine for every
> variant, System.nanoTime() included; I have not dug into why, but the
> variants are the same there.
>
> What it means: on one thread the indirection costs about 0.2 ns per
> nanoTime() call and about 0.5 ns per StopWatch,
> nothing measurable for currentTimeMillis(), and nothing measurable with 8
> threads. The volatile field is not
> measurably slower than a plain static field here (9.10 vs 9.15 ns), so I
> would keep volatile, which also keeps
> install() safe without extra rules. Against about 410 ns per message for
> the simplest direct -> log route, a few
> tenths of a nanosecond per clock read is well below noise. On x86 a
> volatile read is also a plain load, but
> I have only measured on AArch64; numbers from an x86 box would be welcome.
>
> I can put the benchmark in camel-performance-tests if useful.
>
> Thanks,
> Shashank
>
> On Mon, 19 Oct 2026 at 12:44, Shashank Jain <al...@gmail.com>
> wrote:
>
> >
> > Hi all,
> >
> > camel-test-dst (CAMEL-25738, PR
> https://github.com/apache/camel/pull/28067)
> > runs the real consumer code of a
> > component under one seeded scheduler with virtual time, so a test can try
> > thousands of interleavings and faults in
> > seconds and replay a failing seed exactly. Worlds written with it found
> or
> > confirmed several timing bugs, for
> > example CAMEL-25683, CAMEL-25737, CAMEL-25741, CAMEL-25756 and
> CAMEL-25758.
> >
> > The harness controls Camel's thread pools through the existing
> > ThreadPoolFactory SPI. Time is the gap: core reads
> > System directly, so today a test either rewrites the byte code of core
> > classes or lives with real time and real
> > sleeps in core. Claus suggested a small time source in camel-util, and I
> > would like to discuss that here before
> > anything goes into core.
> >
> > Where core reads the clock or sleeps today (src/main/java on main,
> > 2026-10-17):
> >
> > camel-util 4 currentTimeMillis, 5 nanoTime, 3 Thread.sleep
> > camel-support 24 currentTimeMillis, 6 nanoTime, 6 Thread.sleep,
> 5
> > Instant.now
> > camel-base 43 currentTimeMillis (nearly all event timestamps)
> > camel-base-engine 9 currentTimeMillis, 2 Thread.sleep, 1
> > Instant.now, 1 new Date()
> > camel-core-processor 6 currentTimeMillis, 7 nanoTime, 5 Thread.sleep
> > camel-management 9 currentTimeMillis, 1 Thread.sleep, 2 new Date()
> > others (api, languages, console, health, main): a handful
> >
> > About 30 of these change behaviour, and only those need to be controlled
> > in a test:
> > - waits: RedeliveryPolicy.sleep and the sleep loop of
> > RedeliveryErrorHandler, ForegroundTask, the wait loop of
> > DefaultShutdownStrategy, DelayProcessorSupport, MulticastProcessor,
> > OptimisticLockRetryPolicy,
> > DefaultStartupConditionStrategy;
> > - task timing: StopWatch, MonotonicClock, AbstractTask, BackgroundTask,
> > TimeBoundedBudget, BackOffTimerTask,
> > DefaultTimeoutMap;
> > - throttling: TotalRequestsThrottler, ConcurrentRequestsThrottler,
> > SamplingThrottler,
> > ThrottlingExceptionRoutePolicy.
> >
> > Event timestamps, the debugger, the tracer, management and the consoles
> > can stay on System.
> >
> > Options I see:
> >
> > 1. Byte code rewriting only (what stage 1 does). No core change, but
> > runtime agent attach is being phased out in the
> > JDK, it only covers the calls it is told about, and rewritten core
> > classes stay rewritten for the whole test JVM.
> >
> > 2. A time source for all of core: about 120 call sites, many on hot
> paths,
> > for no gain in tests.
> >
> > 3. In between, which is what I would propose:
> > - a small static, internal TimeSource in camel-util:
> > currentTimeMillis(), nanoTime(), instant(), sleep(long).
> > The default delegates to System and Thread.sleep; a test harness can
> > install another provider. Static rather
> > than per CamelContext, because StopWatch and MonotonicClock have no
> > context;
> > - move only the ~30 call sites above;
> > - keep byte code rewriting for the component under test, pointed at
> > TimeSource, with the agent loaded by
> > -javaagent instead of attached at runtime;
> > - later, components use it when they are touched anyway, and a
> > forbidden-apis rule keeps new
> > Instant.now() / Thread.sleep() out of consumers.
> >
> > Rough shape:
> >
> > public final class TimeSource {
> > public interface Provider {
> > long currentTimeMillis();
> > long nanoTime();
> > void sleep(long millis) throws InterruptedException;
> > }
> > private static volatile Provider provider = SYSTEM;
> > public static long nanoTime() { return provider.nanoTime(); }
> > ...
> > public static Provider install(Provider p) { ... } // test
> > harnesses only
> > }
> >
> > Open points: package and name; a volatile field or a plain static field
> > set once before the context starts; and
> > whether sleep belongs there or in a separate wait helper.
> >
> > On cost: I will run a JMH benchmark (direct System.nanoTime vs.
> TimeSource
> > with a volatile and a plain field,
> > StopWatch, and a direct -> log route with statistics on) and post the
> > numbers in this thread. My expectation is
> > that the default path inlines to the same call plus one ordinary load, so
> > it should be within noise.
> >
> > Thoughts?
> >
> > Thanks,
> > Shashank
> >
>
--
Claus Ibsen
Re: [DISCUSS] An internal time source in camel-util (for deterministic simulation tests)
Posted by Shashank Jain <al...@gmail.com>.
Hi all,
Here are the JMH numbers I promised. JMH 1.37, JDK 21.0.11, Apple M4 Pro
laptop (8P+4E cores), camel 4.24.0-SNAPSHOT;
3 forks, 5 x 1 s warmup, 10 x 1 s measurement, average time in ns/op. A
laptop result, so indicative only.
1 thread 8 threads
System.nanoTime() 8.92 +- 0.01 356.1 +- 3.3
TimeSource.nanoTime(), volatile 9.10 +- 0.06 354.9 +- 3.2
TimeSource.nanoTime(), plain 9.15 +- 0.08 358.0 +- 3.3
TimeSource.nanoTime(), bimorphic 9.22 +- 0.13 355.7 +- 2.8
System.currentTimeMillis() 13.59 +- 0.09 186.5 +- 7.8
TimeSource.currentTimeMillis() 13.62 +- 0.02 185.2 +- 9.5
new StopWatch().taken(), today 19.96 +- 0.04 709.6 +- 7.6
the same with TimeSource 20.52 +- 0.12 710.8 +- 7.8
direct:a -> log:x?level=OFF, route statistics on (today's core, for
scale):
2.43M msg/s 10.6M msg/s (8
threads)
"bimorphic" means a second provider class was installed and used once in
setup, then SYSTEM was put back, so the
call site has seen two receiver types. With 8 threads the clock calls are
much slower on this machine for every
variant, System.nanoTime() included; I have not dug into why, but the
variants are the same there.
What it means: on one thread the indirection costs about 0.2 ns per
nanoTime() call and about 0.5 ns per StopWatch,
nothing measurable for currentTimeMillis(), and nothing measurable with 8
threads. The volatile field is not
measurably slower than a plain static field here (9.10 vs 9.15 ns), so I
would keep volatile, which also keeps
install() safe without extra rules. Against about 410 ns per message for
the simplest direct -> log route, a few
tenths of a nanosecond per clock read is well below noise. On x86 a
volatile read is also a plain load, but
I have only measured on AArch64; numbers from an x86 box would be welcome.
I can put the benchmark in camel-performance-tests if useful.
Thanks,
Shashank
On Mon, 19 Oct 2026 at 12:44, Shashank Jain <al...@gmail.com>
wrote:
>
> Hi all,
>
> camel-test-dst (CAMEL-25738, PR https://github.com/apache/camel/pull/28067)
> runs the real consumer code of a
> component under one seeded scheduler with virtual time, so a test can try
> thousands of interleavings and faults in
> seconds and replay a failing seed exactly. Worlds written with it found or
> confirmed several timing bugs, for
> example CAMEL-25683, CAMEL-25737, CAMEL-25741, CAMEL-25756 and CAMEL-25758.
>
> The harness controls Camel's thread pools through the existing
> ThreadPoolFactory SPI. Time is the gap: core reads
> System directly, so today a test either rewrites the byte code of core
> classes or lives with real time and real
> sleeps in core. Claus suggested a small time source in camel-util, and I
> would like to discuss that here before
> anything goes into core.
>
> Where core reads the clock or sleeps today (src/main/java on main,
> 2026-10-17):
>
> camel-util 4 currentTimeMillis, 5 nanoTime, 3 Thread.sleep
> camel-support 24 currentTimeMillis, 6 nanoTime, 6 Thread.sleep, 5
> Instant.now
> camel-base 43 currentTimeMillis (nearly all event timestamps)
> camel-base-engine 9 currentTimeMillis, 2 Thread.sleep, 1
> Instant.now, 1 new Date()
> camel-core-processor 6 currentTimeMillis, 7 nanoTime, 5 Thread.sleep
> camel-management 9 currentTimeMillis, 1 Thread.sleep, 2 new Date()
> others (api, languages, console, health, main): a handful
>
> About 30 of these change behaviour, and only those need to be controlled
> in a test:
> - waits: RedeliveryPolicy.sleep and the sleep loop of
> RedeliveryErrorHandler, ForegroundTask, the wait loop of
> DefaultShutdownStrategy, DelayProcessorSupport, MulticastProcessor,
> OptimisticLockRetryPolicy,
> DefaultStartupConditionStrategy;
> - task timing: StopWatch, MonotonicClock, AbstractTask, BackgroundTask,
> TimeBoundedBudget, BackOffTimerTask,
> DefaultTimeoutMap;
> - throttling: TotalRequestsThrottler, ConcurrentRequestsThrottler,
> SamplingThrottler,
> ThrottlingExceptionRoutePolicy.
>
> Event timestamps, the debugger, the tracer, management and the consoles
> can stay on System.
>
> Options I see:
>
> 1. Byte code rewriting only (what stage 1 does). No core change, but
> runtime agent attach is being phased out in the
> JDK, it only covers the calls it is told about, and rewritten core
> classes stay rewritten for the whole test JVM.
>
> 2. A time source for all of core: about 120 call sites, many on hot paths,
> for no gain in tests.
>
> 3. In between, which is what I would propose:
> - a small static, internal TimeSource in camel-util:
> currentTimeMillis(), nanoTime(), instant(), sleep(long).
> The default delegates to System and Thread.sleep; a test harness can
> install another provider. Static rather
> than per CamelContext, because StopWatch and MonotonicClock have no
> context;
> - move only the ~30 call sites above;
> - keep byte code rewriting for the component under test, pointed at
> TimeSource, with the agent loaded by
> -javaagent instead of attached at runtime;
> - later, components use it when they are touched anyway, and a
> forbidden-apis rule keeps new
> Instant.now() / Thread.sleep() out of consumers.
>
> Rough shape:
>
> public final class TimeSource {
> public interface Provider {
> long currentTimeMillis();
> long nanoTime();
> void sleep(long millis) throws InterruptedException;
> }
> private static volatile Provider provider = SYSTEM;
> public static long nanoTime() { return provider.nanoTime(); }
> ...
> public static Provider install(Provider p) { ... } // test
> harnesses only
> }
>
> Open points: package and name; a volatile field or a plain static field
> set once before the context starts; and
> whether sleep belongs there or in a separate wait helper.
>
> On cost: I will run a JMH benchmark (direct System.nanoTime vs. TimeSource
> with a volatile and a plain field,
> StopWatch, and a direct -> log route with statistics on) and post the
> numbers in this thread. My expectation is
> that the default path inlines to the same call plus one ordinary load, so
> it should be within noise.
>
> Thoughts?
>
> Thanks,
> Shashank
>