# 07 — Failure modes ← prev [06 — The classpath-scan tax](06-the-classpath-scan-tax.md) · next → [08 — What actually helps](08-what-actually-helps.md) --- ## The buffer truncates silently `BufferingApplicationStartup` takes a capacity in its only constructor. Every guide picks 2048. Nothing tells you what happens when the application produces more steps than that — so here it is, with 5,000 extra `@Component` classes on the classpath ([`docs/output/06-buffer-overflow.txt`](output/06-buffer-overflow.txt)): ``` --- capacity 2048 --- started, recorded steps: 2048 --- capacity 16384 --- started, recorded steps: 5400 ``` The endpoint returns `200` with a well-formed answer. `grep -ic buffer app.log` returns **0**. There is no warning and no exception. It is worse than losing a suffix. Steps are recorded **when they end**, so the buffer keeps the *first* ones to finish — the innermost leaves — and drops the enclosing ones: ``` first 3: ['spring.boot.application.starting', 'spring.boot.application.environment-prepared', 'spring.boot.application.context-prepared'] last 3 : ['spring.beans.instantiate', ...] last tags: [{'key': 'beanName', 'value': 'bulk2718'}] ``` `spring.context.refresh`, `spring.boot.application.started` and `spring.boot.application.ready` are absent from the truncated timeline. Any self-time calculation is now missing its outer frame, and any "the whole refresh took N" figure is simply not in the data. The capacity is the number of **steps**, not beans, and this application records slightly more than one step per bean. 16384 costs a few megabytes that are freed on the first drain. Pick the large number. `aFullBufferTruncatesSilentlyAndLosesTheOuterSteps` in [`StartupTimelineContractTests`](../src/test/java/com/ankurm/startup/StartupTimelineContractTests.java) pins this behaviour. ## `POST` destroys the recording Covered in [chapter 02](02-turning-instrumentation-on.md), repeated here because it is the most expensive one: `POST /actuator/startup` drains. The second call returns an empty timeline, and so does every subsequent `GET`. If you are following a guide that pipes the `POST` into `jq` and the `jq` expression is wrong, restart the application — the data is gone. ## Injecting the concrete type ```java public StartupDiagnosticsEndpoint(BufferingApplicationStartup startup) { ... } ``` compiles, works in every test, and stops the application from starting the first time somebody runs it without `-Dstartup.tracking=buffering`, because the registered singleton is then a `DefaultApplicationStartup`. Inject the interface, narrow with `instanceof`, and answer honestly when tracking is off. ## Killing the app by its main class Not a Spring problem, but it cost a run while producing this repository. `pkill -f StartupDiagnosisApplication` also matches the shell that is running the script that contains that string, and kills it. [`scripts/stop.sh`](../scripts/stop.sh) matches on the executable being `java` **and** the jar name: ```bash ps -eo pid=,comm=,args= | awk '$2 == "java" && /startup-diagnosis-1\.0\.0\.jar/ { print $1 }' ``` ## Trusting a single run Startup timings on a shared or containerised machine move by several hundred milliseconds between runs of the same jar — see the per-run columns in [`docs/output/05-what-helps.txt`](output/05-what-helps.txt), where the same configuration produced 6.456 s and 7.473 s. Take a median of four or more, and treat any difference under about 10% as noise. Every number in this repository that matters is a ratio between two such medians, not a single measurement. --- ← prev [06 — The classpath-scan tax](06-the-classpath-scan-tax.md) · next → [08 — What actually helps](08-what-actually-helps.md)