Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
4ad21d9
add github action to check bean instantiations on startup
tobias-lippert Jun 28, 2025
4dfc909
always run on pull request for easier testing
tobias-lippert Jun 28, 2025
6950b79
improve naming
tobias-lippert Jun 28, 2025
74bde6d
avoid webapp build
tobias-lippert Jun 28, 2025
27492e2
avoid webapp build
tobias-lippert Jun 28, 2025
8664c06
dump complete log
tobias-lippert Jun 28, 2025
dc00e85
dump complete log
tobias-lippert Jun 28, 2025
c7276d2
fix boot run command
tobias-lippert Jun 28, 2025
73511b1
fix boot run command
tobias-lippert Jun 28, 2025
6e1297e
extend bean instantiation tracer
tobias-lippert Jun 28, 2025
da9b8da
try to fix different issues
tobias-lippert Jun 28, 2025
3cae3fa
fix build command
tobias-lippert Jun 28, 2025
67d1208
fix disabling liquibase
tobias-lippert Jun 28, 2025
f68210a
fix disabling liquibase
tobias-lippert Jun 28, 2025
32d7721
fix health check
tobias-lippert Jun 28, 2025
a3456f6
fix health check
tobias-lippert Jun 28, 2025
1cd6cc8
do not suppress curl output
tobias-lippert Jun 29, 2025
90aeb68
fix health check
tobias-lippert Jun 29, 2025
b1c4e33
allow access to health/liveness and health/readiness
tobias-lippert Jun 29, 2025
4533ce1
revert SecurityConfiguration change
tobias-lippert Jun 29, 2025
47fa1db
fix application started check
tobias-lippert Jun 29, 2025
8180608
fix application started check
tobias-lippert Jun 29, 2025
87ceafc
improve metrics validation
tobias-lippert Jun 29, 2025
2b26406
improve metrics validation
tobias-lippert Jun 29, 2025
802535d
Merge branch 'develop' into chore/test-instantiations-on-startup
tobias-lippert Jul 19, 2025
a1c21b8
Merge branch 'develop' into chore/test-instantiations-on-startup
tobias-lippert Jul 29, 2025
4c7c12a
improve gh action
tobias-lippert Jul 29, 2025
03e4ab7
simplify liquibase handling
tobias-lippert Jul 29, 2025
c4ccc21
improve bean instantiations check
tobias-lippert Jul 29, 2025
03244ae
fix check and log message
tobias-lippert Aug 1, 2025
e1ac1e4
fix gh action
tobias-lippert Aug 1, 2025
33e6985
fix message
tobias-lippert Aug 1, 2025
29c6ed5
debug print entire log
tobias-lippert Aug 1, 2025
ddb972d
increase timeout
tobias-lippert Aug 1, 2025
1724156
debug log
tobias-lippert Aug 1, 2025
533b9f4
debug log
tobias-lippert Aug 1, 2025
bd285ba
fix liquibase for h2
tobias-lippert Aug 1, 2025
3c58f8a
always enable liquibase
tobias-lippert Aug 1, 2025
e9c7324
reduce thresholds
tobias-lippert Aug 1, 2025
e4068b6
cleanup
tobias-lippert Aug 1, 2025
995a34f
cleanup
tobias-lippert Aug 1, 2025
a8b1b22
improve naming
tobias-lippert Aug 1, 2025
47ab84a
adapt threshold
tobias-lippert Aug 1, 2025
757d8e6
add threshold consistency check
tobias-lippert Aug 1, 2025
05da9d4
test threshold violation
tobias-lippert Aug 1, 2025
1149c8e
do not exit immediately
tobias-lippert Aug 1, 2025
8d1f1d0
fix threshold
tobias-lippert Aug 1, 2025
9750fa7
test deferred violation
tobias-lippert Aug 1, 2025
62dfa1a
fix gh action
tobias-lippert Aug 1, 2025
3c3385d
use correct thresholds again
tobias-lippert Aug 2, 2025
6311084
use correct condition again
tobias-lippert Aug 2, 2025
01963d2
Merge branch 'develop' into chore/test-instantiations-on-startup
tobias-lippert Aug 2, 2025
153e4c0
better naming in DatabaseMigration
tobias-lippert Aug 4, 2025
4ace040
be more helpful
tobias-lippert Aug 4, 2025
4d100d6
code rabbit
tobias-lippert Aug 4, 2025
17ebc29
fix gh action
tobias-lippert Aug 6, 2025
bbe7edb
Merge branch 'develop' into chore/test-instantiations-on-startup
tobias-lippert Aug 6, 2025
a7df261
better error message
tobias-lippert Aug 6, 2025
c9dfdc4
Merge branch 'develop' into chore/test-instantiations-on-startup
tobias-lippert Aug 12, 2025
c351bbb
fix gh action
tobias-lippert Aug 12, 2025
e01e1cf
upload artifacts
tobias-lippert Aug 12, 2025
f077a70
reset data before deferred init
tobias-lippert Aug 12, 2025
de2f70b
Merge branch 'develop' into chore/test-instantiations-on-startup
tobias-lippert Aug 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 334 additions & 0 deletions .github/workflows/bean-instantiations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
name: Check Bean Instantiations on Startup and with deferred eager Initialization

