Skip to content

v1.1.0 β€” Thread Distribution & Startup Time Trend

Choose a tag to compare

@ddsha441981 ddsha441981 released this 23 Aug 05:51
· 38 commits to main since this release
ed56530

Two new diagnostics, one architecture migration that fixes a real bug, and a UI pass on the HTML report. Purely additive β€” schemaVersion stays 1, no config property renamed, no new dependency.

🧡 Thread Distribution β€” did anything actually leave main?

spring.beans.instantiate startup steps now carry a threadName tag captured at step-creation time β€” the only point where the instantiating thread is still on the stack. Reading Thread.currentThread() at analysis time, as the old code did, always returned main.

The report gains a threadDistribution section mapping each thread to its beans plus a per-thread count. The Timing tab renders it as a donut chart and a table ordered by bean count.

"threadDistribution": {
  "perThread": { "main": ["dataSource", "entityManagerFactory"], "app-bg-1": ["warmCacheBean"] },
  "counts": { "main": 2, "app-bg-1": 1 }
}

The honest framing matters here: Spring does not parallelise bean initialisation by default, and no Boot version has. Background init is opt-in per bean and needs both @Bean(bootstrap = Bean.Bootstrap.BACKGROUND) (Framework 6.2+ / Boot 3.4+) and a bootstrapExecutor. Without both, a 600-bean application legitimately reports 100% on main β€” the card shows a parallel init not active pill rather than implying something is wrong. The one case where this chart really earns its place: you marked a slow bean BACKGROUND and gave it an executor β€” did it actually move off main, or is it silently still blocking startup?

Validated on Spring Cloud Config Server (616 beans, Boot 4.1): 610 on main, 1 on configserver-bg-1 β€” the single @Bean(bootstrap = BACKGROUND).

πŸ“ˆ Startup Time Trend β€” catch creep before the gate trips

Every run with wiredoctor.baseline-write=true appends {timestamp, totalStartupMs, slowBeanCount} to trendHistory[] in wiredoctor-baseline.json, capped by wiredoctor.trend-history-size (default 30, 0 = unlimited). The Timing tab renders a sparkline, so a +50ms/week drift becomes visible long before the startup-time gate trips on a single bad run.

Worth knowing: the sparkline appears only in reports from a baseline-write=true run, and only from the second write onward β€” trendHistory[] lives in the baseline file, not in the per-run report. A nightly baseline-refresh job is the natural place to look.

πŸ“Š Report UI

Still plain HTML/CSS/JS in one self-contained file β€” no libraries added.

  • Overview charts β€” a 5-bucket bean instantiation-time histogram (0 / 1–9 / 10–49 / 50–99 / β‰₯100 ms), a bean-role stacked bar (ROLE_APPLICATION / SUPPORT / INFRASTRUCTURE), and an autoconfiguration-outcome stacked bar (matched / unconditional / notMatched / excluded). Every column carries its exact count, so the slow tail β€” always the smallest bucket and always the one you care about β€” never renders as an invisible sliver. Palettes are colour-blind validated against the dark surface, and every series is legend-labelled so identity is never colour-alone.
  • Responsive sidebar β€” under 860px the sticky nav becomes an off-canvas drawer (hamburger, backdrop, Escape, auto-close on tab select). KPIs, cards, and header chips reflow at 860px/560px instead of overflowing. Motion respects prefers-reduced-motion.
  • Table polish β€” sticky uppercase headers, zebra rows, tabular-aligned numeric columns, hover row highlight with an accent edge marker, and horizontal scroll confined to the card instead of the page.

πŸ”§ Changed

  • WireDoctorStartupListener (ApplicationListener) β†’ WireDoctorEnvironmentPostProcessor (EnvironmentPostProcessor) + WireDoctorBufferingApplicationStartup (extends BufferingApplicationStartup). The processor now runs before context preparation, so ApplicationStartup is installed at the correct lifecycle phase; the subclass keeps Actuator /actuator/startup type checks intact.
  • beanThreadMap is now Map<String, Set<String>>, so a prototype instantiated on several threads keeps all of them instead of overwriting.

πŸ› Fixed

  • Politeness contract restored. The processor only replaces ApplicationStartup.DEFAULT or null. A plain BufferingApplicationStartup is transparently upgraded (same type, extra thread tagging); a foreign implementation is never overwritten β€” threadDistribution is omitted with a warning and every other analysis still runs.
  • Compat fix: a new test used JsonNode.values(), which only exists on the Jackson shipped with Boot 3.5+/4.x. Now iterates JsonNode directly, which works on Jackson 2.x and 3.x alike.
  • Report layout: fan-in counts no longer wrap one digit per line; two-column rows no longer stretch the shorter card.
  • Docs: five false claims removed from docs/thread-distribution.md (including a non-existent donut hover interaction and invented task-1/task-2 thread names); wiredoctor.trend.history-size corrected to wiredoctor.trend-history-size.

βœ… Verification

  • 253 tests green β€” 242 autoconfigure + 11 actuator, 0 failures. 7 new tests cover trend-history carry-forward and capping, thread tagging, and the EnvironmentPostProcessor politeness paths.
  • Compatibility matrix green across Boot 2.7.18 / 3.3.13 / 3.5.15 / 4.0.1 on Java 17 / 21 / 25 (12 combinations).
  • Report UI checked at 1440px / 430px / 390px on every tab.

πŸ“¦ Install

<dependency>
    <groupId>io.github.ddsha441981</groupId>
    <artifactId>wiredoctor-spring-boot-starter</artifactId>
    <version>1.1.0</version>
</dependency>

πŸ“š Documentation

Full docs are live at https://ddsha441981.github.io/wiredoctor/ β€” including the two new v1.1.0 guides, Thread Distribution and Startup Time Trend.

A set of links on the docs site was broken at the moment this release was published and was fixed immediately after in #42: doc-to-doc links pointed at .md, which serves raw Markdown source instead of the rendered page, and the sample-report link 404'd because sample/ lives at the repo root while Pages publishes only docs/. The start.spring.io sample report is now published and browsable: open it live.

Full changelog: v1.0.0...v1.1.0