Skip to content
Merged
Show file tree
Hide file tree
Changes from 56 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
298 changes: 298 additions & 0 deletions .github/workflows/bean-instantiations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
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: 10
MIN_INSTANTIATED_BEANS: 20
MAX_INSTANTIATED_BEANS: 92
MIN_DEFERRED_CHAIN_LENGTH: 1

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

- name: Validate threshold consistency between BeanInstantiationTracer and GitHub Action
run: |
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)
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)

echo "Extracted thresholds from Java file:"
echo "STARTUP_MAX_DEPENDENCY_CHAIN_THRESHOLD = $JAVA_MAX_STARTUP_DEPENDENCY_CHAIN_LENGTH"
echo "DEFERRED_INIT_MAX_DEPENDENCY_CHAIN_THRESHOLD = $JAVA_MAX_DEFERRED_CHAIN_LENGTH"

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
run: ./gradlew clean bootJar -x test -x webapp

- name: Start Spring Boot app
run: |
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
run: |
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
run: |
STARTUP_LOG_PATTERN="Bean instantiation graph exported to startupBeans\.dot \(([0-9]+) edges, longest dependency chain length: ([0-9]+)\)"
LOG_FILE="app.log"

# Extract search text from pattern (everything before opening parenthesis)
STARTUP_SEARCH=$(echo "$STARTUP_LOG_PATTERN" | sed 's/ \\(.*$//')

LINE=$(grep -E "$STARTUP_SEARCH" $LOG_FILE) \
|| { echo "❌ No startup metrics line found"; cat $LOG_FILE; exit 1; }

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" >> $GITHUB_ENV
echo "STARTUP_VALIDATION_FAILED=$STARTUP_VALIDATION_FAILED" >> $GITHUB_ENV

- name: Check for startup dependency chains exceeding threshold
run: |
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 startupBeans.dot when starting the application locally."
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
run: |
DEFERRED_BEAN_PATTERN="Maximum dependency chain length during deferred eager init: ([0-9]+)"
DEFERRED_TIMEOUT_ATTEMPTS=10
DEFERRED_RETRY_INTERVAL=12
DEFERRED_TOTAL_TIMEOUT=$((DEFERRED_TIMEOUT_ATTEMPTS * DEFERRED_RETRY_INTERVAL))
LOG_FILE="app.log"

# Extract search text from pattern (everything before :)
DEFERRED_BEAN_SEARCH=$(echo "$DEFERRED_BEAN_PATTERN" | sed 's/:.*$//')

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)"

# Set validation flags instead of exiting immediately
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

# Export variables for next step
echo "DEFERRED_CHAIN_LENGTH=$DEFERRED_CHAIN_LENGTH" >> $GITHUB_ENV
echo "DEFERRED_VALIDATION_FAILED=$DEFERRED_VALIDATION_FAILED" >> $GITHUB_ENV

- name: Check for deferred dependency chains exceeding threshold
run: |
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 deferredEagerBeanInstantiationViolations.dot when starting the application locally."
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()
run: kill $(<app.pid) || true
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