on:
push:
branches: [ develop ]
paths:
- src/main/java/**
pull_request:
paths:
- src/main/java/**

jobs:
bean-instantiation-check:
runs-on: ubuntu-latest

env:
MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH: 9
MAX_DEFERRED_CHAIN_LENGTH: 16
MIN_INSTANTIATED_BEANS: 20
MAX_INSTANTIATED_BEANS: 96
MIN_DEFERRED_CHAIN_LENGTH: 1

steps:
- name: Check out code
uses: actions/checkout@v4

- name: Validate threshold consistency between BeanInstantiationTracer and GitHub Action
shell: bash
run: |
set -Eeuo pipefail

echo "Validating that thresholds in BeanInstantiationTracer match GitHub Action values..."

echo "GitHub Action thresholds:"
echo " MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH = $MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH"
echo " MAX_DEFERRED_CHAIN_LENGTH = $MAX_DEFERRED_CHAIN_LENGTH"
echo " MIN_INSTANTIATED_BEANS = $MIN_INSTANTIATED_BEANS"
echo " MAX_INSTANTIATED_BEANS = $MAX_INSTANTIATED_BEANS"
echo " MIN_DEFERRED_CHAIN_LENGTH = $MIN_DEFERRED_CHAIN_LENGTH"

JAVA_FILE="src/main/java/de/tum/cit/aet/artemis/core/config/BeanInstantiationTracer.java"

if [[ ! -f "$JAVA_FILE" ]]; then
echo "❌ BeanInstantiationTracer.java not found at expected location: $JAVA_FILE"
echo "Searching for the file..."
find . -name "BeanInstantiationTracer.java" -type f || echo "File not found anywhere"
exit 1
fi

EXTRACT_NUMBER_REGEX='.*=\s*([0-9]+).*'

JAVA_MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH=$(grep -E "STARTUP_MAX_DEPENDENCY_CHAIN_THRESHOLD\s*=" "$JAVA_FILE" | sed -E "s/${EXTRACT_NUMBER_REGEX}/\1/" | head -1 || true)
JAVA_MAX_DEFERRED_CHAIN_LENGTH=$(grep -E "DEFERRED_INIT_MAX_DEPENDENCY_CHAIN_THRESHOLD\s*=" "$JAVA_FILE" | sed -E "s/${EXTRACT_NUMBER_REGEX}/\1/" | head -1 || true)

echo "Extracted thresholds from Java file:"
echo "STARTUP_MAX_DEPENDENCY_CHAIN_THRESHOLD = ${JAVA_MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH:-<empty>}"
echo "DEFERRED_INIT_MAX_DEPENDENCY_CHAIN_THRESHOLD = ${JAVA_MAX_DEFERRED_CHAIN_LENGTH:-<empty>}"

if [[ -z "${JAVA_MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH:-}" || -z "${JAVA_MAX_DEFERRED_CHAIN_LENGTH:-}" ]]; then
echo "❌ Failed to extract threshold values from Java file"
echo "Java file contents around threshold definitions:"
grep -A2 -B2 -E "(STARTUP_MAX_DEPENDENCY_CHAIN_THRESHOLD|DEFERRED_INIT_MAX_DEPENDENCY_CHAIN_THRESHOLD)" "$JAVA_FILE" || echo "Threshold constants not found"
exit 1
fi

THRESHOLD_MISMATCH=false

if [[ "$JAVA_MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH" != "$MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH" ]]; then
echo "❌ STARTUP_MAX_DEPENDENCY_CHAIN_THRESHOLD mismatch:"
echo " Java: $JAVA_MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH"
echo " GitHub Action: $MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH"
THRESHOLD_MISMATCH=true
fi

if [[ "$JAVA_MAX_DEFERRED_CHAIN_LENGTH" != "$MAX_DEFERRED_CHAIN_LENGTH" ]]; then
echo "❌ DEFERRED_INIT_MAX_DEPENDENCY_CHAIN_THRESHOLD mismatch:"
echo " Java: $JAVA_MAX_DEFERRED_CHAIN_LENGTH"
echo " GitHub Action: $MAX_DEFERRED_CHAIN_LENGTH"
THRESHOLD_MISMATCH=true
fi

if [[ "$THRESHOLD_MISMATCH" == true ]]; then
echo ""
echo "🔧 Please update either the Java constants or GitHub Action variables to match:"
echo " - Java file: $JAVA_FILE"
echo " - GitHub Action env variables at the job level"
exit 1
fi

echo "✅ All thresholds match between Java file and GitHub Action"

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'

- name: Build application
shell: bash
run: |
set -Eeuo pipefail
./gradlew clean bootJar -x test -x webapp

- name: Start Spring Boot app
shell: bash
run: |
set -Eeuo pipefail
PROFILES=dev,localci,lti,aeolus,theia,iris,localvc,artemis,scheduling,buildagent,core,ldap
JAR=$(ls build/libs/Artemis*.jar | head -n1)
nohup java -jar "$JAR" \
--spring.profiles.active="$PROFILES" \
--artemis.user-management.passkey.enabled=true \
--artemis.user-management.use-external=false \
--artemis.iris.url=http://iris.fake \
--artemis.iris.secret-token=token \
--spring.datasource.url="jdbc:h2:mem:mydb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" \
--spring.datasource.username=sa \
--spring.datasource.password= \
--eureka.client.enabled=false \
--aeolus.url=http://aeolus.fake \
> app.log 2>&1 &
echo $! > app.pid

- name: Wait for the application to start
shell: bash
run: |
set -Eeuo pipefail
RUNNING_MESSAGE="'Artemis' is running!"
STARTUP_TIMEOUT_ATTEMPTS=30
STARTUP_RETRY_INTERVAL=2
STARTUP_TOTAL_TIMEOUT=$((STARTUP_TIMEOUT_ATTEMPTS * STARTUP_RETRY_INTERVAL))
LOG_FILE="app.log"

echo "Waiting up to ${STARTUP_TOTAL_TIMEOUT}s for $RUNNING_MESSAGE to appear in $LOG_FILE..."

isAppRunning=false
for i in $(seq 1 "$STARTUP_TIMEOUT_ATTEMPTS"); do
if grep -q "$RUNNING_MESSAGE" "$LOG_FILE"; then
isAppRunning=true
echo "✅ Found $RUNNING_MESSAGE in $LOG_FILE after $i attempts"
break
fi
echo " attempt $i/$STARTUP_TIMEOUT_ATTEMPTS: not found yet"
sleep "$STARTUP_RETRY_INTERVAL"
done

if [[ "$isAppRunning" == false ]]; then
echo "❌ Timeout: $RUNNING_MESSAGE not found in $LOG_FILE"
cat "$LOG_FILE"
exit 1
fi

- name: Extract and validate startup bean instantiation metrics
shell: bash
run: |
set -Eeuo pipefail
# Use a safe, single-quoted regex and a simple search prefix
STARTUP_LOG_PATTERN='Bean instantiation graph exported to startupBeans\.dot \(([0-9]+) edges, longest dependency chain length: ([0-9]+)\)'
STARTUP_SEARCH='Bean instantiation graph exported to startupBeans\.dot'
LOG_FILE="app.log"

LINE=$(grep -E "$STARTUP_SEARCH" "$LOG_FILE" || true)
if [[ -z "$LINE" ]]; then
echo "❌ No startup metrics line found"
cat "$LOG_FILE"
exit 1
fi

if [[ "$LINE" =~ $STARTUP_LOG_PATTERN ]]; then
INSTANTIATED_BEANS="${BASH_REMATCH[1]}"
LONGEST_CHAIN_LENGTH="${BASH_REMATCH[2]}"
else
echo "❌ Failed to parse startup metrics from: $LINE"
exit 1
fi

echo "• Number of instantiated beans = $INSTANTIATED_BEANS"
echo "• Longest dependency chain length = $LONGEST_CHAIN_LENGTH"

echo "Validating against thresholds: MIN_INSTANTIATED_BEANS=$MIN_INSTANTIATED_BEANS (expected ≥ $MIN_INSTANTIATED_BEANS), MAX_INSTANTIATED_BEANS=$MAX_INSTANTIATED_BEANS (expected ≤ $MAX_INSTANTIATED_BEANS), MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH=$MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH (expected ≤ $MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH)"

STARTUP_VALIDATION_FAILED=false

if (( INSTANTIATED_BEANS < MIN_INSTANTIATED_BEANS )); then
echo "❌ $INSTANTIATED_BEANS < $MIN_INSTANTIATED_BEANS beans. Something seems to be wrong, as usually more beans are instantiated."
STARTUP_VALIDATION_FAILED=true
fi

if (( INSTANTIATED_BEANS > MAX_INSTANTIATED_BEANS )); then
echo "❌ $INSTANTIATED_BEANS > $MAX_INSTANTIATED_BEANS threshold"
Comment thread
tobias-lippert marked this conversation as resolved.
STARTUP_VALIDATION_FAILED=true
fi

if (( LONGEST_CHAIN_LENGTH > MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH )); then
echo "❌ Longest dependency chain length $LONGEST_CHAIN_LENGTH > $MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH threshold"
Comment thread
tobias-lippert marked this conversation as resolved.
STARTUP_VALIDATION_FAILED=true
fi

if [[ "$STARTUP_VALIDATION_FAILED" == false ]]; then
echo "✅ Final values → Beans: $INSTANTIATED_BEANS; Longest dependency chain length: $LONGEST_CHAIN_LENGTH"
echo "✅ Startup bean instantiation metrics within expected ranges"
fi

# Export variables for next step
{
echo "LONGEST_CHAIN_LENGTH=$LONGEST_CHAIN_LENGTH"
echo "STARTUP_VALIDATION_FAILED=$STARTUP_VALIDATION_FAILED"
} >> "$GITHUB_ENV"

- name: Check for startup dependency chains exceeding threshold
shell: bash
run: |
set -Eeuo pipefail
STARTUP_DEEP_CHAIN_PATTERN='Startup long bean instantiation chain'
LOG_FILE="app.log"

if (( LONGEST_CHAIN_LENGTH > MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH )); then
echo "❌ Startup dependency chain length $LONGEST_CHAIN_LENGTH > $MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH threshold"
echo "Chains exceeding threshold:"
(grep -E "$STARTUP_DEEP_CHAIN_PATTERN" "$LOG_FILE" || echo " (No detailed chains found in log)")
echo "🔧 These chains violate the threshold MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH. Please refactor them to break these chains."
Comment thread
tobias-lippert marked this conversation as resolved.
echo "You can find a visualization of the bean instantiation graph in the artifacts of this job in startupBeans.dot"
echo "You can visualize the file on http://webgraphviz.com/"
else
echo "✅ No startup dependency chains exceed the threshold of $MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH"
fi

if [[ "$STARTUP_VALIDATION_FAILED" == "true" ]]; then
exit 1
fi

- name: Extract and validate deferred eager bean chain length
shell: bash
run: |
set -Eeuo pipefail
DEFERRED_BEAN_PATTERN='Maximum dependency chain length during deferred eager init: ([0-9]+)'
DEFERRED_BEAN_SEARCH='Maximum dependency chain length during deferred eager init'
DEFERRED_TIMEOUT_ATTEMPTS=10
DEFERRED_RETRY_INTERVAL=12
DEFERRED_TOTAL_TIMEOUT=$((DEFERRED_TIMEOUT_ATTEMPTS * DEFERRED_RETRY_INTERVAL))
LOG_FILE="app.log"

echo "Waiting for deferred eager bean initialization to complete..."

DEFERRED_LINE=""
for i in $(seq 1 "$DEFERRED_TIMEOUT_ATTEMPTS"); do
DEFERRED_LINE=$(grep -E "$DEFERRED_BEAN_SEARCH" "$LOG_FILE" 2>/dev/null || true)
if [[ -n "$DEFERRED_LINE" ]]; then
echo "✅ Found deferred eager bean initialization log after $i attempts"
break
fi
echo " attempt $i/$DEFERRED_TIMEOUT_ATTEMPTS: deferred eager bean initialization not completed yet"
sleep "$DEFERRED_RETRY_INTERVAL"
done

if [[ -z "$DEFERRED_LINE" ]]; then
echo "❌ No deferred eager bean chain length line found after ${DEFERRED_TOTAL_TIMEOUT}s"
cat "$LOG_FILE"
exit 1
fi

if [[ "$DEFERRED_LINE" =~ $DEFERRED_BEAN_PATTERN ]]; then
DEFERRED_CHAIN_LENGTH="${BASH_REMATCH[1]}"
else
echo "❌ Failed to parse deferred eager bean chain length from: $DEFERRED_LINE"
exit 1
fi

echo "• Maximum dependency chain length during deferred eager bean initialization = $DEFERRED_CHAIN_LENGTH"
echo "Validating against thresholds: MIN_DEFERRED_CHAIN_LENGTH=$MIN_DEFERRED_CHAIN_LENGTH (expected ≥ $MIN_DEFERRED_CHAIN_LENGTH), MAX_DEFERRED_CHAIN_LENGTH=$MAX_DEFERRED_CHAIN_LENGTH (expected ≤ $MAX_DEFERRED_CHAIN_LENGTH)"

DEFERRED_VALIDATION_FAILED=false

if (( DEFERRED_CHAIN_LENGTH < MIN_DEFERRED_CHAIN_LENGTH )); then
echo "❌ Deferred chain length $DEFERRED_CHAIN_LENGTH < $MIN_DEFERRED_CHAIN_LENGTH. Something seems to be wrong, as usually some deferred beans are instantiated."
Comment thread
tobias-lippert marked this conversation as resolved.
echo "Was the deferred eager bean initialization removed? If so, you have to adapt this Github Action."
DEFERRED_VALIDATION_FAILED=true
fi

if (( DEFERRED_CHAIN_LENGTH > MAX_DEFERRED_CHAIN_LENGTH )); then
echo "❌ Deferred chain length $DEFERRED_CHAIN_LENGTH > $MAX_DEFERRED_CHAIN_LENGTH threshold"
Comment thread
tobias-lippert marked this conversation as resolved.
DEFERRED_VALIDATION_FAILED=true
fi

if [[ "$DEFERRED_VALIDATION_FAILED" == false ]]; then
echo "✅ Deferred eager bean chain length: $DEFERRED_CHAIN_LENGTH"
echo "✅ Deferred eager bean dependency chain length within expected ranges"
fi

{
echo "DEFERRED_CHAIN_LENGTH=$DEFERRED_CHAIN_LENGTH"
echo "DEFERRED_VALIDATION_FAILED=$DEFERRED_VALIDATION_FAILED"
} >> "$GITHUB_ENV"

- name: Check for deferred dependency chains exceeding threshold
shell: bash
run: |
set -Eeuo pipefail
DEFERRED_LONG_CHAIN_PATTERN='Deferred long bean instantiation chain'
LOG_FILE="app.log"

echo "Checking for deferred dependency chains exceeding threshold..."

if (( DEFERRED_CHAIN_LENGTH > MAX_DEFERRED_CHAIN_LENGTH )); then
echo "❌ Deferred dependency chain length $DEFERRED_CHAIN_LENGTH > $MAX_DEFERRED_CHAIN_LENGTH threshold"
echo "Chains exceeding threshold:"
(grep -E "$DEFERRED_LONG_CHAIN_PATTERN" "$LOG_FILE" || echo " (No detailed chains found in log)")
echo "🔧 These chains violate the threshold MAX_DEFERRED_CHAIN_LENGTH. Please refactor them to break these chains."
Comment thread
tobias-lippert marked this conversation as resolved.
echo "You can find a visualization of the bean instantiation graph in in the artifacts of this job in deferredEagerBeanInstantiationViolations.dot."
echo "You can visualize the file on http://webgraphviz.com/"
else
echo "✅ No deferred dependency chains exceed the threshold of $MAX_DEFERRED_CHAIN_LENGTH"
fi

if [[ "$DEFERRED_VALIDATION_FAILED" == "true" ]]; then
exit 1
fi

- name: Stop application
if: always()
shell: bash
run: |
set -Eeuo pipefail
kill "$(cat app.pid)" || true

- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: bean-instantiation-graphs-and-logs
path: |
startupBeans.dot
deferredEagerBeanInstantiationViolations.dot
app.log
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,5 @@ data-exports/
/legal
/src/test/resources/config/application-local.yml
startupBeans.dot
deferredEagerBeanInstantiationViolations.dot
/junit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package de.tum.cit.aet.artemis.core;

public record DeferredEagerBeanInitializationCompletedEvent() {
}
Loading
Loading